psx - big update from upstream (0.9.36.5 -> 0.9.37-UNSTABLE)
This commit is contained in:
parent
9dea7cf0b0
commit
bcf053d0e9
Binary file not shown.
|
@ -20,7 +20,6 @@
|
|||
<ClCompile Include="..\emuware\emuware.cpp" />
|
||||
<ClCompile Include="..\emuware\EW_state.cpp" />
|
||||
<ClCompile Include="..\endian.cpp" />
|
||||
<ClCompile Include="..\file.cpp" />
|
||||
<ClCompile Include="..\octoshock.cpp" />
|
||||
<ClCompile Include="..\psx\cdc.cpp" />
|
||||
<ClCompile Include="..\psx\cpu.cpp" />
|
||||
|
@ -28,6 +27,9 @@
|
|||
<ClCompile Include="..\psx\dma.cpp" />
|
||||
<ClCompile Include="..\psx\frontio.cpp" />
|
||||
<ClCompile Include="..\psx\gpu.cpp" />
|
||||
<ClCompile Include="..\psx\gpu_line.cpp" />
|
||||
<ClCompile Include="..\psx\gpu_polygon.cpp" />
|
||||
<ClCompile Include="..\psx\gpu_sprite.cpp" />
|
||||
<ClCompile Include="..\psx\gte.cpp" />
|
||||
<ClCompile Include="..\psx\input\dualanalog.cpp" />
|
||||
<ClCompile Include="..\psx\input\dualshock.cpp" />
|
||||
|
@ -44,7 +46,6 @@
|
|||
<ClCompile Include="..\psx\sio.cpp" />
|
||||
<ClCompile Include="..\psx\spu.cpp" />
|
||||
<ClCompile Include="..\psx\timer.cpp" />
|
||||
<ClCompile Include="..\state.cpp" />
|
||||
<ClCompile Include="..\Stream.cpp" />
|
||||
<ClCompile Include="..\video\Deinterlacer.cpp" />
|
||||
<ClCompile Include="..\video\surface.cpp" />
|
||||
|
@ -60,9 +61,7 @@
|
|||
<ClInclude Include="..\emuware\msvc\stdint.h" />
|
||||
<ClInclude Include="..\endian.h" />
|
||||
<ClInclude Include="..\error.h" />
|
||||
<ClInclude Include="..\file.h" />
|
||||
<ClInclude Include="..\git.h" />
|
||||
<ClInclude Include="..\masmem.h" />
|
||||
<ClInclude Include="..\math_ops.h" />
|
||||
<ClInclude Include="..\octoshock.h" />
|
||||
<ClInclude Include="..\psx\cdc.h" />
|
||||
|
@ -82,23 +81,25 @@
|
|||
<ClInclude Include="..\psx\input\multitap.h" />
|
||||
<ClInclude Include="..\psx\input\negcon.h" />
|
||||
<ClInclude Include="..\psx\irq.h" />
|
||||
<ClInclude Include="..\psx\masmem.h" />
|
||||
<ClInclude Include="..\psx\mdec.h" />
|
||||
<ClInclude Include="..\psx\psx.h" />
|
||||
<ClInclude Include="..\psx\sio.h" />
|
||||
<ClInclude Include="..\psx\spu.h" />
|
||||
<ClInclude Include="..\psx\timer.h" />
|
||||
<ClInclude Include="..\state.h" />
|
||||
<ClInclude Include="..\Stream.h" />
|
||||
<ClInclude Include="..\video\Deinterlacer.h" />
|
||||
<ClInclude Include="..\video\surface.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\psx\cpu_bigswitch.inc" />
|
||||
<None Include="..\psx\cpu_computedgoto.inc" />
|
||||
<None Include="..\psx\gpu_command_table.inc" />
|
||||
<None Include="..\psx\gpu_common.inc" />
|
||||
<None Include="..\psx\gpu_line.inc" />
|
||||
<None Include="..\psx\gpu_polygon.inc" />
|
||||
<None Include="..\psx\gpu_sprite.inc" />
|
||||
<None Include="..\psx\spu_fir_table.inc" />
|
||||
<None Include="..\psx\spu_nft.inc" />
|
||||
<None Include="..\psx\spu_reverb.inc" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
|
|
|
@ -33,10 +33,6 @@
|
|||
<ClCompile Include="..\psx\gte.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gpu.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\file.cpp" />
|
||||
<ClCompile Include="..\psx\spu.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
|
@ -62,7 +58,6 @@
|
|||
<ClCompile Include="..\psx\timer.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\state.cpp" />
|
||||
<ClCompile Include="..\endian.cpp" />
|
||||
<ClCompile Include="..\cdrom\lec.cpp">
|
||||
<Filter>cdrom</Filter>
|
||||
|
@ -122,6 +117,18 @@
|
|||
<ClCompile Include="..\emuware\EW_state.cpp">
|
||||
<Filter>emuware</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gpu.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gpu_line.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gpu_polygon.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\psx\gpu_sprite.cpp">
|
||||
<Filter>psx</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\psx\cdc.h">
|
||||
|
@ -139,14 +146,8 @@
|
|||
<ClInclude Include="..\psx\gte.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\state.h" />
|
||||
<ClInclude Include="..\masmem.h" />
|
||||
<ClInclude Include="..\math_ops.h" />
|
||||
<ClInclude Include="..\psx\gpu.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\git.h" />
|
||||
<ClInclude Include="..\file.h" />
|
||||
<ClInclude Include="..\octoshock.h" />
|
||||
<ClInclude Include="..\psx\spu.h">
|
||||
<Filter>psx</Filter>
|
||||
|
@ -229,11 +230,26 @@
|
|||
<ClInclude Include="..\cdrom\SimpleFIFO.h">
|
||||
<Filter>cdrom</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\gpu.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\psx\masmem.h">
|
||||
<Filter>psx</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\psx\spu_fir_table.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\spu_reverb.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\gpu_command_table.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\gpu_common.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\gpu_line.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
|
@ -243,13 +259,10 @@
|
|||
<None Include="..\psx\gpu_sprite.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\spu_fir_table.inc">
|
||||
<None Include="..\psx\cpu_computedgoto.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\spu_nft.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
<None Include="..\psx\spu_reverb.inc">
|
||||
<None Include="..\psx\cpu_bigswitch.inc">
|
||||
<Filter>psx</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
|
|
@ -21,6 +21,51 @@ typedef unsigned __int32 uint32;
|
|||
typedef unsigned __int16 uint16;
|
||||
typedef unsigned __int8 uint8;
|
||||
|
||||
#define final
|
||||
#define noexcept
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
//http://stackoverflow.com/questions/355967/how-to-use-msvc-intrinsics-to-get-the-equivalent-of-this-gcc-code
|
||||
//if needed
|
||||
//uint32_t __inline ctz( uint32_t value )
|
||||
//{
|
||||
// DWORD trailing_zero = 0;
|
||||
//
|
||||
// if ( _BitScanForward( &trailing_zero, value ) )
|
||||
// {
|
||||
// return trailing_zero;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // This is undefined, I better choose 32 than 0
|
||||
// return 32;
|
||||
// }
|
||||
//}
|
||||
|
||||
uint32 __inline __builtin_clz( uint32_t value )
|
||||
{
|
||||
unsigned long leading_zero = 0;
|
||||
|
||||
if ( _BitScanReverse( &leading_zero, value ) )
|
||||
{
|
||||
return 31 - leading_zero;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Same remarks as above
|
||||
return 32;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//#if MDFN_GCC_VERSION >= MDFN_MAKE_GCCV(4,7,0)
|
||||
// #define MDFN_ASSUME_ALIGNED(p, align) __builtin_assume_aligned((p), (align))
|
||||
//#else
|
||||
// #define MDFN_ASSUME_ALIGNED(p, align) (p)
|
||||
//#endif
|
||||
#define MDFN_ASSUME_ALIGNED(p, align) (p)
|
||||
|
||||
//#define MDFN_WARN_UNUSED_RESULT __attribute__ ((warn_unused_result))
|
||||
#define MDFN_WARN_UNUSED_RESULT
|
||||
|
||||
|
@ -53,6 +98,15 @@ typedef unsigned __int8 uint8;
|
|||
#define TRUE_1 1
|
||||
#define FALSE_0 0
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
//taken from winnt.h
|
||||
extern "C++" // templates cannot be declared to have 'C' linkage
|
||||
template <typename T, size_t N>
|
||||
char (*BLAHBLAHBLAH( UNALIGNED T (&)[N] ))[N];
|
||||
|
||||
#define ARRAY_SIZE(A) (sizeof(*BLAHBLAHBLAH(A)))
|
||||
#endif
|
||||
|
||||
//------------alignment macros-------------
|
||||
//dont apply these to types without further testing. it only works portably here on declarations of variables
|
||||
//cant we find a pattern other people use more successfully?
|
||||
|
|
|
@ -1,246 +1,151 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* 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 of the License, 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
|
||||
*/
|
||||
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* 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 of the License, 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 <stdio.h>
|
||||
#include "octoshock.h"
|
||||
#include "endian.h"
|
||||
|
||||
void Endian_A16_Swap(void *src, uint32 nelements)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp = nsrc[i * 2];
|
||||
|
||||
nsrc[i * 2] = nsrc[i * 2 + 1];
|
||||
nsrc[i * 2 + 1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_A32_Swap(void *src, uint32 nelements)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp1 = nsrc[i * 4];
|
||||
uint8 tmp2 = nsrc[i * 4 + 1];
|
||||
|
||||
nsrc[i * 4] = nsrc[i * 4 + 3];
|
||||
nsrc[i * 4 + 1] = nsrc[i * 4 + 2];
|
||||
|
||||
nsrc[i * 4 + 2] = tmp2;
|
||||
nsrc[i * 4 + 3] = tmp1;
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_A64_Swap(void *src, uint32 nelements)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 *base = &nsrc[i * 8];
|
||||
|
||||
for(int z = 0; z < 4; z++)
|
||||
{
|
||||
uint8 tmp = base[z];
|
||||
|
||||
base[z] = base[7 - z];
|
||||
base[7 - z] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_A16_NE_to_LE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
Endian_A16_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A32_NE_to_LE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
Endian_A32_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A64_NE_to_LE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
Endian_A64_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Endian_A16_LE_to_NE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp = nsrc[i * 2];
|
||||
|
||||
nsrc[i * 2] = nsrc[i * 2 + 1];
|
||||
nsrc[i * 2 + 1] = tmp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A16_BE_to_NE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef LSB_FIRST
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp = nsrc[i * 2];
|
||||
|
||||
nsrc[i * 2] = nsrc[i * 2 + 1];
|
||||
nsrc[i * 2 + 1] = tmp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Endian_A32_LE_to_NE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp1 = nsrc[i * 4];
|
||||
uint8 tmp2 = nsrc[i * 4 + 1];
|
||||
|
||||
nsrc[i * 4] = nsrc[i * 4 + 3];
|
||||
nsrc[i * 4 + 1] = nsrc[i * 4 + 2];
|
||||
|
||||
nsrc[i * 4 + 2] = tmp2;
|
||||
nsrc[i * 4 + 3] = tmp1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A64_LE_to_NE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 *base = &nsrc[i * 8];
|
||||
|
||||
for(int z = 0; z < 4; z++)
|
||||
{
|
||||
uint8 tmp = base[z];
|
||||
|
||||
base[z] = base[7 - z];
|
||||
base[7 - z] = tmp;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlipByteOrder(uint8 *src, uint32 count)
|
||||
{
|
||||
uint8 *start=src;
|
||||
uint8 *end=src+count-1;
|
||||
|
||||
if((count&1) || !count) return; /* This shouldn't happen. */
|
||||
|
||||
count >>= 1;
|
||||
|
||||
while(count--)
|
||||
{
|
||||
uint8 tmp;
|
||||
|
||||
tmp=*end;
|
||||
*end=*start;
|
||||
*start=tmp;
|
||||
end--;
|
||||
start++;
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_V_LE_to_NE(void *src, uint32 bytesize)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
FlipByteOrder((uint8 *)src, bytesize);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_V_NE_to_LE(void *src, uint32 bytesize)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
FlipByteOrder((uint8 *)src, bytesize);
|
||||
#endif
|
||||
}
|
||||
|
||||
int write16le(uint16 b, FILE *fp)
|
||||
{
|
||||
uint8 s[2];
|
||||
s[0]=b;
|
||||
s[1]=b>>8;
|
||||
return((fwrite(s,1,2,fp)<2)?0:2);
|
||||
}
|
||||
|
||||
int write32le(uint32 b, FILE *fp)
|
||||
{
|
||||
uint8 s[4];
|
||||
s[0]=b;
|
||||
s[1]=b>>8;
|
||||
s[2]=b>>16;
|
||||
s[3]=b>>24;
|
||||
return((fwrite(s,1,4,fp)<4)?0:4);
|
||||
}
|
||||
|
||||
int read32le(uint32 *Bufo, FILE *fp)
|
||||
{
|
||||
uint32 buf;
|
||||
if(fread(&buf,1,4,fp)<4)
|
||||
return 0;
|
||||
#ifdef LSB_FIRST
|
||||
*(uint32*)Bufo=buf;
|
||||
#else
|
||||
*(uint32*)Bufo=((buf&0xFF)<<24)|((buf&0xFF00)<<8)|((buf&0xFF0000)>>8)|((buf&0xFF000000)>>24);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read16le(char *d, FILE *fp)
|
||||
{
|
||||
#ifdef LSB_FIRST
|
||||
return((fread(d,1,2,fp)<2)?0:2);
|
||||
#else
|
||||
int ret;
|
||||
ret=fread(d+1,1,1,fp);
|
||||
ret+=fread(d,1,1,fp);
|
||||
return ret<2?0:2;
|
||||
#endif
|
||||
}
|
||||
|
||||
#include "octoshock.h"
|
||||
#include "endian.h"
|
||||
|
||||
void Endian_A16_Swap(void *src, uint32 nelements)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp = nsrc[i * 2];
|
||||
|
||||
nsrc[i * 2] = nsrc[i * 2 + 1];
|
||||
nsrc[i * 2 + 1] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_A32_Swap(void *src, uint32 nelements)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 tmp1 = nsrc[i * 4];
|
||||
uint8 tmp2 = nsrc[i * 4 + 1];
|
||||
|
||||
nsrc[i * 4] = nsrc[i * 4 + 3];
|
||||
nsrc[i * 4 + 1] = nsrc[i * 4 + 2];
|
||||
|
||||
nsrc[i * 4 + 2] = tmp2;
|
||||
nsrc[i * 4 + 3] = tmp1;
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_A64_Swap(void *src, uint32 nelements)
|
||||
{
|
||||
uint32 i;
|
||||
uint8 *nsrc = (uint8 *)src;
|
||||
|
||||
for(i = 0; i < nelements; i++)
|
||||
{
|
||||
uint8 *base = &nsrc[i * 8];
|
||||
|
||||
for(int z = 0; z < 4; z++)
|
||||
{
|
||||
uint8 tmp = base[z];
|
||||
|
||||
base[z] = base[7 - z];
|
||||
base[7 - z] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_A16_NE_LE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
Endian_A16_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A32_NE_LE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
Endian_A32_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A64_NE_LE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
Endian_A64_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
void Endian_A16_NE_BE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef LSB_FIRST
|
||||
Endian_A16_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A32_NE_BE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef LSB_FIRST
|
||||
Endian_A32_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_A64_NE_BE(void *src, uint32 nelements)
|
||||
{
|
||||
#ifdef LSB_FIRST
|
||||
Endian_A64_Swap(src, nelements);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void FlipByteOrder(uint8 *src, uint32 count)
|
||||
{
|
||||
uint8 *start=src;
|
||||
uint8 *end=src+count-1;
|
||||
|
||||
if((count&1) || !count) return; /* This shouldn't happen. */
|
||||
|
||||
count >>= 1;
|
||||
|
||||
while(count--)
|
||||
{
|
||||
uint8 tmp;
|
||||
|
||||
tmp=*end;
|
||||
*end=*start;
|
||||
*start=tmp;
|
||||
end--;
|
||||
start++;
|
||||
}
|
||||
}
|
||||
|
||||
void Endian_V_NE_LE(void *src, uint32 bytesize)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
FlipByteOrder((uint8 *)src, bytesize);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Endian_V_NE_BE(void *src, uint32 bytesize)
|
||||
{
|
||||
#ifdef LSB_FIRST
|
||||
FlipByteOrder((uint8 *)src, bytesize);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,209 +1,296 @@
|
|||
#ifndef __MDFN_ENDIAN_H
|
||||
#define __MDFN_ENDIAN_H
|
||||
|
||||
#include "octoshock.h"
|
||||
#pragma warning(once : 4519)
|
||||
static INLINE uint32 BitsExtract(const uint8* ptr, const size_t bit_offset, const size_t bit_count)
|
||||
{
|
||||
uint32 ret = 0;
|
||||
|
||||
#ifdef MSB_FIRST
|
||||
#ifdef LSB_FIRST
|
||||
#error Only define one of LSB_FIRST and MSB_FIRST
|
||||
#endif
|
||||
for(size_t x = 0; x < bit_count; x++)
|
||||
{
|
||||
size_t co = bit_offset + x;
|
||||
bool b = (ptr[co >> 3] >> (co & 7)) & 1;
|
||||
|
||||
#ifndef le32toh
|
||||
#define le32toh(l) ((((l)>>24) & 0xff) | (((l)>>8) & 0xff00) \
|
||||
| (((l)<<8) & 0xff0000) | (((l)<<24) & 0xff000000))
|
||||
#endif
|
||||
#ifndef le16toh
|
||||
#define le16toh(l) ((((l)>>8) & 0xff) | (((l)<<8) & 0xff00))
|
||||
#endif
|
||||
#else
|
||||
#ifndef le32toh
|
||||
#define le32toh(l) (l)
|
||||
#endif
|
||||
#ifndef le16toh
|
||||
#define le16toh(l) (l)
|
||||
#endif
|
||||
#endif
|
||||
ret |= (uint64)b << x;
|
||||
}
|
||||
|
||||
#ifndef htole32
|
||||
#define htole32 le32toh
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef htole16
|
||||
#define htole16 le16toh
|
||||
#endif
|
||||
static INLINE void BitsIntract(uint8* ptr, const size_t bit_offset, const size_t bit_count, uint32 value)
|
||||
{
|
||||
for(size_t x = 0; x < bit_count; x++)
|
||||
{
|
||||
size_t co = bit_offset + x;
|
||||
bool b = (value >> x) & 1;
|
||||
uint8 tmp = ptr[co >> 3];
|
||||
|
||||
tmp &= ~(1 << (co & 7));
|
||||
tmp |= b << (co & 7);
|
||||
|
||||
int write16le(uint16 b, FILE *fp);
|
||||
int write32le(uint32 b, FILE *fp);
|
||||
int read32le(uint32 *Bufo, FILE *fp);
|
||||
ptr[co >> 3] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Regarding safety of calling MDFN_*sb<true> on dynamically-allocated memory with new uint8[], see C++ standard 3.7.3.1(i.e. it should be
|
||||
safe provided the offsets into the memory are aligned/multiples of the MDFN_*sb access type). malloc()'d and calloc()'d
|
||||
memory should be safe as well.
|
||||
|
||||
Statically-allocated arrays/memory should be unioned with a big POD type or C++11 "alignas"'d. (May need to audit code to ensure
|
||||
this is being done).
|
||||
*/
|
||||
|
||||
void Endian_A16_Swap(void *src, uint32 nelements);
|
||||
void Endian_A32_Swap(void *src, uint32 nelements);
|
||||
void Endian_A64_Swap(void *src, uint32 nelements);
|
||||
|
||||
void Endian_A16_NE_to_LE(void *src, uint32 nelements);
|
||||
void Endian_A32_NE_to_LE(void *src, uint32 nelements);
|
||||
void Endian_A64_NE_to_LE(void *src, uint32 nelements);
|
||||
void Endian_A16_NE_LE(void *src, uint32 nelements);
|
||||
void Endian_A32_NE_LE(void *src, uint32 nelements);
|
||||
void Endian_A64_NE_LE(void *src, uint32 nelements);
|
||||
|
||||
void Endian_A16_LE_to_NE(void *src, uint32 nelements);
|
||||
void Endian_A16_BE_to_NE(void *src, uint32 nelements);
|
||||
void Endian_A32_LE_to_NE(void *src, uint32 nelements);
|
||||
void Endian_A64_LE_to_NE(void *src, uint32 nelements);
|
||||
void Endian_A16_NE_BE(void *src, uint32 nelements);
|
||||
void Endian_A32_NE_BE(void *src, uint32 nelements);
|
||||
void Endian_A64_NE_BE(void *src, uint32 nelements);
|
||||
|
||||
void Endian_V_LE_to_NE(void *src, uint32 bytesize);
|
||||
void Endian_V_NE_to_LE(void *src, uint32 bytesize);
|
||||
void Endian_V_NE_LE(void *src, uint32 bytesize);
|
||||
void Endian_V_NE_BE(void *src, uint32 bytesize);
|
||||
|
||||
void FlipByteOrder(uint8 *src, uint32 count);
|
||||
|
||||
// The following functions can encode/decode to unaligned addresses.
|
||||
|
||||
static INLINE void MDFN_en16lsb(uint8 *buf, uint16 morp)
|
||||
static INLINE uint16 MDFN_bswap16(uint16 v)
|
||||
{
|
||||
buf[0]=morp;
|
||||
buf[1]=morp>>8;
|
||||
return (v << 8) | (v >> 8);
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en24lsb(uint8 *buf, uint32 morp)
|
||||
static INLINE uint32 MDFN_bswap32(uint32 v)
|
||||
{
|
||||
buf[0]=morp;
|
||||
buf[1]=morp>>8;
|
||||
buf[2]=morp>>16;
|
||||
return (v << 24) | ((v & 0xFF00) << 8) | ((v >> 8) & 0xFF00) | (v >> 24);
|
||||
}
|
||||
|
||||
|
||||
static INLINE void MDFN_en32lsb(uint8 *buf, uint32 morp)
|
||||
static INLINE uint64 MDFN_bswap64(uint64 v)
|
||||
{
|
||||
buf[0]=morp;
|
||||
buf[1]=morp>>8;
|
||||
buf[2]=morp>>16;
|
||||
buf[3]=morp>>24;
|
||||
//octoshock edit
|
||||
//return (v << 56) | (v >> 56) | ((v & 0xFF00) << 40) | ((v >> 40) & 0xFF00) | ((uint64)MDFN_bswap32(v >> 16) << 16);
|
||||
return (v << 56) | (v >> 56) | ((v & 0xFF00) << 40) | ((v >> 40) & 0xFF00) | ((uint64)MDFN_bswap32(((uint32)v) >> 16) << 16);
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en64lsb(uint8 *buf, uint64 morp)
|
||||
#ifdef LSB_FIRST
|
||||
#define MDFN_ENDIANH_IS_BIGENDIAN 0
|
||||
#else
|
||||
#define MDFN_ENDIANH_IS_BIGENDIAN 1
|
||||
#endif
|
||||
|
||||
//
|
||||
// X endian.
|
||||
//
|
||||
template<int isbigendian, typename T, bool aligned>
|
||||
static INLINE T MDFN_deXsb(const void* ptr)
|
||||
{
|
||||
buf[0]=morp >> 0;
|
||||
buf[1]=morp >> 8;
|
||||
buf[2]=morp >> 16;
|
||||
buf[3]=morp >> 24;
|
||||
buf[4]=morp >> 32;
|
||||
buf[5]=morp >> 40;
|
||||
buf[6]=morp >> 48;
|
||||
buf[7]=morp >> 56;
|
||||
T tmp;
|
||||
|
||||
memcpy(&tmp, MDFN_ASSUME_ALIGNED(ptr, (aligned ? sizeof(T) : 1)), sizeof(T));
|
||||
|
||||
if(isbigendian != -1 && isbigendian != MDFN_ENDIANH_IS_BIGENDIAN)
|
||||
{
|
||||
static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Gummy penguins.");
|
||||
|
||||
if(sizeof(T) == 8)
|
||||
return (T)MDFN_bswap64(tmp);
|
||||
else if(sizeof(T) == 4)
|
||||
return (T)MDFN_bswap32(tmp);
|
||||
else if(sizeof(T) == 2)
|
||||
return (T)MDFN_bswap16(tmp);
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
static INLINE void MDFN_en16msb(uint8 *buf, uint16 morp)
|
||||
//
|
||||
// Native endian.
|
||||
//
|
||||
template<typename T, bool aligned = false>
|
||||
static INLINE T MDFN_densb(const void* ptr)
|
||||
{
|
||||
buf[0] = morp >> 8;
|
||||
buf[1] = morp;
|
||||
return MDFN_deXsb<-1, T, aligned>(ptr);
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en24msb(uint8 *buf, uint32 morp)
|
||||
//
|
||||
// Little endian.
|
||||
//
|
||||
template<typename T, bool aligned = false>
|
||||
static INLINE T MDFN_delsb(const void* ptr)
|
||||
{
|
||||
buf[0] = morp >> 16;
|
||||
buf[1] = morp >> 8;
|
||||
buf[2] = morp;
|
||||
return MDFN_deXsb<0, T, aligned>(ptr);
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en32msb(uint8 *buf, uint32 morp)
|
||||
template<bool aligned = false>
|
||||
static INLINE uint16 MDFN_de16lsb(const void* ptr)
|
||||
{
|
||||
buf[0] = morp >> 24;
|
||||
buf[1] = morp >> 16;
|
||||
buf[2] = morp >> 8;
|
||||
buf[3] = morp;
|
||||
return MDFN_delsb<uint16, aligned>(ptr);
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en64msb(uint8 *buf, uint64 morp)
|
||||
{
|
||||
buf[0] = morp >> 56;
|
||||
buf[1] = morp >> 48;
|
||||
buf[2] = morp >> 40;
|
||||
buf[3] = morp >> 32;
|
||||
buf[4] = morp >> 24;
|
||||
buf[5] = morp >> 16;
|
||||
buf[6] = morp >> 8;
|
||||
buf[7] = morp >> 0;
|
||||
}
|
||||
|
||||
|
||||
// Overloaded functions, yay.
|
||||
static INLINE void MDFN_enlsb(uint16 * buf, uint16 value)
|
||||
{
|
||||
MDFN_en16lsb((uint8 *)buf, value);
|
||||
}
|
||||
|
||||
static INLINE void MDFN_enlsb(uint32 * buf, uint32 value)
|
||||
{
|
||||
MDFN_en32lsb((uint8 *)buf, value);
|
||||
}
|
||||
|
||||
static INLINE void MDFN_enlsb(uint64 * buf, uint64 value)
|
||||
{
|
||||
MDFN_en64lsb((uint8 *)buf, value);
|
||||
}
|
||||
|
||||
|
||||
static INLINE uint16 MDFN_de16lsb(const uint8 *morp)
|
||||
{
|
||||
return(morp[0] | (morp[1] << 8));
|
||||
}
|
||||
|
||||
|
||||
static INLINE uint32 MDFN_de24lsb(const uint8 *morp)
|
||||
static INLINE uint32 MDFN_de24lsb(const void* ptr)
|
||||
{
|
||||
const uint8* morp = (const uint8*)ptr;
|
||||
return(morp[0]|(morp[1]<<8)|(morp[2]<<16));
|
||||
}
|
||||
|
||||
static INLINE uint32 MDFN_de32lsb(const uint8 *morp)
|
||||
template<bool aligned = false>
|
||||
static INLINE uint32 MDFN_de32lsb(const void* ptr)
|
||||
{
|
||||
return(morp[0]|(morp[1]<<8)|(morp[2]<<16)|(morp[3]<<24));
|
||||
return MDFN_delsb<uint32, aligned>(ptr);
|
||||
}
|
||||
|
||||
static INLINE uint64 MDFN_de64lsb(const uint8 *morp)
|
||||
template<bool aligned = false>
|
||||
static INLINE uint64 MDFN_de64lsb(const void* ptr)
|
||||
{
|
||||
uint64 ret = 0;
|
||||
|
||||
ret |= (uint64)morp[0];
|
||||
ret |= (uint64)morp[1] << 8;
|
||||
ret |= (uint64)morp[2] << 16;
|
||||
ret |= (uint64)morp[3] << 24;
|
||||
ret |= (uint64)morp[4] << 32;
|
||||
ret |= (uint64)morp[5] << 40;
|
||||
ret |= (uint64)morp[6] << 48;
|
||||
ret |= (uint64)morp[7] << 56;
|
||||
|
||||
return(ret);
|
||||
return MDFN_delsb<uint64, aligned>(ptr);
|
||||
}
|
||||
|
||||
static INLINE uint16 MDFN_delsb(const uint16 *buf)
|
||||
//
|
||||
// Big endian.
|
||||
//
|
||||
template<typename T, bool aligned = false>
|
||||
static INLINE T MDFN_demsb(const void* ptr)
|
||||
{
|
||||
return(MDFN_de16lsb((uint8 *)buf));
|
||||
return MDFN_deXsb<1, T, aligned>(ptr);
|
||||
}
|
||||
|
||||
static INLINE uint32 MDFN_delsb(const uint32 *buf)
|
||||
template<bool aligned = false>
|
||||
static INLINE uint16 MDFN_de16msb(const void* ptr)
|
||||
{
|
||||
return(MDFN_de32lsb((uint8 *)buf));
|
||||
return MDFN_demsb<uint16, aligned>(ptr);
|
||||
}
|
||||
|
||||
static INLINE uint64 MDFN_delsb(const uint64 *buf)
|
||||
{
|
||||
return(MDFN_de64lsb((uint8 *)buf));
|
||||
}
|
||||
|
||||
static INLINE uint16 MDFN_de16msb(const uint8 *morp)
|
||||
{
|
||||
return(morp[1] | (morp[0] << 8));
|
||||
}
|
||||
|
||||
static INLINE uint32 MDFN_de24msb(const uint8 *morp)
|
||||
static INLINE uint32 MDFN_de24msb(const void* ptr)
|
||||
{
|
||||
const uint8* morp = (const uint8*)ptr;
|
||||
return((morp[2]<<0)|(morp[1]<<8)|(morp[0]<<16));
|
||||
}
|
||||
|
||||
|
||||
static INLINE uint32 MDFN_de32msb(const uint8 *morp)
|
||||
template<bool aligned = false>
|
||||
static INLINE uint32 MDFN_de32msb(const void* ptr)
|
||||
{
|
||||
return(morp[3]|(morp[2]<<8)|(morp[1]<<16)|(morp[0]<<24));
|
||||
return MDFN_demsb<uint32, aligned>(ptr);
|
||||
}
|
||||
|
||||
template<bool aligned = false>
|
||||
static INLINE uint64 MDFN_de64msb(const void* ptr)
|
||||
{
|
||||
return MDFN_demsb<uint64, aligned>(ptr);
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
//
|
||||
// X endian.
|
||||
//
|
||||
template<int isbigendian, typename T, bool aligned>
|
||||
static INLINE void MDFN_enXsb(void* ptr, T value)
|
||||
{
|
||||
T tmp = value;
|
||||
|
||||
if(isbigendian != -1 && isbigendian != MDFN_ENDIANH_IS_BIGENDIAN)
|
||||
{
|
||||
static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4 || sizeof(T) == 8, "Gummy penguins.");
|
||||
|
||||
if(sizeof(T) == 8)
|
||||
tmp = (T)MDFN_bswap64(value);
|
||||
else if(sizeof(T) == 4)
|
||||
tmp = (T)MDFN_bswap32(value);
|
||||
else if(sizeof(T) == 2)
|
||||
tmp = (T)MDFN_bswap16(value);
|
||||
}
|
||||
|
||||
memcpy(MDFN_ASSUME_ALIGNED(ptr, (aligned ? sizeof(T) : 1)), &tmp, sizeof(T));
|
||||
}
|
||||
|
||||
//
|
||||
// Native endian.
|
||||
//
|
||||
template<typename T, bool aligned = false>
|
||||
static INLINE void MDFN_ennsb(void* ptr, T value)
|
||||
{
|
||||
MDFN_enXsb<-1, T, aligned>(ptr, value);
|
||||
}
|
||||
|
||||
//
|
||||
// Little endian.
|
||||
//
|
||||
template<typename T, bool aligned = false>
|
||||
static INLINE void MDFN_enlsb(void* ptr, T value)
|
||||
{
|
||||
MDFN_enXsb<0, T, aligned>(ptr, value);
|
||||
}
|
||||
|
||||
template<bool aligned = false>
|
||||
static INLINE void MDFN_en16lsb(void* ptr, uint16 value)
|
||||
{
|
||||
MDFN_enlsb<uint16, aligned>(ptr, value);
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en24lsb(void* ptr, uint32 value)
|
||||
{
|
||||
uint8* morp = (uint8*)ptr;
|
||||
|
||||
morp[0] = value;
|
||||
morp[1] = value >> 8;
|
||||
morp[2] = value >> 16;
|
||||
}
|
||||
|
||||
template<bool aligned = false>
|
||||
static INLINE void MDFN_en32lsb(void* ptr, uint32 value)
|
||||
{
|
||||
MDFN_enlsb<uint32, aligned>(ptr, value);
|
||||
}
|
||||
|
||||
template<bool aligned = false>
|
||||
static INLINE void MDFN_en64lsb(void* ptr, uint64 value)
|
||||
{
|
||||
MDFN_enlsb<uint64, aligned>(ptr, value);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Big endian.
|
||||
//
|
||||
template<typename T, bool aligned = false>
|
||||
static INLINE void MDFN_enmsb(void* ptr, T value)
|
||||
{
|
||||
MDFN_enXsb<1, T, aligned>(ptr, value);
|
||||
}
|
||||
|
||||
template<bool aligned = false>
|
||||
static INLINE void MDFN_en16msb(void* ptr, uint16 value)
|
||||
{
|
||||
MDFN_enmsb<uint16, aligned>(ptr, value);
|
||||
}
|
||||
|
||||
static INLINE void MDFN_en24msb(void* ptr, uint32 value)
|
||||
{
|
||||
uint8* morp = (uint8*)ptr;
|
||||
|
||||
morp[0] = value;
|
||||
morp[1] = value >> 8;
|
||||
morp[2] = value >> 16;
|
||||
}
|
||||
|
||||
template<bool aligned = false>
|
||||
static INLINE void MDFN_en32msb(void* ptr, uint32 value)
|
||||
{
|
||||
MDFN_enmsb<uint32, aligned>(ptr, value);
|
||||
}
|
||||
|
||||
template<bool aligned = false>
|
||||
static INLINE void MDFN_en64msb(void* ptr, uint64 value)
|
||||
{
|
||||
MDFN_enmsb<uint64, aligned>(ptr, value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,75 +1,75 @@
|
|||
#ifndef __MDFN_ERROR_H
|
||||
#define __MDFN_ERROR_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <exception>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
class ErrnoHolder;
|
||||
class MDFN_Error : public std::exception
|
||||
{
|
||||
public:
|
||||
|
||||
MDFN_Error() throw();
|
||||
|
||||
MDFN_Error(int errno_code_new, const char *format, ...) throw() MDFN_FORMATSTR(gnu_printf, 3, 4);
|
||||
MDFN_Error(const ErrnoHolder &enh);
|
||||
|
||||
~MDFN_Error() throw();
|
||||
|
||||
MDFN_Error(const MDFN_Error &ze_error) throw();
|
||||
MDFN_Error & operator=(const MDFN_Error &ze_error) throw();
|
||||
|
||||
virtual const char *what(void) const throw();
|
||||
int GetErrno(void) const throw();
|
||||
|
||||
private:
|
||||
|
||||
int errno_code;
|
||||
char *error_message;
|
||||
};
|
||||
|
||||
class ErrnoHolder
|
||||
{
|
||||
public:
|
||||
|
||||
ErrnoHolder()
|
||||
{
|
||||
//SetErrno(0);
|
||||
local_errno = 0;
|
||||
local_strerror[0] = 0;
|
||||
}
|
||||
|
||||
ErrnoHolder(int the_errno)
|
||||
{
|
||||
SetErrno(the_errno);
|
||||
}
|
||||
|
||||
inline int Errno(void) const
|
||||
{
|
||||
return(local_errno);
|
||||
}
|
||||
|
||||
const char *StrError(void) const
|
||||
{
|
||||
return(local_strerror);
|
||||
}
|
||||
|
||||
void operator=(int the_errno)
|
||||
{
|
||||
SetErrno(the_errno);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void SetErrno(int the_errno);
|
||||
|
||||
int local_errno;
|
||||
char local_strerror[256];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#ifndef __MDFN_ERROR_H
|
||||
#define __MDFN_ERROR_H
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <exception>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
class ErrnoHolder;
|
||||
class MDFN_Error : public std::exception
|
||||
{
|
||||
public:
|
||||
|
||||
MDFN_Error() noexcept;
|
||||
|
||||
MDFN_Error(int errno_code_new, const char *format, ...) noexcept MDFN_FORMATSTR(gnu_printf, 3, 4);
|
||||
MDFN_Error(const ErrnoHolder &enh);
|
||||
|
||||
~MDFN_Error() noexcept;
|
||||
|
||||
MDFN_Error(const MDFN_Error &ze_error) noexcept;
|
||||
MDFN_Error & operator=(const MDFN_Error &ze_error) noexcept;
|
||||
|
||||
virtual const char *what(void) const noexcept;
|
||||
int GetErrno(void) const noexcept;
|
||||
|
||||
private:
|
||||
|
||||
int errno_code;
|
||||
char *error_message;
|
||||
};
|
||||
|
||||
class ErrnoHolder
|
||||
{
|
||||
public:
|
||||
|
||||
ErrnoHolder()
|
||||
{
|
||||
//SetErrno(0);
|
||||
local_errno = 0;
|
||||
local_strerror[0] = 0;
|
||||
}
|
||||
|
||||
ErrnoHolder(int the_errno)
|
||||
{
|
||||
SetErrno(the_errno);
|
||||
}
|
||||
|
||||
inline int Errno(void) const
|
||||
{
|
||||
return(local_errno);
|
||||
}
|
||||
|
||||
const char *StrError(void) const
|
||||
{
|
||||
return(local_strerror);
|
||||
}
|
||||
|
||||
void operator=(int the_errno)
|
||||
{
|
||||
SetErrno(the_errno);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void SetErrno(int the_errno);
|
||||
|
||||
int local_errno;
|
||||
char local_strerror[256];
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -45,8 +45,6 @@ typedef enum
|
|||
GMT_PLAYER // Music player(NSF, HES, GSF)
|
||||
} GameMediumTypes;
|
||||
|
||||
#include "state.h"
|
||||
|
||||
#ifdef WANT_DEBUGGER
|
||||
// #ifdef WANT_DEBUGGER
|
||||
// typedef struct DebuggerInfoStruct;
|
||||
|
@ -276,7 +274,6 @@ typedef struct
|
|||
// Main save state routine, called by the save state code in state.cpp.
|
||||
// When saving, load is set to 0. When loading, load is set to the version field of the save state being loaded.
|
||||
// data_only is true when the save state data is temporary, such as being saved into memory for state rewinding.
|
||||
int (*StateAction)(StateMem *sm, int load, int data_only);
|
||||
|
||||
void (*Emulate)(EmulateSpecStruct *espec);
|
||||
void (*SetInput)(int port, const char *type, void *ptr);
|
||||
|
|
|
@ -1,209 +0,0 @@
|
|||
#ifndef __MDFN_PSX_MASMEM_H
|
||||
#define __MDFN_PSX_MASMEM_H
|
||||
|
||||
// TODO, WIP (big-endian stores and loads not fully supported yet)
|
||||
|
||||
#ifdef LSB_FIRST
|
||||
#define MAS_NATIVE_IS_BIGENDIAN 0
|
||||
#else
|
||||
#define MAS_NATIVE_IS_BIGENDIAN 1
|
||||
#endif
|
||||
|
||||
static INLINE uint16 LoadU16_RBO(const uint16 *a)
|
||||
{
|
||||
#ifdef ARCH_POWERPC
|
||||
uint16 tmp;
|
||||
|
||||
__asm__ ("lhbrx %0, %y1" : "=r"(tmp) : "Z"(*a));
|
||||
|
||||
return(tmp);
|
||||
|
||||
#else
|
||||
uint16 tmp = *a;
|
||||
return((tmp << 8) | (tmp >> 8));
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE uint32 LoadU32_RBO(const uint32 *a)
|
||||
{
|
||||
#ifdef ARCH_POWERPC
|
||||
uint32 tmp;
|
||||
|
||||
__asm__ ("lwbrx %0, %y1" : "=r"(tmp) : "Z"(*a));
|
||||
|
||||
return(tmp);
|
||||
#else
|
||||
uint32 tmp = *a;
|
||||
return((tmp << 24) | ((tmp & 0xFF00) << 8) | ((tmp >> 8) & 0xFF00) | (tmp >> 24));
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE void StoreU16_RBO(uint16 *a, const uint16 v)
|
||||
{
|
||||
#ifdef ARCH_POWERPC
|
||||
__asm__ ("sthbrx %0, %y1" : : "r"(v), "Z"(*a));
|
||||
#else
|
||||
uint16 tmp = (v << 8) | (v >> 8);
|
||||
*a = tmp;
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE void StoreU32_RBO(uint32 *a, const uint32 v)
|
||||
{
|
||||
#ifdef ARCH_POWERPC
|
||||
__asm__ ("stwbrx %0, %y1" : : "r"(v), "Z"(*a));
|
||||
#else
|
||||
uint32 tmp = (v << 24) | ((v & 0xFF00) << 8) | ((v >> 8) & 0xFF00) | (v >> 24);
|
||||
*a = tmp;
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE uint16 LoadU16_LE(const uint16 *a)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
return LoadU16_RBO(a);
|
||||
#else
|
||||
return *a;
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE uint32 LoadU32_LE(const uint32 *a)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
return LoadU32_RBO(a);
|
||||
#else
|
||||
return *a;
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE void StoreU16_LE(uint16 *a, const uint16 v)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
StoreU16_RBO(a, v);
|
||||
#else
|
||||
*a = v;
|
||||
#endif
|
||||
}
|
||||
|
||||
static INLINE void StoreU32_LE(uint32 *a, const uint32 v)
|
||||
{
|
||||
#ifdef MSB_FIRST
|
||||
StoreU32_RBO(a, v);
|
||||
#else
|
||||
*a = v;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// address must not be >= size specified by template parameter, and address must be a multiple of the byte-size of the
|
||||
// unit(1,2,4) being read(except for Read/WriteU24, which only needs to be byte-aligned).
|
||||
//
|
||||
// max_unit_type should be uint16 or uint32
|
||||
//
|
||||
// pre_padding and post_padding are specified in units of sizeof(max_unit_type).
|
||||
//
|
||||
template<unsigned size, typename max_unit_type, bool big_endian> //, unsigned pre_padding_count, unsigned post_padding_count>
|
||||
struct MultiAccessSizeMem
|
||||
{
|
||||
//max_unit_type pre_padding[pre_padding_count ? pre_padding_count : 1];
|
||||
|
||||
union
|
||||
{
|
||||
uint8 data8[size];
|
||||
uint16 data16[size / sizeof(uint16)];
|
||||
uint32 data32[size / sizeof(uint32)];
|
||||
};
|
||||
|
||||
//max_unit_type post_padding[post_padding_count ? post_padding_count : 1];
|
||||
|
||||
INLINE uint8 ReadU8(uint32 address)
|
||||
{
|
||||
return data8[address];
|
||||
}
|
||||
|
||||
INLINE uint16 ReadU16(uint32 address)
|
||||
{
|
||||
if(MAS_NATIVE_IS_BIGENDIAN == big_endian)
|
||||
return *(uint16*)(((uint8*)data16) + address);
|
||||
else
|
||||
return LoadU16_RBO((uint16*)(((uint8*)data16) + address));
|
||||
}
|
||||
|
||||
INLINE uint32 ReadU32(uint32 address)
|
||||
{
|
||||
if(MAS_NATIVE_IS_BIGENDIAN == big_endian)
|
||||
return *(uint32*)(((uint8*)data32) + address);
|
||||
else
|
||||
return LoadU32_RBO((uint32*)(((uint8*)data32) + address));
|
||||
}
|
||||
|
||||
INLINE uint32 ReadU24(uint32 address)
|
||||
{
|
||||
uint32 ret;
|
||||
|
||||
if(!big_endian)
|
||||
{
|
||||
ret = ReadU8(address) | (ReadU8(address + 1) << 8) | (ReadU8(address + 2) << 16);
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
INLINE void WriteU8(uint32 address, uint8 value)
|
||||
{
|
||||
data8[address] = value;
|
||||
}
|
||||
|
||||
INLINE void WriteU16(uint32 address, uint16 value)
|
||||
{
|
||||
if(MAS_NATIVE_IS_BIGENDIAN == big_endian)
|
||||
*(uint16*)(((uint8*)data16) + address) = value;
|
||||
else
|
||||
StoreU16_RBO((uint16*)(((uint8*)data16) + address), value);
|
||||
}
|
||||
|
||||
INLINE void WriteU32(uint32 address, uint32 value)
|
||||
{
|
||||
if(MAS_NATIVE_IS_BIGENDIAN == big_endian)
|
||||
*(uint32*)(((uint8*)data32) + address) = value;
|
||||
else
|
||||
StoreU32_RBO((uint32*)(((uint8*)data32) + address), value);
|
||||
}
|
||||
|
||||
INLINE void WriteU24(uint32 address, uint32 value)
|
||||
{
|
||||
if(!big_endian)
|
||||
{
|
||||
WriteU8(address + 0, value >> 0);
|
||||
WriteU8(address + 1, value >> 8);
|
||||
WriteU8(address + 2, value >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE T Read(uint32 address)
|
||||
{
|
||||
if(sizeof(T) == 4)
|
||||
return(ReadU32(address));
|
||||
else if(sizeof(T) == 2)
|
||||
return(ReadU16(address));
|
||||
else
|
||||
return(ReadU8(address));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void Write(uint32 address, T value)
|
||||
{
|
||||
if(sizeof(T) == 4)
|
||||
WriteU32(address, value);
|
||||
else if(sizeof(T) == 2)
|
||||
WriteU16(address, value);
|
||||
else
|
||||
WriteU8(address, value);
|
||||
}
|
||||
};
|
||||
|
||||
#undef MAS_NATIVE_IS_BIGENDIAN
|
||||
|
||||
#endif
|
|
@ -3,8 +3,8 @@ DEFS = -DLOCALEDIR=\"$(datadir)/locale\" @DEFS@ @MMX_CFLAGS@ @SSE_CFLAGS@ @SSE2
|
|||
DEFAULT_INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/intl
|
||||
|
||||
noinst_LIBRARIES = libpsx.a
|
||||
libpsx_a_SOURCES = psx.cpp irq.cpp timer.cpp dma.cpp frontio.cpp sio.cpp cpu.cpp gte.cpp dis.cpp cdc.cpp spu.cpp gpu.cpp mdec.cpp
|
||||
|
||||
libpsx_a_SOURCES = psx.cpp irq.cpp timer.cpp dma.cpp frontio.cpp sio.cpp cpu.cpp gte.cpp dis.cpp cdc.cpp spu.cpp mdec.cpp
|
||||
libpsx_a_SOURCES += gpu.cpp gpu_polygon.cpp gpu_line.cpp gpu_sprite.cpp
|
||||
libpsx_a_SOURCES += input/gamepad.cpp input/dualanalog.cpp input/dualshock.cpp input/memcard.cpp input/multitap.cpp input/mouse.cpp input/negcon.cpp input/guncon.cpp input/justifier.cpp
|
||||
|
||||
if WANT_DEBUGGER
|
||||
|
|
|
@ -57,6 +57,7 @@ subdir = src/psx
|
|||
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cflags_gcc_option.m4 \
|
||||
$(top_srcdir)/m4/ax_cxx_compile_stdcxx_11.m4 \
|
||||
$(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/fcntl-o.m4 \
|
||||
$(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/glibc2.m4 \
|
||||
$(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/iconv.m4 \
|
||||
|
@ -94,21 +95,23 @@ libpsx_a_AR = $(AR) $(ARFLAGS)
|
|||
libpsx_a_LIBADD =
|
||||
am__libpsx_a_SOURCES_DIST = psx.cpp irq.cpp timer.cpp dma.cpp \
|
||||
frontio.cpp sio.cpp cpu.cpp gte.cpp dis.cpp cdc.cpp spu.cpp \
|
||||
gpu.cpp mdec.cpp input/gamepad.cpp input/dualanalog.cpp \
|
||||
input/dualshock.cpp input/memcard.cpp input/multitap.cpp \
|
||||
input/mouse.cpp input/negcon.cpp input/guncon.cpp \
|
||||
input/justifier.cpp debug.cpp
|
||||
mdec.cpp gpu.cpp gpu_polygon.cpp gpu_line.cpp gpu_sprite.cpp \
|
||||
input/gamepad.cpp input/dualanalog.cpp input/dualshock.cpp \
|
||||
input/memcard.cpp input/multitap.cpp input/mouse.cpp \
|
||||
input/negcon.cpp input/guncon.cpp input/justifier.cpp \
|
||||
debug.cpp
|
||||
am__dirstamp = $(am__leading_dot)dirstamp
|
||||
@WANT_DEBUGGER_TRUE@am__objects_1 = debug.$(OBJEXT)
|
||||
am_libpsx_a_OBJECTS = psx.$(OBJEXT) irq.$(OBJEXT) timer.$(OBJEXT) \
|
||||
dma.$(OBJEXT) frontio.$(OBJEXT) sio.$(OBJEXT) cpu.$(OBJEXT) \
|
||||
gte.$(OBJEXT) dis.$(OBJEXT) cdc.$(OBJEXT) spu.$(OBJEXT) \
|
||||
gpu.$(OBJEXT) mdec.$(OBJEXT) input/gamepad.$(OBJEXT) \
|
||||
input/dualanalog.$(OBJEXT) input/dualshock.$(OBJEXT) \
|
||||
input/memcard.$(OBJEXT) input/multitap.$(OBJEXT) \
|
||||
input/mouse.$(OBJEXT) input/negcon.$(OBJEXT) \
|
||||
input/guncon.$(OBJEXT) input/justifier.$(OBJEXT) \
|
||||
$(am__objects_1)
|
||||
mdec.$(OBJEXT) gpu.$(OBJEXT) gpu_polygon.$(OBJEXT) \
|
||||
gpu_line.$(OBJEXT) gpu_sprite.$(OBJEXT) \
|
||||
input/gamepad.$(OBJEXT) input/dualanalog.$(OBJEXT) \
|
||||
input/dualshock.$(OBJEXT) input/memcard.$(OBJEXT) \
|
||||
input/multitap.$(OBJEXT) input/mouse.$(OBJEXT) \
|
||||
input/negcon.$(OBJEXT) input/guncon.$(OBJEXT) \
|
||||
input/justifier.$(OBJEXT) $(am__objects_1)
|
||||
libpsx_a_OBJECTS = $(am_libpsx_a_OBJECTS)
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
|
@ -186,7 +189,6 @@ ECHO_T = @ECHO_T@
|
|||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
FGREP = @FGREP@
|
||||
GBA_EXTRA_FLAGS = @GBA_EXTRA_FLAGS@
|
||||
GENCAT = @GENCAT@
|
||||
GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
|
||||
GLIBC2 = @GLIBC2@
|
||||
|
@ -195,6 +197,7 @@ GMSGFMT = @GMSGFMT@
|
|||
GMSGFMT_015 = @GMSGFMT_015@
|
||||
GREP = @GREP@
|
||||
HAVE_ASPRINTF = @HAVE_ASPRINTF@
|
||||
HAVE_CXX11 = @HAVE_CXX11@
|
||||
HAVE_NEWLOCALE = @HAVE_NEWLOCALE@
|
||||
HAVE_POSIX_PRINTF = @HAVE_POSIX_PRINTF@
|
||||
HAVE_SNPRINTF = @HAVE_SNPRINTF@
|
||||
|
@ -349,11 +352,12 @@ AUTOMAKE_OPTIONS = subdir-objects
|
|||
DEFAULT_INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include -I$(top_srcdir)/intl
|
||||
noinst_LIBRARIES = libpsx.a
|
||||
libpsx_a_SOURCES = psx.cpp irq.cpp timer.cpp dma.cpp frontio.cpp \
|
||||
sio.cpp cpu.cpp gte.cpp dis.cpp cdc.cpp spu.cpp gpu.cpp \
|
||||
mdec.cpp input/gamepad.cpp input/dualanalog.cpp \
|
||||
input/dualshock.cpp input/memcard.cpp input/multitap.cpp \
|
||||
input/mouse.cpp input/negcon.cpp input/guncon.cpp \
|
||||
input/justifier.cpp $(am__append_1)
|
||||
sio.cpp cpu.cpp gte.cpp dis.cpp cdc.cpp spu.cpp mdec.cpp \
|
||||
gpu.cpp gpu_polygon.cpp gpu_line.cpp gpu_sprite.cpp \
|
||||
input/gamepad.cpp input/dualanalog.cpp input/dualshock.cpp \
|
||||
input/memcard.cpp input/multitap.cpp input/mouse.cpp \
|
||||
input/negcon.cpp input/guncon.cpp input/justifier.cpp \
|
||||
$(am__append_1)
|
||||
all: all-am
|
||||
|
||||
.SUFFIXES:
|
||||
|
@ -442,6 +446,9 @@ distclean-compile:
|
|||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dma.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/frontio.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpu.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpu_line.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpu_polygon.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpu_sprite.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gte.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/irq.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdec.Po@am__quote@
|
||||
|
|
|
@ -1,336 +0,0 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* 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 of the License, 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
|
||||
*/
|
||||
/*
|
||||
TODO:
|
||||
Time string parsing convenience functions.
|
||||
|
||||
Character set autodetect heuristics and conversion for when the "utf8" tag is missing.
|
||||
*/
|
||||
#include "mednafen.h"
|
||||
#include "PSFLoader.h"
|
||||
#include "endian.h"
|
||||
#include "general.h"
|
||||
#include "string/trim.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <trio/trio.h>
|
||||
#include <ctype.h>
|
||||
//#include <iconv.h>
|
||||
|
||||
|
||||
PSFTags::PSFTags()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
PSFTags::~PSFTags()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PSFTags::AddTag(char *tag_line)
|
||||
{
|
||||
char *eq;
|
||||
|
||||
// Transform 0x01-0x1F -> 0x20
|
||||
for(unsigned int i = 0; i < strlen(tag_line); i++)
|
||||
if((unsigned char)tag_line[i] < 0x20)
|
||||
tag_line[i] = 0x20;
|
||||
|
||||
eq = strchr(tag_line, '=');
|
||||
|
||||
if(eq)
|
||||
{
|
||||
*eq = 0;
|
||||
|
||||
MDFN_trim(tag_line);
|
||||
MDFN_trim(eq + 1);
|
||||
|
||||
for(unsigned int i = 0; i < strlen(tag_line); i++)
|
||||
tag_line[i] = tolower(tag_line[i]);
|
||||
|
||||
if(TagExists(tag_line))
|
||||
tags[tag_line] = tags[std::string(tag_line)] + std::string(1, '\n') + std::string(eq + 1);
|
||||
else
|
||||
tags[tag_line] = std::string(eq + 1);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static const char *DetectCharset(const uint8 *data, const uint32 data_size)
|
||||
{
|
||||
static const char *TestCharsets[] = { "UTF-8", /*"SJIS",*/ "WINDOWS-1252" };
|
||||
|
||||
for(unsigned int i = 0; i < sizeof(TestCharsets) / sizeof(TestCharsets[0]); i++)
|
||||
{
|
||||
iconv_t cd;
|
||||
|
||||
cd = iconv_open("UTF-32", TestCharsets[i]);
|
||||
if(cd != (iconv_t)-1)
|
||||
{
|
||||
size_t in_len = data_size;
|
||||
size_t out_len = data_size * 4 + 4;
|
||||
char *in_ptr = (char *)data;
|
||||
char *const out_ptr_mem = new char[out_len];
|
||||
char *out_ptr = out_ptr_mem;
|
||||
|
||||
if(iconv(cd, (ICONV_CONST char **)&in_ptr, &in_len, &out_ptr, &out_len) != (size_t)-1)
|
||||
{
|
||||
delete[] out_ptr_mem;
|
||||
return(TestCharsets[i]);
|
||||
}
|
||||
delete[] out_ptr_mem;
|
||||
}
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
void PSFTags::LoadTags(const uint8 *data_in, uint32 size)
|
||||
{
|
||||
std::vector<char> tags_heap;
|
||||
char *data;
|
||||
char *spos;
|
||||
//const char *detected_charset = DetectCharset(data_in, size);
|
||||
|
||||
tags_heap.resize(size + 1);
|
||||
tags_heap[size] = 0;
|
||||
|
||||
memcpy(&tags_heap[0], data_in, size);
|
||||
|
||||
data = &tags_heap[0];
|
||||
spos = data;
|
||||
|
||||
while(size)
|
||||
{
|
||||
if(*data == 0x0A || *data == 0x00)
|
||||
{
|
||||
*data = 0;
|
||||
|
||||
if(data - spos)
|
||||
{
|
||||
if(*(data - 1) == 0xD) // handle \r
|
||||
*(data - 1) = 0;
|
||||
|
||||
AddTag(spos);
|
||||
}
|
||||
|
||||
spos = data + 1; // Skip \n for next tag
|
||||
}
|
||||
|
||||
size--;
|
||||
data++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int64 PSFTags::GetTagI(const char *name)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
|
||||
it = tags.find(name);
|
||||
if(it != tags.end())
|
||||
{
|
||||
long long ret = 0;
|
||||
std::string &tmp = tags[name];
|
||||
|
||||
trio_sscanf(tmp.c_str(), "%lld", &ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
return(0); // INT64_MIN
|
||||
}
|
||||
|
||||
bool PSFTags::TagExists(const char *name)
|
||||
{
|
||||
if(tags.find(name) != tags.end())
|
||||
return(true);
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
std::string PSFTags::GetTag(const char *name)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
|
||||
it = tags.find(name);
|
||||
|
||||
if(it != tags.end())
|
||||
return(it->second);
|
||||
|
||||
return("");
|
||||
}
|
||||
|
||||
void PSFTags::EraseTag(const char *name)
|
||||
{
|
||||
std::map<std::string, std::string>::iterator it;
|
||||
|
||||
it = tags.find(name);
|
||||
if(it != tags.end())
|
||||
tags.erase(it);
|
||||
}
|
||||
|
||||
PSFLoader::PSFLoader()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
PSFLoader::~PSFLoader()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool PSFLoader::TestMagic(uint8 version, MDFNFILE *fp)
|
||||
{
|
||||
if(fp->size < (3 + 1 + 4 + 4 + 4))
|
||||
return(false);
|
||||
|
||||
if(memcmp(fp->data, "PSF", 3))
|
||||
return(false);
|
||||
|
||||
if(fp->data[3] != version)
|
||||
return(false);
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
PSFTags PSFLoader::LoadInternal(uint8 version, uint32 max_exe_size, MDFNFILE *fp, uint32 level, bool force_ignore_pcsp)
|
||||
{
|
||||
uint32 reserved_size, compressed_size, compressed_crc32;
|
||||
bool _lib_present = false;
|
||||
PSFTags tags;
|
||||
|
||||
std::vector<uint8> decompress_buffer;
|
||||
uLongf decompress_len;
|
||||
|
||||
if(!TestMagic(version, fp))
|
||||
throw(MDFN_Error(0, _("Not a PSF(version=0x%02x) file!"), version));
|
||||
|
||||
reserved_size = MDFN_de32lsb(fp->data + 4);
|
||||
compressed_size = MDFN_de32lsb(fp->data + 8);
|
||||
compressed_crc32 = MDFN_de32lsb(fp->data + 12);
|
||||
|
||||
if(fp->size < ((int64)16 + reserved_size + compressed_size))
|
||||
throw MDFN_Error(0, _("PSF is missing at least %lld bytes of data!"), (long long)((int64)16 + reserved_size + compressed_size - fp->size));
|
||||
|
||||
if(crc32(0, fp->data + 16 + reserved_size, compressed_size) != compressed_crc32)
|
||||
throw MDFN_Error(0, _("PSF compressed CRC32 mismatch(data is corrupt)!"));
|
||||
|
||||
|
||||
{
|
||||
const uint8 *tag_section = fp->data + 16 + reserved_size + compressed_size;
|
||||
uint32 tag_section_size = fp->size - 16 - reserved_size - compressed_size;
|
||||
|
||||
if(tag_section_size > 5 && !memcmp(tag_section, "[TAG]", 5))
|
||||
tags.LoadTags(tag_section + 5, tag_section_size - 5);
|
||||
}
|
||||
|
||||
//
|
||||
// Handle minipsf simple _lib
|
||||
//
|
||||
|
||||
if(level < 15)
|
||||
{
|
||||
if(tags.TagExists("_lib"))
|
||||
{
|
||||
std::string tp = tags.GetTag("_lib");
|
||||
|
||||
if(!MDFN_IsFIROPSafe(tp))
|
||||
{
|
||||
throw(MDFN_Error(0, _("Referenced path \"%s\" is potentially unsafe. See \"filesys.untrusted_fip_check\" setting."), tp.c_str()));
|
||||
}
|
||||
|
||||
MDFNFILE subfile(MDFN_MakeFName(MDFNMKF_AUX, 0, tp.c_str()).c_str(), NULL, NULL);
|
||||
|
||||
LoadInternal(version, max_exe_size, &subfile, level + 1);
|
||||
|
||||
_lib_present = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
decompress_buffer.resize(max_exe_size);
|
||||
decompress_len = max_exe_size;
|
||||
switch( uncompress((Bytef *)&decompress_buffer[0], &decompress_len, (const Bytef *)(fp->data + 16 + reserved_size), compressed_size) )
|
||||
{
|
||||
default:
|
||||
throw(MDFN_Error(0, "zlib unknown error"));
|
||||
|
||||
case Z_OK: break;
|
||||
|
||||
case Z_MEM_ERROR:
|
||||
throw(MDFN_Error(0, "zlib Z_MEM_ERROR"));
|
||||
|
||||
case Z_BUF_ERROR:
|
||||
throw(MDFN_Error(0, _("PSF decompressed size exceeds maximum allowed!")));
|
||||
|
||||
case Z_DATA_ERROR:
|
||||
throw(MDFN_Error(0, _("PSF compressed data is bad.")));
|
||||
}
|
||||
|
||||
HandleReserved(fp->data + 16, reserved_size);
|
||||
HandleEXE(&decompress_buffer[0], decompress_len, force_ignore_pcsp | _lib_present);
|
||||
decompress_buffer.resize(0);
|
||||
|
||||
//
|
||||
// handle libN
|
||||
//
|
||||
if(level < 15)
|
||||
{
|
||||
for(unsigned int n = 2; n <= INT_MAX; n++)
|
||||
{
|
||||
char tmpbuf[32];
|
||||
|
||||
trio_snprintf(tmpbuf, 32, "_lib%d", (int)n);
|
||||
|
||||
if(tags.TagExists(tmpbuf))
|
||||
{
|
||||
MDFNFILE subfile(MDFN_MakeFName(MDFNMKF_AUX, 0, tags.GetTag(tmpbuf).c_str()).c_str(), NULL, NULL);
|
||||
|
||||
LoadInternal(version, max_exe_size, &subfile, level + 1, true);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return(tags);
|
||||
}
|
||||
|
||||
PSFTags PSFLoader::Load(uint8 version, uint32 max_exe_size, MDFNFILE *fp)
|
||||
{
|
||||
return(LoadInternal(version, max_exe_size, fp, 0, false));
|
||||
}
|
||||
|
||||
void PSFLoader::HandleReserved(const uint8 *data, uint32 len)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PSFLoader::HandleEXE(const uint8 *data, uint32 len, bool ignore_pcsp)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
#ifndef __MDFN_PSFLOADER_H
|
||||
#define __MDFN_PSFLOADER_H
|
||||
|
||||
#include <map>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class PSFTags
|
||||
{
|
||||
public:
|
||||
|
||||
PSFTags();
|
||||
~PSFTags();
|
||||
|
||||
int64 GetTagI(const char *name);
|
||||
std::string GetTag(const char *name);
|
||||
bool TagExists(const char *name);
|
||||
|
||||
void LoadTags(const uint8 *data, uint32 size);
|
||||
void EraseTag(const char *name);
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void AddTag(char *tag_line);
|
||||
std::map<std::string, std::string> tags;
|
||||
};
|
||||
|
||||
class PSFLoader
|
||||
{
|
||||
public:
|
||||
PSFLoader();
|
||||
~PSFLoader();
|
||||
|
||||
static bool TestMagic(uint8 version, MDFNFILE *fp);
|
||||
|
||||
PSFTags Load(uint8 version, uint32 max_exe_size, MDFNFILE *fp);
|
||||
|
||||
virtual void HandleReserved(const uint8 *data, uint32 len);
|
||||
virtual void HandleEXE(const uint8 *data, uint32 len, bool ignore_pcsp = false);
|
||||
|
||||
private:
|
||||
|
||||
PSFTags LoadInternal(uint8 version, uint32 max_exe_size, MDFNFILE *fp, uint32 level, bool force_ignore_pcsp = false);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -15,6 +15,13 @@
|
|||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
Games to test after changing code affecting CD reading and buffering:
|
||||
Bedlam
|
||||
Rise 2
|
||||
|
||||
*/
|
||||
|
||||
// TODO: async command counter and async command phase?
|
||||
/*
|
||||
|
||||
|
@ -67,7 +74,25 @@ PS_CDC::~PS_CDC()
|
|||
|
||||
}
|
||||
|
||||
void PS_CDC::SetDisc(bool tray_open, ShockDiscRef *disc, const char *disc_id)
|
||||
void PS_CDC::DMForceStop(void)
|
||||
{
|
||||
PSRCounter = 0;
|
||||
|
||||
if((DriveStatus != DS_PAUSED && DriveStatus != DS_STOPPED) || PendingCommandPhase >= 2)
|
||||
{
|
||||
PendingCommand = 0x00;
|
||||
PendingCommandCounter = 0;
|
||||
PendingCommandPhase = 0;
|
||||
}
|
||||
|
||||
HeaderBufValid = false;
|
||||
DriveStatus = DS_STOPPED;
|
||||
ClearAIP();
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
SectorsRead = 0;
|
||||
}
|
||||
|
||||
void PS_CDC::SetDisc(bool tray_open, ShockDiscRef *disc, const char *disc_id)
|
||||
{
|
||||
if(tray_open)
|
||||
disc = NULL;
|
||||
|
@ -78,19 +103,7 @@ void PS_CDC::SetDisc(bool tray_open, ShockDiscRef *disc, const char *disc_id)
|
|||
|
||||
if(!Cur_disc)
|
||||
{
|
||||
PSRCounter = 0;
|
||||
|
||||
if((DriveStatus != DS_PAUSED && DriveStatus != DS_STOPPED) || PendingCommandPhase >= 2)
|
||||
{
|
||||
PendingCommand = 0x00;
|
||||
PendingCommandCounter = 0;
|
||||
PendingCommandPhase = 0;
|
||||
}
|
||||
|
||||
HeaderBufValid = false;
|
||||
DriveStatus = DS_STOPPED;
|
||||
ClearAIP();
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
DMForceStop();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -118,7 +131,7 @@ int32 PS_CDC::CalcNextEvent(void)
|
|||
if(PendingCommandCounter > 0 && next_event > PendingCommandCounter)
|
||||
next_event = PendingCommandCounter;
|
||||
|
||||
if(!IRQBuffer)
|
||||
if(!(IRQBuffer & 0xF))
|
||||
{
|
||||
if(CDCReadyReceiveCounter > 0 && next_event > CDCReadyReceiveCounter)
|
||||
next_event = CDCReadyReceiveCounter;
|
||||
|
@ -161,6 +174,7 @@ void PS_CDC::SoftReset(void)
|
|||
DMABuffer.Flush();
|
||||
SB_In = 0;
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
SectorsRead = 0;
|
||||
|
||||
memset(SubQBuf, 0, sizeof(SubQBuf));
|
||||
memset(SubQBuf_Safe, 0, sizeof(SubQBuf_Safe));
|
||||
|
@ -286,6 +300,7 @@ SYNCFUNC(PS_CDC)
|
|||
NSS(PSRCounter);
|
||||
|
||||
NSS(CurSector);
|
||||
NSS(SectorsRead);
|
||||
|
||||
NSS(AsyncIRQPending);
|
||||
NSS(AsyncResultsPending);
|
||||
|
@ -326,13 +341,13 @@ void PS_CDC::RecalcIRQ(void)
|
|||
void PS_CDC::WriteIRQ(uint8 V)
|
||||
{
|
||||
assert(CDCReadyReceiveCounter <= 0);
|
||||
assert(!IRQBuffer);
|
||||
assert(!(IRQBuffer & 0xF));
|
||||
|
||||
//PSX_WARNING("[CDC] ***IRQTHINGY: 0x%02x -- %u", V, doom_ts);
|
||||
|
||||
CDCReadyReceiveCounter = 2000; //1024;
|
||||
|
||||
IRQBuffer = V;
|
||||
IRQBuffer = (IRQBuffer & 0x10) | V;
|
||||
RecalcIRQ();
|
||||
}
|
||||
|
||||
|
@ -817,8 +832,8 @@ void PS_CDC::EnbufferizeCDDASector(const uint8 *buf)
|
|||
{
|
||||
for(int i = 0; i < 588; i++)
|
||||
{
|
||||
ab->Samples[0][i] = (int16)MDFN_de16lsb(&buf[i * sizeof(int16) * 2 + 0]);
|
||||
ab->Samples[1][i] = (int16)MDFN_de16lsb(&buf[i * sizeof(int16) * 2 + 2]);
|
||||
ab->Samples[0][i] = (int16)MDFN_de16lsb<false>(&buf[i * sizeof(int16) * 2 + 0]);
|
||||
ab->Samples[1][i] = (int16)MDFN_de16lsb<false>(&buf[i * sizeof(int16) * 2 + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -837,6 +852,7 @@ void PS_CDC::HandlePlayRead(void)
|
|||
PSX_WARNING("[CDC] Read/Play position waaay too far out(%u), forcing STOP", CurSector);
|
||||
DriveStatus = DS_STOPPED;
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
SectorsRead = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -866,6 +882,7 @@ void PS_CDC::HandlePlayRead(void)
|
|||
// Status in this end-of-disc context here should be generated after we're in the pause state.
|
||||
DriveStatus = DS_PAUSED;
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
SectorsRead = 0;
|
||||
SetAIP(CDCIRQ_DATA_END, MakeStatus());
|
||||
|
||||
return;
|
||||
|
@ -884,6 +901,7 @@ void PS_CDC::HandlePlayRead(void)
|
|||
|
||||
DriveStatus = DS_PAUSED;
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
SectorsRead = 0;
|
||||
PSRCounter = 0;
|
||||
return;
|
||||
}
|
||||
|
@ -1019,6 +1037,7 @@ void PS_CDC::HandlePlayRead(void)
|
|||
else
|
||||
CurSector++;
|
||||
|
||||
SectorsRead++;
|
||||
}
|
||||
|
||||
pscpu_timestamp_t PS_CDC::Update(const pscpu_timestamp_t timestamp)
|
||||
|
@ -1055,7 +1074,7 @@ pscpu_timestamp_t PS_CDC::Update(const pscpu_timestamp_t timestamp)
|
|||
|
||||
//MDFN_DispMessage("%02x %d -- %d %d -- %02x", IRQBuffer, CDCReadyReceiveCounter, PSRCounter, PendingCommandCounter, PendingCommand);
|
||||
|
||||
if(!IRQBuffer)
|
||||
if(!(IRQBuffer & 0xF))
|
||||
{
|
||||
if(CDCReadyReceiveCounter > 0 && chunk_clocks > CDCReadyReceiveCounter)
|
||||
chunk_clocks = CDCReadyReceiveCounter;
|
||||
|
@ -1083,6 +1102,7 @@ pscpu_timestamp_t PS_CDC::Update(const pscpu_timestamp_t timestamp)
|
|||
|
||||
SB_In = 0;
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
SectorsRead = 0;
|
||||
|
||||
Mode = 0;
|
||||
CurSector = 0;
|
||||
|
@ -1271,7 +1291,7 @@ void PS_CDC::Write(const pscpu_timestamp_t timestamp, uint32 A, uint8 V)
|
|||
PendingCommandCounter, V);
|
||||
}
|
||||
|
||||
if(IRQBuffer)
|
||||
if(IRQBuffer & 0xF)
|
||||
{
|
||||
PSX_WARNING("[CDC] Attempting to start command(0x%02x) while IRQBuffer(0x%02x) is not clear.", V, IRQBuffer);
|
||||
}
|
||||
|
@ -1336,6 +1356,7 @@ void PS_CDC::Write(const pscpu_timestamp_t timestamp, uint32 A, uint8 V)
|
|||
{
|
||||
PSX_WARNING("[CDC] Mystery IRQ trigger bit set.");
|
||||
IRQBuffer |= 0x10;
|
||||
RecalcIRQ();
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1561,10 +1582,12 @@ int32 PS_CDC::CalcSeekTime(int32 initial, int32 target, bool motor_on, bool paus
|
|||
ret += 1237952 * ((Mode & MODE_SPEED) ? 1 : 2);
|
||||
}
|
||||
}
|
||||
//else if(target < initial)
|
||||
// ret += 1000000;
|
||||
|
||||
ret += PSX_GetRandU32(0, 25000);
|
||||
|
||||
PSX_DBG(PSX_DBG_SPARSE, "[CDC] CalcSeekTime() = %d\n", ret);
|
||||
PSX_DBG(PSX_DBG_SPARSE, "[CDC] CalcSeekTime() %d->%d = %d\n", initial, target, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
@ -1636,6 +1659,7 @@ int32 PS_CDC::Command_Play(const int arg_count, const uint8 *args)
|
|||
|
||||
ClearAudioBuffers();
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
SectorsRead = 0;
|
||||
|
||||
PlayTrackMatch = track;
|
||||
|
||||
|
@ -1655,6 +1679,7 @@ int32 PS_CDC::Command_Play(const int arg_count, const uint8 *args)
|
|||
{
|
||||
ClearAudioBuffers();
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
SectorsRead = 0;
|
||||
|
||||
if(CommandLoc_Dirty)
|
||||
SeekTarget = CommandLoc;
|
||||
|
@ -1727,6 +1752,7 @@ void PS_CDC::ReadBase(void)
|
|||
ClearAudioBuffers();
|
||||
SB_In = 0;
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
SectorsRead = 0;
|
||||
|
||||
// TODO: separate motor start from seek phase?
|
||||
|
||||
|
@ -1775,6 +1801,7 @@ int32 PS_CDC::Command_Stop(const int arg_count, const uint8 *args)
|
|||
ClearAudioBuffers();
|
||||
ClearAIP();
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
SectorsRead = 0;
|
||||
|
||||
DriveStatus = DS_STOPPED;
|
||||
HeaderBufValid = false;
|
||||
|
@ -1812,6 +1839,7 @@ int32 PS_CDC::Command_Standby(const int arg_count, const uint8 *args)
|
|||
ClearAudioBuffers();
|
||||
ClearAIP();
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
SectorsRead = 0;
|
||||
|
||||
DriveStatus = DS_STANDBY;
|
||||
|
||||
|
@ -1842,6 +1870,9 @@ int32 PS_CDC::Command_Pause(const int arg_count, const uint8 *args)
|
|||
}
|
||||
else
|
||||
{
|
||||
CurSector -= std::min<uint32>(4, SectorsRead); // See: Bedlam, Rise 2
|
||||
SectorsRead = 0;
|
||||
|
||||
// "Viewpoint" flips out and crashes if reading isn't stopped (almost?) immediately.
|
||||
//ClearAudioBuffers();
|
||||
SectorPipe_Pos = SectorPipe_In = 0;
|
||||
|
|
|
@ -17,7 +17,6 @@ struct CD_Audio_Buffer
|
|||
int32 ReadPos;
|
||||
};
|
||||
|
||||
|
||||
class PS_CDC
|
||||
{
|
||||
public:
|
||||
|
@ -30,7 +29,6 @@ class PS_CDC
|
|||
void SetDisc(bool tray_open, ShockDiscRef *disc, const char disc_id[4]);
|
||||
|
||||
void Power(void);
|
||||
int StateAction(StateMem *sm, int load, int data_only);
|
||||
void ResetTS(void);
|
||||
|
||||
int32 CalcNextEvent(void); // Returns in master cycles to next event.
|
||||
|
@ -177,6 +175,7 @@ class PS_CDC
|
|||
int32 PSRCounter;
|
||||
|
||||
int32 CurSector;
|
||||
uint32 SectorsRead; // Reset to 0 on Read*/Play command start; used in the rough simulation of PS1 SetLoc->Read->Pause->Read behavior.
|
||||
|
||||
unsigned AsyncIRQPending;
|
||||
uint8 AsyncResultsPending[16];
|
||||
|
@ -204,6 +203,7 @@ class PS_CDC
|
|||
uint8 MakeStatus(bool cmd_error = false);
|
||||
bool DecodeSubQ(uint8 *subpw);
|
||||
bool CommandCheckDiscPresent(void);
|
||||
void DMForceStop();
|
||||
|
||||
void EnbufferizeCDDASector(const uint8 *buf);
|
||||
bool XA_Test(const uint8 *sdata);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include "psx.h"
|
||||
#include "cpu.h"
|
||||
#include "gte.h"
|
||||
|
||||
/* TODO
|
||||
Make sure load delays are correct.
|
||||
|
@ -48,6 +49,21 @@ PS_CPU::PS_CPU()
|
|||
|
||||
CPUHook = NULL;
|
||||
ADDBT = NULL;
|
||||
|
||||
GTE_Init();
|
||||
|
||||
for(unsigned i = 0; i < 24; i++)
|
||||
{
|
||||
uint8 v = 7;
|
||||
|
||||
if(i < 12)
|
||||
v += 4;
|
||||
|
||||
if(i < 21)
|
||||
v += 3;
|
||||
|
||||
MULT_Tab24[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
PS_CPU::~PS_CPU()
|
||||
|
@ -131,57 +147,9 @@ void PS_CPU::Power(void)
|
|||
GTE_Power();
|
||||
}
|
||||
|
||||
int PS_CPU::StateAction(StateMem *sm, int load, int data_only)
|
||||
void PS_CPU::AssertIRQ(unsigned which, bool asserted)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFARRAY32(GPR, 32),
|
||||
SFVAR(LO),
|
||||
SFVAR(HI),
|
||||
SFVAR(BACKED_PC),
|
||||
SFVAR(BACKED_new_PC),
|
||||
SFVAR(BACKED_new_PC_mask),
|
||||
|
||||
SFVAR(IPCache),
|
||||
SFVAR(Halted),
|
||||
|
||||
SFVAR(BACKED_LDWhich),
|
||||
SFVAR(BACKED_LDValue),
|
||||
SFVAR(LDAbsorb),
|
||||
|
||||
SFVAR(next_event_ts),
|
||||
SFVAR(gte_ts_done),
|
||||
SFVAR(muldiv_ts_done),
|
||||
|
||||
SFVAR(BIU),
|
||||
SFARRAY32(ICache_Bulk, 2048),
|
||||
|
||||
SFARRAY32(CP0.Regs, 32),
|
||||
|
||||
SFARRAY(ReadAbsorb, 0x20),
|
||||
SFVAR(ReadAbsorbDummy),
|
||||
SFVAR(ReadAbsorbWhich),
|
||||
SFVAR(ReadFudge),
|
||||
|
||||
SFARRAY(ScratchRAM.data8, 1024),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "CPU");
|
||||
|
||||
ret &= GTE_StateAction(sm, load, data_only);
|
||||
|
||||
if(load)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void PS_CPU::AssertIRQ(int which, bool asserted)
|
||||
{
|
||||
assert(which >= 0 && which <= 5);
|
||||
assert(which <= 5);
|
||||
|
||||
CP0.CAUSE &= ~(1 << (10 + which));
|
||||
|
||||
|
@ -428,7 +396,7 @@ uint32_t PS_CPU::Exception(uint32_t code, uint32_t PC, const uint32_t NPM)
|
|||
#define GPR_RES(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; }
|
||||
#define GPR_DEPRES_END ReadAbsorb[0] = back; }
|
||||
|
||||
template<bool DebugMode, bool ILHMode>
|
||||
template<bool DebugMode, bool BIOSPrintMode, bool ILHMode>
|
||||
pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
|
||||
{
|
||||
register pscpu_timestamp_t timestamp = timestamp_in;
|
||||
|
@ -474,16 +442,28 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
|
|||
BACKING_TO_ACTIVE;
|
||||
}
|
||||
|
||||
if(!ILHMode)
|
||||
{
|
||||
if(PC == 0xB0)
|
||||
{
|
||||
if(MDFN_UNLIKELY(GPR[9] == 0x3D))
|
||||
{
|
||||
PSX_DBG(PSX_DBG_BIOS_PRINT, "%c", GPR[4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(BIOSPrintMode)
|
||||
{
|
||||
if(PC == 0xB0)
|
||||
{
|
||||
if(MDFN_UNLIKELY(GPR[9] == 0x3D))
|
||||
{
|
||||
PSX_DBG_BIOS_PUTC(GPR[4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can't fold this into the ICache[] != PC handling, since the lower 2 bits of TV
|
||||
// are already used for cache management purposes and it assumes that the lower 2 bits of PC will be 0.
|
||||
if(MDFN_UNLIKELY(PC & 0x3))
|
||||
{
|
||||
// This will block interrupt processing, but since we're going more for keeping broken homebrew/hacks from working
|
||||
// than super-duper-accurate pipeline emulation, it shouldn't be a problem.
|
||||
new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask);
|
||||
new_PC_mask = 0;
|
||||
goto OpDone;
|
||||
}
|
||||
|
||||
instr = ICache[(PC & 0xFFC) >> 2].Data;
|
||||
|
||||
|
@ -498,13 +478,14 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
|
|||
// FIXME: Handle executing out of scratchpad.
|
||||
if(PC >= 0xA0000000 || !(BIU & 0x800))
|
||||
{
|
||||
instr = LoadU32_LE((uint32_t *)&FastMap[PC >> FAST_MAP_SHIFT][PC]);
|
||||
timestamp += 4; // Approximate best-case cache-disabled time, per PS1 tests(executing out of 0xA0000000+); it can be 5 in *some* sequences of code(like a lot of sequential "nop"s, probably other simple instructions too).
|
||||
}
|
||||
else
|
||||
{
|
||||
__ICache *ICI = &ICache[((PC & 0xFF0) >> 2)];
|
||||
const uint32_t *FMP = (uint32_t *)&FastMap[(PC &~ 0xF) >> FAST_MAP_SHIFT][PC &~ 0xF];
|
||||
instr = MDFN_de32lsb<true>(&FastMap[PC >> FAST_MAP_SHIFT][PC]);
|
||||
timestamp += 4; // Approximate best-case cache-disabled time, per PS1 tests(executing out of 0xA0000000+); it can be 5 in *some* sequences of code(like a lot of sequential "nop"s, probably other simple instructions too).
|
||||
}
|
||||
else
|
||||
{
|
||||
__ICache *ICI = &ICache[((PC & 0xFF0) >> 2)];
|
||||
const uint8 *FMP = &FastMap[(PC &~ 0xF) >> FAST_MAP_SHIFT][PC &~ 0xF];
|
||||
|
||||
|
||||
// | 0x2 to simulate (in)validity bits.
|
||||
ICI[0x00].TV = (PC &~ 0xF) | 0x00 | 0x2;
|
||||
|
@ -514,29 +495,30 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
|
|||
|
||||
timestamp += 3;
|
||||
|
||||
switch(PC & 0xC)
|
||||
{
|
||||
case 0x0:
|
||||
timestamp++;
|
||||
ICI[0x00].TV &= ~0x2;
|
||||
ICI[0x00].Data = LoadU32_LE(&FMP[0]);
|
||||
case 0x4:
|
||||
timestamp++;
|
||||
ICI[0x01].TV &= ~0x2;
|
||||
ICI[0x01].Data = LoadU32_LE(&FMP[1]);
|
||||
case 0x8:
|
||||
timestamp++;
|
||||
ICI[0x02].TV &= ~0x2;
|
||||
ICI[0x02].Data = LoadU32_LE(&FMP[2]);
|
||||
case 0xC:
|
||||
timestamp++;
|
||||
ICI[0x03].TV &= ~0x2;
|
||||
ICI[0x03].Data = LoadU32_LE(&FMP[3]);
|
||||
break;
|
||||
}
|
||||
instr = ICache[(PC & 0xFFC) >> 2].Data;
|
||||
}
|
||||
}
|
||||
|
||||
switch(PC & 0xC)
|
||||
{
|
||||
case 0x0:
|
||||
timestamp++;
|
||||
ICI[0x00].TV &= ~0x2;
|
||||
ICI[0x00].Data = MDFN_de32lsb<true>(&FMP[0x0]);
|
||||
case 0x4:
|
||||
timestamp++;
|
||||
ICI[0x01].TV &= ~0x2;
|
||||
ICI[0x01].Data = MDFN_de32lsb<true>(&FMP[0x4]);
|
||||
case 0x8:
|
||||
timestamp++;
|
||||
ICI[0x02].TV &= ~0x2;
|
||||
ICI[0x02].Data = MDFN_de32lsb<true>(&FMP[0x8]);
|
||||
case 0xC:
|
||||
timestamp++;
|
||||
ICI[0x03].TV &= ~0x2;
|
||||
ICI[0x03].Data = MDFN_de32lsb<true>(&FMP[0xC]);
|
||||
break;
|
||||
}
|
||||
instr = ICache[(PC & 0xFFC) >> 2].Data;
|
||||
}
|
||||
}
|
||||
|
||||
//printf("PC=%08x, SP=%08x - op=0x%02x - funct=0x%02x - instr=0x%08x\n", PC, GPR[29], instr >> 26, instr & 0x3F, instr);
|
||||
//for(int i = 0; i < 32; i++)
|
||||
|
@ -586,7 +568,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
|
|||
PC = (PC & new_PC_mask) + new_PC; \
|
||||
if(old_PC == ((PC & (mask)) + (offset))) \
|
||||
{ \
|
||||
if(*(uint32_t *)&FastMap[PC >> FAST_MAP_SHIFT][PC] == 0) \
|
||||
if(MDFN_densb<uint32, true>(&FastMap[PC >> FAST_MAP_SHIFT][PC]) == 0) \
|
||||
{ \
|
||||
if(next_event_ts > timestamp) /* Necessary since next_event_ts might be set to something like "0" to force a call to the event handler. */ \
|
||||
{ \
|
||||
|
@ -608,15 +590,15 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
|
|||
goto SkipNPCStuff; \
|
||||
}
|
||||
|
||||
#define ITYPE uint32_t rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32_t rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; int32_t immediate = (int16)(instr & 0xFFFF); /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/
|
||||
#define ITYPE uint32 rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32 rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32 immediate = (int32)(int16)(instr & 0xFFFF); /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/
|
||||
#define ITYPE_ZE uint32_t rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32_t rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32_t immediate = instr & 0xFFFF; /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/
|
||||
#define JTYPE uint32_t target = instr & ((1 << 26) - 1); /*printf(" target=(%08x) ", target);*/
|
||||
#define RTYPE uint32_t rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32_t rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32_t rd MDFN_NOWARN_UNUSED = (instr >> 11) & 0x1F; uint32_t shamt MDFN_NOWARN_UNUSED = (instr >> 6) & 0x1F; /*printf(" rs=%02x(%08x), rt=%02x(%08x), rd=%02x(%08x) ", rs, GPR[rs], rt, GPR[rt], rd, GPR[rd]);*/
|
||||
|
||||
#if 1
|
||||
#include "cpu_bigswitch.c"
|
||||
#include "cpu_bigswitch.inc"
|
||||
#else
|
||||
#include "cpu_coputedgoto.c"
|
||||
#include "cpu_coputedgoto.inc"
|
||||
#endif
|
||||
|
||||
OpDone: ;
|
||||
|
@ -642,15 +624,25 @@ SkipNPCStuff: ;
|
|||
return(timestamp);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t PS_CPU::Run(pscpu_timestamp_t timestamp_in, const bool ILHMode)
|
||||
pscpu_timestamp_t PS_CPU::Run(pscpu_timestamp_t timestamp_in, bool BIOSPrintMode, bool ILHMode)
|
||||
{
|
||||
if(CPUHook || ADDBT)
|
||||
return(RunReal<true, false>(timestamp_in));
|
||||
if (ILHMode)
|
||||
return(RunReal<false, true>(timestamp_in));
|
||||
return(RunReal<false, false>(timestamp_in));
|
||||
if(CPUHook || ADDBT)
|
||||
return(RunReal<true, true, false>(timestamp_in));
|
||||
else
|
||||
{
|
||||
if(ILHMode)
|
||||
return(RunReal<false, false, true>(timestamp_in));
|
||||
else
|
||||
{
|
||||
if(BIOSPrintMode)
|
||||
return(RunReal<false, true, false>(timestamp_in));
|
||||
else
|
||||
return(RunReal<false, false, false>(timestamp_in));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PS_CPU::SetCPUHook(void (*cpuh)(const pscpu_timestamp_t timestamp, uint32_t pc), void (*addbt)(uint32_t from, uint32_t to, bool exception))
|
||||
{
|
||||
ADDBT = addbt;
|
||||
|
|
|
@ -49,8 +49,8 @@ class PS_CPU
|
|||
{
|
||||
public:
|
||||
|
||||
PS_CPU();
|
||||
~PS_CPU();
|
||||
PS_CPU() MDFN_COLD;
|
||||
~PS_CPU() MDFN_COLD;
|
||||
|
||||
template<bool isReader>void SyncState(EW::NewState *ns);
|
||||
|
||||
|
@ -66,12 +66,13 @@ class PS_CPU
|
|||
next_event_ts = next_event_ts_arg;
|
||||
}
|
||||
|
||||
pscpu_timestamp_t Run(pscpu_timestamp_t timestamp_in, const bool ILHMode);
|
||||
pscpu_timestamp_t Run(pscpu_timestamp_t timestamp_in, bool BIOSPrintMode, bool ILHMode);
|
||||
|
||||
void Power(void);
|
||||
void Power(void) MDFN_COLD;
|
||||
|
||||
// which ranges 0-5, inclusive
|
||||
void AssertIRQ(int which, bool asserted);
|
||||
void AssertIRQ(unsigned which, bool asserted);
|
||||
|
||||
|
||||
void SetHalt(bool status);
|
||||
|
||||
|
@ -79,8 +80,6 @@ class PS_CPU
|
|||
void SetBIU(uint32_t val);
|
||||
uint32_t GetBIU(void);
|
||||
|
||||
int StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
private:
|
||||
|
||||
struct
|
||||
|
@ -192,8 +191,9 @@ class PS_CPU
|
|||
//uint32_t WriteAbsorb;
|
||||
//uint8_t WriteAbsorbCount;
|
||||
//uint8_t WriteAbsorbMonkey;
|
||||
uint8 MULT_Tab24[24];
|
||||
|
||||
MultiAccessSizeMem<1024, uint32, false> ScratchRAM;
|
||||
MultiAccessSizeMem<1024, false> ScratchRAM;
|
||||
|
||||
//PS_GTE GTE;
|
||||
|
||||
|
@ -219,7 +219,7 @@ class PS_CPU
|
|||
|
||||
uint32_t Exception(uint32_t code, uint32_t PC, const uint32_t NPM) MDFN_WARN_UNUSED_RESULT;
|
||||
|
||||
template<bool DebugMode, bool ILHMode> pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in);
|
||||
template<bool DebugMode, bool BIOSPrintMode, bool ILHMode> pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in) NO_INLINE;
|
||||
|
||||
template<typename T> T PeekMemory(uint32_t address) MDFN_COLD;
|
||||
template<typename T> T ReadMemory(pscpu_timestamp_t ×tamp, uint32_t address, bool DS24 = false, bool LWC_timing = false);
|
||||
|
|
|
@ -399,7 +399,7 @@
|
|||
uint64 result;
|
||||
|
||||
result = (int64)(int32)GPR[rs] * (int32)GPR[rt];
|
||||
muldiv_ts_done = timestamp + 7;
|
||||
muldiv_ts_done = timestamp + MULT_Tab24[__builtin_clz((GPR[rs] ^ ((int32)GPR[rs] >> 31)) | 0x400)];
|
||||
|
||||
DO_LDS();
|
||||
|
||||
|
@ -425,7 +425,7 @@
|
|||
uint64 result;
|
||||
|
||||
result = (uint64)GPR[rs] * GPR[rt];
|
||||
muldiv_ts_done = timestamp + 7;
|
||||
muldiv_ts_done = timestamp + MULT_Tab24[__builtin_clz(GPR[rs] | 0x400)];
|
||||
|
||||
DO_LDS();
|
||||
|
||||
|
@ -755,7 +755,7 @@
|
|||
BEGIN_OPF(BCOND, 0x01, 0);
|
||||
const uint32_t tv = GPR[(instr >> 21) & 0x1F];
|
||||
uint32_t riv = (instr >> 16) & 0x1F;
|
||||
int32_t immediate = (int16)(instr & 0xFFFF);
|
||||
uint32 immediate = (int32)(int16)(instr & 0xFFFF);
|
||||
bool result = (int32)(tv ^ (riv << 31)) < 0;
|
||||
|
||||
GPR_DEPRES_BEGIN
|
||||
|
@ -974,7 +974,8 @@
|
|||
GPR_RES(rt);
|
||||
GPR_DEPRES_END
|
||||
|
||||
uint32_t result = (bool)((int32)GPR[rs] < immediate);
|
||||
uint32 result = (bool)((int32)GPR[rs] < (int32)immediate);
|
||||
|
||||
|
||||
DO_LDS();
|
||||
|
|
@ -206,7 +206,7 @@
|
|||
BEGIN_OPF(BCOND, 0x01, 0);
|
||||
const uint32_t tv = GPR[(instr >> 21) & 0x1F];
|
||||
uint32_t riv = (instr >> 16) & 0x1F;
|
||||
int32_t immediate = (int16)(instr & 0xFFFF);
|
||||
uint32 immediate = (int32)(int16)(instr & 0xFFFF);
|
||||
bool result = (int32)(tv ^ (riv << 31)) < 0;
|
||||
|
||||
GPR_DEPRES_BEGIN
|
||||
|
@ -872,7 +872,7 @@
|
|||
uint64 result;
|
||||
|
||||
result = (int64)(int32)GPR[rs] * (int32)GPR[rt];
|
||||
muldiv_ts_done = timestamp + 7;
|
||||
muldiv_ts_done = timestamp + MULT_Tab24[__builtin_clz((GPR[rs] ^ ((int32)GPR[rs] >> 31)) | 0x400)];
|
||||
|
||||
DO_LDS();
|
||||
|
||||
|
@ -895,7 +895,7 @@
|
|||
uint64 result;
|
||||
|
||||
result = (uint64)GPR[rs] * GPR[rt];
|
||||
muldiv_ts_done = timestamp + 7;
|
||||
muldiv_ts_done = timestamp + MULT_Tab24[__builtin_clz(GPR[rs] | 0x400)];
|
||||
|
||||
DO_LDS();
|
||||
|
||||
|
@ -1038,7 +1038,8 @@
|
|||
GPR_RES(rt);
|
||||
GPR_DEPRES_END
|
||||
|
||||
uint32_t result = (bool)((int32)GPR[rs] < immediate);
|
||||
uint32 result = (bool)((int32)GPR[rs] < (int32)immediate);
|
||||
|
||||
|
||||
DO_LDS();
|
||||
|
|
@ -332,11 +332,6 @@ static int GfxDecode_PBN = 0;
|
|||
|
||||
static void DoGfxDecode(void)
|
||||
{
|
||||
unsigned tp_w, tp_h;
|
||||
|
||||
tp_w = 256;
|
||||
tp_h = 256;
|
||||
|
||||
if(GfxDecode_Buf)
|
||||
{
|
||||
for(int sy = 0; sy < GfxDecode_Buf->h; sy++)
|
||||
|
@ -344,7 +339,7 @@ static void DoGfxDecode(void)
|
|||
for(int sx = 0; sx < GfxDecode_Buf->w; sx++)
|
||||
{
|
||||
unsigned fb_x = ((sx % GfxDecode_Buf->w) + ((sy + GfxDecode_Scroll) / GfxDecode_Buf->w * GfxDecode_Buf->w)) & 1023;
|
||||
unsigned fb_y = (((sy + GfxDecode_Scroll) % GfxDecode_Buf->w) + ((((sx % GfxDecode_Buf->w) + ((sy + GfxDecode_Scroll) / GfxDecode_Buf->w * GfxDecode_Buf->w)) / 1024) * 256)) & 511;
|
||||
unsigned fb_y = (((sy + GfxDecode_Scroll) % GfxDecode_Buf->w) + ((((sx % GfxDecode_Buf->w) + ((sy + GfxDecode_Scroll) / GfxDecode_Buf->w * GfxDecode_Buf->w)) / 1024) * GfxDecode_Buf->w)) & 511;
|
||||
|
||||
uint16 pixel = GPU->PeekRAM(fb_y * 1024 + fb_x);
|
||||
|
||||
|
@ -467,14 +462,14 @@ static RegType Regs_SPU[] =
|
|||
{ PS_SPU::GSREG_CDVOL_L, "CDVolL", "CD Volume Left", 2 },
|
||||
{ PS_SPU::GSREG_CDVOL_R, "CDVolR", "CD Volume Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_DRYVOL_CTRL_L, "DryVolCL", "Dry Volume Control Left", 2 },
|
||||
{ PS_SPU::GSREG_DRYVOL_CTRL_R, "DryVolCR", "Dry Volume Control Right", 2 },
|
||||
{ PS_SPU::GSREG_RVBVOL_L, "RvbVolL", "Reverb Volume Left", 2 },
|
||||
{ PS_SPU::GSREG_RVBVOL_R, "RvbVolR", "Reverb Volume Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_DRYVOL_L, "DryVolL", "Dry Volume Left", 2 },
|
||||
{ PS_SPU::GSREG_DRYVOL_R, "DryVolR", "Dry Volume Right", 2 },
|
||||
{ PS_SPU::GSREG_MAINVOL_CTRL_L, "MainVolCL", "Main Volume Control Left", 2 },
|
||||
{ PS_SPU::GSREG_MAINVOL_CTRL_R, "MainVolCR", "Main Volume Control Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_WETVOL_L, "WetVolL", "Wet Volume Left", 2 },
|
||||
{ PS_SPU::GSREG_WETVOL_R, "WetVolR", "Wet Volume Right", 2 },
|
||||
{ PS_SPU::GSREG_MAINVOL_L, "MainVolL", "Dry Volume Left", 2 },
|
||||
{ PS_SPU::GSREG_MAINVOL_R, "MainVolR", "Dry Volume Right", 2 },
|
||||
|
||||
{ PS_SPU::GSREG_RWADDR, "RWAddr", "SPURAM Read/Write Address", 3 },
|
||||
|
||||
|
|
|
@ -96,20 +96,17 @@ void DMA_Kill(void)
|
|||
|
||||
}
|
||||
|
||||
static INLINE void RecalcIRQOut(void)
|
||||
{
|
||||
bool irqo;
|
||||
|
||||
irqo = (bool)(DMAIntStatus & ((DMAIntControl >> 16) & 0x7F));
|
||||
irqo &= (DMAIntControl >> 23) & 1;
|
||||
|
||||
// I think it's logical OR, not XOR/invert. Still kind of weird, maybe it actually does something more complicated?
|
||||
//irqo ^= (DMAIntControl >> 15) & 1;
|
||||
irqo |= (DMAIntControl >> 15) & 1;
|
||||
|
||||
IRQOut = irqo;
|
||||
IRQ_Assert(IRQ_DMA, irqo);
|
||||
}
|
||||
static INLINE void RecalcIRQOut(void)
|
||||
{
|
||||
bool irqo;
|
||||
|
||||
irqo = (bool)DMAIntStatus;
|
||||
irqo &= (DMAIntControl >> 23) & 1;
|
||||
irqo |= (DMAIntControl >> 15) & 1;
|
||||
|
||||
IRQOut = irqo;
|
||||
IRQ_Assert(IRQ_DMA, irqo);
|
||||
}
|
||||
|
||||
void DMA_ResetTS(void)
|
||||
{
|
||||
|
@ -629,153 +626,144 @@ static void CheckLinkedList(uint32 addr)
|
|||
}
|
||||
#endif
|
||||
|
||||
void DMA_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V)
|
||||
{
|
||||
int ch = (A & 0x7F) >> 4;
|
||||
|
||||
void DMA_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V)
|
||||
{
|
||||
int ch = (A & 0x7F) >> 4;
|
||||
|
||||
//if(ch == 2 || ch == 7)
|
||||
//PSX_WARNING("[DMA] Write: %08x %08x, DMAIntStatus=%08x", A, V, DMAIntStatus);
|
||||
|
||||
// FIXME if we ever have "accurate" bus emulation
|
||||
V <<= (A & 3) * 8;
|
||||
|
||||
DMA_Update(timestamp);
|
||||
|
||||
if(ch == 7)
|
||||
{
|
||||
switch(A & 0xC)
|
||||
{
|
||||
case 0x0: //fprintf(stderr, "Global DMA control: 0x%08x\n", V);
|
||||
DMAControl = V;
|
||||
RecalcHalt();
|
||||
break;
|
||||
|
||||
case 0x4:
|
||||
DMAIntControl = V & 0x00ff803f;
|
||||
DMAIntStatus &= ~(V >> 24);
|
||||
RecalcIRQOut();
|
||||
break;
|
||||
|
||||
default: PSX_WARNING("[DMA] Unknown write: %08x %08x", A, V);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch(A & 0xC)
|
||||
{
|
||||
case 0x0: DMACH[ch].BaseAddr = V & 0xFFFFFF;
|
||||
break;
|
||||
|
||||
case 0x4: DMACH[ch].BlockControl = V;
|
||||
break;
|
||||
|
||||
case 0xC:
|
||||
case 0x8:
|
||||
{
|
||||
uint32 OldCC = DMACH[ch].ChanControl;
|
||||
|
||||
//printf("CHCR: %u, %08x --- 0x%08x\n", ch, V, DMACH[ch].BlockControl);
|
||||
//
|
||||
// Kludge for DMA timing granularity and other issues. Needs to occur before setting all bits of ChanControl to the new value, to accommodate the
|
||||
// case of a game cancelling DMA and changing the type of DMA(read/write, etc.) at the same time.
|
||||
//
|
||||
if((DMACH[ch].ChanControl & (1 << 24)) && !(V & (1 << 24)))
|
||||
{
|
||||
DMACH[ch].ChanControl &= ~(1 << 24); // Clear bit before RunChannel(), so it will only finish the block it's on at most.
|
||||
RunChannel(timestamp, 128 * 16, ch);
|
||||
DMACH[ch].WordCounter = 0;
|
||||
|
||||
#if 0 // TODO(maybe, need to work out worst-case performance for abnormally/brokenly large block sizes)
|
||||
DMACH[ch].ClockCounter = (1 << 30);
|
||||
RunChannel(timestamp, 1, ch);
|
||||
DMACH[ch].ClockCounter = 0;
|
||||
#endif
|
||||
PSX_WARNING("[DMA] Forced stop for channel %d -- scanline=%d", ch, GPU->GetScanlineNum());
|
||||
//MDFN_DispMessage("[DMA] Forced stop for channel %d", ch);
|
||||
}
|
||||
|
||||
if(ch == 6)
|
||||
DMACH[ch].ChanControl = (V & 0x51000000) | 0x2;
|
||||
else
|
||||
DMACH[ch].ChanControl = V & 0x71770703;
|
||||
|
||||
if(!(OldCC & (1 << 24)) && (V & (1 << 24)))
|
||||
{
|
||||
//if(ch == 0 || ch == 1)
|
||||
// PSX_WARNING("[DMA] Started DMA for channel=%d --- CHCR=0x%08x --- BCR=0x%08x --- scanline=%d", ch, DMACH[ch].ChanControl, DMACH[ch].BlockControl, GPU->GetScanlineNum());
|
||||
|
||||
DMACH[ch].WordCounter = 0;
|
||||
DMACH[ch].ClockCounter = 0;
|
||||
|
||||
//
|
||||
// Viewpoint starts a short MEM->GPU LL DMA and apparently has race conditions that can cause a crash if it doesn't finish almost immediately(
|
||||
// or at least very quickly, which the current DMA granularity has issues with, so run the channel ahead a bit to take of this issue and potentially
|
||||
// games with similar issues).
|
||||
//
|
||||
// Though, Viewpoint isn't exactly a good game, so maybe we shouldn't bother? ;)
|
||||
//
|
||||
// Also, it's needed for RecalcHalt() to work with some semblance of workiness.
|
||||
//
|
||||
RunChannel(timestamp, 64, ch); //std::max<int>(128 - DMACycleCounter, 1)); //64); //1); //128 - DMACycleCounter);
|
||||
}
|
||||
|
||||
RecalcHalt();
|
||||
}
|
||||
break;
|
||||
}
|
||||
PSX_SetEventNT(PSX_EVENT_DMA, timestamp + CalcNextEvent(0x10000000));
|
||||
}
|
||||
|
||||
uint32 DMA_Read(const pscpu_timestamp_t timestamp, uint32 A)
|
||||
{
|
||||
int ch = (A & 0x7F) >> 4;
|
||||
uint32 ret = 0;
|
||||
|
||||
if(ch == 7)
|
||||
{
|
||||
switch(A & 0xC)
|
||||
{
|
||||
default: PSX_WARNING("[DMA] Unknown read: %08x", A);
|
||||
break;
|
||||
|
||||
case 0x0: ret = DMAControl;
|
||||
break;
|
||||
|
||||
case 0x4: ret = DMAIntControl | (DMAIntStatus << 24) | (IRQOut << 31);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else switch(A & 0xC)
|
||||
{
|
||||
case 0x0: ret = DMACH[ch].BaseAddr;
|
||||
break;
|
||||
|
||||
case 0x4: ret = DMACH[ch].BlockControl;
|
||||
break;
|
||||
|
||||
case 0xC:
|
||||
case 0x8: ret = DMACH[ch].ChanControl;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
ret >>= (A & 3) * 8;
|
||||
|
||||
//PSX_WARNING("[DMA] Read: %08x %08x", A, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
//if(ch == 2 || ch == 7)
|
||||
//PSX_WARNING("[DMA] Write: %08x %08x, DMAIntStatus=%08x", A, V, DMAIntStatus);
|
||||
|
||||
// FIXME if we ever have "accurate" bus emulation
|
||||
V <<= (A & 3) * 8;
|
||||
|
||||
DMA_Update(timestamp);
|
||||
|
||||
if(ch == 7)
|
||||
{
|
||||
switch(A & 0xC)
|
||||
{
|
||||
case 0x0: //fprintf(stderr, "Global DMA control: 0x%08x\n", V);
|
||||
DMAControl = V;
|
||||
RecalcHalt();
|
||||
break;
|
||||
|
||||
case 0x4:
|
||||
//for(int x = 0; x < 7; x++)
|
||||
//{
|
||||
// if(DMACH[x].WordCounter || (DMACH[x].ChanControl & (1 << 24)))
|
||||
// {
|
||||
// fprintf(stderr, "Write DMAIntControl while channel %d active: 0x%08x\n", x, V);
|
||||
// }
|
||||
//}
|
||||
DMAIntControl = V & 0x00ff803f;
|
||||
DMAIntStatus &= ~(V >> 24);
|
||||
|
||||
//if(DMAIntStatus ^ (DMAIntStatus & (V >> 16)))
|
||||
// fprintf(stderr, "DMAINT Fudge: %02x\n", DMAIntStatus ^ (DMAIntStatus & (V >> 16)));
|
||||
DMAIntStatus &= (V >> 16); // THIS IS ALMOST CERTAINLY WRONG AND A HACK. Remove when CDC emulation is better.
|
||||
RecalcIRQOut();
|
||||
break;
|
||||
|
||||
default: PSX_WARNING("[DMA] Unknown write: %08x %08x", A, V);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch(A & 0xC)
|
||||
{
|
||||
case 0x0: DMACH[ch].BaseAddr = V & 0xFFFFFF;
|
||||
break;
|
||||
|
||||
case 0x4: DMACH[ch].BlockControl = V;
|
||||
break;
|
||||
|
||||
case 0xC:
|
||||
case 0x8:
|
||||
{
|
||||
uint32 OldCC = DMACH[ch].ChanControl;
|
||||
|
||||
//printf("CHCR: %u, %08x --- 0x%08x\n", ch, V, DMACH[ch].BlockControl);
|
||||
//
|
||||
// Kludge for DMA timing granularity and other issues. Needs to occur before setting all bits of ChanControl to the new value, to accommodate the
|
||||
// case of a game cancelling DMA and changing the type of DMA(read/write, etc.) at the same time.
|
||||
//
|
||||
if((DMACH[ch].ChanControl & (1 << 24)) && !(V & (1 << 24)))
|
||||
{
|
||||
DMACH[ch].ChanControl &= ~(1 << 24); // Clear bit before RunChannel(), so it will only finish the block it's on at most.
|
||||
RunChannel(timestamp, 128 * 16, ch);
|
||||
DMACH[ch].WordCounter = 0;
|
||||
|
||||
#if 0 // TODO(maybe, need to work out worst-case performance for abnormally/brokenly large block sizes)
|
||||
DMACH[ch].ClockCounter = (1 << 30);
|
||||
RunChannel(timestamp, 1, ch);
|
||||
DMACH[ch].ClockCounter = 0;
|
||||
#endif
|
||||
PSX_WARNING("[DMA] Forced stop for channel %d -- scanline=%d", ch, GPU->GetScanlineNum());
|
||||
//MDFN_DispMessage("[DMA] Forced stop for channel %d", ch);
|
||||
}
|
||||
|
||||
if(ch == 6)
|
||||
DMACH[ch].ChanControl = (V & 0x51000000) | 0x2;
|
||||
else
|
||||
DMACH[ch].ChanControl = V & 0x71770703;
|
||||
|
||||
if(!(OldCC & (1 << 24)) && (V & (1 << 24)))
|
||||
{
|
||||
//if(ch == 0 || ch == 1)
|
||||
// PSX_WARNING("[DMA] Started DMA for channel=%d --- CHCR=0x%08x --- BCR=0x%08x --- scanline=%d", ch, DMACH[ch].ChanControl, DMACH[ch].BlockControl, GPU->GetScanlineNum());
|
||||
|
||||
DMACH[ch].WordCounter = 0;
|
||||
DMACH[ch].ClockCounter = 0;
|
||||
|
||||
//
|
||||
// Viewpoint starts a short MEM->GPU LL DMA and apparently has race conditions that can cause a crash if it doesn't finish almost immediately(
|
||||
// or at least very quickly, which the current DMA granularity has issues with, so run the channel ahead a bit to take of this issue and potentially
|
||||
// games with similar issues).
|
||||
//
|
||||
// Though, Viewpoint isn't exactly a good game, so maybe we shouldn't bother? ;)
|
||||
//
|
||||
// Also, it's needed for RecalcHalt() to work with some semblance of workiness.
|
||||
//
|
||||
RunChannel(timestamp, 64, ch); //std::max<int>(128 - DMACycleCounter, 1)); //64); //1); //128 - DMACycleCounter);
|
||||
}
|
||||
|
||||
RecalcHalt();
|
||||
}
|
||||
break;
|
||||
}
|
||||
PSX_SetEventNT(PSX_EVENT_DMA, timestamp + CalcNextEvent(0x10000000));
|
||||
}
|
||||
|
||||
uint32 DMA_Read(const pscpu_timestamp_t timestamp, uint32 A)
|
||||
{
|
||||
int ch = (A & 0x7F) >> 4;
|
||||
uint32 ret = 0;
|
||||
|
||||
if(ch == 7)
|
||||
{
|
||||
switch(A & 0xC)
|
||||
{
|
||||
default: PSX_WARNING("[DMA] Unknown read: %08x", A);
|
||||
break;
|
||||
|
||||
case 0x0: ret = DMAControl;
|
||||
break;
|
||||
|
||||
case 0x4: ret = DMAIntControl | (DMAIntStatus << 24) | (IRQOut << 31);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else switch(A & 0xC)
|
||||
{
|
||||
case 0x0: ret = DMACH[ch].BaseAddr;
|
||||
break;
|
||||
|
||||
case 0x4: ret = DMACH[ch].BlockControl;
|
||||
break;
|
||||
|
||||
case 0xC:
|
||||
case 0x8: ret = DMACH[ch].ChanControl;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
ret >>= (A & 3) * 8;
|
||||
|
||||
//PSX_WARNING("[DMA] Read: %08x %08x", A, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void DMA_SyncState(bool isReader, EW::NewState *ns)
|
||||
{
|
||||
|
|
|
@ -12,12 +12,12 @@ uint32 DMA_Read(const pscpu_timestamp_t timestamp, uint32 A);
|
|||
|
||||
void DMA_ResetTS(void);
|
||||
|
||||
void DMA_Power(void);
|
||||
void DMA_Power(void) MDFN_COLD;
|
||||
|
||||
void DMA_Init(void) MDFN_COLD;
|
||||
void DMA_Kill(void) MDFN_COLD;
|
||||
|
||||
void DMA_Init(void);
|
||||
void DMA_Kill(void);
|
||||
|
||||
int DMA_StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -47,12 +47,6 @@ void InputDevice::Power(void)
|
|||
{
|
||||
}
|
||||
|
||||
int InputDevice::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice::Update(const pscpu_timestamp_t timestamp)
|
||||
{
|
||||
|
||||
|
@ -97,14 +91,14 @@ bool InputDevice::Clock(bool TxD, int32 &dsr_pulse_delay)
|
|||
return(1);
|
||||
}
|
||||
|
||||
uint32 InputDevice::GetNVSize(void)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
void InputDevice::ReadNV(uint8 *buffer, uint32 offset, uint32 count)
|
||||
{
|
||||
|
||||
uint32 InputDevice::GetNVSize(void) const
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
const uint8* InputDevice::ReadNV(void) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void InputDevice::WriteNV(const uint8 *buffer, uint32 offset, uint32 count)
|
||||
|
@ -112,7 +106,7 @@ void InputDevice::WriteNV(const uint8 *buffer, uint32 offset, uint32 count)
|
|||
|
||||
}
|
||||
|
||||
uint64 InputDevice::GetNVDirtyCount(void)
|
||||
uint64 InputDevice::GetNVDirtyCount(void) const
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
@ -236,159 +230,163 @@ INLINE void FrontIO::DoDSRIRQ(void)
|
|||
}
|
||||
|
||||
|
||||
void FrontIO::Write(pscpu_timestamp_t timestamp, uint32 A, uint32 V)
|
||||
{
|
||||
assert(!(A & 0x1));
|
||||
|
||||
void FrontIO::Write(pscpu_timestamp_t timestamp, uint32 A, uint32 V)
|
||||
{
|
||||
PSX_FIODBGINFO("[FIO] Write: %08x %08x", A, V);
|
||||
|
||||
V <<= (A & 1) * 8;
|
||||
|
||||
Update(timestamp);
|
||||
|
||||
switch(A & 0xE)
|
||||
{
|
||||
case 0x0:
|
||||
case 0x2:
|
||||
V <<= (A & 2) * 8;
|
||||
TransmitBuffer = V;
|
||||
TransmitPending = true;
|
||||
TransmitInProgress = false;
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
Mode = V & 0x013F;
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
if(ClockDivider > 0 && ((V & 0x2000) != (Control & 0x2000)) && ((Control & 0x2) == (V & 0x2)) )
|
||||
PSX_DBG(PSX_DBG_WARNING, "FIO device selection changed during comm %04x->%04x\n", Control, V);
|
||||
|
||||
//printf("Control: %d, %04x\n", timestamp, V);
|
||||
Control = V & 0x3F2F;
|
||||
|
||||
if(V & 0x10)
|
||||
{
|
||||
istatus = false;
|
||||
IRQ_Assert(IRQ_SIO, false);
|
||||
}
|
||||
|
||||
if(V & 0x40) // Reset
|
||||
{
|
||||
istatus = false;
|
||||
IRQ_Assert(IRQ_SIO, false);
|
||||
|
||||
ClockDivider = 0;
|
||||
ReceivePending = false;
|
||||
TransmitPending = false;
|
||||
|
||||
ReceiveInProgress = false;
|
||||
TransmitInProgress = false;
|
||||
|
||||
ReceiveBufferAvail = false;
|
||||
|
||||
TransmitBuffer = 0;
|
||||
ReceiveBuffer = 0;
|
||||
|
||||
ReceiveBitCounter = 0;
|
||||
TransmitBitCounter = 0;
|
||||
|
||||
Mode = 0;
|
||||
Control = 0;
|
||||
Baudrate = 0;
|
||||
}
|
||||
|
||||
Ports[0]->SetDTR((Control & 0x2) && !(Control & 0x2000));
|
||||
MCPorts[0]->SetDTR((Control & 0x2) && !(Control & 0x2000));
|
||||
Ports[1]->SetDTR((Control & 0x2) && (Control & 0x2000));
|
||||
MCPorts[1]->SetDTR((Control & 0x2) && (Control & 0x2000));
|
||||
|
||||
#if 1
|
||||
if(!((Control & 0x2) && !(Control & 0x2000)))
|
||||
{
|
||||
dsr_pulse_delay[0] = 0;
|
||||
dsr_pulse_delay[2] = 0;
|
||||
dsr_active_until_ts[0] = -1;
|
||||
dsr_active_until_ts[2] = -1;
|
||||
}
|
||||
|
||||
if(!((Control & 0x2) && (Control & 0x2000)))
|
||||
{
|
||||
dsr_pulse_delay[1] = 0;
|
||||
dsr_pulse_delay[3] = 0;
|
||||
dsr_active_until_ts[1] = -1;
|
||||
dsr_active_until_ts[3] = -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
// TODO: Uncomment out in the future once our CPU emulation is a bit more accurate with timing, to prevent causing problems with games
|
||||
// that may clear the IRQ in an unsafe pattern that only works because its execution was slow enough to allow DSR to go inactive. (Whether or not
|
||||
// such games even exist though is unknown!)
|
||||
//if(timestamp < dsr_active_until_ts[0] || timestamp < dsr_active_until_ts[1] || timestamp < dsr_active_until_ts[2] || timestamp < dsr_active_until_ts[3])
|
||||
// DoDSRIRQ();
|
||||
|
||||
break;
|
||||
|
||||
case 0xe:
|
||||
Baudrate = V;
|
||||
//printf("%02x\n", V);
|
||||
//MDFN_DispMessage("%02x\n", V);
|
||||
break;
|
||||
}
|
||||
|
||||
CheckStartStopPending(timestamp, false);
|
||||
}
|
||||
|
||||
PSX_FIODBGINFO("[FIO] Write: %08x %08x", A, V);
|
||||
|
||||
Update(timestamp);
|
||||
|
||||
switch(A & 0xF)
|
||||
{
|
||||
case 0x0:
|
||||
TransmitBuffer = V;
|
||||
TransmitPending = true;
|
||||
TransmitInProgress = false;
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
Mode = V & 0x013F;
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
if(ClockDivider > 0 && ((V & 0x2000) != (Control & 0x2000)) && ((Control & 0x2) == (V & 0x2)) )
|
||||
PSX_DBG(PSX_DBG_WARNING, "FIO device selection changed during comm %04x->%04x\n", Control, V);
|
||||
|
||||
//printf("Control: %d, %04x\n", timestamp, V);
|
||||
Control = V & 0x3F2F;
|
||||
|
||||
if(V & 0x10)
|
||||
{
|
||||
istatus = false;
|
||||
IRQ_Assert(IRQ_SIO, false);
|
||||
}
|
||||
|
||||
if(V & 0x40) // Reset
|
||||
{
|
||||
istatus = false;
|
||||
IRQ_Assert(IRQ_SIO, false);
|
||||
|
||||
ClockDivider = 0;
|
||||
ReceivePending = false;
|
||||
TransmitPending = false;
|
||||
|
||||
ReceiveInProgress = false;
|
||||
TransmitInProgress = false;
|
||||
|
||||
ReceiveBufferAvail = false;
|
||||
|
||||
TransmitBuffer = 0;
|
||||
ReceiveBuffer = 0;
|
||||
|
||||
ReceiveBitCounter = 0;
|
||||
TransmitBitCounter = 0;
|
||||
|
||||
Mode = 0;
|
||||
Control = 0;
|
||||
Baudrate = 0;
|
||||
}
|
||||
|
||||
Ports[0]->SetDTR((Control & 0x2) && !(Control & 0x2000));
|
||||
MCPorts[0]->SetDTR((Control & 0x2) && !(Control & 0x2000));
|
||||
Ports[1]->SetDTR((Control & 0x2) && (Control & 0x2000));
|
||||
MCPorts[1]->SetDTR((Control & 0x2) && (Control & 0x2000));
|
||||
|
||||
#if 1
|
||||
if(!((Control & 0x2) && !(Control & 0x2000)))
|
||||
{
|
||||
dsr_pulse_delay[0] = 0;
|
||||
dsr_pulse_delay[2] = 0;
|
||||
dsr_active_until_ts[0] = -1;
|
||||
dsr_active_until_ts[2] = -1;
|
||||
}
|
||||
|
||||
if(!((Control & 0x2) && (Control & 0x2000)))
|
||||
{
|
||||
dsr_pulse_delay[1] = 0;
|
||||
dsr_pulse_delay[3] = 0;
|
||||
dsr_active_until_ts[1] = -1;
|
||||
dsr_active_until_ts[3] = -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
// TODO: Uncomment out in the future once our CPU emulation is a bit more accurate with timing, to prevent causing problems with games
|
||||
// that may clear the IRQ in an unsafe pattern that only works because its execution was slow enough to allow DSR to go inactive. (Whether or not
|
||||
// such games even exist though is unknown!)
|
||||
//if(timestamp < dsr_active_until_ts[0] || timestamp < dsr_active_until_ts[1] || timestamp < dsr_active_until_ts[2] || timestamp < dsr_active_until_ts[3])
|
||||
// DoDSRIRQ();
|
||||
|
||||
break;
|
||||
|
||||
case 0xe:
|
||||
Baudrate = V;
|
||||
//printf("%02x\n", V);
|
||||
//MDFN_DispMessage("%02x\n", V);
|
||||
break;
|
||||
}
|
||||
|
||||
CheckStartStopPending(timestamp, false);
|
||||
}
|
||||
|
||||
|
||||
uint32 FrontIO::Read(pscpu_timestamp_t timestamp, uint32 A)
|
||||
{
|
||||
uint32 ret = 0;
|
||||
|
||||
assert(!(A & 0x1));
|
||||
|
||||
Update(timestamp);
|
||||
|
||||
switch(A & 0xF)
|
||||
{
|
||||
case 0x0:
|
||||
//printf("FIO Read: 0x%02x\n", ReceiveBuffer);
|
||||
ret = ReceiveBuffer | (ReceiveBuffer << 8) | (ReceiveBuffer << 16) | (ReceiveBuffer << 24);
|
||||
ReceiveBufferAvail = false;
|
||||
ReceivePending = true;
|
||||
ReceiveInProgress = false;
|
||||
CheckStartStopPending(timestamp, false);
|
||||
break;
|
||||
|
||||
case 0x4:
|
||||
ret = 0;
|
||||
|
||||
if(!TransmitPending && !TransmitInProgress)
|
||||
ret |= 0x1;
|
||||
|
||||
if(ReceiveBufferAvail)
|
||||
ret |= 0x2;
|
||||
|
||||
if(timestamp < dsr_active_until_ts[0] || timestamp < dsr_active_until_ts[1] || timestamp < dsr_active_until_ts[2] || timestamp < dsr_active_until_ts[3])
|
||||
ret |= 0x80;
|
||||
|
||||
if(istatus)
|
||||
ret |= 0x200;
|
||||
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
ret = Mode;
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
ret = Control;
|
||||
break;
|
||||
|
||||
case 0xe:
|
||||
ret = Baudrate;
|
||||
break;
|
||||
}
|
||||
|
||||
if((A & 0xF) != 0x4)
|
||||
PSX_FIODBGINFO("[FIO] Read: %08x %08x", A, ret);
|
||||
|
||||
return(ret);
|
||||
uint32 FrontIO::Read(pscpu_timestamp_t timestamp, uint32 A)
|
||||
{
|
||||
uint32 ret = 0;
|
||||
|
||||
Update(timestamp);
|
||||
|
||||
switch(A & 0xE)
|
||||
{
|
||||
case 0x0:
|
||||
case 0x2:
|
||||
//printf("FIO Read: 0x%02x\n", ReceiveBuffer);
|
||||
ret = ReceiveBuffer | (ReceiveBuffer << 8) | (ReceiveBuffer << 16) | (ReceiveBuffer << 24);
|
||||
ReceiveBufferAvail = false;
|
||||
ReceivePending = true;
|
||||
ReceiveInProgress = false;
|
||||
CheckStartStopPending(timestamp, false);
|
||||
ret >>= (A & 2) * 8;
|
||||
break;
|
||||
|
||||
case 0x4:
|
||||
ret = 0;
|
||||
|
||||
if(!TransmitPending && !TransmitInProgress)
|
||||
ret |= 0x1;
|
||||
|
||||
if(ReceiveBufferAvail)
|
||||
ret |= 0x2;
|
||||
|
||||
if(timestamp < dsr_active_until_ts[0] || timestamp < dsr_active_until_ts[1] || timestamp < dsr_active_until_ts[2] || timestamp < dsr_active_until_ts[3])
|
||||
ret |= 0x80;
|
||||
|
||||
if(istatus)
|
||||
ret |= 0x200;
|
||||
|
||||
break;
|
||||
|
||||
case 0x8:
|
||||
ret = Mode;
|
||||
break;
|
||||
|
||||
case 0xa:
|
||||
ret = Control;
|
||||
break;
|
||||
|
||||
case 0xe:
|
||||
ret = Baudrate;
|
||||
break;
|
||||
}
|
||||
|
||||
ret >>= (A & 1) * 8;
|
||||
|
||||
if((A & 0xF) != 0x4)
|
||||
PSX_FIODBGINFO("[FIO] Read: %08x %08x", A, ret);
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t FrontIO::Update(pscpu_timestamp_t timestamp)
|
||||
|
@ -526,7 +524,7 @@ void FrontIO::ResetTS(void)
|
|||
}
|
||||
|
||||
|
||||
void FrontIO::Power(void)
|
||||
void FrontIO::Reset(bool powering_up)
|
||||
{
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
|
@ -565,10 +563,13 @@ void FrontIO::Power(void)
|
|||
Baudrate = 0;
|
||||
|
||||
//power on all plugged devices (are we doing this when attaching them?)
|
||||
for(int i=0;i<2;i++)
|
||||
if(powering_up)
|
||||
{
|
||||
if(Ports[i] != NULL) Ports[i]->Power();
|
||||
if(MCPorts[i] != NULL) MCPorts[i]->Power();
|
||||
for(int i=0;i<2;i++)
|
||||
{
|
||||
if(Ports[i] != NULL) Ports[i]->Power();
|
||||
if(MCPorts[i] != NULL) MCPorts[i]->Power();
|
||||
}
|
||||
}
|
||||
|
||||
istatus = false;
|
||||
|
@ -582,16 +583,13 @@ void FrontIO::UpdateInput(void)
|
|||
}
|
||||
}
|
||||
|
||||
// Take care to call ->Power() only if the device actually changed.
|
||||
void FrontIO::SetInput(unsigned int port, const char *type, void *ptr)
|
||||
{
|
||||
//clean up the old device
|
||||
delete Ports[port];
|
||||
Ports[port] = NULL;
|
||||
|
||||
//OCTOSHOCK TODO - not sure I understand this
|
||||
if(port < 2)
|
||||
irq10_pulse_ts[port] = PSX_EVENT_MAXTS;
|
||||
|
||||
if(!strcmp(type, "gamepad") || !strcmp(type, "dancepad"))
|
||||
Ports[port] = Device_Gamepad_Create();
|
||||
else if(!strcmp(type, "dualanalog"))
|
||||
|
@ -599,11 +597,7 @@ void FrontIO::SetInput(unsigned int port, const char *type, void *ptr)
|
|||
else if(!strcmp(type, "analogjoy"))
|
||||
Ports[port] = Device_DualAnalog_Create(true);
|
||||
else if(!strcmp(type, "dualshock"))
|
||||
{
|
||||
char name[256];
|
||||
snprintf(name, 256, "DualShock on port %u", port + 1);
|
||||
Ports[port] = Device_DualShock_Create(std::string(name));
|
||||
}
|
||||
Ports[port] = Device_DualShock_Create();
|
||||
else if(!strcmp(type, "mouse"))
|
||||
Ports[port] = Device_Mouse_Create();
|
||||
else if(!strcmp(type, "negcon"))
|
||||
|
@ -615,7 +609,14 @@ void FrontIO::SetInput(unsigned int port, const char *type, void *ptr)
|
|||
else
|
||||
Ports[port] = new InputDevice();
|
||||
|
||||
//Devices[port]->SetCrosshairsColor(chair_colors[port]);
|
||||
// " Take care to call ->Power() only if the device actually changed. " - TO THINK ABOUT. maybe irrelevant in octoshock
|
||||
//if(Devices[port] != nd)
|
||||
|
||||
//OCTOSHOCK TODO - not sure I understand this
|
||||
if(port < 2)
|
||||
irq10_pulse_ts[port] = PSX_EVENT_MAXTS;
|
||||
|
||||
Ports[port]->Power();
|
||||
PortData[port] = ptr;
|
||||
}
|
||||
|
||||
|
@ -688,231 +689,36 @@ SYNCFUNC(FrontIO)
|
|||
|
||||
}
|
||||
|
||||
int FrontIO::StateAction(StateMem* sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(ClockDivider),
|
||||
|
||||
SFVAR(ReceivePending),
|
||||
SFVAR(TransmitPending),
|
||||
|
||||
SFVAR(ReceiveInProgress),
|
||||
SFVAR(TransmitInProgress),
|
||||
|
||||
SFVAR(ReceiveBufferAvail),
|
||||
|
||||
SFVAR(ReceiveBuffer),
|
||||
SFVAR(TransmitBuffer),
|
||||
|
||||
SFVAR(ReceiveBitCounter),
|
||||
SFVAR(TransmitBitCounter),
|
||||
|
||||
SFVAR(Mode),
|
||||
SFVAR(Control),
|
||||
SFVAR(Baudrate),
|
||||
|
||||
SFVAR(istatus),
|
||||
|
||||
// FIXME: Step mode save states.
|
||||
SFARRAY32(irq10_pulse_ts, sizeof(irq10_pulse_ts) / sizeof(irq10_pulse_ts[0])),
|
||||
SFARRAY32(dsr_pulse_delay, sizeof(dsr_pulse_delay) / sizeof(dsr_pulse_delay[0])),
|
||||
SFARRAY32(dsr_active_until_ts, sizeof(dsr_active_until_ts) / sizeof(dsr_active_until_ts[0])),
|
||||
|
||||
SFEND
|
||||
};
|
||||
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "FIO");
|
||||
|
||||
//TODO - SAVESTATES
|
||||
|
||||
//for(unsigned i = 0; i < 8; i++)
|
||||
//{
|
||||
//static const char* labels[] = {
|
||||
// "FIODEV0","FIODEV1","FIODEV2","FIODEV3","FIODEV4","FIODEV5","FIODEV6","FIODEV7"
|
||||
//};
|
||||
|
||||
// ret &= Devices[i]->StateAction(sm, load, data_only, labels[i]);
|
||||
//}
|
||||
|
||||
//for(unsigned i = 0; i < 8; i++)
|
||||
//{
|
||||
//static const char* labels[] = {
|
||||
// "FIOMC0","FIOMC1","FIOMC2","FIOMC3","FIOMC4","FIOMC5","FIOMC6","FIOMC7"
|
||||
//};
|
||||
|
||||
|
||||
// ret &= DevicesMC[i]->StateAction(sm, load, data_only, labels[i]);
|
||||
//}
|
||||
|
||||
//for(unsigned i = 0; i < 2; i++)
|
||||
//{
|
||||
//static const char* labels[] = {
|
||||
// "FIOTAP0","FIOTAP1",
|
||||
//};
|
||||
|
||||
// ret &= DevicesTap[i]->StateAction(sm, load, data_only, labels[i]);
|
||||
//}
|
||||
|
||||
if(load)
|
||||
{
|
||||
IRQ_Assert(IRQ_SIO, istatus);
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
bool FrontIO::RequireNoFrameskip(void)
|
||||
{
|
||||
//this whole function is nonsense. frontend should know what it has attached
|
||||
return(false);
|
||||
}
|
||||
|
||||
void FrontIO::GPULineHook(const pscpu_timestamp_t timestamp, const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider)
|
||||
{
|
||||
Update(timestamp);
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
//octoshock edits.. not sure how safe it is
|
||||
if(Ports[i] == NULL)
|
||||
continue;
|
||||
|
||||
pscpu_timestamp_t plts = Ports[i]->GPULineHook(line_timestamp, vsync, pixels, format, width, pix_clock_offset, pix_clock, pix_clock_divider);
|
||||
|
||||
if(i < 2)
|
||||
{
|
||||
irq10_pulse_ts[i] = plts;
|
||||
|
||||
if(irq10_pulse_ts[i] <= timestamp)
|
||||
{
|
||||
irq10_pulse_ts[i] = PSX_EVENT_MAXTS;
|
||||
IRQ_Assert(IRQ_PIO, true);
|
||||
IRQ_Assert(IRQ_PIO, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PSX_SetEventNT(PSX_EVENT_FIO, CalcNextEventTS(timestamp, 0x10000000));
|
||||
}
|
||||
|
||||
static InputDeviceInfoStruct InputDeviceInfoPSXPort[] =
|
||||
{
|
||||
// None
|
||||
{
|
||||
"none",
|
||||
"none",
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
NULL
|
||||
},
|
||||
|
||||
// Gamepad(SCPH-1080)
|
||||
{
|
||||
"gamepad",
|
||||
"Digital Gamepad",
|
||||
"PlayStation digital gamepad; SCPH-1080.",
|
||||
NULL,
|
||||
sizeof(Device_Gamepad_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_Gamepad_IDII,
|
||||
},
|
||||
|
||||
// Dual Shock Gamepad(SCPH-1200)
|
||||
{
|
||||
"dualshock",
|
||||
"DualShock",
|
||||
"DualShock gamepad; SCPH-1200. Emulation in Mednafen includes the analog mode toggle button. Rumble is emulated, but currently only supported on Linux, and MS Windows via the XInput API and XInput-compatible gamepads/joysticks. If you're having trouble getting rumble to work on Linux, see if Mednafen is printing out error messages during startup regarding /dev/input/event*, and resolve the issue(s) as necessary.",
|
||||
NULL,
|
||||
sizeof(Device_DualShock_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_DualShock_IDII,
|
||||
},
|
||||
|
||||
// Dual Analog Gamepad(SCPH-1180), forced to analog mode.
|
||||
{
|
||||
"dualanalog",
|
||||
"Dual Analog",
|
||||
"Dual Analog gamepad; SCPH-1180. It is the predecessor/prototype to the more advanced DualShock. Emulated in Mednafen as forced to analog mode, and without rumble.",
|
||||
NULL,
|
||||
sizeof(Device_DualAnalog_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_DualAnalog_IDII,
|
||||
},
|
||||
|
||||
void FrontIO::GPULineHook(const pscpu_timestamp_t timestamp, const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider)
|
||||
{
|
||||
Update(timestamp);
|
||||
|
||||
for(unsigned i = 0; i < 2; i++)
|
||||
{
|
||||
pscpu_timestamp_t plts = Ports[i]->GPULineHook(line_timestamp, vsync, pixels, format, width, pix_clock_offset, pix_clock, pix_clock_divider);
|
||||
|
||||
irq10_pulse_ts[i] = plts;
|
||||
|
||||
if(irq10_pulse_ts[i] <= timestamp)
|
||||
{
|
||||
irq10_pulse_ts[i] = PSX_EVENT_MAXTS;
|
||||
IRQ_Assert(IRQ_PIO, true);
|
||||
IRQ_Assert(IRQ_PIO, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PSX_SetEventNT(PSX_EVENT_FIO, CalcNextEventTS(timestamp, 0x10000000));
|
||||
}
|
||||
|
||||
|
||||
// Analog joystick(SCPH-1110), forced to analog mode - emulated through a tweak to dual analog gamepad emulation.
|
||||
{
|
||||
"analogjoy",
|
||||
"Analog Joystick",
|
||||
"Flight-game-oriented dual-joystick controller; SCPH-1110. Emulated in Mednafen as forced to analog mode.",
|
||||
NULL,
|
||||
sizeof(Device_AnalogJoy_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_AnalogJoy_IDII,
|
||||
},
|
||||
|
||||
{
|
||||
"mouse",
|
||||
"Mouse",
|
||||
NULL,
|
||||
NULL,
|
||||
sizeof(Device_Mouse_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_Mouse_IDII,
|
||||
},
|
||||
|
||||
{
|
||||
"negcon",
|
||||
"neGcon",
|
||||
"Namco's unconventional twisty racing-game-oriented gamepad; NPC-101.",
|
||||
NULL,
|
||||
sizeof(Device_neGcon_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_neGcon_IDII,
|
||||
},
|
||||
|
||||
{
|
||||
"guncon",
|
||||
"GunCon",
|
||||
"Namco's light gun; NPC-103.",
|
||||
NULL,
|
||||
sizeof(Device_GunCon_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_GunCon_IDII,
|
||||
},
|
||||
|
||||
{
|
||||
"justifier",
|
||||
"Konami Justifier",
|
||||
"Konami's light gun; SLUH-00017. Rumored to be wrought of the coagulated rage of all who tried to shoot The Dog. If the game you want to play supports the \"GunCon\", you should use that instead. NOTE: Currently does not work properly when on any of ports 1B-1D and 2B-2D.",
|
||||
NULL,
|
||||
sizeof(Device_Justifier_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_Justifier_IDII,
|
||||
},
|
||||
|
||||
{
|
||||
"dancepad",
|
||||
"Dance Pad",
|
||||
"Dingo Dingo Rodeo!",
|
||||
NULL,
|
||||
sizeof(Device_Dancepad_IDII) / sizeof(InputDeviceInputInfoStruct),
|
||||
Device_Dancepad_IDII,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
static const InputPortInfoStruct PortInfo[] =
|
||||
{
|
||||
{ "port1", "Virtual Port 1", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port2", "Virtual Port 2", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port3", "Virtual Port 3", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port4", "Virtual Port 4", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port5", "Virtual Port 5", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port6", "Virtual Port 6", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port7", "Virtual Port 7", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
{ "port8", "Virtual Port 8", sizeof(InputDeviceInfoPSXPort) / sizeof(InputDeviceInfoStruct), InputDeviceInfoPSXPort, "gamepad" },
|
||||
};
|
||||
|
||||
InputInfoStruct FIO_InputInfo =
|
||||
{
|
||||
sizeof(PortInfo) / sizeof(InputPortInfoStruct),
|
||||
PortInfo
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ class InputDevice
|
|||
virtual void UpdateInput(const void *data);
|
||||
|
||||
virtual void SyncState(bool isReader, EW::NewState *ns) {}
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
|
||||
virtual bool RequireNoFrameskip(void);
|
||||
|
||||
|
@ -36,18 +35,19 @@ class InputDevice
|
|||
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
|
||||
//
|
||||
//
|
||||
virtual uint32 GetNVSize(void);
|
||||
virtual void ReadNV(uint8 *buffer, uint32 offset, uint32 count);
|
||||
virtual void WriteNV(const uint8 *buffer, uint32 offset, uint32 count);
|
||||
//
|
||||
//
|
||||
virtual uint32 GetNVSize(void) const;
|
||||
virtual const uint8* ReadNV(void) const; // Pointer returned should be considered temporary and assumed invalidated upon further calls to non-const functions on the object.
|
||||
virtual void WriteNV(const uint8 *buffer, uint32 offset, uint32 count);
|
||||
|
||||
//
|
||||
// Dirty count should be incremented on each call to a method this class that causes at least 1 write to occur to the
|
||||
// nonvolatile memory(IE Clock() in the correct command phase, and WriteNV()).
|
||||
//
|
||||
virtual uint64 GetNVDirtyCount(void) const;
|
||||
virtual void ResetNVDirtyCount(void);
|
||||
|
||||
//
|
||||
// Dirty count should be incremented on each call to a method this class that causes at least 1 write to occur to the
|
||||
// nonvolatile memory(IE Clock() in the correct command phase, and WriteNV()).
|
||||
//
|
||||
virtual uint64 GetNVDirtyCount(void);
|
||||
virtual void ResetNVDirtyCount(void);
|
||||
|
||||
private:
|
||||
unsigned chair_r, chair_g, chair_b;
|
||||
|
@ -65,7 +65,7 @@ class FrontIO
|
|||
|
||||
template<bool isReader>void SyncState(EW::NewState *ns);
|
||||
|
||||
void Power(void);
|
||||
void Reset(bool powering_up);
|
||||
void Write(pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
||||
uint32 Read(pscpu_timestamp_t timestamp, uint32 A);
|
||||
pscpu_timestamp_t CalcNextEventTS(pscpu_timestamp_t timestamp, int32 next_event);
|
||||
|
@ -82,8 +82,6 @@ class FrontIO
|
|||
uint64 GetMemcardDirtyCount(unsigned int which);
|
||||
|
||||
|
||||
int StateAction(StateMem* sm, int load, int data_only);
|
||||
|
||||
InputDevice *Ports[2];
|
||||
void *PortData[2];
|
||||
InputDevice *MCPorts[2];
|
||||
|
|
|
@ -18,21 +18,6 @@
|
|||
#include "psx.h"
|
||||
#include "timer.h"
|
||||
|
||||
/*
|
||||
TODO:
|
||||
Test and clean up line, particularly polyline, drawing.
|
||||
|
||||
"abe" transparency testing might be not correct, the transparency in regards to mask bit setting and evaluation may not be correct.
|
||||
|
||||
Not everything is returned in the status port read yet(double check).
|
||||
|
||||
"dfe" bit of drawing mode is probably not implemented 100% correctly.
|
||||
|
||||
Initialize more stuff in the Power() function.
|
||||
|
||||
Fix triangle span rendering order(it's bottom-to-up sometimes on the real thing, to avoid negative x step/increment values).
|
||||
*/
|
||||
|
||||
/*
|
||||
GPU display timing master clock is nominally 53.693182 MHz for NTSC PlayStations, and 53.203425 MHz for PAL PlayStations.
|
||||
|
||||
|
@ -58,24 +43,6 @@
|
|||
|
||||
*/
|
||||
|
||||
/*
|
||||
Known problematic games to do regression testing on:
|
||||
|
||||
Dukes of Hazzard: Racing For Home
|
||||
Sensitive about the GPU busy status flag being set long enough; double-check if we ever make CPU emulation more timing-accurate(
|
||||
the fix will likely just involve reducing the timing granularity for DMA and GPU updates).
|
||||
|
||||
Final Fantasy 7
|
||||
WHERE DO I BEGIN?! (Currently broken as of Jan. 1, 2012)
|
||||
|
||||
Pro Pinball (series)
|
||||
Sensitive to correct interlace and draw line skipping emulation.
|
||||
|
||||
Valkyrie Profile
|
||||
Battle scenes will go all kaka with no graphics updates if GPU LL DMA completes too soon.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
November 29, 2012 notes:
|
||||
|
||||
|
@ -90,9 +57,8 @@
|
|||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
//FILE *fp;
|
||||
|
||||
static const int32 dither_table[4][4] =
|
||||
static const int8 dither_table[4][4] =
|
||||
{
|
||||
{ -4, 0, -3, 1 },
|
||||
{ 2, -2, 3, -1 },
|
||||
|
@ -100,10 +66,12 @@ static const int32 dither_table[4][4] =
|
|||
{ 3, -1, 2, -2 },
|
||||
};
|
||||
|
||||
PS_GPU::PS_GPU(bool pal_clock_and_tv, int sls, int sle) : BlitterFIFO(0x20) // 0x10 on actual PS1 GPU, 0x20 here(see comment at top of gpu.h) // 0x10)
|
||||
PS_GPU::PS_GPU(bool pal_clock_and_tv, int sls, int sle, bool show_h_overscan) : BlitterFIFO(0x20) // 0x10 on actual PS1 GPU, 0x20 here(see comment at top of gpu.h) // 0x10)
|
||||
{
|
||||
HardwarePALType = pal_clock_and_tv;
|
||||
|
||||
hide_hoverscan = !show_h_overscan;
|
||||
|
||||
for(int y = 0; y < 4; y++)
|
||||
for(int x = 0; x < 4; x++)
|
||||
for(int v = 0; v < 512; v++)
|
||||
|
@ -124,21 +92,23 @@ PS_GPU::PS_GPU(bool pal_clock_and_tv, int sls, int sle) : BlitterFIFO(0x20) // 0
|
|||
if(HardwarePALType == false) // NTSC clock
|
||||
{
|
||||
GPUClockRatio = 103896; // 65536 * 53693181.818 / (44100 * 768)
|
||||
hmc_to_visible = 520;
|
||||
}
|
||||
else // PAL clock
|
||||
{
|
||||
GPUClockRatio = 102948; // 65536 * 53203425 / (44100 * 768)
|
||||
hmc_to_visible = 560;
|
||||
}
|
||||
|
||||
memset(RGB8SAT_Under, 0, sizeof(RGB8SAT_Under));
|
||||
|
||||
for(int i = 0; i < 256; i++)
|
||||
RGB8SAT[i] = i;
|
||||
|
||||
memset(RGB8SAT_Over, 0xFF, sizeof(RGB8SAT_Over));
|
||||
|
||||
LineVisFirst = sls;
|
||||
LineVisLast = sle;
|
||||
|
||||
|
||||
memcpy(&Commands[0x00], Commands_00_1F, sizeof(Commands_00_1F));
|
||||
memcpy(&Commands[0x20], Commands_20_3F, sizeof(Commands_20_3F));
|
||||
memcpy(&Commands[0x40], Commands_40_5F, sizeof(Commands_40_5F));
|
||||
memcpy(&Commands[0x60], Commands_60_7F, sizeof(Commands_60_7F));
|
||||
memcpy(&Commands[0x80], Commands_80_FF, sizeof(Commands_80_FF));
|
||||
}
|
||||
|
||||
PS_GPU::~PS_GPU()
|
||||
|
@ -150,10 +120,10 @@ void PS_GPU::FillVideoParams(MDFNGI* gi)
|
|||
{
|
||||
if(HardwarePALType)
|
||||
{
|
||||
gi->lcm_width = 2800;
|
||||
gi->lcm_width = hide_hoverscan ? 2640 : 2800;
|
||||
gi->lcm_height = (LineVisLast + 1 - LineVisFirst) * 2; //576;
|
||||
|
||||
gi->nominal_width = 377; // Dunno. :(
|
||||
gi->nominal_width = hide_hoverscan ? 363 : 384; // More like 385.stuff according to calculations derived from BT.601, but 384 is a nicer number. :p
|
||||
gi->nominal_height = LineVisLast + 1 - LineVisFirst; //288;
|
||||
|
||||
gi->fb_width = 768;
|
||||
|
@ -165,10 +135,10 @@ void PS_GPU::FillVideoParams(MDFNGI* gi)
|
|||
}
|
||||
else
|
||||
{
|
||||
gi->lcm_width = 2800;
|
||||
gi->lcm_width = hide_hoverscan ? 2640 : 2800;
|
||||
gi->lcm_height = (LineVisLast + 1 - LineVisFirst) * 2; //480;
|
||||
|
||||
gi->nominal_width = 320;
|
||||
gi->nominal_width = (hide_hoverscan ? 302 : 320);
|
||||
gi->nominal_height = LineVisLast + 1 - LineVisFirst; //240;
|
||||
|
||||
gi->fb_width = 768;
|
||||
|
@ -195,6 +165,8 @@ void PS_GPU::SoftReset(void) // Control command 0x00
|
|||
IRQPending = false;
|
||||
IRQ_Assert(IRQ_GPU, IRQPending);
|
||||
|
||||
InvalidateCache();
|
||||
|
||||
DMAControl = 0;
|
||||
|
||||
if(DrawTimeAvail < 0)
|
||||
|
@ -247,7 +219,7 @@ void PS_GPU::SoftReset(void) // Control command 0x00
|
|||
twx = 0;
|
||||
twy = 0;
|
||||
|
||||
RecalcTexWindowLUT();
|
||||
RecalcTexWindowStuff();
|
||||
|
||||
//
|
||||
ClipX0 = 0;
|
||||
|
@ -270,6 +242,11 @@ void PS_GPU::Power(void)
|
|||
{
|
||||
memset(GPURAM, 0, sizeof(GPURAM));
|
||||
|
||||
memset(CLUT_Cache, 0, sizeof(CLUT_Cache));
|
||||
CLUT_Cache_VB = ~0U;
|
||||
|
||||
memset(TexCache, 0xFF, sizeof(TexCache));
|
||||
|
||||
DMAControl = 0;
|
||||
|
||||
ClipX0 = 0;
|
||||
|
@ -291,8 +268,6 @@ void PS_GPU::Power(void)
|
|||
twx = 0;
|
||||
twy = 0;
|
||||
|
||||
RecalcTexWindowLUT();
|
||||
|
||||
TexPageX = 0;
|
||||
TexPageY = 0;
|
||||
SpriteFlip = 0;
|
||||
|
@ -300,6 +275,8 @@ void PS_GPU::Power(void)
|
|||
abr = 0;
|
||||
TexMode = 0;
|
||||
|
||||
RecalcTexWindowStuff();
|
||||
|
||||
BlitterFIFO.Flush();
|
||||
|
||||
InCmd = INCMD_NONE;
|
||||
|
@ -362,129 +339,7 @@ void PS_GPU::ResetTS(void)
|
|||
lastts = 0;
|
||||
}
|
||||
|
||||
template<int BlendMode, bool MaskEval_TA, bool textured>
|
||||
INLINE void PS_GPU::PlotPixel(int32 x, int32 y, uint16 fore_pix)
|
||||
{
|
||||
y &= 511; // More Y precision bits than GPU RAM installed in (non-arcade, at least) Playstation hardware.
|
||||
|
||||
if(BlendMode >= 0 && (fore_pix & 0x8000))
|
||||
{
|
||||
uint16 bg_pix = GPURAM[y][x]; // Don't use bg_pix for mask evaluation, it's modified in blending code paths.
|
||||
uint16 pix; // = fore_pix & 0x8000;
|
||||
|
||||
/*
|
||||
static const int32 tab[4][2] =
|
||||
{
|
||||
{ 2, 2 },
|
||||
{ 4, 4 },
|
||||
{ 4, -4 },
|
||||
{ 4, 1 }
|
||||
};
|
||||
*/
|
||||
// Efficient 15bpp pixel math algorithms from blargg
|
||||
switch(BlendMode)
|
||||
{
|
||||
case 0:
|
||||
bg_pix |= 0x8000;
|
||||
pix = ((fore_pix + bg_pix) - ((fore_pix ^ bg_pix) & 0x0421)) >> 1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
{
|
||||
bg_pix &= ~0x8000;
|
||||
|
||||
uint32 sum = fore_pix + bg_pix;
|
||||
uint32 carry = (sum - ((fore_pix ^ bg_pix) & 0x8421)) & 0x8420;
|
||||
|
||||
pix = (sum - carry) | (carry - (carry >> 5));
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
bg_pix |= 0x8000;
|
||||
fore_pix &= ~0x8000;
|
||||
|
||||
uint32 diff = bg_pix - fore_pix + 0x108420;
|
||||
uint32 borrow = (diff - ((bg_pix ^ fore_pix) & 0x108420)) & 0x108420;
|
||||
|
||||
pix = (diff - borrow) & (borrow - (borrow >> 5));
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
{
|
||||
bg_pix &= ~0x8000;
|
||||
fore_pix = ((fore_pix >> 2) & 0x1CE7) | 0x8000;
|
||||
|
||||
uint32 sum = fore_pix + bg_pix;
|
||||
uint32 carry = (sum - ((fore_pix ^ bg_pix) & 0x8421)) & 0x8420;
|
||||
|
||||
pix = (sum - carry) | (carry - (carry >> 5));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(!MaskEval_TA || !(GPURAM[y][x] & 0x8000))
|
||||
GPURAM[y][x] = (textured ? pix : (pix & 0x7FFF)) | MaskSetOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!MaskEval_TA || !(GPURAM[y][x] & 0x8000))
|
||||
GPURAM[y][x] = (textured ? fore_pix : (fore_pix & 0x7FFF)) | MaskSetOR;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE uint16 PS_GPU::ModTexel(uint16 texel, int32 r, int32 g, int32 b, const int32 dither_x, const int32 dither_y)
|
||||
{
|
||||
uint16 ret = texel & 0x8000;
|
||||
|
||||
ret |= DitherLUT[dither_y][dither_x][(((texel & 0x1F) * r) >> (5 - 1))] << 0;
|
||||
ret |= DitherLUT[dither_y][dither_x][(((texel & 0x3E0) * g) >> (10 - 1))] << 5;
|
||||
ret |= DitherLUT[dither_y][dither_x][(((texel & 0x7C00) * b) >> (15 - 1))] << 10;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
template<uint32 TexMode_TA>
|
||||
INLINE uint16 PS_GPU::GetTexel(const uint32 clut_offset, int32 u_arg, int32 v_arg)
|
||||
{
|
||||
uint32 u = TexWindowXLUT[u_arg];
|
||||
uint32 v = TexWindowYLUT[v_arg];
|
||||
uint32 fbtex_x = TexPageX + (u >> (2 - TexMode_TA));
|
||||
uint32 fbtex_y = TexPageY + v;
|
||||
uint16 fbw = GPURAM[fbtex_y][fbtex_x & 1023];
|
||||
|
||||
if(TexMode_TA != 2)
|
||||
{
|
||||
if(TexMode_TA == 0)
|
||||
fbw = (fbw >> ((u & 3) * 4)) & 0xF;
|
||||
else
|
||||
fbw = (fbw >> ((u & 1) * 8)) & 0xFF;
|
||||
|
||||
fbw = GPURAM[(clut_offset >> 10) & 511][(clut_offset + fbw) & 1023];
|
||||
}
|
||||
|
||||
return(fbw);
|
||||
}
|
||||
|
||||
INLINE bool PS_GPU::LineSkipTest(unsigned y)
|
||||
{
|
||||
//DisplayFB_XStart >= OffsX && DisplayFB_YStart >= OffsY &&
|
||||
// ((y & 1) == (DisplayFB_CurLineYReadout & 1))
|
||||
|
||||
if((DisplayMode & 0x24) != 0x24)
|
||||
return false;
|
||||
|
||||
if(!dfe && ((y & 1) == ((DisplayFB_YStart + field_ram_readout) & 1))/* && !DisplayOff*/) //&& (y >> 1) >= DisplayFB_YStart && (y >> 1) < (DisplayFB_YStart + (VertEnd - VertStart)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#include "gpu_polygon.inc"
|
||||
#include "gpu_sprite.inc"
|
||||
#include "gpu_line.inc"
|
||||
#include "gpu_common.inc"
|
||||
|
||||
// Special RAM write mode(16 pixels at a time), does *not* appear to use mask drawing environment settings.
|
||||
INLINE void PS_GPU::Command_FBFill(const uint32 *cb)
|
||||
|
@ -546,7 +401,7 @@ INLINE void PS_GPU::Command_FBCopy(const uint32 *cb)
|
|||
for(int32 x = 0; x < width; x += 128)
|
||||
{
|
||||
const int32 chunk_x_max = std::min<int32>(width - x, 128);
|
||||
uint16 tmpbuf[128]; // TODO: Check and see if the GPU is actually (ab)using the CLUT or texture cache.
|
||||
uint16 tmpbuf[128]; // TODO: Check and see if the GPU is actually (ab)using the texture cache(doesn't seem to be affecting CLUT cache...).
|
||||
|
||||
for(int32 chunk_x = 0; chunk_x < chunk_x_max; chunk_x++)
|
||||
{
|
||||
|
@ -615,35 +470,13 @@ INLINE void PS_GPU::Command_FBRead(const uint32 *cb)
|
|||
InCmd = INCMD_FBREAD;
|
||||
}
|
||||
|
||||
|
||||
INLINE void PS_GPU::RecalcTexWindowLUT(void)
|
||||
/*
|
||||
INLINE void PS_GPU::RecalcTexPageStuff(uint32 tpage)
|
||||
{
|
||||
const unsigned TexWindowX_AND = ~(tww << 3);
|
||||
const unsigned TexWindowX_OR = (twx & tww) << 3;
|
||||
|
||||
const unsigned TexWindowY_AND = ~(twh << 3);
|
||||
const unsigned TexWindowY_OR = (twy & twh) << 3;
|
||||
|
||||
// printf("TWX: 0x%02x, TWW: 0x%02x\n", twx, tww);
|
||||
// printf("TWY: 0x%02x, TWH: 0x%02x\n", twy, twh);
|
||||
|
||||
for(unsigned x = 0; x < 256; x++)
|
||||
{
|
||||
TexWindowXLUT[x] = (x & TexWindowX_AND) | TexWindowX_OR;
|
||||
}
|
||||
|
||||
for(unsigned y = 0; y < 256; y++)
|
||||
{
|
||||
TexWindowYLUT[y] = (y & TexWindowY_AND) | TexWindowY_OR;
|
||||
}
|
||||
|
||||
memset(TexWindowXLUT_Pre, TexWindowXLUT[0], sizeof(TexWindowXLUT_Pre));
|
||||
memset(TexWindowXLUT_Post, TexWindowXLUT[255], sizeof(TexWindowXLUT_Post));
|
||||
|
||||
memset(TexWindowYLUT_Pre, TexWindowYLUT[0], sizeof(TexWindowYLUT_Pre));
|
||||
memset(TexWindowYLUT_Post, TexWindowYLUT[255], sizeof(TexWindowYLUT_Post));
|
||||
}
|
||||
|
||||
*/
|
||||
INLINE void PS_GPU::Command_DrawMode(const uint32 *cb)
|
||||
{
|
||||
TexPageX = (*cb & 0xF) * 64;
|
||||
|
@ -656,6 +489,8 @@ INLINE void PS_GPU::Command_DrawMode(const uint32 *cb)
|
|||
|
||||
dtd = (*cb >> 9) & 1;
|
||||
dfe = (*cb >> 10) & 1;
|
||||
|
||||
RecalcTexWindowStuff();
|
||||
//printf("*******************DFE: %d -- scanline=%d\n", dfe, scanline);
|
||||
}
|
||||
|
||||
|
@ -666,19 +501,23 @@ INLINE void PS_GPU::Command_TexWindow(const uint32 *cb)
|
|||
twx = ((*cb >> 10) & 0x1F);
|
||||
twy = ((*cb >> 15) & 0x1F);
|
||||
|
||||
RecalcTexWindowLUT();
|
||||
RecalcTexWindowStuff();
|
||||
}
|
||||
|
||||
INLINE void PS_GPU::Command_Clip0(const uint32 *cb)
|
||||
{
|
||||
ClipX0 = *cb & 1023;
|
||||
ClipY0 = (*cb >> 10) & 1023;
|
||||
|
||||
//fprintf(stderr, "[GPU] Clip0: x=%d y=%d, raw=0x%08x --- %d\n", ClipX0, ClipY0, *cb, scanline);
|
||||
}
|
||||
|
||||
INLINE void PS_GPU::Command_Clip1(const uint32 *cb)
|
||||
{
|
||||
ClipX1 = *cb & 1023;
|
||||
ClipY1 = (*cb >> 10) & 1023;
|
||||
|
||||
//fprintf(stderr, "[GPU] Clip1: x=%d y=%d, raw=0x%08x --- %d\n", ClipX1, ClipY1, *cb, scanline);
|
||||
}
|
||||
|
||||
INLINE void PS_GPU::Command_DrawingOffset(const uint32 *cb)
|
||||
|
@ -686,7 +525,7 @@ INLINE void PS_GPU::Command_DrawingOffset(const uint32 *cb)
|
|||
OffsX = sign_x_to_s32(11, (*cb & 2047));
|
||||
OffsY = sign_x_to_s32(11, ((*cb >> 11) & 2047));
|
||||
|
||||
//fprintf(stderr, "[GPU] Drawing offset: %d(raw=%d) %d(raw=%d) -- %d\n", OffsX, *cb, OffsY, *cb >> 11, scanline);
|
||||
//fprintf(stderr, "[GPU] Drawing offset: x=%d y=%d, raw=0x%08x --- %d\n", OffsX, OffsY, *cb, scanline);
|
||||
}
|
||||
|
||||
INLINE void PS_GPU::Command_MaskSetting(const uint32 *cb)
|
||||
|
@ -696,9 +535,17 @@ INLINE void PS_GPU::Command_MaskSetting(const uint32 *cb)
|
|||
MaskEvalAND = (*cb & 2) ? 0x8000 : 0x0000;
|
||||
}
|
||||
|
||||
void PS_GPU::InvalidateCache(void)
|
||||
{
|
||||
CLUT_Cache_VB = ~0U;
|
||||
|
||||
for(int i=0;i<ARRAY_SIZE(TexCache);i++)
|
||||
TexCache[i].Tag = ~0U;
|
||||
}
|
||||
|
||||
INLINE void PS_GPU::Command_ClearCache(const uint32 *cb)
|
||||
{
|
||||
|
||||
InvalidateCache();
|
||||
}
|
||||
|
||||
INLINE void PS_GPU::Command_IRQ(const uint32 *cb)
|
||||
|
@ -710,24 +557,6 @@ INLINE void PS_GPU::Command_IRQ(const uint32 *cb)
|
|||
//
|
||||
// C-style function wrappers so our command table isn't so ginormous(in memory usage).
|
||||
//
|
||||
template<int numvertices, bool shaded, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
static void G_Command_DrawPolygon(PS_GPU* g, const uint32 *cb)
|
||||
{
|
||||
g->Command_DrawPolygon<numvertices, shaded, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(cb);
|
||||
}
|
||||
|
||||
template<uint8 raw_size, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
static void G_Command_DrawSprite(PS_GPU* g, const uint32 *cb)
|
||||
{
|
||||
g->Command_DrawSprite<raw_size, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(cb);
|
||||
}
|
||||
|
||||
template<bool polyline, bool goraud, int BlendMode, bool MaskEval_TA>
|
||||
static void G_Command_DrawLine(PS_GPU* g, const uint32 *cb)
|
||||
{
|
||||
g->Command_DrawLine<polyline, goraud, BlendMode, MaskEval_TA>(cb);
|
||||
}
|
||||
|
||||
static void G_Command_ClearCache(PS_GPU* g, const uint32 *cb)
|
||||
{
|
||||
g->Command_ClearCache(cb);
|
||||
|
@ -788,10 +617,53 @@ static void G_Command_MaskSetting(PS_GPU* g, const uint32 *cb)
|
|||
g->Command_MaskSetting(cb);
|
||||
}
|
||||
|
||||
CTEntry PS_GPU::Commands[0x100];
|
||||
|
||||
CTEntry PS_GPU::Commands[256] =
|
||||
const CTEntry PS_GPU::Commands_00_1F[0x20] =
|
||||
{
|
||||
#include "gpu_command_table.inc"
|
||||
/* 0x00 */
|
||||
NULLCMD(),
|
||||
OTHER_HELPER(1, 2, false, G_Command_ClearCache),
|
||||
OTHER_HELPER(3, 3, false, G_Command_FBFill),
|
||||
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0x10 */
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0x1F */
|
||||
OTHER_HELPER(1, 1, false, G_Command_IRQ)
|
||||
};
|
||||
|
||||
const CTEntry PS_GPU::Commands_80_FF[0x80] =
|
||||
{
|
||||
/* 0x80 ... 0x9F */
|
||||
OTHER_HELPER_X32(4, 2, false, G_Command_FBCopy),
|
||||
|
||||
/* 0xA0 ... 0xBF */
|
||||
OTHER_HELPER_X32(3, 2, false, G_Command_FBWrite),
|
||||
|
||||
/* 0xC0 ... 0xDF */
|
||||
OTHER_HELPER_X32(3, 2, false, G_Command_FBRead),
|
||||
|
||||
/* 0xE0 */
|
||||
|
||||
NULLCMD(),
|
||||
OTHER_HELPER(1, 2, false, G_Command_DrawMode),
|
||||
OTHER_HELPER(1, 2, false, G_Command_TexWindow),
|
||||
OTHER_HELPER(1, 1, true, G_Command_Clip0),
|
||||
OTHER_HELPER(1, 1, true, G_Command_Clip1),
|
||||
OTHER_HELPER(1, 1, true, G_Command_DrawingOffset),
|
||||
OTHER_HELPER(1, 2, false, G_Command_MaskSetting),
|
||||
|
||||
NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0xF0 */
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD()
|
||||
};
|
||||
|
||||
void PS_GPU::ProcessFIFO(void)
|
||||
|
@ -932,6 +804,7 @@ void PS_GPU::ProcessFIFO(void)
|
|||
|
||||
abr = (tpage >> 5) & 0x3;
|
||||
TexMode = (tpage >> 7) & 0x3;
|
||||
RecalcTexWindowStuff();
|
||||
}
|
||||
|
||||
if(!command->func[abr][TexMode])
|
||||
|
@ -1047,6 +920,7 @@ void PS_GPU::Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V)
|
|||
case 0x8: DataReadBuffer = 0;
|
||||
break;
|
||||
}
|
||||
//fprintf(stderr, "[GPU] CC 0x10:0x%02x, DRB=0x%02x\n", V & 0xF, DataReadBuffer);
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -1153,20 +1027,23 @@ uint32 PS_GPU::Read(const pscpu_timestamp_t timestamp, uint32 A)
|
|||
return(ret >> ((A & 3) * 8));
|
||||
}
|
||||
|
||||
/*
|
||||
static INLINE uint32 ShiftHelper(uint32 val, int shamt, uint32 mask)
|
||||
#if 0
|
||||
static INLINE uint32 MDFN_NOWARN_UNUSED ShiftHelper(uint32 val, int shamt, uint32 mask)
|
||||
{
|
||||
if(shamt < 0)
|
||||
return((val >> (-shamt)) & mask);
|
||||
else
|
||||
return((val << shamt) & mask);
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize("no-unroll-loops,no-peel-loops,no-crossjumping")
|
||||
INLINE void PS_GPU::ReorderRGB_Var(uint32 out_Rshift, uint32 out_Gshift, uint32 out_Bshift, bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x)
|
||||
{
|
||||
if(bpp24) // 24bpp
|
||||
{
|
||||
for(int32 x = dx_start; x < dx_end; x++)
|
||||
for(int32 x = dx_start; MDFN_LIKELY(x < dx_end); x++)
|
||||
{
|
||||
uint32 srcpix;
|
||||
|
||||
|
@ -1181,33 +1058,37 @@ INLINE void PS_GPU::ReorderRGB_Var(uint32 out_Rshift, uint32 out_Gshift, uint32
|
|||
} // 15bpp
|
||||
else
|
||||
{
|
||||
for(int32 x = dx_start; x < dx_end; x++)
|
||||
for(int32 x = dx_start; MDFN_LIKELY(x < dx_end); x++)
|
||||
{
|
||||
uint32 srcpix = src[fb_x >> 1];
|
||||
|
||||
dest[x] = OutputLUT[srcpix & 0x7FFF];
|
||||
//dest[x] = ShiftHelper(srcpix, out_Rshift + 3 - 0, (0xF8 << out_Rshift)) |
|
||||
// ShiftHelper(srcpix, out_Gshift + 3 - 5, (0xF8 << out_Gshift)) |
|
||||
// ShiftHelper(srcpix, out_Bshift + 3 - 10, (0xF8 << out_Bshift));
|
||||
|
||||
#if 1
|
||||
dest[x] = OutputLUT[(uint8)srcpix] | (OutputLUT + 256)[(srcpix >> 8) & 0x7F];
|
||||
#else
|
||||
dest[x] = ShiftHelper(srcpix, out_Rshift + 3 - 0, (0xF8 << out_Rshift)) |
|
||||
ShiftHelper(srcpix, out_Gshift + 3 - 5, (0xF8 << out_Gshift)) |
|
||||
ShiftHelper(srcpix, out_Bshift + 3 - 10, (0xF8 << out_Bshift));
|
||||
#endif
|
||||
fb_x = (fb_x + 2) & 0x7FF;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
template<uint32 out_Rshift, uint32 out_Gshift, uint32 out_Bshift>
|
||||
void PS_GPU::ReorderRGB(bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x)
|
||||
void NO_INLINE PS_GPU::ReorderRGB(bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x)
|
||||
{
|
||||
ReorderRGB_Var(out_Rshift, out_Gshift, out_Bshift, bpp24, src, dest, dx_start, dx_end, fb_x);
|
||||
}
|
||||
#pragma GCC pop_options
|
||||
|
||||
pscpu_timestamp_t PS_GPU::Update(const pscpu_timestamp_t sys_timestamp)
|
||||
{
|
||||
static const uint32 DotClockRatios[5] = { 10, 8, 5, 4, 7 };
|
||||
const uint32 dmc = (DisplayMode & 0x40) ? 4 : (DisplayMode & 0x3);
|
||||
const uint32 dmw = 2800 / DotClockRatios[dmc]; // Must be <= 768
|
||||
const uint32 dmw = 2800 / DotClockRatios[dmc]; // Must be <= (768 - 32)
|
||||
const uint32 dmpa = (2800 - (hide_hoverscan ? 2640 : 2800)) / DotClockRatios[dmc] / 2; // Must be <= 32
|
||||
const uint32 drxbo = 32;
|
||||
|
||||
int32 sys_clocks = sys_timestamp - lastts;
|
||||
int32 gpu_clocks;
|
||||
|
@ -1357,8 +1238,8 @@ pscpu_timestamp_t PS_GPU::Update(const pscpu_timestamp_t sys_timestamp)
|
|||
}
|
||||
char buffer[256];
|
||||
|
||||
// trio_snprintf(buffer, sizeof(buffer), _("VIDEO STANDARD MISMATCH"));
|
||||
// DrawTextTrans(surface->pixels + ((DisplayRect->h / 2) - (13 / 2)) * surface->pitch32, surface->pitch32 << 2, DisplayRect->w, (UTF8*)buffer,
|
||||
snprintf(buffer, sizeof(buffer), ("VIDEO STANDARD MISMATCH"));
|
||||
//DrawTextTrans(surface->pixels + ((DisplayRect->h / 2) - (13 / 2)) * surface->pitch32, surface->pitch32 << 2, DisplayRect->w, buffer,
|
||||
//surface->MakeColor(0x00, 0xFF, 0x00), true, MDFN_FONT_6x13_12x13);
|
||||
}
|
||||
else
|
||||
|
@ -1368,7 +1249,7 @@ pscpu_timestamp_t PS_GPU::Update(const pscpu_timestamp_t sys_timestamp)
|
|||
espec->InterlaceOn = (bool)(DisplayMode & 0x20);
|
||||
espec->InterlaceField = (bool)(DisplayMode & 0x20) && field;
|
||||
|
||||
DisplayRect->x = 0;
|
||||
DisplayRect->x = drxbo;
|
||||
DisplayRect->y = 0;
|
||||
DisplayRect->w = 0;
|
||||
DisplayRect->h = VisibleLineCount << (bool)(DisplayMode & 0x20);
|
||||
|
@ -1442,13 +1323,13 @@ pscpu_timestamp_t PS_GPU::Update(const pscpu_timestamp_t sys_timestamp)
|
|||
|
||||
if((bool)(DisplayMode & 0x08) == HardwarePALType && scanline >= FirstVisibleLine && scanline < (FirstVisibleLine + VisibleLineCount) && !skip && espec)
|
||||
{
|
||||
uint32 *dest; // = surface->pixels + (scanline - VisibleStartLine) * surface->pitch32;
|
||||
uint32 *dest;
|
||||
int32 dest_line;
|
||||
int32 fb_x = DisplayFB_XStart * 2;
|
||||
int32 dx_start = HorizStart, dx_end = HorizEnd;
|
||||
|
||||
dest_line = ((scanline - FirstVisibleLine) << espec->InterlaceOn) + espec->InterlaceField;
|
||||
dest = surface->pixels + dest_line * surface->pitch32;
|
||||
dest = surface->pixels + (drxbo - dmpa) + dest_line * surface->pitch32;
|
||||
|
||||
if(dx_end < dx_start)
|
||||
dx_end = dx_start;
|
||||
|
@ -1456,8 +1337,10 @@ pscpu_timestamp_t PS_GPU::Update(const pscpu_timestamp_t sys_timestamp)
|
|||
dx_start = dx_start / DotClockRatios[dmc];
|
||||
dx_end = dx_end / DotClockRatios[dmc];
|
||||
|
||||
dx_start -= 488 / DotClockRatios[dmc];
|
||||
dx_end -= 488 / DotClockRatios[dmc];
|
||||
dx_start -= hmc_to_visible / DotClockRatios[dmc];
|
||||
dx_end -= hmc_to_visible / DotClockRatios[dmc];
|
||||
dx_start += 7;
|
||||
dx_end += 7;
|
||||
|
||||
if(dx_start < 0)
|
||||
{
|
||||
|
@ -1472,11 +1355,7 @@ pscpu_timestamp_t PS_GPU::Update(const pscpu_timestamp_t sys_timestamp)
|
|||
if(InVBlank || DisplayOff)
|
||||
dx_start = dx_end = 0;
|
||||
|
||||
// TODO, but there are problems with this, as not all blitter busy cycles(crudely abstracted with DrawTimeAvail) are GPU RAM access cycles.
|
||||
// Also, it shouldn't be here per-se, since this code won't be all if we're frameskipping or there's a video standard mismatch
|
||||
//DrawTimeAvail -= (dx_end - dx_start) + ((DisplayMode & 0x10) ? ((dx_end - dx_start + 1) >> 1) : 0);
|
||||
|
||||
LineWidths[dest_line] = dmw;
|
||||
LineWidths[dest_line] = dmw - dmpa * 2;
|
||||
|
||||
{
|
||||
const uint16 *src = GPURAM[DisplayFB_CurLineYReadout];
|
||||
|
@ -1504,7 +1383,7 @@ pscpu_timestamp_t PS_GPU::Update(const pscpu_timestamp_t sys_timestamp)
|
|||
//if(scanline == 64)
|
||||
// printf("%u\n", sys_timestamp - ((uint64)gpu_clocks * 65536) / GPUClockRatio);
|
||||
|
||||
PSX_GPULineHook(sys_timestamp, sys_timestamp - ((uint64)gpu_clocks * 65536) / GPUClockRatio, scanline == 0, dest, &surface->format, dmw, (488 - 146) / DotClockRatios[dmc], (HardwarePALType ? 53203425 : 53693182) / DotClockRatios[dmc], DotClockRatios[dmc]);
|
||||
PSX_GPULineHook(sys_timestamp, sys_timestamp - ((uint64)gpu_clocks * 65536) / GPUClockRatio, scanline == 0, dest, &surface->format, dmw, (hmc_to_visible - 220) / DotClockRatios[dmc], (HardwarePALType ? 53203425 : 53693182) / DotClockRatios[dmc], DotClockRatios[dmc]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1561,14 +1440,15 @@ void PS_GPU::StartFrame(EmulateSpecStruct *espec_arg)
|
|||
|
||||
if(espec->VideoFormatChanged)
|
||||
{
|
||||
const auto& f = surface->format;
|
||||
|
||||
for(int rc = 0; rc < 0x8000; rc++)
|
||||
{
|
||||
uint32 r, g, b;
|
||||
const uint8 a = rc;
|
||||
const uint8 b = rc >> 8;
|
||||
|
||||
r = ((rc >> 0) & 0x1F) << 3;
|
||||
g = ((rc >> 5) & 0x1F) << 3;
|
||||
b = ((rc >> 10) & 0x1F) << 3;
|
||||
OutputLUT[rc] = espec->surface->format.MakeColor(r, g, b, 0);
|
||||
(OutputLUT + 0)[a] = ((a & 0x1F) << (3 + f.Rshift)) | ((a >> 5) << (3 + f.Gshift));
|
||||
(OutputLUT + 256)[b] = ((b & 0x3) << (6 + f.Gshift)) | (((b >> 2) & 0x1F) << (3 + f.Bshift));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1577,6 +1457,10 @@ SYNCFUNC(PS_GPU)
|
|||
{
|
||||
NSS(GPURAM);
|
||||
|
||||
NSS(CLUT_Cache);
|
||||
NSS(CLUT_Cache_VB);
|
||||
NSS(TexCache);
|
||||
|
||||
NSS(DMAControl);
|
||||
|
||||
NSS(ClipX0);
|
||||
|
@ -1617,8 +1501,6 @@ SYNCFUNC(PS_GPU)
|
|||
|
||||
NSS(InQuad_F3Vertices);
|
||||
|
||||
NSS(InQuad_clut);
|
||||
|
||||
NSS(InPLine_PrevPoint);
|
||||
|
||||
NSS(FBRW_X);
|
||||
|
@ -1660,12 +1542,7 @@ SYNCFUNC(PS_GPU)
|
|||
|
||||
if(isReader)
|
||||
{
|
||||
//cached luts; safe not to sync
|
||||
RecalcTexWindowLUT();
|
||||
|
||||
//what the hell is this? I dont like it at all.
|
||||
HorizStart &= 0xFFF;
|
||||
HorizEnd &= 0xFFF;
|
||||
RecalcTexWindowStuff();
|
||||
|
||||
//this is kind of typical stuff, BUT: a cursory inspection reveals nothing. the IRQ and CPU modules should be handling it OK
|
||||
//LOOK HERE FOR BUGS
|
||||
|
|
|
@ -39,7 +39,7 @@ class PS_GPU
|
|||
{
|
||||
public:
|
||||
|
||||
PS_GPU(bool pal_clock_and_tv, int sls, int sle) MDFN_COLD;
|
||||
PS_GPU(bool pal_clock_and_tv, int sls, int sle, bool show_h_overscan) MDFN_COLD;
|
||||
~PS_GPU() MDFN_COLD;
|
||||
|
||||
template<bool isReader>void SyncState(EW::NewState *ns);
|
||||
|
@ -48,8 +48,6 @@ class PS_GPU
|
|||
|
||||
void Power(void) MDFN_COLD;
|
||||
|
||||
int StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
void ResetTS(void);
|
||||
|
||||
void StartFrame(EmulateSpecStruct *espec);
|
||||
|
@ -105,6 +103,31 @@ class PS_GPU
|
|||
|
||||
private:
|
||||
|
||||
uint16 CLUT_Cache[256];
|
||||
uint32 CLUT_Cache_VB; // Don't try to be clever and reduce it to 16 bits... ~0U is value for invalidated state.
|
||||
|
||||
template<uint32 TexMode_TA>
|
||||
void Update_CLUT_Cache(uint16 raw_clut);
|
||||
|
||||
struct // Speedup-cache varibles, derived from other variables; shouldn't be saved in save states.
|
||||
{
|
||||
// TW*_* variables derived from tww, twh, twx, twy, TexPageX, TexPageY
|
||||
uint32 TWX_AND;
|
||||
uint32 TWX_ADD;
|
||||
|
||||
uint32 TWY_AND;
|
||||
uint32 TWY_ADD;
|
||||
} SUCV;
|
||||
void RecalcTexWindowStuff(void);
|
||||
|
||||
struct tTexCache
|
||||
{
|
||||
uint16 Data[4];
|
||||
uint32 Tag;
|
||||
} TexCache[256];
|
||||
|
||||
void InvalidateCache(void);
|
||||
|
||||
void ProcessFIFO(void);
|
||||
void WriteCB(uint32 data);
|
||||
uint32 ReadData(void);
|
||||
|
@ -135,20 +158,6 @@ class PS_GPU
|
|||
uint32 MaskEvalAND;
|
||||
|
||||
uint8 tww, twh, twx, twy;
|
||||
struct
|
||||
{
|
||||
uint8 TexWindowXLUT_Pre[16];
|
||||
uint8 TexWindowXLUT[256];
|
||||
uint8 TexWindowXLUT_Post[16];
|
||||
};
|
||||
|
||||
struct
|
||||
{
|
||||
uint8 TexWindowYLUT_Pre[16];
|
||||
uint8 TexWindowYLUT[256];
|
||||
uint8 TexWindowYLUT_Post[16];
|
||||
};
|
||||
void RecalcTexWindowLUT(void);
|
||||
|
||||
int32 TexPageX;
|
||||
int32 TexPageY;
|
||||
|
@ -158,33 +167,26 @@ class PS_GPU
|
|||
uint32 abr;
|
||||
uint32 TexMode;
|
||||
|
||||
struct
|
||||
{
|
||||
uint8 RGB8SAT_Under[256];
|
||||
uint8 RGB8SAT[256];
|
||||
uint8 RGB8SAT_Over[256];
|
||||
};
|
||||
|
||||
uint8 DitherLUT[4][4][512]; // Y, X, 8-bit source value(256 extra for saturation)
|
||||
|
||||
bool LineSkipTest(unsigned y);
|
||||
|
||||
template<int BlendMode, bool MaskEval_TA, bool textured>
|
||||
void PlotPixel(int32 x, int32 y, uint16 pix);
|
||||
void PlotPixel(uint32 x, uint32 y, uint16 pix);
|
||||
|
||||
template<uint32 TexMode_TA>
|
||||
uint16 GetTexel(uint32 clut_offset, int32 u, int32 v);
|
||||
uint16 GetTexel(uint32 u, uint32 v);
|
||||
|
||||
uint16 ModTexel(uint16 texel, int32 r, int32 g, int32 b, const int32 dither_x, const int32 dither_y);
|
||||
|
||||
template<bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode, bool MaskEval_TA>
|
||||
void DrawSpan(int y, uint32 clut_offset, const int32 x_start, const int32 x_bound, i_group ig, const i_deltas &idl);
|
||||
void DrawSpan(int y, const int32 x_start, const int32 x_bound, i_group ig, const i_deltas &idl);
|
||||
|
||||
template<bool shaded, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void DrawTriangle(tri_vertex *vertices, uint32 clut);
|
||||
void DrawTriangle(tri_vertex *vertices);
|
||||
|
||||
template<bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA, bool FlipX, bool FlipY>
|
||||
void DrawSprite(int32 x_arg, int32 y_arg, int32 w, int32 h, uint8 u_arg, uint8 v_arg, uint32 color, uint32 clut_offset);
|
||||
void DrawSprite(int32 x_arg, int32 y_arg, int32 w, int32 h, uint8 u_arg, uint8 v_arg, uint32 color);
|
||||
|
||||
template<bool goraud, int BlendMode, bool MaskEval_TA>
|
||||
void DrawLine(line_point *vertices);
|
||||
|
@ -217,6 +219,11 @@ class PS_GPU
|
|||
|
||||
private:
|
||||
static CTEntry Commands[256];
|
||||
static const CTEntry Commands_00_1F[0x20];
|
||||
static const CTEntry Commands_20_3F[0x20];
|
||||
static const CTEntry Commands_40_5F[0x20];
|
||||
static const CTEntry Commands_60_7F[0x20];
|
||||
static const CTEntry Commands_80_FF[0x80];
|
||||
|
||||
SimpleFIFO<uint32> BlitterFIFO;
|
||||
|
||||
|
@ -239,7 +246,6 @@ class PS_GPU
|
|||
uint8 InCmd_CC;
|
||||
|
||||
tri_vertex InQuad_F3Vertices[3];
|
||||
uint32 InQuad_clut;
|
||||
|
||||
line_point InPLine_PrevPoint;
|
||||
|
||||
|
@ -296,6 +302,8 @@ class PS_GPU
|
|||
//
|
||||
//
|
||||
//
|
||||
int32 hmc_to_visible;
|
||||
bool hide_hoverscan;
|
||||
|
||||
bool sl_zero_reached;
|
||||
//
|
||||
|
@ -309,7 +317,7 @@ class PS_GPU
|
|||
bool HardwarePALType;
|
||||
int LineVisFirst, LineVisLast;
|
||||
|
||||
uint32 OutputLUT[32768];
|
||||
uint32 OutputLUT[384];
|
||||
void ReorderRGB_Var(uint32 out_Rshift, uint32 out_Gshift, uint32 out_Bshift, bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x);
|
||||
|
||||
template<uint32 out_Rshift, uint32 out_Gshift, uint32 out_Bshift>
|
||||
|
|
|
@ -1,232 +0,0 @@
|
|||
//#define BM_HELPER(fg) { fg(0), fg(1), fg(2), fg(3) }
|
||||
|
||||
#define POLY_HELPER_SUB(bm, cv, tm, mam) \
|
||||
G_Command_DrawPolygon<3 + ((cv & 0x8) >> 3), ((cv & 0x10) >> 4), ((cv & 0x4) >> 2), ((cv & 0x2) >> 1) ? bm : -1, ((cv & 1) ^ 1) & ((cv & 0x4) >> 2), tm, mam >
|
||||
|
||||
#define POLY_HELPER_FG(bm, cv) \
|
||||
{ \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 1), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 1), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
}
|
||||
|
||||
#define POLY_HELPER(cv) \
|
||||
{ \
|
||||
{ POLY_HELPER_FG(0, cv), POLY_HELPER_FG(1, cv), POLY_HELPER_FG(2, cv), POLY_HELPER_FG(3, cv) }, \
|
||||
1 + (3 /*+ ((cv & 0x8) >> 3)*/) * ( 1 + ((cv & 0x4) >> 2) + ((cv & 0x10) >> 4) ) - ((cv & 0x10) >> 4), \
|
||||
1, \
|
||||
false \
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
#define SPR_HELPER_SUB(bm, cv, tm, mam) G_Command_DrawSprite<(cv >> 3) & 0x3, ((cv & 0x4) >> 2), ((cv & 0x2) >> 1) ? bm : -1, ((cv & 1) ^ 1) & ((cv & 0x4) >> 2), tm, mam>
|
||||
|
||||
#define SPR_HELPER_FG(bm, cv) \
|
||||
{ \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 1), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 1), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
}
|
||||
|
||||
|
||||
#define SPR_HELPER(cv) \
|
||||
{ \
|
||||
{ SPR_HELPER_FG(0, cv), SPR_HELPER_FG(1, cv), SPR_HELPER_FG(2, cv), SPR_HELPER_FG(3, cv) }, \
|
||||
2 + ((cv & 0x4) >> 2) + ((cv & 0x18) ? 0 : 1), \
|
||||
2 | ((cv & 0x4) >> 2) | ((cv & 0x18) ? 0 : 1), /* |, not +, for this */ \
|
||||
false \
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
#define LINE_HELPER_SUB(bm, cv, mam) G_Command_DrawLine<((cv & 0x08) >> 3), ((cv & 0x10) >> 4), ((cv & 0x2) >> 1) ? bm : -1, mam>
|
||||
|
||||
#define LINE_HELPER_FG(bm, cv) \
|
||||
{ \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 1), \
|
||||
LINE_HELPER_SUB(bm, cv, 1), \
|
||||
LINE_HELPER_SUB(bm, cv, 1), \
|
||||
LINE_HELPER_SUB(bm, cv, 1) \
|
||||
}
|
||||
|
||||
#define LINE_HELPER(cv) \
|
||||
{ \
|
||||
{ LINE_HELPER_FG(0, cv), LINE_HELPER_FG(1, cv), LINE_HELPER_FG(2, cv), LINE_HELPER_FG(3, cv) }, \
|
||||
3 + ((cv & 0x10) >> 4), \
|
||||
1, \
|
||||
false \
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
#define OTHER_HELPER_FG(bm, arg_ptr) { arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr }
|
||||
#define OTHER_HELPER(arg_cs, arg_fbcs, arg_ss, arg_ptr) { { OTHER_HELPER_FG(0, arg_ptr), OTHER_HELPER_FG(1, arg_ptr), OTHER_HELPER_FG(2, arg_ptr), OTHER_HELPER_FG(3, arg_ptr) }, arg_cs, arg_fbcs, arg_ss }
|
||||
#define OTHER_HELPER_X2(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X4(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X2(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X2(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X8(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X4(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X4(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X16(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X8(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X8(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X32(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X16(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X16(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
|
||||
#define NULLCMD_FG(bm) { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
|
||||
#define NULLCMD() { { NULLCMD_FG(0), NULLCMD_FG(1), NULLCMD_FG(2), NULLCMD_FG(3) }, 1, 1, true }
|
||||
|
||||
|
||||
/* 0x00 */
|
||||
NULLCMD(),
|
||||
OTHER_HELPER(1, 2, false, G_Command_ClearCache),
|
||||
OTHER_HELPER(3, 3, false, G_Command_FBFill),
|
||||
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0x10 */
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
OTHER_HELPER(1, 1, false, G_Command_IRQ),
|
||||
|
||||
/* 0x20 */
|
||||
POLY_HELPER(0x20),
|
||||
POLY_HELPER(0x21),
|
||||
POLY_HELPER(0x22),
|
||||
POLY_HELPER(0x23),
|
||||
POLY_HELPER(0x24),
|
||||
POLY_HELPER(0x25),
|
||||
POLY_HELPER(0x26),
|
||||
POLY_HELPER(0x27),
|
||||
POLY_HELPER(0x28),
|
||||
POLY_HELPER(0x29),
|
||||
POLY_HELPER(0x2a),
|
||||
POLY_HELPER(0x2b),
|
||||
POLY_HELPER(0x2c),
|
||||
POLY_HELPER(0x2d),
|
||||
POLY_HELPER(0x2e),
|
||||
POLY_HELPER(0x2f),
|
||||
POLY_HELPER(0x30),
|
||||
POLY_HELPER(0x31),
|
||||
POLY_HELPER(0x32),
|
||||
POLY_HELPER(0x33),
|
||||
POLY_HELPER(0x34),
|
||||
POLY_HELPER(0x35),
|
||||
POLY_HELPER(0x36),
|
||||
POLY_HELPER(0x37),
|
||||
POLY_HELPER(0x38),
|
||||
POLY_HELPER(0x39),
|
||||
POLY_HELPER(0x3a),
|
||||
POLY_HELPER(0x3b),
|
||||
POLY_HELPER(0x3c),
|
||||
POLY_HELPER(0x3d),
|
||||
POLY_HELPER(0x3e),
|
||||
POLY_HELPER(0x3f),
|
||||
|
||||
LINE_HELPER(0x40),
|
||||
LINE_HELPER(0x41),
|
||||
LINE_HELPER(0x42),
|
||||
LINE_HELPER(0x43),
|
||||
LINE_HELPER(0x44),
|
||||
LINE_HELPER(0x45),
|
||||
LINE_HELPER(0x46),
|
||||
LINE_HELPER(0x47),
|
||||
LINE_HELPER(0x48),
|
||||
LINE_HELPER(0x49),
|
||||
LINE_HELPER(0x4a),
|
||||
LINE_HELPER(0x4b),
|
||||
LINE_HELPER(0x4c),
|
||||
LINE_HELPER(0x4d),
|
||||
LINE_HELPER(0x4e),
|
||||
LINE_HELPER(0x4f),
|
||||
LINE_HELPER(0x50),
|
||||
LINE_HELPER(0x51),
|
||||
LINE_HELPER(0x52),
|
||||
LINE_HELPER(0x53),
|
||||
LINE_HELPER(0x54),
|
||||
LINE_HELPER(0x55),
|
||||
LINE_HELPER(0x56),
|
||||
LINE_HELPER(0x57),
|
||||
LINE_HELPER(0x58),
|
||||
LINE_HELPER(0x59),
|
||||
LINE_HELPER(0x5a),
|
||||
LINE_HELPER(0x5b),
|
||||
LINE_HELPER(0x5c),
|
||||
LINE_HELPER(0x5d),
|
||||
LINE_HELPER(0x5e),
|
||||
LINE_HELPER(0x5f),
|
||||
|
||||
SPR_HELPER(0x60),
|
||||
SPR_HELPER(0x61),
|
||||
SPR_HELPER(0x62),
|
||||
SPR_HELPER(0x63),
|
||||
SPR_HELPER(0x64),
|
||||
SPR_HELPER(0x65),
|
||||
SPR_HELPER(0x66),
|
||||
SPR_HELPER(0x67),
|
||||
SPR_HELPER(0x68),
|
||||
SPR_HELPER(0x69),
|
||||
SPR_HELPER(0x6a),
|
||||
SPR_HELPER(0x6b),
|
||||
SPR_HELPER(0x6c),
|
||||
SPR_HELPER(0x6d),
|
||||
SPR_HELPER(0x6e),
|
||||
SPR_HELPER(0x6f),
|
||||
SPR_HELPER(0x70),
|
||||
SPR_HELPER(0x71),
|
||||
SPR_HELPER(0x72),
|
||||
SPR_HELPER(0x73),
|
||||
SPR_HELPER(0x74),
|
||||
SPR_HELPER(0x75),
|
||||
SPR_HELPER(0x76),
|
||||
SPR_HELPER(0x77),
|
||||
SPR_HELPER(0x78),
|
||||
SPR_HELPER(0x79),
|
||||
SPR_HELPER(0x7a),
|
||||
SPR_HELPER(0x7b),
|
||||
SPR_HELPER(0x7c),
|
||||
SPR_HELPER(0x7d),
|
||||
SPR_HELPER(0x7e),
|
||||
SPR_HELPER(0x7f),
|
||||
|
||||
/* 0x80 ... 0x9F */
|
||||
OTHER_HELPER_X32(4, 2, false, G_Command_FBCopy),
|
||||
|
||||
/* 0xA0 ... 0xBF */
|
||||
OTHER_HELPER_X32(3, 2, false, G_Command_FBWrite),
|
||||
|
||||
/* 0xC0 ... 0xDF */
|
||||
OTHER_HELPER_X32(3, 2, false, G_Command_FBRead),
|
||||
|
||||
/* 0xE0 */
|
||||
|
||||
NULLCMD(),
|
||||
OTHER_HELPER(1, 2, false, G_Command_DrawMode),
|
||||
OTHER_HELPER(1, 2, false, G_Command_TexWindow),
|
||||
OTHER_HELPER(1, 1, true, G_Command_Clip0),
|
||||
OTHER_HELPER(1, 1, true, G_Command_Clip1),
|
||||
OTHER_HELPER(1, 1, true, G_Command_DrawingOffset),
|
||||
OTHER_HELPER(1, 2, false, G_Command_MaskSetting),
|
||||
|
||||
NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
||||
/* 0xF0 */
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(),
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* 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 of the License, 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
|
||||
*/
|
||||
|
||||
template<int BlendMode, bool MaskEval_TA, bool textured>
|
||||
INLINE void PS_GPU::PlotPixel(uint32 x, uint32 y, uint16 fore_pix)
|
||||
{
|
||||
y &= 511; // More Y precision bits than GPU RAM installed in (non-arcade, at least) Playstation hardware.
|
||||
|
||||
if(BlendMode >= 0 && (fore_pix & 0x8000))
|
||||
{
|
||||
uint16 bg_pix = GPURAM[y][x]; // Don't use bg_pix for mask evaluation, it's modified in blending code paths.
|
||||
uint16 pix; // = fore_pix & 0x8000;
|
||||
|
||||
/*
|
||||
static const int32 tab[4][2] =
|
||||
{
|
||||
{ 2, 2 },
|
||||
{ 4, 4 },
|
||||
{ 4, -4 },
|
||||
{ 4, 1 }
|
||||
};
|
||||
*/
|
||||
// Efficient 15bpp pixel math algorithms from blargg
|
||||
switch(BlendMode)
|
||||
{
|
||||
case 0:
|
||||
bg_pix |= 0x8000;
|
||||
pix = ((fore_pix + bg_pix) - ((fore_pix ^ bg_pix) & 0x0421)) >> 1;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
{
|
||||
bg_pix &= ~0x8000;
|
||||
|
||||
uint32 sum = fore_pix + bg_pix;
|
||||
uint32 carry = (sum - ((fore_pix ^ bg_pix) & 0x8421)) & 0x8420;
|
||||
|
||||
pix = (sum - carry) | (carry - (carry >> 5));
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
{
|
||||
bg_pix |= 0x8000;
|
||||
fore_pix &= ~0x8000;
|
||||
|
||||
uint32 diff = bg_pix - fore_pix + 0x108420;
|
||||
uint32 borrow = (diff - ((bg_pix ^ fore_pix) & 0x108420)) & 0x108420;
|
||||
|
||||
pix = (diff - borrow) & (borrow - (borrow >> 5));
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
{
|
||||
bg_pix &= ~0x8000;
|
||||
fore_pix = ((fore_pix >> 2) & 0x1CE7) | 0x8000;
|
||||
|
||||
uint32 sum = fore_pix + bg_pix;
|
||||
uint32 carry = (sum - ((fore_pix ^ bg_pix) & 0x8421)) & 0x8420;
|
||||
|
||||
pix = (sum - carry) | (carry - (carry >> 5));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(!MaskEval_TA || !(GPURAM[y][x] & 0x8000))
|
||||
GPURAM[y][x] = (textured ? pix : (pix & 0x7FFF)) | MaskSetOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!MaskEval_TA || !(GPURAM[y][x] & 0x8000))
|
||||
GPURAM[y][x] = (textured ? fore_pix : (fore_pix & 0x7FFF)) | MaskSetOR;
|
||||
}
|
||||
}
|
||||
|
||||
INLINE uint16 PS_GPU::ModTexel(uint16 texel, int32 r, int32 g, int32 b, const int32 dither_x, const int32 dither_y)
|
||||
{
|
||||
uint16 ret = texel & 0x8000;
|
||||
|
||||
ret |= DitherLUT[dither_y][dither_x][(((texel & 0x1F) * r) >> (5 - 1))] << 0;
|
||||
ret |= DitherLUT[dither_y][dither_x][(((texel & 0x3E0) * g) >> (10 - 1))] << 5;
|
||||
ret |= DitherLUT[dither_y][dither_x][(((texel & 0x7C00) * b) >> (15 - 1))] << 10;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
template<uint32 TexMode_TA>
|
||||
INLINE void PS_GPU::Update_CLUT_Cache(uint16 raw_clut)
|
||||
{
|
||||
if(TexMode_TA < 2)
|
||||
{
|
||||
const uint32 new_ccvb = ((raw_clut & 0x7FFF) | (TexMode_TA << 16)); // Confirmed upper bit of raw_clut is ignored(at least on SCPH-5501's GPU).
|
||||
|
||||
if(CLUT_Cache_VB != new_ccvb)
|
||||
{
|
||||
uint16* const gpulp = GPURAM[(raw_clut >> 6) & 0x1FF];
|
||||
const uint32 cxo = (raw_clut & 0x3F) << 4;
|
||||
const uint32 count = (TexMode_TA ? 256 : 16);
|
||||
|
||||
DrawTimeAvail -= count;
|
||||
|
||||
for(unsigned i = 0; i < count; i++)
|
||||
{
|
||||
CLUT_Cache[i] = gpulp[(cxo + i) & 0x3FF];
|
||||
}
|
||||
|
||||
CLUT_Cache_VB = new_ccvb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
TexWindowX_AND = ~(tww << 3);
|
||||
TexWindowX_ADD = ((twx & tww) << 3;
|
||||
|
||||
TexWindowY_AND = ~(twh << 3);
|
||||
TexWindowY_OR = (twy & twh) << 3;
|
||||
|
||||
uint32 u = (u_arg & TexWindowX_AND) TexWindowX_OR;
|
||||
uint32 v = (v_arg & TexWindowY_AND) | TexWindowY_OR;
|
||||
uint32 fbtex_x = TexPageX + (u >> (2 - TexMode_TA));
|
||||
uint32 fbtex_y = TexPageY + v;
|
||||
uint16 fbw = GPURAM[fbtex_y][fbtex_x & 1023];
|
||||
|
||||
if(TexMode_TA != 2)
|
||||
{
|
||||
if(TexMode_TA == 0)
|
||||
fbw = (fbw >> ((u & 3) * 4)) & 0xF;
|
||||
else
|
||||
fbw = (fbw >> ((u & 1) * 8)) & 0xFF;
|
||||
|
||||
fbw = CLUT_Cache[fbw];
|
||||
}
|
||||
#endif
|
||||
|
||||
INLINE void PS_GPU::RecalcTexWindowStuff(void)
|
||||
{
|
||||
SUCV.TWX_AND = ~(tww << 3);
|
||||
SUCV.TWX_ADD = ((twx & tww) << 3) + (TexPageX << (2 - std::min<uint32>(2, TexMode)));
|
||||
|
||||
SUCV.TWY_AND = ~(twh << 3);
|
||||
SUCV.TWY_ADD = ((twy & twh) << 3) + TexPageY;
|
||||
}
|
||||
|
||||
template<uint32 TexMode_TA>
|
||||
INLINE uint16 PS_GPU::GetTexel(uint32 u_arg, uint32 v_arg)
|
||||
{
|
||||
static_assert(TexMode_TA <= 2, "TexMode_TA must be <= 2");
|
||||
|
||||
uint32 u_ext = ((u_arg & SUCV.TWX_AND) + SUCV.TWX_ADD);
|
||||
uint32 fbtex_x = ((u_ext >> (2 - TexMode_TA))) & 1023;
|
||||
uint32 fbtex_y = (v_arg & SUCV.TWY_AND) + SUCV.TWY_ADD;
|
||||
uint32 gro = fbtex_y * 1024U + fbtex_x;
|
||||
|
||||
decltype(&TexCache[0]) c;
|
||||
|
||||
switch(TexMode_TA)
|
||||
{
|
||||
case 0: c = &TexCache[((gro >> 2) & 0x3) | ((gro >> 8) & 0xFC)]; break; // 64x64
|
||||
case 1: c = &TexCache[((gro >> 2) & 0x7) | ((gro >> 7) & 0xF8)]; break; // 64x32 (NOT 32x64!)
|
||||
case 2: c = &TexCache[((gro >> 2) & 0x7) | ((gro >> 7) & 0xF8)]; break; // 32x32
|
||||
}
|
||||
|
||||
if(MDFN_UNLIKELY(c->Tag != (gro &~ 0x3)))
|
||||
{
|
||||
// SCPH-1001 old revision GPU is like(for sprites at least): (20 + 4)
|
||||
// SCPH-5501 new revision GPU is like(for sprites at least): (12 + 4)
|
||||
//
|
||||
// We'll be conservative and just go with 4 for now, until we can run some tests with triangles too.
|
||||
//
|
||||
DrawTimeAvail -= 4;
|
||||
c->Data[0] = (&GPURAM[0][0])[gro &~ 0x3];
|
||||
c->Data[1] = (&GPURAM[0][1])[gro &~ 0x3];
|
||||
c->Data[2] = (&GPURAM[0][2])[gro &~ 0x3];
|
||||
c->Data[3] = (&GPURAM[0][3])[gro &~ 0x3];
|
||||
c->Tag = (gro &~ 0x3);
|
||||
}
|
||||
|
||||
uint16 fbw = c->Data[gro & 0x3];
|
||||
|
||||
if(TexMode_TA != 2)
|
||||
{
|
||||
if(TexMode_TA == 0)
|
||||
fbw = (fbw >> ((u_ext & 3) * 4)) & 0xF;
|
||||
else
|
||||
fbw = (fbw >> ((u_ext & 1) * 8)) & 0xFF;
|
||||
|
||||
fbw = CLUT_Cache[fbw];
|
||||
}
|
||||
|
||||
return(fbw);
|
||||
}
|
||||
|
||||
INLINE bool PS_GPU::LineSkipTest(unsigned y)
|
||||
{
|
||||
//DisplayFB_XStart >= OffsX && DisplayFB_YStart >= OffsY &&
|
||||
// ((y & 1) == (DisplayFB_CurLineYReadout & 1))
|
||||
|
||||
if((DisplayMode & 0x24) != 0x24)
|
||||
return false;
|
||||
|
||||
if(!dfe && ((y & 1) == ((DisplayFB_YStart + field_ram_readout) & 1))/* && !DisplayOff*/) //&& (y >> 1) >= DisplayFB_YStart && (y >> 1) < (DisplayFB_YStart + (VertEnd - VertStart)))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Command table generation macros follow:
|
||||
//
|
||||
|
||||
//#define BM_HELPER(fg) { fg(0), fg(1), fg(2), fg(3) }
|
||||
|
||||
#define POLY_HELPER_SUB(bm, cv, tm, mam) \
|
||||
G_Command_DrawPolygon<3 + ((cv & 0x8) >> 3), ((cv & 0x10) >> 4), ((cv & 0x4) >> 2), ((cv & 0x2) >> 1) ? bm : -1, ((cv & 1) ^ 1) & ((cv & 0x4) >> 2), tm, mam >
|
||||
|
||||
#define POLY_HELPER_FG(bm, cv) \
|
||||
{ \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 1), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 1), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
POLY_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
}
|
||||
|
||||
#define POLY_HELPER(cv) \
|
||||
{ \
|
||||
{ POLY_HELPER_FG(0, cv), POLY_HELPER_FG(1, cv), POLY_HELPER_FG(2, cv), POLY_HELPER_FG(3, cv) }, \
|
||||
1 + (3 /*+ ((cv & 0x8) >> 3)*/) * ( 1 + ((cv & 0x4) >> 2) + ((cv & 0x10) >> 4) ) - ((cv & 0x10) >> 4), \
|
||||
1, \
|
||||
false \
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
#define SPR_HELPER_SUB(bm, cv, tm, mam) G_Command_DrawSprite<(cv >> 3) & 0x3, ((cv & 0x4) >> 2), ((cv & 0x2) >> 1) ? bm : -1, ((cv & 1) ^ 1) & ((cv & 0x4) >> 2), tm, mam>
|
||||
|
||||
#define SPR_HELPER_FG(bm, cv) \
|
||||
{ \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 0), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 0 : 0), 1), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 1 : 0), 1), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
SPR_HELPER_SUB(bm, cv, ((cv & 0x4) ? 2 : 0), 1), \
|
||||
}
|
||||
|
||||
|
||||
#define SPR_HELPER(cv) \
|
||||
{ \
|
||||
{ SPR_HELPER_FG(0, cv), SPR_HELPER_FG(1, cv), SPR_HELPER_FG(2, cv), SPR_HELPER_FG(3, cv) }, \
|
||||
2 + ((cv & 0x4) >> 2) + ((cv & 0x18) ? 0 : 1), \
|
||||
2 | ((cv & 0x4) >> 2) | ((cv & 0x18) ? 0 : 1), /* |, not +, for this */ \
|
||||
false \
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
#define LINE_HELPER_SUB(bm, cv, mam) G_Command_DrawLine<((cv & 0x08) >> 3), ((cv & 0x10) >> 4), ((cv & 0x2) >> 1) ? bm : -1, mam>
|
||||
|
||||
#define LINE_HELPER_FG(bm, cv) \
|
||||
{ \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 0), \
|
||||
LINE_HELPER_SUB(bm, cv, 1), \
|
||||
LINE_HELPER_SUB(bm, cv, 1), \
|
||||
LINE_HELPER_SUB(bm, cv, 1), \
|
||||
LINE_HELPER_SUB(bm, cv, 1) \
|
||||
}
|
||||
|
||||
#define LINE_HELPER(cv) \
|
||||
{ \
|
||||
{ LINE_HELPER_FG(0, cv), LINE_HELPER_FG(1, cv), LINE_HELPER_FG(2, cv), LINE_HELPER_FG(3, cv) }, \
|
||||
3 + ((cv & 0x10) >> 4), \
|
||||
1, \
|
||||
false \
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
#define OTHER_HELPER_FG(bm, arg_ptr) { arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr, arg_ptr }
|
||||
#define OTHER_HELPER(arg_cs, arg_fbcs, arg_ss, arg_ptr) { { OTHER_HELPER_FG(0, arg_ptr), OTHER_HELPER_FG(1, arg_ptr), OTHER_HELPER_FG(2, arg_ptr), OTHER_HELPER_FG(3, arg_ptr) }, arg_cs, arg_fbcs, arg_ss }
|
||||
#define OTHER_HELPER_X2(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X4(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X2(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X2(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X8(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X4(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X4(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X16(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X8(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X8(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
#define OTHER_HELPER_X32(arg_cs, arg_fbcs, arg_ss, arg_ptr) OTHER_HELPER_X16(arg_cs, arg_fbcs, arg_ss, arg_ptr), OTHER_HELPER_X16(arg_cs, arg_fbcs, arg_ss, arg_ptr)
|
||||
|
||||
#define NULLCMD_FG(bm) { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
|
||||
#define NULLCMD() { { NULLCMD_FG(0), NULLCMD_FG(1), NULLCMD_FG(2), NULLCMD_FG(3) }, 1, 1, true }
|
||||
|
|
@ -1,3 +1,27 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* 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 of the License, 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 "psx.h"
|
||||
#include "gpu.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
#include "gpu_common.inc"
|
||||
|
||||
struct line_fxp_coord
|
||||
{
|
||||
int64 x, y;
|
||||
|
@ -236,3 +260,49 @@ INLINE void PS_GPU::Command_DrawLine(const uint32 *cb)
|
|||
DrawLine<goraud, BlendMode, MaskEval_TA>(points);
|
||||
}
|
||||
|
||||
//
|
||||
// C-style function wrappers so our command table isn't so ginormous(in memory usage).
|
||||
//
|
||||
template<bool polyline, bool goraud, int BlendMode, bool MaskEval_TA>
|
||||
static void G_Command_DrawLine(PS_GPU* g, const uint32 *cb)
|
||||
{
|
||||
g->Command_DrawLine<polyline, goraud, BlendMode, MaskEval_TA>(cb);
|
||||
}
|
||||
|
||||
const CTEntry PS_GPU::Commands_40_5F[0x20] =
|
||||
{
|
||||
LINE_HELPER(0x40),
|
||||
LINE_HELPER(0x41),
|
||||
LINE_HELPER(0x42),
|
||||
LINE_HELPER(0x43),
|
||||
LINE_HELPER(0x44),
|
||||
LINE_HELPER(0x45),
|
||||
LINE_HELPER(0x46),
|
||||
LINE_HELPER(0x47),
|
||||
LINE_HELPER(0x48),
|
||||
LINE_HELPER(0x49),
|
||||
LINE_HELPER(0x4a),
|
||||
LINE_HELPER(0x4b),
|
||||
LINE_HELPER(0x4c),
|
||||
LINE_HELPER(0x4d),
|
||||
LINE_HELPER(0x4e),
|
||||
LINE_HELPER(0x4f),
|
||||
LINE_HELPER(0x50),
|
||||
LINE_HELPER(0x51),
|
||||
LINE_HELPER(0x52),
|
||||
LINE_HELPER(0x53),
|
||||
LINE_HELPER(0x54),
|
||||
LINE_HELPER(0x55),
|
||||
LINE_HELPER(0x56),
|
||||
LINE_HELPER(0x57),
|
||||
LINE_HELPER(0x58),
|
||||
LINE_HELPER(0x59),
|
||||
LINE_HELPER(0x5a),
|
||||
LINE_HELPER(0x5b),
|
||||
LINE_HELPER(0x5c),
|
||||
LINE_HELPER(0x5d),
|
||||
LINE_HELPER(0x5e),
|
||||
LINE_HELPER(0x5f)
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,632 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* 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 of the License, 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
|
||||
*/
|
||||
|
||||
#pragma GCC optimize ("no-unroll-loops,no-peel-loops")
|
||||
|
||||
#include "psx.h"
|
||||
#include "gpu.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
#include "gpu_common.inc"
|
||||
|
||||
#define COORD_FBS 12
|
||||
#define COORD_MF_INT(n) ((n) << COORD_FBS)
|
||||
#define COORD_POST_PADDING 12
|
||||
|
||||
struct i_group
|
||||
{
|
||||
uint32 u, v;
|
||||
uint32 r, g, b;
|
||||
};
|
||||
|
||||
struct i_deltas
|
||||
{
|
||||
uint32 du_dx, dv_dx;
|
||||
uint32 dr_dx, dg_dx, db_dx;
|
||||
|
||||
uint32 du_dy, dv_dy;
|
||||
uint32 dr_dy, dg_dy, db_dy;
|
||||
};
|
||||
|
||||
static INLINE int64 MakePolyXFP(uint32 x)
|
||||
{
|
||||
return ((uint64)x << 32) + ((1ULL << 32) - (1 << 11));
|
||||
}
|
||||
|
||||
static INLINE int64 MakePolyXFPStep(int32 dx, int32 dy)
|
||||
{
|
||||
int64 ret;
|
||||
int64 dx_ex = (uint64)dx << 32;
|
||||
|
||||
if(dx_ex < 0)
|
||||
dx_ex -= dy - 1;
|
||||
|
||||
if(dx_ex > 0)
|
||||
dx_ex += dy - 1;
|
||||
|
||||
ret = dx_ex / dy;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static INLINE int32 GetPolyXFP_Int(int64 xfp)
|
||||
{
|
||||
return(xfp >> 32);
|
||||
}
|
||||
|
||||
#define CALCIS(x,y) (((B.x - A.x) * (C.y - B.y)) - ((C.x - B.x) * (B.y - A.y)))
|
||||
template<bool goraud, bool textured>
|
||||
static INLINE bool CalcIDeltas(i_deltas &idl, const tri_vertex &A, const tri_vertex &B, const tri_vertex &C)
|
||||
{
|
||||
int32 denom = CALCIS(x, y);
|
||||
|
||||
if(!denom)
|
||||
return(false);
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
idl.dr_dx = (uint32)(CALCIS(r, y) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
idl.dr_dy = (uint32)(CALCIS(x, r) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
|
||||
idl.dg_dx = (uint32)(CALCIS(g, y) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
idl.dg_dy = (uint32)(CALCIS(x, g) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
|
||||
idl.db_dx = (uint32)(CALCIS(b, y) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
idl.db_dy = (uint32)(CALCIS(x, b) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
}
|
||||
|
||||
if(textured)
|
||||
{
|
||||
idl.du_dx = (uint32)(CALCIS(u, y) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
idl.du_dy = (uint32)(CALCIS(x, u) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
|
||||
idl.dv_dx = (uint32)(CALCIS(v, y) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
idl.dv_dy = (uint32)(CALCIS(x, v) * (1 << COORD_FBS) / denom) << COORD_POST_PADDING;
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
#undef CALCIS
|
||||
|
||||
template<bool goraud, bool textured>
|
||||
static INLINE void AddIDeltas_DX(i_group &ig, const i_deltas &idl, uint32 count = 1)
|
||||
{
|
||||
if(textured)
|
||||
{
|
||||
ig.u += idl.du_dx * count;
|
||||
ig.v += idl.dv_dx * count;
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += idl.dr_dx * count;
|
||||
ig.g += idl.dg_dx * count;
|
||||
ig.b += idl.db_dx * count;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured>
|
||||
static INLINE void AddIDeltas_DY(i_group &ig, const i_deltas &idl, uint32 count = 1)
|
||||
{
|
||||
if(textured)
|
||||
{
|
||||
ig.u += idl.du_dy * count;
|
||||
ig.v += idl.dv_dy * count;
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += idl.dr_dy * count;
|
||||
ig.g += idl.dg_dy * count;
|
||||
ig.b += idl.db_dy * count;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
INLINE void PS_GPU::DrawSpan(int y, const int32 x_start, const int32 x_bound, i_group ig, const i_deltas &idl)
|
||||
{
|
||||
if(LineSkipTest(y))
|
||||
return;
|
||||
|
||||
if(textured)
|
||||
{
|
||||
ig.u += (x_start * idl.du_dx) + (y * idl.du_dy);
|
||||
ig.v += (x_start * idl.dv_dx) + (y * idl.dv_dy);
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += (x_start * idl.dr_dx) + (y * idl.dr_dy);
|
||||
ig.g += (x_start * idl.dg_dx) + (y * idl.dg_dy);
|
||||
ig.b += (x_start * idl.db_dx) + (y * idl.db_dy);
|
||||
}
|
||||
|
||||
for(int32 xi = x_start; MDFN_LIKELY(xi < x_bound); xi++, AddIDeltas_DX<goraud, textured>(ig, idl))
|
||||
{
|
||||
uint32 r, g, b;
|
||||
int32 x = sign_x_to_s32(11, xi);
|
||||
|
||||
if(goraud || textured)
|
||||
DrawTimeAvail -= 2;
|
||||
else
|
||||
DrawTimeAvail--;
|
||||
|
||||
if(x > ClipX1)
|
||||
break;
|
||||
|
||||
if(x < ClipX0)
|
||||
continue;
|
||||
|
||||
if(!(goraud || textured) && ((BlendMode >= 0) || MaskEval_TA))
|
||||
{
|
||||
DrawTimeAvail -= (x & 1);
|
||||
}
|
||||
|
||||
r = ig.r >> (COORD_FBS + COORD_POST_PADDING);
|
||||
g = ig.g >> (COORD_FBS + COORD_POST_PADDING);
|
||||
b = ig.b >> (COORD_FBS + COORD_POST_PADDING);
|
||||
|
||||
if(textured)
|
||||
{
|
||||
uint16 fbw = GetTexel<TexMode_TA>(ig.u >> (COORD_FBS + COORD_POST_PADDING), ig.v >> (COORD_FBS + COORD_POST_PADDING));
|
||||
|
||||
if(fbw)
|
||||
{
|
||||
if(TexMult)
|
||||
{
|
||||
uint32 dither_x = x & 3;
|
||||
uint32 dither_y = y & 3;
|
||||
|
||||
if(!dtd)
|
||||
{
|
||||
dither_x = 3;
|
||||
dither_y = 2;
|
||||
}
|
||||
|
||||
fbw = ModTexel(fbw, r, g, b, dither_x, dither_y);
|
||||
}
|
||||
PlotPixel<BlendMode, MaskEval_TA, true>(x, y, fbw);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16 pix = 0x8000;
|
||||
|
||||
if(goraud && dtd)
|
||||
{
|
||||
pix |= DitherLUT[y & 3][x & 3][r] << 0;
|
||||
pix |= DitherLUT[y & 3][x & 3][g] << 5;
|
||||
pix |= DitherLUT[y & 3][x & 3][b] << 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
pix |= (r >> 3) << 0;
|
||||
pix |= (g >> 3) << 5;
|
||||
pix |= (b >> 3) << 10;
|
||||
}
|
||||
|
||||
PlotPixel<BlendMode, MaskEval_TA, false>(x, y, pix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
INLINE void PS_GPU::DrawTriangle(tri_vertex *vertices)
|
||||
{
|
||||
i_deltas idl;
|
||||
unsigned core_vertex;
|
||||
|
||||
//
|
||||
// Calculate the "core" vertex based on the unsorted input vertices, and sort vertices by Y.
|
||||
//
|
||||
{
|
||||
unsigned cvtemp = 0;
|
||||
|
||||
if(vertices[1].x <= vertices[0].x)
|
||||
{
|
||||
if(vertices[2].x <= vertices[1].x)
|
||||
cvtemp = (1 << 2);
|
||||
else
|
||||
cvtemp = (1 << 1);
|
||||
}
|
||||
else
|
||||
cvtemp = (1 << 0);
|
||||
|
||||
if(vertices[2].y < vertices[1].y)
|
||||
{
|
||||
std::swap(vertices[2], vertices[1]);
|
||||
cvtemp = ((cvtemp >> 1) & 0x2) | ((cvtemp << 1) & 0x4) | (cvtemp & 0x1);
|
||||
}
|
||||
|
||||
if(vertices[1].y < vertices[0].y)
|
||||
{
|
||||
std::swap(vertices[1], vertices[0]);
|
||||
cvtemp = ((cvtemp >> 1) & 0x1) | ((cvtemp << 1) & 0x2) | (cvtemp & 0x4);
|
||||
}
|
||||
|
||||
if(vertices[2].y < vertices[1].y)
|
||||
{
|
||||
std::swap(vertices[2], vertices[1]);
|
||||
cvtemp = ((cvtemp >> 1) & 0x2) | ((cvtemp << 1) & 0x4) | (cvtemp & 0x1);
|
||||
}
|
||||
|
||||
core_vertex = cvtemp >> 1;
|
||||
}
|
||||
|
||||
//
|
||||
// 0-height, abort out.
|
||||
//
|
||||
if(vertices[0].y == vertices[2].y)
|
||||
return;
|
||||
|
||||
if((vertices[2].y - vertices[0].y) >= 512)
|
||||
{
|
||||
//PSX_WARNING("[GPU] Triangle height too large: %d", (vertices[2].y - vertices[0].y));
|
||||
return;
|
||||
}
|
||||
|
||||
if(abs(vertices[2].x - vertices[0].x) >= 1024 ||
|
||||
abs(vertices[2].x - vertices[1].x) >= 1024 ||
|
||||
abs(vertices[1].x - vertices[0].x) >= 1024)
|
||||
{
|
||||
//PSX_WARNING("[GPU] Triangle width too large: %d %d %d", abs(vertices[2].x - vertices[0].x), abs(vertices[2].x - vertices[1].x), abs(vertices[1].x - vertices[0].x));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!CalcIDeltas<goraud, textured>(idl, vertices[0], vertices[1], vertices[2]))
|
||||
return;
|
||||
|
||||
// [0] should be top vertex, [2] should be bottom vertex, [1] should be off to the side vertex.
|
||||
//
|
||||
//
|
||||
int64 base_coord;
|
||||
int64 base_step;
|
||||
|
||||
int64 bound_coord_us;
|
||||
int64 bound_coord_ls;
|
||||
|
||||
bool right_facing;
|
||||
i_group ig;
|
||||
|
||||
if(textured)
|
||||
{
|
||||
ig.u = (COORD_MF_INT(vertices[core_vertex].u) + (1 << (COORD_FBS - 1))) << COORD_POST_PADDING;
|
||||
ig.v = (COORD_MF_INT(vertices[core_vertex].v) + (1 << (COORD_FBS - 1))) << COORD_POST_PADDING;
|
||||
}
|
||||
|
||||
ig.r = (COORD_MF_INT(vertices[core_vertex].r) + (1 << (COORD_FBS - 1))) << COORD_POST_PADDING;
|
||||
ig.g = (COORD_MF_INT(vertices[core_vertex].g) + (1 << (COORD_FBS - 1))) << COORD_POST_PADDING;
|
||||
ig.b = (COORD_MF_INT(vertices[core_vertex].b) + (1 << (COORD_FBS - 1))) << COORD_POST_PADDING;
|
||||
|
||||
AddIDeltas_DX<goraud, textured>(ig, idl, -vertices[core_vertex].x);
|
||||
AddIDeltas_DY<goraud, textured>(ig, idl, -vertices[core_vertex].y);
|
||||
|
||||
base_coord = MakePolyXFP(vertices[0].x);
|
||||
base_step = MakePolyXFPStep((vertices[2].x - vertices[0].x), (vertices[2].y - vertices[0].y));
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
if(vertices[1].y == vertices[0].y)
|
||||
{
|
||||
bound_coord_us = 0;
|
||||
right_facing = (bool)(vertices[1].x > vertices[0].x);
|
||||
}
|
||||
else
|
||||
{
|
||||
bound_coord_us = MakePolyXFPStep((vertices[1].x - vertices[0].x), (vertices[1].y - vertices[0].y));
|
||||
right_facing = (bool)(bound_coord_us > base_step);
|
||||
}
|
||||
|
||||
if(vertices[2].y == vertices[1].y)
|
||||
bound_coord_ls = 0;
|
||||
else
|
||||
bound_coord_ls = MakePolyXFPStep((vertices[2].x - vertices[1].x), (vertices[2].y - vertices[1].y));
|
||||
|
||||
//
|
||||
// Left side draw order
|
||||
//
|
||||
// core_vertex == 0
|
||||
// Left(base): vertices[0] -> (?vertices[1]?) -> vertices[2]
|
||||
//
|
||||
// core_vertex == 1:
|
||||
// Left(base): vertices[1] -> vertices[2], vertices[1] -> vertices[0]
|
||||
//
|
||||
// core_vertex == 2:
|
||||
// Left(base): vertices[2] -> (?vertices[1]?) -> vertices[0]
|
||||
//printf("%d %d\n", core_vertex, right_facing);
|
||||
struct
|
||||
{
|
||||
uint64 x_coord[2];
|
||||
uint64 x_step[2];
|
||||
|
||||
int32 y_coord;
|
||||
int32 y_bound;
|
||||
|
||||
bool dec_mode;
|
||||
} tripart[2];
|
||||
|
||||
#if 0
|
||||
switch(core_vertex)
|
||||
{
|
||||
case 0:
|
||||
tripart[0].dec_mode = tripart[1].dec_mode = false;
|
||||
|
||||
tripart[0].y_coord = vertices[0].y;
|
||||
tripart[0].y_bound = vertices[1].y;
|
||||
if(vertices[0].y != vertices[1].y)
|
||||
{
|
||||
tripart[0].x_coord[0] = MakePolyXFP(vertices[0].x);
|
||||
tripart[0].x_step[0] =
|
||||
|
||||
tripart[0].x_coord[1] = MakePolyXFP(vertices[0].x);
|
||||
tripart[0].x_step[1] =
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
break;
|
||||
|
||||
case 2:
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned vo = 0;
|
||||
unsigned vp = 0;
|
||||
|
||||
if(core_vertex)
|
||||
vo = 1;
|
||||
|
||||
if(core_vertex == 2)
|
||||
vp = 3;
|
||||
|
||||
{
|
||||
auto* tp = &tripart[vo];
|
||||
|
||||
tp->y_coord = vertices[0 ^ vo].y;
|
||||
tp->y_bound = vertices[1 ^ vo].y;
|
||||
tp->x_coord[right_facing] = MakePolyXFP(vertices[0 ^ vo].x);
|
||||
tp->x_step[right_facing] = bound_coord_us;
|
||||
tp->x_coord[!right_facing] = base_coord + ((vertices[vo].y - vertices[0].y) * base_step);
|
||||
tp->x_step[!right_facing] = base_step;
|
||||
tp->dec_mode = !!vo;
|
||||
}
|
||||
|
||||
{
|
||||
auto* tp = &tripart[vo ^ 1];
|
||||
|
||||
tp->y_coord = vertices[1 ^ vp].y;
|
||||
tp->y_bound = vertices[2 ^ vp].y;
|
||||
tp->x_coord[right_facing] = MakePolyXFP(vertices[1 ^ vp].x);
|
||||
tp->x_step[right_facing] = bound_coord_ls;
|
||||
tp->x_coord[!right_facing] = base_coord + ((vertices[1 ^ vp].y - vertices[0].y) * base_step); //base_coord + ((vertices[1].y - vertices[0].y) * base_step);
|
||||
tp->x_step[!right_facing] = base_step;
|
||||
tp->dec_mode = !!vp;
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < 2; i++) //2; i++)
|
||||
{
|
||||
int32 yi = tripart[i].y_coord;
|
||||
int32 yb = tripart[i].y_bound;
|
||||
|
||||
uint64 lc = tripart[i].x_coord[0];
|
||||
uint64 ls = tripart[i].x_step[0];
|
||||
|
||||
uint64 rc = tripart[i].x_coord[1];
|
||||
uint64 rs = tripart[i].x_step[1];
|
||||
|
||||
if(tripart[i].dec_mode)
|
||||
{
|
||||
while(MDFN_LIKELY(yi > yb))
|
||||
{
|
||||
yi--;
|
||||
lc -= ls;
|
||||
rc -= rs;
|
||||
//
|
||||
//
|
||||
//
|
||||
int32 y = sign_x_to_s32(11, yi);
|
||||
|
||||
if(y < ClipY0)
|
||||
break;
|
||||
|
||||
if(y > ClipY1)
|
||||
{
|
||||
DrawTimeAvail -= 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, GetPolyXFP_Int(lc), GetPolyXFP_Int(rc), ig, idl);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(MDFN_LIKELY(yi < yb))
|
||||
{
|
||||
int32 y = sign_x_to_s32(11, yi);
|
||||
|
||||
if(y > ClipY1)
|
||||
break;
|
||||
|
||||
if(y < ClipY0)
|
||||
{
|
||||
DrawTimeAvail -= 2;
|
||||
goto skipit;
|
||||
}
|
||||
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, GetPolyXFP_Int(lc), GetPolyXFP_Int(rc), ig, idl);
|
||||
//
|
||||
//
|
||||
//
|
||||
skipit: ;
|
||||
yi++;
|
||||
lc += ls;
|
||||
rc += rs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("[GPU] Vertices: %d:%d(r=%d, g=%d, b=%d) -> %d:%d(r=%d, g=%d, b=%d) -> %d:%d(r=%d, g=%d, b=%d)\n\n\n", vertices[0].x, vertices[0].y,
|
||||
vertices[0].r, vertices[0].g, vertices[0].b,
|
||||
vertices[1].x, vertices[1].y,
|
||||
vertices[1].r, vertices[1].g, vertices[1].b,
|
||||
vertices[2].x, vertices[2].y,
|
||||
vertices[2].r, vertices[2].g, vertices[2].b);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<int numvertices, bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
INLINE void PS_GPU::Command_DrawPolygon(const uint32 *cb)
|
||||
{
|
||||
const unsigned cb0 = cb[0];
|
||||
tri_vertex vertices[3];
|
||||
unsigned sv = 0;
|
||||
//uint32 tpage = 0;
|
||||
|
||||
// Base timing is approximate, and could be improved.
|
||||
if(numvertices == 4 && InCmd == INCMD_QUAD)
|
||||
DrawTimeAvail -= (28 + 18);
|
||||
else
|
||||
DrawTimeAvail -= (64 + 18);
|
||||
|
||||
if(goraud && textured)
|
||||
DrawTimeAvail -= 150 * 3;
|
||||
else if(goraud)
|
||||
DrawTimeAvail -= 96 * 3;
|
||||
else if(textured)
|
||||
DrawTimeAvail -= 60 * 3;
|
||||
|
||||
if(numvertices == 4)
|
||||
{
|
||||
if(InCmd == INCMD_QUAD)
|
||||
{
|
||||
memcpy(&vertices[0], &InQuad_F3Vertices[1], 2 * sizeof(tri_vertex));
|
||||
sv = 2;
|
||||
}
|
||||
}
|
||||
//else
|
||||
// memset(vertices, 0, sizeof(vertices));
|
||||
|
||||
for(unsigned v = sv; v < 3; v++)
|
||||
{
|
||||
if(v == 0 || goraud)
|
||||
{
|
||||
uint32 raw_color = (*cb & 0xFFFFFF);
|
||||
|
||||
vertices[v].r = raw_color & 0xFF;
|
||||
vertices[v].g = (raw_color >> 8) & 0xFF;
|
||||
vertices[v].b = (raw_color >> 16) & 0xFF;
|
||||
|
||||
cb++;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertices[v].r = vertices[0].r;
|
||||
vertices[v].g = vertices[0].g;
|
||||
vertices[v].b = vertices[0].b;
|
||||
}
|
||||
|
||||
vertices[v].x = sign_x_to_s32(11, ((int16)(*cb & 0xFFFF))) + OffsX;
|
||||
vertices[v].y = sign_x_to_s32(11, ((int16)(*cb >> 16))) + OffsY;
|
||||
cb++;
|
||||
|
||||
if(textured)
|
||||
{
|
||||
vertices[v].u = (*cb & 0xFF);
|
||||
vertices[v].v = (*cb >> 8) & 0xFF;
|
||||
|
||||
if(v == 0)
|
||||
{
|
||||
Update_CLUT_Cache<TexMode_TA>((*cb >> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
cb++;
|
||||
}
|
||||
}
|
||||
|
||||
if(numvertices == 4)
|
||||
{
|
||||
if(InCmd == INCMD_QUAD)
|
||||
{
|
||||
InCmd = INCMD_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
InCmd = INCMD_QUAD;
|
||||
InCmd_CC = cb0 >> 24;
|
||||
memcpy(&InQuad_F3Vertices[0], &vertices[0], sizeof(tri_vertex) * 3);
|
||||
}
|
||||
}
|
||||
|
||||
DrawTriangle<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(vertices);
|
||||
}
|
||||
|
||||
#undef COORD_POST_PADDING
|
||||
#undef COORD_FBS
|
||||
#undef COORD_MF_INT
|
||||
|
||||
//
|
||||
// C-style function wrappers so our command table isn't so ginormous(in memory usage).
|
||||
//
|
||||
template<int numvertices, bool shaded, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
static void G_Command_DrawPolygon(PS_GPU* g, const uint32 *cb)
|
||||
{
|
||||
g->Command_DrawPolygon<numvertices, shaded, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(cb);
|
||||
}
|
||||
|
||||
const CTEntry PS_GPU::Commands_20_3F[0x20] =
|
||||
{
|
||||
/* 0x20 */
|
||||
POLY_HELPER(0x20),
|
||||
POLY_HELPER(0x21),
|
||||
POLY_HELPER(0x22),
|
||||
POLY_HELPER(0x23),
|
||||
POLY_HELPER(0x24),
|
||||
POLY_HELPER(0x25),
|
||||
POLY_HELPER(0x26),
|
||||
POLY_HELPER(0x27),
|
||||
POLY_HELPER(0x28),
|
||||
POLY_HELPER(0x29),
|
||||
POLY_HELPER(0x2a),
|
||||
POLY_HELPER(0x2b),
|
||||
POLY_HELPER(0x2c),
|
||||
POLY_HELPER(0x2d),
|
||||
POLY_HELPER(0x2e),
|
||||
POLY_HELPER(0x2f),
|
||||
POLY_HELPER(0x30),
|
||||
POLY_HELPER(0x31),
|
||||
POLY_HELPER(0x32),
|
||||
POLY_HELPER(0x33),
|
||||
POLY_HELPER(0x34),
|
||||
POLY_HELPER(0x35),
|
||||
POLY_HELPER(0x36),
|
||||
POLY_HELPER(0x37),
|
||||
POLY_HELPER(0x38),
|
||||
POLY_HELPER(0x39),
|
||||
POLY_HELPER(0x3a),
|
||||
POLY_HELPER(0x3b),
|
||||
POLY_HELPER(0x3c),
|
||||
POLY_HELPER(0x3d),
|
||||
POLY_HELPER(0x3e),
|
||||
POLY_HELPER(0x3f)
|
||||
};
|
||||
|
||||
}
|
|
@ -1,514 +0,0 @@
|
|||
#define COORD_FBS 12
|
||||
#define COORD_MF_INT(n) ((n) << COORD_FBS)
|
||||
|
||||
/*
|
||||
Store and do most math with interpolant coordinates and deltas as unsigned to avoid violating strict overflow(due to biasing),
|
||||
but when actually grabbing the coordinates, treat them as signed(with signed right shift) so we can do saturation properly.
|
||||
*/
|
||||
static INLINE int32 COORD_GET_INT(int32 n)
|
||||
{
|
||||
return(n >> COORD_FBS);
|
||||
}
|
||||
|
||||
struct i_group
|
||||
{
|
||||
uint32 u, v;
|
||||
uint32 r, g, b;
|
||||
uint32 dummy0[3];
|
||||
};
|
||||
|
||||
struct i_deltas
|
||||
{
|
||||
uint32 du_dx, dv_dx;
|
||||
uint32 dr_dx, dg_dx, db_dx;
|
||||
uint32 dummy0[3];
|
||||
|
||||
uint32 du_dy, dv_dy;
|
||||
uint32 dr_dy, dg_dy, db_dy;
|
||||
uint32 dummy1[3];
|
||||
};
|
||||
|
||||
static INLINE int64 MakePolyXFP(int32 x)
|
||||
{
|
||||
return ((int64)x << 32) + ((1LL << 32) - (1 << 11));
|
||||
}
|
||||
|
||||
static INLINE int64 MakePolyXFPStep(int32 dx, int32 dy)
|
||||
{
|
||||
int64 ret;
|
||||
int64 dx_ex = (int64)dx << 32;
|
||||
|
||||
if(dx_ex < 0)
|
||||
dx_ex -= dy - 1;
|
||||
|
||||
if(dx_ex > 0)
|
||||
dx_ex += dy - 1;
|
||||
|
||||
ret = dx_ex / dy;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static INLINE int32 GetPolyXFP_Int(int64 xfp)
|
||||
{
|
||||
return(xfp >> 32);
|
||||
}
|
||||
|
||||
//#define CALCIS(x,y) ( A.x * (B.y - C.y) + B.x * (C.y - A.y) + C.x * (A.y - B.y) )
|
||||
#define CALCIS(x,y) (((B.x - A.x) * (C.y - B.y)) - ((C.x - B.x) * (B.y - A.y)))
|
||||
static INLINE bool CalcIDeltas(i_deltas &idl, const tri_vertex &A, const tri_vertex &B, const tri_vertex &C)
|
||||
{
|
||||
const unsigned sa = 32;
|
||||
int64 num = ((int64)COORD_MF_INT(1)) << sa;
|
||||
int64 denom = CALCIS(x, y);
|
||||
int64 one_div;
|
||||
|
||||
if(!denom)
|
||||
return(false);
|
||||
|
||||
one_div = num / denom;
|
||||
|
||||
idl.dr_dx = ((one_div * CALCIS(r, y)) + 0x00000000) >> sa;
|
||||
idl.dr_dy = ((one_div * CALCIS(x, r)) + 0x00000000) >> sa;
|
||||
|
||||
idl.dg_dx = ((one_div * CALCIS(g, y)) + 0x00000000) >> sa;
|
||||
idl.dg_dy = ((one_div * CALCIS(x, g)) + 0x00000000) >> sa;
|
||||
|
||||
idl.db_dx = ((one_div * CALCIS(b, y)) + 0x00000000) >> sa;
|
||||
idl.db_dy = ((one_div * CALCIS(x, b)) + 0x00000000) >> sa;
|
||||
|
||||
idl.du_dx = ((one_div * CALCIS(u, y)) + 0x00000000) >> sa;
|
||||
idl.du_dy = ((one_div * CALCIS(x, u)) + 0x00000000) >> sa;
|
||||
|
||||
idl.dv_dx = ((one_div * CALCIS(v, y)) + 0x00000000) >> sa;
|
||||
idl.dv_dy = ((one_div * CALCIS(x, v)) + 0x00000000) >> sa;
|
||||
|
||||
// idl.du_dx = ((int64)CALCIS(u, y) << COORD_FBS) / denom;
|
||||
// idl.du_dy = ((int64)CALCIS(x, u) << COORD_FBS) / denom;
|
||||
|
||||
// idl.dv_dx = ((int64)CALCIS(v, y) << COORD_FBS) / denom;
|
||||
// idl.dv_dy = ((int64)CALCIS(x, v) << COORD_FBS) / denom;
|
||||
|
||||
//printf("Denom=%lld - CIS_UY=%d, CIS_XU=%d, CIS_VY=%d, CIS_XV=%d\n", denom, CALCIS(u, y), CALCIS(x, u), CALCIS(v, y), CALCIS(x, v));
|
||||
//printf(" du_dx=0x%08x, du_dy=0x%08x --- dv_dx=0x%08x, dv_dy=0x%08x\n", idl.du_dx, idl.du_dy, idl.dv_dx, idl.dv_dy);
|
||||
|
||||
return(true);
|
||||
}
|
||||
#undef CALCIS
|
||||
|
||||
template<bool goraud, bool textured>
|
||||
static INLINE void AddIDeltas_DX(i_group &ig, const i_deltas &idl, uint32 count = 1)
|
||||
{
|
||||
if(textured)
|
||||
{
|
||||
ig.u += idl.du_dx * count;
|
||||
ig.v += idl.dv_dx * count;
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += idl.dr_dx * count;
|
||||
ig.g += idl.dg_dx * count;
|
||||
ig.b += idl.db_dx * count;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured>
|
||||
static INLINE void AddIDeltas_DY(i_group &ig, const i_deltas &idl, uint32 count = 1)
|
||||
{
|
||||
if(textured)
|
||||
{
|
||||
ig.u += idl.du_dy * count;
|
||||
ig.v += idl.dv_dy * count;
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += idl.dr_dy * count;
|
||||
ig.g += idl.dg_dy * count;
|
||||
ig.b += idl.db_dy * count;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
INLINE void PS_GPU::DrawSpan(int y, uint32 clut_offset, const int32 x_start, const int32 x_bound, i_group ig, const i_deltas &idl)
|
||||
{
|
||||
int32 xs = x_start, xb = x_bound;
|
||||
|
||||
if(LineSkipTest(y))
|
||||
return;
|
||||
|
||||
if(xs < xb) // (xs != xb)
|
||||
{
|
||||
if(xs < ClipX0)
|
||||
xs = ClipX0;
|
||||
|
||||
if(xb > (ClipX1 + 1))
|
||||
xb = ClipX1 + 1;
|
||||
|
||||
if(xs < xb)
|
||||
{
|
||||
DrawTimeAvail -= (xb - xs);
|
||||
|
||||
if(goraud || textured)
|
||||
{
|
||||
DrawTimeAvail -= (xb - xs);
|
||||
}
|
||||
else if((BlendMode >= 0) || MaskEval_TA)
|
||||
{
|
||||
DrawTimeAvail -= (((xb + 1) & ~1) - (xs & ~1)) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(textured)
|
||||
{
|
||||
ig.u += (xs * idl.du_dx) + (y * idl.du_dy);
|
||||
ig.v += (xs * idl.dv_dx) + (y * idl.dv_dy);
|
||||
}
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
ig.r += (xs * idl.dr_dx) + (y * idl.dr_dy);
|
||||
ig.g += (xs * idl.dg_dx) + (y * idl.dg_dy);
|
||||
ig.b += (xs * idl.db_dx) + (y * idl.db_dy);
|
||||
}
|
||||
|
||||
for(int32 x = xs; MDFN_LIKELY(x < xb); x++)
|
||||
{
|
||||
uint32 r, g, b;
|
||||
|
||||
if(goraud)
|
||||
{
|
||||
r = RGB8SAT[COORD_GET_INT(ig.r)];
|
||||
g = RGB8SAT[COORD_GET_INT(ig.g)];
|
||||
b = RGB8SAT[COORD_GET_INT(ig.b)];
|
||||
}
|
||||
else
|
||||
{
|
||||
r = COORD_GET_INT(ig.r);
|
||||
g = COORD_GET_INT(ig.g);
|
||||
b = COORD_GET_INT(ig.b);
|
||||
}
|
||||
|
||||
if(textured)
|
||||
{
|
||||
uint16 fbw = GetTexel<TexMode_TA>(clut_offset, COORD_GET_INT(ig.u), COORD_GET_INT(ig.v));
|
||||
|
||||
if(fbw)
|
||||
{
|
||||
if(TexMult)
|
||||
{
|
||||
if(dtd)
|
||||
fbw = ModTexel(fbw, r, g, b, x & 3, y & 3);
|
||||
else
|
||||
fbw = ModTexel(fbw, r, g, b, 3, 2); //x & 3, y & 3);
|
||||
}
|
||||
PlotPixel<BlendMode, MaskEval_TA, true>(x, y, fbw);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint16 pix = 0x8000;
|
||||
|
||||
if(goraud && dtd)
|
||||
{
|
||||
pix |= DitherLUT[y & 3][x & 3][r] << 0;
|
||||
pix |= DitherLUT[y & 3][x & 3][g] << 5;
|
||||
pix |= DitherLUT[y & 3][x & 3][b] << 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
pix |= (r >> 3) << 0;
|
||||
pix |= (g >> 3) << 5;
|
||||
pix |= (b >> 3) << 10;
|
||||
}
|
||||
|
||||
PlotPixel<BlendMode, MaskEval_TA, false>(x, y, pix);
|
||||
}
|
||||
|
||||
AddIDeltas_DX<goraud, textured>(ig, idl);
|
||||
//AddStep<goraud, textured>(perp_coord, perp_step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
void PS_GPU::DrawTriangle(tri_vertex *vertices, uint32 clut)
|
||||
{
|
||||
i_deltas idl;
|
||||
|
||||
//
|
||||
// Sort vertices by y.
|
||||
//
|
||||
if(vertices[2].y < vertices[1].y)
|
||||
{
|
||||
tri_vertex tmp = vertices[1];
|
||||
vertices[1] = vertices[2];
|
||||
vertices[2] = tmp;
|
||||
}
|
||||
|
||||
if(vertices[1].y < vertices[0].y)
|
||||
{
|
||||
tri_vertex tmp = vertices[0];
|
||||
vertices[0] = vertices[1];
|
||||
vertices[1] = tmp;
|
||||
}
|
||||
|
||||
if(vertices[2].y < vertices[1].y)
|
||||
{
|
||||
tri_vertex tmp = vertices[1];
|
||||
vertices[1] = vertices[2];
|
||||
vertices[2] = tmp;
|
||||
}
|
||||
|
||||
if(vertices[0].y == vertices[2].y)
|
||||
return;
|
||||
|
||||
if((vertices[2].y - vertices[0].y) >= 512)
|
||||
{
|
||||
//PSX_WARNING("[GPU] Triangle height too large: %d", (vertices[2].y - vertices[0].y));
|
||||
return;
|
||||
}
|
||||
|
||||
if(abs(vertices[2].x - vertices[0].x) >= 1024 ||
|
||||
abs(vertices[2].x - vertices[1].x) >= 1024 ||
|
||||
abs(vertices[1].x - vertices[0].x) >= 1024)
|
||||
{
|
||||
//PSX_WARNING("[GPU] Triangle width too large: %d %d %d", abs(vertices[2].x - vertices[0].x), abs(vertices[2].x - vertices[1].x), abs(vertices[1].x - vertices[0].x));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!CalcIDeltas(idl, vertices[0], vertices[1], vertices[2]))
|
||||
return;
|
||||
|
||||
// [0] should be top vertex, [2] should be bottom vertex, [1] should be off to the side vertex.
|
||||
//
|
||||
//
|
||||
int32 y_start = vertices[0].y;
|
||||
int32 y_middle = vertices[1].y;
|
||||
int32 y_bound = vertices[2].y;
|
||||
|
||||
int64 base_coord;
|
||||
int64 base_step;
|
||||
|
||||
int64 bound_coord_ul;
|
||||
int64 bound_coord_us;
|
||||
|
||||
int64 bound_coord_ll;
|
||||
int64 bound_coord_ls;
|
||||
|
||||
bool right_facing;
|
||||
//bool bottom_up;
|
||||
i_group ig;
|
||||
|
||||
//
|
||||
// Find vertex with lowest X coordinate, and use as the base for calculating interpolants from.
|
||||
//
|
||||
{
|
||||
unsigned iggvi = 0;
|
||||
|
||||
//
|
||||
// <=, not <
|
||||
//
|
||||
if(vertices[1].x <= vertices[iggvi].x)
|
||||
iggvi = 1;
|
||||
|
||||
if(vertices[2].x <= vertices[iggvi].x)
|
||||
iggvi = 2;
|
||||
|
||||
ig.u = COORD_MF_INT(vertices[iggvi].u) + (1 << (COORD_FBS - 1));
|
||||
ig.v = COORD_MF_INT(vertices[iggvi].v) + (1 << (COORD_FBS - 1));
|
||||
ig.r = COORD_MF_INT(vertices[iggvi].r);
|
||||
ig.g = COORD_MF_INT(vertices[iggvi].g);
|
||||
ig.b = COORD_MF_INT(vertices[iggvi].b);
|
||||
|
||||
AddIDeltas_DX<goraud, textured>(ig, idl, -vertices[iggvi].x);
|
||||
AddIDeltas_DY<goraud, textured>(ig, idl, -vertices[iggvi].y);
|
||||
}
|
||||
|
||||
base_coord = MakePolyXFP(vertices[0].x);
|
||||
base_step = MakePolyXFPStep((vertices[2].x - vertices[0].x), (vertices[2].y - vertices[0].y));
|
||||
|
||||
bound_coord_ul = MakePolyXFP(vertices[0].x);
|
||||
bound_coord_ll = MakePolyXFP(vertices[1].x);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
|
||||
if(vertices[1].y == vertices[0].y)
|
||||
{
|
||||
bound_coord_us = 0;
|
||||
right_facing = (bool)(vertices[1].x > vertices[0].x);
|
||||
}
|
||||
else
|
||||
{
|
||||
bound_coord_us = MakePolyXFPStep((vertices[1].x - vertices[0].x), (vertices[1].y - vertices[0].y));
|
||||
right_facing = (bool)(bound_coord_us > base_step);
|
||||
}
|
||||
|
||||
if(vertices[2].y == vertices[1].y)
|
||||
bound_coord_ls = 0;
|
||||
else
|
||||
bound_coord_ls = MakePolyXFPStep((vertices[2].x - vertices[1].x), (vertices[2].y - vertices[1].y));
|
||||
|
||||
if(y_start < ClipY0)
|
||||
{
|
||||
int32 count = ClipY0 - y_start;
|
||||
|
||||
y_start = ClipY0;
|
||||
base_coord += base_step * count;
|
||||
bound_coord_ul += bound_coord_us * count;
|
||||
|
||||
if(y_middle < ClipY0)
|
||||
{
|
||||
int32 count_ls = ClipY0 - y_middle;
|
||||
|
||||
y_middle = ClipY0;
|
||||
bound_coord_ll += bound_coord_ls * count_ls;
|
||||
}
|
||||
}
|
||||
|
||||
if(y_bound > (ClipY1 + 1))
|
||||
{
|
||||
y_bound = ClipY1 + 1;
|
||||
|
||||
if(y_middle > y_bound)
|
||||
y_middle = y_bound;
|
||||
}
|
||||
|
||||
if(right_facing)
|
||||
{
|
||||
for(int32 y = y_start; y < y_middle; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(base_coord), GetPolyXFP_Int(bound_coord_ul), ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ul += bound_coord_us;
|
||||
}
|
||||
|
||||
for(int32 y = y_middle; y < y_bound; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(base_coord), GetPolyXFP_Int(bound_coord_ll), ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ll += bound_coord_ls;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int32 y = y_start; y < y_middle; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(bound_coord_ul), GetPolyXFP_Int(base_coord), ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ul += bound_coord_us;
|
||||
}
|
||||
|
||||
for(int32 y = y_middle; y < y_bound; y++)
|
||||
{
|
||||
DrawSpan<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(y, clut, GetPolyXFP_Int(bound_coord_ll), GetPolyXFP_Int(base_coord), ig, idl);
|
||||
base_coord += base_step;
|
||||
bound_coord_ll += bound_coord_ls;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
printf("[GPU] Vertices: %d:%d(r=%d, g=%d, b=%d) -> %d:%d(r=%d, g=%d, b=%d) -> %d:%d(r=%d, g=%d, b=%d)\n\n\n", vertices[0].x, vertices[0].y,
|
||||
vertices[0].r, vertices[0].g, vertices[0].b,
|
||||
vertices[1].x, vertices[1].y,
|
||||
vertices[1].r, vertices[1].g, vertices[1].b,
|
||||
vertices[2].x, vertices[2].y,
|
||||
vertices[2].r, vertices[2].g, vertices[2].b);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<int numvertices, bool goraud, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
INLINE void PS_GPU::Command_DrawPolygon(const uint32 *cb)
|
||||
{
|
||||
const unsigned cb0 = cb[0];
|
||||
tri_vertex vertices[3];
|
||||
uint32 clut = 0;
|
||||
unsigned sv = 0;
|
||||
//uint32 tpage = 0;
|
||||
|
||||
// Base timing is approximate, and could be improved.
|
||||
if(numvertices == 4 && InCmd == INCMD_QUAD)
|
||||
DrawTimeAvail -= (28 + 18);
|
||||
else
|
||||
DrawTimeAvail -= (64 + 18);
|
||||
|
||||
if(goraud && textured)
|
||||
DrawTimeAvail -= 150 * 3;
|
||||
else if(goraud)
|
||||
DrawTimeAvail -= 96 * 3;
|
||||
else if(textured)
|
||||
DrawTimeAvail -= 60 * 3;
|
||||
|
||||
if(numvertices == 4)
|
||||
{
|
||||
if(InCmd == INCMD_QUAD)
|
||||
{
|
||||
memcpy(&vertices[0], &InQuad_F3Vertices[1], 2 * sizeof(tri_vertex));
|
||||
clut = InQuad_clut;
|
||||
sv = 2;
|
||||
}
|
||||
}
|
||||
//else
|
||||
// memset(vertices, 0, sizeof(vertices));
|
||||
|
||||
for(unsigned v = sv; v < 3; v++)
|
||||
{
|
||||
if(v == 0 || goraud)
|
||||
{
|
||||
uint32 raw_color = (*cb & 0xFFFFFF);
|
||||
|
||||
vertices[v].r = raw_color & 0xFF;
|
||||
vertices[v].g = (raw_color >> 8) & 0xFF;
|
||||
vertices[v].b = (raw_color >> 16) & 0xFF;
|
||||
|
||||
cb++;
|
||||
}
|
||||
else
|
||||
{
|
||||
vertices[v].r = vertices[0].r;
|
||||
vertices[v].g = vertices[0].g;
|
||||
vertices[v].b = vertices[0].b;
|
||||
}
|
||||
|
||||
vertices[v].x = sign_x_to_s32(11, ((int16)(*cb & 0xFFFF))) + OffsX;
|
||||
vertices[v].y = sign_x_to_s32(11, ((int16)(*cb >> 16))) + OffsY;
|
||||
cb++;
|
||||
|
||||
if(textured)
|
||||
{
|
||||
vertices[v].u = (*cb & 0xFF);
|
||||
vertices[v].v = (*cb >> 8) & 0xFF;
|
||||
|
||||
if(v == 0)
|
||||
{
|
||||
clut = ((*cb >> 16) & 0xFFFF) << 4;
|
||||
}
|
||||
|
||||
cb++;
|
||||
}
|
||||
}
|
||||
|
||||
if(numvertices == 4)
|
||||
{
|
||||
if(InCmd == INCMD_QUAD)
|
||||
{
|
||||
InCmd = INCMD_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
InCmd = INCMD_QUAD;
|
||||
InCmd_CC = cb0 >> 24;
|
||||
memcpy(&InQuad_F3Vertices[0], &vertices[0], sizeof(tri_vertex) * 3);
|
||||
InQuad_clut = clut;
|
||||
}
|
||||
}
|
||||
|
||||
DrawTriangle<goraud, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(vertices, clut);
|
||||
}
|
||||
|
||||
#undef COORD_FBS
|
||||
#undef COORD_MF_INT
|
|
@ -1,5 +1,29 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* 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 of the License, 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 "psx.h"
|
||||
#include "gpu.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
#include "gpu_common.inc"
|
||||
|
||||
template<bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA, bool FlipX, bool FlipY>
|
||||
void PS_GPU::DrawSprite(int32 x_arg, int32 y_arg, int32 w, int32 h, uint8 u_arg, uint8 v_arg, uint32 color, uint32 clut_offset)
|
||||
void PS_GPU::DrawSprite(int32 x_arg, int32 y_arg, int32 w, int32 h, uint8 u_arg, uint8 v_arg, uint32 color)
|
||||
{
|
||||
const int32 r = color & 0xFF;
|
||||
const int32 g = (color >> 8) & 0xFF;
|
||||
|
@ -72,30 +96,6 @@ void PS_GPU::DrawSprite(int32 x_arg, int32 y_arg, int32 w, int32 h, uint8 u_arg,
|
|||
//
|
||||
int32 suck_time = (x_bound - x_start) * (y_bound - y_start);
|
||||
|
||||
// Disabled until we can get it to take into account texture windowing, which can cause large sprites to be drawn entirely from cache(and not suffer from a texturing
|
||||
// penalty); and disabled until we find a game that needs more accurate sprite draw timing. :b
|
||||
#if 0
|
||||
if(textured)
|
||||
{
|
||||
// Empirically-observed approximations of time(66MHz cycles) taken to draw large textured sprites in the various texture depths, when the texture data and CLUT data
|
||||
// was zero(non-zero takes longer to draw, TODO test that more):
|
||||
// 4bpp - area * 2
|
||||
// 8bpp - area * 3
|
||||
// 15/16bpp - area * 5
|
||||
// (other factors come into more importance for smaller sprites)
|
||||
static const int cw[4] = { 64, 32, 32, 32 };
|
||||
static const int ch[4] = { 64, 64, 32, 32 };
|
||||
static const int mm[4] = { 2 - 1, 3 - 1, 5 - 1, 5 - 1 };
|
||||
|
||||
// Parts of the first few(up to texture cache height) horizontal lines can be in cache, so assume they are.
|
||||
suck_time += mm[TexMode_TA] * std::max<int>(0, (x_bound - x_start) - cw[TexMode_TA]) * std::min<int>(ch[TexMode_TA], y_bound - y_start);
|
||||
|
||||
// The rest of the horizontal lines should not possibly have parts in the cache now.
|
||||
suck_time += mm[TexMode_TA] * (x_bound - x_start) * std::max<int>(0, (y_bound - y_start) - ch[TexMode_TA]);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
if((BlendMode >= 0) || MaskEval_TA)
|
||||
{
|
||||
suck_time += ((((x_bound + 1) & ~1) - (x_start & ~1)) * (y_bound - y_start)) >> 1;
|
||||
|
@ -121,7 +121,7 @@ void PS_GPU::DrawSprite(int32 x_arg, int32 y_arg, int32 w, int32 h, uint8 u_arg,
|
|||
{
|
||||
if(textured)
|
||||
{
|
||||
uint16 fbw = GetTexel<TexMode_TA>(clut_offset, u_r, v);
|
||||
uint16 fbw = GetTexel<TexMode_TA>(u_r, v);
|
||||
|
||||
if(fbw)
|
||||
{
|
||||
|
@ -151,7 +151,6 @@ INLINE void PS_GPU::Command_DrawSprite(const uint32 *cb)
|
|||
int32 w, h;
|
||||
uint8 u = 0, v = 0;
|
||||
uint32 color = 0;
|
||||
uint32 clut = 0;
|
||||
|
||||
DrawTimeAvail -= 16; // FIXME, correct time.
|
||||
|
||||
|
@ -166,7 +165,7 @@ INLINE void PS_GPU::Command_DrawSprite(const uint32 *cb)
|
|||
{
|
||||
u = *cb & 0xFF;
|
||||
v = (*cb >> 8) & 0xFF;
|
||||
clut = ((*cb >> 16) & 0xFFFF) << 4;
|
||||
Update_CLUT_Cache<TexMode_TA>((*cb >> 16) & 0xFFFF);
|
||||
cb++;
|
||||
}
|
||||
|
||||
|
@ -204,32 +203,77 @@ INLINE void PS_GPU::Command_DrawSprite(const uint32 *cb)
|
|||
{
|
||||
case 0x0000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, false, false>(x, y, w, h, u, v, color, clut);
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, false, false>(x, y, w, h, u, v, color);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, false, false>(x, y, w, h, u, v, color, clut);
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, false, false>(x, y, w, h, u, v, color);
|
||||
break;
|
||||
|
||||
case 0x1000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, true, false>(x, y, w, h, u, v, color, clut);
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, true, false>(x, y, w, h, u, v, color);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, true, false>(x, y, w, h, u, v, color, clut);
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, true, false>(x, y, w, h, u, v, color);
|
||||
break;
|
||||
|
||||
case 0x2000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, false, true>(x, y, w, h, u, v, color, clut);
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, false, true>(x, y, w, h, u, v, color);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, false, true>(x, y, w, h, u, v, color, clut);
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, false, true>(x, y, w, h, u, v, color);
|
||||
break;
|
||||
|
||||
case 0x3000:
|
||||
if(!TexMult || color == 0x808080)
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, true, true>(x, y, w, h, u, v, color, clut);
|
||||
DrawSprite<textured, BlendMode, false, TexMode_TA, MaskEval_TA, true, true>(x, y, w, h, u, v, color);
|
||||
else
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, true, true>(x, y, w, h, u, v, color, clut);
|
||||
DrawSprite<textured, BlendMode, true, TexMode_TA, MaskEval_TA, true, true>(x, y, w, h, u, v, color);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// C-style function wrappers so our command table isn't so ginormous(in memory usage).
|
||||
//
|
||||
template<uint8 raw_size, bool textured, int BlendMode, bool TexMult, uint32 TexMode_TA, bool MaskEval_TA>
|
||||
static void G_Command_DrawSprite(PS_GPU* g, const uint32 *cb)
|
||||
{
|
||||
g->Command_DrawSprite<raw_size, textured, BlendMode, TexMult, TexMode_TA, MaskEval_TA>(cb);
|
||||
}
|
||||
|
||||
const CTEntry PS_GPU::Commands_60_7F[0x20] =
|
||||
{
|
||||
SPR_HELPER(0x60),
|
||||
SPR_HELPER(0x61),
|
||||
SPR_HELPER(0x62),
|
||||
SPR_HELPER(0x63),
|
||||
SPR_HELPER(0x64),
|
||||
SPR_HELPER(0x65),
|
||||
SPR_HELPER(0x66),
|
||||
SPR_HELPER(0x67),
|
||||
SPR_HELPER(0x68),
|
||||
SPR_HELPER(0x69),
|
||||
SPR_HELPER(0x6a),
|
||||
SPR_HELPER(0x6b),
|
||||
SPR_HELPER(0x6c),
|
||||
SPR_HELPER(0x6d),
|
||||
SPR_HELPER(0x6e),
|
||||
SPR_HELPER(0x6f),
|
||||
SPR_HELPER(0x70),
|
||||
SPR_HELPER(0x71),
|
||||
SPR_HELPER(0x72),
|
||||
SPR_HELPER(0x73),
|
||||
SPR_HELPER(0x74),
|
||||
SPR_HELPER(0x75),
|
||||
SPR_HELPER(0x76),
|
||||
SPR_HELPER(0x77),
|
||||
SPR_HELPER(0x78),
|
||||
SPR_HELPER(0x79),
|
||||
SPR_HELPER(0x7a),
|
||||
SPR_HELPER(0x7b),
|
||||
SPR_HELPER(0x7c),
|
||||
SPR_HELPER(0x7d),
|
||||
SPR_HELPER(0x7e),
|
||||
SPR_HELPER(0x7f)
|
||||
};
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "state.h"
|
||||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
void GTE_Power(void);
|
||||
int GTE_StateAction(StateMem *sm, int load, int data_only);
|
||||
void GTE_Init(void) MDFN_COLD;
|
||||
void GTE_Power(void) MDFN_COLD;
|
||||
|
||||
int32 GTE_Instruction(uint32 instr);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,7 +13,6 @@ class InputDevice_DualAnalog : public InputDevice
|
|||
virtual ~InputDevice_DualAnalog();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
|
||||
//
|
||||
|
@ -72,41 +71,6 @@ void InputDevice_DualAnalog::Power(void)
|
|||
transmit_count = 0;
|
||||
}
|
||||
|
||||
int InputDevice_DualAnalog::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFARRAY(buttons, sizeof(buttons)),
|
||||
SFARRAY(&axes[0][0], sizeof(axes)),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_DualAnalog::UpdateInput(const void *data)
|
||||
{
|
||||
|
|
|
@ -56,7 +56,7 @@ class InputDevice_DualShock : public InputDevice
|
|||
{
|
||||
public:
|
||||
|
||||
InputDevice_DualShock(const std::string &arg_name);
|
||||
InputDevice_DualShock();
|
||||
virtual ~InputDevice_DualShock();
|
||||
|
||||
virtual void Power(void);
|
||||
|
@ -112,9 +112,6 @@ class InputDevice_DualShock : public InputDevice
|
|||
//
|
||||
//
|
||||
//
|
||||
bool am_prev_info;
|
||||
bool aml_prev_info;
|
||||
std::string gp_name;
|
||||
pscpu_timestamp_t lastts;
|
||||
|
||||
//
|
||||
|
@ -122,13 +119,9 @@ class InputDevice_DualShock : public InputDevice
|
|||
bool amct_enabled;
|
||||
};
|
||||
|
||||
InputDevice_DualShock::InputDevice_DualShock(const std::string &name)
|
||||
InputDevice_DualShock::InputDevice_DualShock()
|
||||
{
|
||||
gp_name = name;
|
||||
Power();
|
||||
am_prev_info = analog_mode;
|
||||
aml_prev_info = analog_mode_locked;
|
||||
amct_enabled = false;
|
||||
}
|
||||
|
||||
InputDevice_DualShock::~InputDevice_DualShock()
|
||||
|
@ -306,7 +299,7 @@ void InputDevice_DualShock::UpdateInput(const void *data)
|
|||
if(rumble_param[0] == 0x01)
|
||||
sneaky_weaky = 0xFF;
|
||||
|
||||
MDFN_en16lsb(rumb_dp, (sneaky_weaky << 0) | (rumble_param[1] << 8));
|
||||
MDFN_en16lsb<false>(rumb_dp, (sneaky_weaky << 0) | (rumble_param[1] << 8));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -315,7 +308,7 @@ void InputDevice_DualShock::UpdateInput(const void *data)
|
|||
if(((rumble_param[0] & 0xC0) == 0x40) && ((rumble_param[1] & 0x01) == 0x01))
|
||||
sneaky_weaky = 0xFF;
|
||||
|
||||
MDFN_en16lsb(rumb_dp, sneaky_weaky << 0);
|
||||
MDFN_en16lsb<false>(rumb_dp, sneaky_weaky << 0);
|
||||
}
|
||||
|
||||
//printf("%d %d %d %d\n", axes[0][0], axes[0][1], axes[1][0], axes[1][1]);
|
||||
|
@ -325,12 +318,12 @@ void InputDevice_DualShock::UpdateInput(const void *data)
|
|||
//
|
||||
CheckManualAnaModeChange();
|
||||
|
||||
if(am_prev_info != analog_mode || aml_prev_info != analog_mode_locked)
|
||||
{
|
||||
//MDFN_DispMessage(_("%s: Analog mode is %s(%s)."), gp_name.c_str(), analog_mode ? _("on") : _("off"), analog_mode_locked ? _("locked") : _("unlocked"));
|
||||
}
|
||||
aml_prev_info = analog_mode_locked;
|
||||
am_prev_info = analog_mode;
|
||||
//
|
||||
// Encode analog mode state last.
|
||||
//
|
||||
d8[2] &= ~0x6;
|
||||
d8[2] |= (analog_mode ? 0x02 : 0x00);
|
||||
d8[2] |= (analog_mode_locked ? 0x04 : 0x00);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1046,9 +1039,9 @@ bool InputDevice_DualShock::Clock(bool TxD, int32 &dsr_pulse_delay)
|
|||
return(ret);
|
||||
}
|
||||
|
||||
InputDevice *Device_DualShock_Create(const std::string &name)
|
||||
InputDevice *Device_DualShock_Create()
|
||||
{
|
||||
return new InputDevice_DualShock(name);
|
||||
return new InputDevice_DualShock();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
InputDevice *Device_DualShock_Create(const std::string &name);
|
||||
InputDevice *Device_DualShock_Create();
|
||||
extern InputDeviceInputInfoStruct Device_DualShock_IDII[26];
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -13,7 +13,6 @@ class InputDevice_Gamepad : public InputDevice
|
|||
virtual ~InputDevice_Gamepad();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
|
||||
//
|
||||
|
@ -70,40 +69,6 @@ void InputDevice_Gamepad::Power(void)
|
|||
transmit_count = 0;
|
||||
}
|
||||
|
||||
int InputDevice_Gamepad::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFARRAY(buttons, sizeof(buttons)),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void InputDevice_Gamepad::UpdateInput(const void *data)
|
||||
{
|
||||
|
|
|
@ -22,25 +22,24 @@
|
|||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_GunCon : public InputDevice
|
||||
class InputDevice_GunCon final : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_GunCon(void);
|
||||
virtual ~InputDevice_GunCon();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
virtual bool RequireNoFrameskip(void);
|
||||
virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider);
|
||||
virtual void Power(void) override;
|
||||
virtual void UpdateInput(const void *data) override;
|
||||
virtual bool RequireNoFrameskip(void) override;
|
||||
virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider) override;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
virtual void SetDTR(bool new_dtr) override;
|
||||
virtual bool GetDSR(void) override;
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay) override;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -114,59 +113,60 @@ void InputDevice_GunCon::Power(void)
|
|||
line_counter = 0;
|
||||
}
|
||||
|
||||
int InputDevice_GunCon::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFVAR(buttons),
|
||||
SFVAR(trigger_eff),
|
||||
SFVAR(trigger_noclear),
|
||||
SFVAR(hit_x),
|
||||
SFVAR(hit_y),
|
||||
|
||||
SFVAR(nom_x),
|
||||
SFVAR(nom_y),
|
||||
SFVAR(os_shot_counter),
|
||||
SFVAR(prev_oss),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFVAR(prev_vsync),
|
||||
SFVAR(line_counter),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
//void InputDevice_GunCon::StateAction(StateMem* sm, const unsigned load, const bool data_only, const char* sname_prefix)
|
||||
//{
|
||||
// SFORMAT StateRegs[] =
|
||||
// {
|
||||
// SFVAR(dtr),
|
||||
//
|
||||
// SFVAR(buttons),
|
||||
// SFVAR(trigger_eff),
|
||||
// SFVAR(trigger_noclear),
|
||||
// SFVAR(hit_x),
|
||||
// SFVAR(hit_y),
|
||||
//
|
||||
// SFVAR(nom_x),
|
||||
// SFVAR(nom_y),
|
||||
// SFVAR(os_shot_counter),
|
||||
// SFVAR(prev_oss),
|
||||
//
|
||||
// SFVAR(command_phase),
|
||||
// SFVAR(bitpos),
|
||||
// SFVAR(receive_buffer),
|
||||
//
|
||||
// SFVAR(command),
|
||||
//
|
||||
// SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
// SFVAR(transmit_pos),
|
||||
// SFVAR(transmit_count),
|
||||
//
|
||||
// SFVAR(prev_vsync),
|
||||
// SFVAR(line_counter),
|
||||
//
|
||||
// SFEND
|
||||
// };
|
||||
// char section_name[32];
|
||||
// trio_snprintf(section_name, sizeof(section_name), "%s_GunCon", sname_prefix);
|
||||
//
|
||||
// if(!MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name, true) && load)
|
||||
// Power();
|
||||
// else if(load)
|
||||
// {
|
||||
// if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
// {
|
||||
// transmit_pos = 0;
|
||||
// transmit_count = 0;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
void InputDevice_GunCon::UpdateInput(const void *data)
|
||||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
nom_x = (int16)MDFN_de16lsb(&d8[0]);
|
||||
nom_y = (int16)MDFN_de16lsb(&d8[2]);
|
||||
nom_x = (int16)MDFN_de16lsb<false>(&d8[0]);
|
||||
nom_y = (int16)MDFN_de16lsb<false>(&d8[2]);
|
||||
|
||||
trigger_noclear = (bool)(d8[4] & 0x1);
|
||||
trigger_eff |= trigger_noclear;
|
||||
|
@ -329,8 +329,8 @@ bool InputDevice_GunCon::Clock(bool TxD, int32 &dsr_pulse_delay)
|
|||
}
|
||||
}
|
||||
|
||||
MDFN_en16lsb(&transmit_buffer[3], hit_x);
|
||||
MDFN_en16lsb(&transmit_buffer[5], hit_y);
|
||||
MDFN_en16lsb<false>(&transmit_buffer[3], hit_x);
|
||||
MDFN_en16lsb<false>(&transmit_buffer[5], hit_y);
|
||||
|
||||
hit_x = 0x01;
|
||||
hit_y = 0x0A;
|
||||
|
@ -365,19 +365,5 @@ InputDevice *Device_GunCon_Create(void)
|
|||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_GunCon_IDII[6] =
|
||||
{
|
||||
{ "x_axis", "X Axis", -1, IDIT_X_AXIS },
|
||||
{ "y_axis", "Y Axis", -1, IDIT_Y_AXIS },
|
||||
|
||||
{ "trigger", "Trigger", 0, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "a", "A", 1, IDIT_BUTTON, NULL },
|
||||
{ "b", "B", 2, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "offscreen_shot", "Offscreen Shot(Simulated)", 3, IDIT_BUTTON, NULL }, // Useful for "Judge Dredd", and probably not much else.
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ namespace MDFN_IEN_PSX
|
|||
{
|
||||
|
||||
InputDevice *Device_GunCon_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_GunCon_IDII[6];
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -22,25 +22,24 @@
|
|||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Justifier : public InputDevice
|
||||
class InputDevice_Justifier final : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Justifier(void);
|
||||
virtual ~InputDevice_Justifier();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
virtual bool RequireNoFrameskip(void);
|
||||
virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider);
|
||||
virtual void Power(void) override;
|
||||
virtual void UpdateInput(const void *data) override;
|
||||
virtual bool RequireNoFrameskip(void) override;
|
||||
virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider) override;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
virtual void SetDTR(bool new_dtr) override;
|
||||
virtual bool GetDSR(void) override;
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay) override;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -119,8 +118,8 @@ void InputDevice_Justifier::UpdateInput(const void *data)
|
|||
{
|
||||
uint8 *d8 = (uint8 *)data;
|
||||
|
||||
nom_x = (int16)MDFN_de16lsb(&d8[0]);
|
||||
nom_y = (int16)MDFN_de16lsb(&d8[2]);
|
||||
nom_x = (int16)MDFN_de16lsb<false>(&d8[0]);
|
||||
nom_y = (int16)MDFN_de16lsb<false>(&d8[2]);
|
||||
|
||||
trigger_noclear = (bool)(d8[4] & 0x1);
|
||||
trigger_eff |= trigger_noclear;
|
||||
|
@ -135,51 +134,52 @@ void InputDevice_Justifier::UpdateInput(const void *data)
|
|||
prev_oss = d8[4] & 0x8;
|
||||
}
|
||||
|
||||
int InputDevice_Justifier::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFVAR(buttons),
|
||||
SFVAR(trigger_eff),
|
||||
SFVAR(trigger_noclear),
|
||||
|
||||
SFVAR(need_hit_detect),
|
||||
|
||||
SFVAR(nom_x),
|
||||
SFVAR(nom_y),
|
||||
SFVAR(os_shot_counter),
|
||||
SFVAR(prev_oss),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFVAR(prev_vsync),
|
||||
SFVAR(line_counter),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
//void InputDevice_Justifier::StateAction(StateMem* sm, const unsigned load, const bool data_only, const char* sname_prefix)
|
||||
//{
|
||||
// SFORMAT StateRegs[] =
|
||||
// {
|
||||
// SFVAR(dtr),
|
||||
//
|
||||
// SFVAR(buttons),
|
||||
// SFVAR(trigger_eff),
|
||||
// SFVAR(trigger_noclear),
|
||||
//
|
||||
// SFVAR(need_hit_detect),
|
||||
//
|
||||
// SFVAR(nom_x),
|
||||
// SFVAR(nom_y),
|
||||
// SFVAR(os_shot_counter),
|
||||
// SFVAR(prev_oss),
|
||||
//
|
||||
// SFVAR(command_phase),
|
||||
// SFVAR(bitpos),
|
||||
// SFVAR(receive_buffer),
|
||||
//
|
||||
// SFVAR(command),
|
||||
//
|
||||
// SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
// SFVAR(transmit_pos),
|
||||
// SFVAR(transmit_count),
|
||||
//
|
||||
// SFVAR(prev_vsync),
|
||||
// SFVAR(line_counter),
|
||||
//
|
||||
// SFEND
|
||||
// };
|
||||
// char section_name[32];
|
||||
// trio_snprintf(section_name, sizeof(section_name), "%s_Justifier", sname_prefix);
|
||||
//
|
||||
// if(!MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name, true) && load)
|
||||
// Power();
|
||||
// else if(load)
|
||||
// {
|
||||
// if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
// {
|
||||
// transmit_pos = 0;
|
||||
// transmit_count = 0;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
bool InputDevice_Justifier::RequireNoFrameskip(void)
|
||||
|
@ -208,15 +208,22 @@ pscpu_timestamp_t InputDevice_Justifier::GPULineHook(const pscpu_timestamp_t tim
|
|||
//if(gxa < 0 && gx >= 0)
|
||||
// gxa = 0;
|
||||
|
||||
if(!os_shot_counter && need_hit_detect && gxa >= 0 && gxa < (int)width && line_counter >= (avs + gy - 1) && line_counter <= (avs + gy + 1))
|
||||
if(!os_shot_counter && need_hit_detect)
|
||||
{
|
||||
int r, g, b, a;
|
||||
|
||||
format->DecodeColor(pixels[gxa], r, g, b, a);
|
||||
|
||||
if((r + g + b) >= 0x40) // Wrong, but not COMPLETELY ABSOLUTELY wrong, at least. ;)
|
||||
for(int32 ix = gxa; ix < (gxa + (int32)(pix_clock / 762925)); ix++)
|
||||
{
|
||||
ret = timestamp + (int64)(gxa + pix_clock_offset) * (44100 * 768) / pix_clock - 177;
|
||||
if(ix >= 0 && ix < (int)width && line_counter >= (avs + gy - 6) && line_counter <= (avs + gy + 6))
|
||||
{
|
||||
int r, g, b, a;
|
||||
|
||||
format->DecodeColor(pixels[ix], r, g, b, a);
|
||||
|
||||
if((r + g + b) >= 0x40) // Wrong, but not COMPLETELY ABSOLUTELY wrong, at least. ;)
|
||||
{
|
||||
ret = timestamp + (int64)(ix + pix_clock_offset) * (44100 * 768) / pix_clock - 177;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -363,19 +370,6 @@ InputDevice *Device_Justifier_Create(void)
|
|||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_Justifier_IDII[6] =
|
||||
{
|
||||
{ "x_axis", "X Axis", -1, IDIT_X_AXIS },
|
||||
{ "y_axis", "Y Axis", -1, IDIT_Y_AXIS },
|
||||
|
||||
{ "trigger", "Trigger", 0, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "o", "O", 1, IDIT_BUTTON, NULL },
|
||||
{ "start", "Start", 2, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "offscreen_shot", "Offscreen Shot(Simulated)", 3, IDIT_BUTTON, NULL },
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ namespace MDFN_IEN_PSX
|
|||
{
|
||||
|
||||
InputDevice *Device_Justifier_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_Justifier_IDII[6];
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -40,7 +40,6 @@ class InputDevice_Memcard : public InputDevice
|
|||
virtual ~InputDevice_Memcard();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void SyncState(bool isReader, EW::NewState *ns);
|
||||
//
|
||||
//
|
||||
|
@ -52,10 +51,10 @@ class InputDevice_Memcard : public InputDevice
|
|||
//
|
||||
//
|
||||
virtual uint32 GetNVSize(void);
|
||||
virtual void ReadNV(uint8 *buffer, uint32 offset, uint32 size);
|
||||
virtual const uint8* ReadNV(void) const override;
|
||||
virtual void WriteNV(const uint8 *buffer, uint32 offset, uint32 size);
|
||||
|
||||
virtual uint64 GetNVDirtyCount(void);
|
||||
virtual uint64 GetNVDirtyCount(void) const;
|
||||
virtual void ResetNVDirtyCount(void);
|
||||
|
||||
private:
|
||||
|
@ -193,67 +192,67 @@ void InputDevice_Memcard::SyncState(bool isReader, EW::NewState *ns)
|
|||
NSS(card_data);
|
||||
dirty_count = 0;
|
||||
}
|
||||
|
||||
int InputDevice_Memcard::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
// Don't save dirty_count.
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(presence_new),
|
||||
|
||||
SFARRAY(rw_buffer, sizeof(rw_buffer)),
|
||||
SFVAR(write_xor),
|
||||
|
||||
SFVAR(dtr),
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
SFVAR(addr),
|
||||
SFVAR(calced_xor),
|
||||
|
||||
SFVAR(transmit_buffer),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFVAR(data_used),
|
||||
|
||||
SFEND
|
||||
};
|
||||
|
||||
SFORMAT CD_StateRegs[] =
|
||||
{
|
||||
SFARRAY(card_data, sizeof(card_data)),
|
||||
SFEND
|
||||
};
|
||||
int ret = 1;
|
||||
|
||||
if(MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name) != 0)
|
||||
{
|
||||
//printf("%s data_used=%d\n", section_name, data_used);
|
||||
if(data_used)
|
||||
{
|
||||
std::string tmp_name = std::string(section_name) + "_DT";
|
||||
|
||||
ret &= MDFNSS_StateAction(sm, load, data_only, CD_StateRegs, tmp_name.c_str());
|
||||
}
|
||||
|
||||
if(load)
|
||||
{
|
||||
if(data_used)
|
||||
dirty_count++;
|
||||
else
|
||||
{
|
||||
//printf("Format: %s\n", section_name);
|
||||
Format();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
return(ret);
|
||||
}
|
||||
//
|
||||
//int InputDevice_Memcard::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
//{
|
||||
// // Don't save dirty_count.
|
||||
// SFORMAT StateRegs[] =
|
||||
// {
|
||||
// SFVAR(presence_new),
|
||||
//
|
||||
// SFARRAY(rw_buffer, sizeof(rw_buffer)),
|
||||
// SFVAR(write_xor),
|
||||
//
|
||||
// SFVAR(dtr),
|
||||
// SFVAR(command_phase),
|
||||
// SFVAR(bitpos),
|
||||
// SFVAR(receive_buffer),
|
||||
//
|
||||
// SFVAR(command),
|
||||
// SFVAR(addr),
|
||||
// SFVAR(calced_xor),
|
||||
//
|
||||
// SFVAR(transmit_buffer),
|
||||
// SFVAR(transmit_count),
|
||||
//
|
||||
// SFVAR(data_used),
|
||||
//
|
||||
// SFEND
|
||||
// };
|
||||
//
|
||||
// SFORMAT CD_StateRegs[] =
|
||||
// {
|
||||
// SFARRAY(card_data, sizeof(card_data)),
|
||||
// SFEND
|
||||
// };
|
||||
// int ret = 1;
|
||||
//
|
||||
// if(MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name) != 0)
|
||||
// {
|
||||
// //printf("%s data_used=%d\n", section_name, data_used);
|
||||
// if(data_used)
|
||||
// {
|
||||
// std::string tmp_name = std::string(section_name) + "_DT";
|
||||
//
|
||||
// ret &= MDFNSS_StateAction(sm, load, data_only, CD_StateRegs, tmp_name.c_str());
|
||||
// }
|
||||
//
|
||||
// if(load)
|
||||
// {
|
||||
// if(data_used)
|
||||
// dirty_count++;
|
||||
// else
|
||||
// {
|
||||
// //printf("Format: %s\n", section_name);
|
||||
// Format();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// ret = 0;
|
||||
//
|
||||
// return(ret);
|
||||
//}
|
||||
|
||||
|
||||
void InputDevice_Memcard::SetDTR(bool new_dtr)
|
||||
|
@ -531,14 +530,9 @@ uint32 InputDevice_Memcard::GetNVSize(void)
|
|||
return(sizeof(card_data));
|
||||
}
|
||||
|
||||
void InputDevice_Memcard::ReadNV(uint8 *buffer, uint32 offset, uint32 size)
|
||||
{
|
||||
while(size--)
|
||||
{
|
||||
*buffer = card_data[offset & (sizeof(card_data) - 1)];
|
||||
buffer++;
|
||||
offset++;
|
||||
}
|
||||
const uint8* InputDevice_Memcard::ReadNV(void) const
|
||||
{
|
||||
return card_data;
|
||||
}
|
||||
|
||||
void InputDevice_Memcard::WriteNV(const uint8 *buffer, uint32 offset, uint32 size)
|
||||
|
@ -559,7 +553,7 @@ void InputDevice_Memcard::WriteNV(const uint8 *buffer, uint32 offset, uint32 siz
|
|||
}
|
||||
}
|
||||
|
||||
uint64 InputDevice_Memcard::GetNVDirtyCount(void)
|
||||
uint64 InputDevice_Memcard::GetNVDirtyCount(void) const
|
||||
{
|
||||
return(dirty_count);
|
||||
}
|
||||
|
|
|
@ -5,25 +5,24 @@
|
|||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Mouse : public InputDevice
|
||||
class InputDevice_Mouse final : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Mouse();
|
||||
virtual ~InputDevice_Mouse();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
virtual void Power(void) override;
|
||||
virtual void UpdateInput(const void *data) override;
|
||||
|
||||
virtual void Update(const pscpu_timestamp_t timestamp);
|
||||
virtual void ResetTS(void);
|
||||
virtual void Update(const pscpu_timestamp_t timestamp) override;
|
||||
virtual void ResetTS(void) override;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
virtual void SetDTR(bool new_dtr) override;
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay) override;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -106,50 +105,51 @@ void InputDevice_Mouse::Power(void)
|
|||
transmit_count = 0;
|
||||
}
|
||||
|
||||
int InputDevice_Mouse::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(clear_timeout),
|
||||
|
||||
SFVAR(dtr),
|
||||
|
||||
SFVAR(button),
|
||||
SFVAR(button_post_mask),
|
||||
SFVAR(accum_xdelta),
|
||||
SFVAR(accum_ydelta),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
//void InputDevice_Mouse::StateAction(StateMem* sm, const unsigned load, const bool data_only, const char* sname_prefix)
|
||||
//{
|
||||
// SFORMAT StateRegs[] =
|
||||
// {
|
||||
// SFVAR(clear_timeout),
|
||||
//
|
||||
// SFVAR(dtr),
|
||||
//
|
||||
// SFVAR(button),
|
||||
// SFVAR(button_post_mask),
|
||||
// SFVAR(accum_xdelta),
|
||||
// SFVAR(accum_ydelta),
|
||||
//
|
||||
// SFVAR(command_phase),
|
||||
// SFVAR(bitpos),
|
||||
// SFVAR(receive_buffer),
|
||||
//
|
||||
// SFVAR(command),
|
||||
//
|
||||
// SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
// SFVAR(transmit_pos),
|
||||
// SFVAR(transmit_count),
|
||||
//
|
||||
// SFEND
|
||||
// };
|
||||
// char section_name[32];
|
||||
// trio_snprintf(section_name, sizeof(section_name), "%s_Mouse", sname_prefix);
|
||||
//
|
||||
// if(!MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name, true) && load)
|
||||
// Power();
|
||||
// else if(load)
|
||||
// {
|
||||
// if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
// {
|
||||
// transmit_pos = 0;
|
||||
// transmit_count = 0;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
void InputDevice_Mouse::UpdateInput(const void *data)
|
||||
{
|
||||
accum_xdelta += (int32)MDFN_de32lsb((uint8*)data + 0);
|
||||
accum_ydelta += (int32)MDFN_de32lsb((uint8*)data + 4);
|
||||
accum_xdelta += (int32)MDFN_de32lsb<false>((uint8*)data + 0);
|
||||
accum_ydelta += (int32)MDFN_de32lsb<false>((uint8*)data + 4);
|
||||
|
||||
if(accum_xdelta > 30 * 127) accum_xdelta = 30 * 127;
|
||||
if(accum_xdelta < 30 * -128) accum_xdelta = 30 * -128;
|
||||
|
@ -280,14 +280,4 @@ InputDevice *Device_Mouse_Create(void)
|
|||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_Mouse_IDII[4] =
|
||||
{
|
||||
{ "x_axis", "X Axis", -1, IDIT_X_AXIS_REL },
|
||||
{ "y_axis", "Y Axis", -1, IDIT_Y_AXIS_REL },
|
||||
{ "right", "Right Button", 1, IDIT_BUTTON, NULL },
|
||||
{ "left", "Left Button", 0, IDIT_BUTTON, NULL },
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -5,7 +5,5 @@ namespace MDFN_IEN_PSX
|
|||
{
|
||||
|
||||
InputDevice *Device_Mouse_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_Mouse_IDII[4];
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -144,42 +144,102 @@ void InputDevice_Multitap::Power(void)
|
|||
}
|
||||
}
|
||||
|
||||
int InputDevice_Multitap::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
//void InputDevice_Multitap::StateAction(StateMem* sm, const unsigned load, const bool data_only, const char* sname_prefix)
|
||||
//{
|
||||
// SFORMAT StateRegs[] =
|
||||
// {
|
||||
// SFVAR(dtr),
|
||||
//
|
||||
// SFVAR(selected_device),
|
||||
// SFVAR(full_mode_setting),
|
||||
//
|
||||
// SFVAR(full_mode),
|
||||
// SFVAR(mc_mode),
|
||||
//
|
||||
// SFVAR(fm_dp),
|
||||
// SFARRAY(&fm_buffer[0][0], sizeof(fm_buffer) / sizeof(fm_buffer[0][0])),
|
||||
//
|
||||
// SFVAR(fm_deferred_error_temp),
|
||||
// SFVAR(fm_deferred_error),
|
||||
// SFVAR(fm_command_error),
|
||||
//
|
||||
// SFVAR(command),
|
||||
// SFVAR(receive_buffer),
|
||||
// SFVAR(bit_counter),
|
||||
// SFVAR(byte_counter),
|
||||
//
|
||||
// SFEND
|
||||
// };
|
||||
// char section_name[32];
|
||||
// trio_snprintf(section_name, sizeof(section_name), "%s_MT", sname_prefix);
|
||||
//
|
||||
// if(!MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name, true) && load)
|
||||
// Power();
|
||||
// else if(load)
|
||||
// {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// for(unsigned i = 0; i < 4; i++)
|
||||
// {
|
||||
// char tmpbuf[32];
|
||||
//
|
||||
// trio_snprintf(tmpbuf, sizeof(tmpbuf), "%sP%u", section_name, i);
|
||||
// pad_devices[i]->StateAction(sm, load, data_only, tmpbuf);
|
||||
// }
|
||||
//
|
||||
// for(unsigned i = 0; i < 4; i++)
|
||||
// {
|
||||
// char tmpbuf[32];
|
||||
//
|
||||
// trio_snprintf(tmpbuf, sizeof(tmpbuf), "%sMC%u", section_name, i);
|
||||
// mc_devices[i]->StateAction(sm, load, data_only, tmpbuf);
|
||||
// }
|
||||
//}
|
||||
|
||||
void InputDevice_Multitap::Update(const pscpu_timestamp_t timestamp)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFVAR(selected_device),
|
||||
SFVAR(full_mode_setting),
|
||||
|
||||
SFVAR(full_mode),
|
||||
SFVAR(mc_mode),
|
||||
|
||||
SFVAR(fm_dp),
|
||||
SFARRAY(&fm_buffer[0][0], sizeof(fm_buffer) / sizeof(fm_buffer[0][0])),
|
||||
|
||||
SFVAR(fm_deferred_error_temp),
|
||||
SFVAR(fm_deferred_error),
|
||||
SFVAR(fm_command_error),
|
||||
|
||||
SFVAR(command),
|
||||
SFVAR(receive_buffer),
|
||||
SFVAR(bit_counter),
|
||||
SFVAR(byte_counter),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
|
||||
pad_devices[i]->Update(timestamp);
|
||||
mc_devices[i]->Update(timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
void InputDevice_Multitap::ResetTS(void)
|
||||
{
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
pad_devices[i]->ResetTS();
|
||||
mc_devices[i]->ResetTS();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool InputDevice_Multitap::RequireNoFrameskip(void)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
ret |= pad_devices[i]->RequireNoFrameskip();
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
pscpu_timestamp_t InputDevice_Multitap::GPULineHook(const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider)
|
||||
{
|
||||
pscpu_timestamp_t ret = PSX_EVENT_MAXTS;
|
||||
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
pscpu_timestamp_t tmp = pad_devices[i]->GPULineHook(line_timestamp, vsync, pixels, format, width, pix_clock_offset, pix_clock, pix_clock_divider);
|
||||
|
||||
if(i == 0) // FIXME; though the problems the design flaw causes(multitap issues with justifier) are documented at least.
|
||||
ret = tmp;
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void InputDevice_Multitap::SetDTR(bool new_dtr)
|
||||
{
|
||||
|
|
|
@ -4,23 +4,28 @@
|
|||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_Multitap : public InputDevice
|
||||
class InputDevice_Multitap final : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_Multitap();
|
||||
virtual ~InputDevice_Multitap();
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void Power(void) override;
|
||||
|
||||
virtual void Update(const pscpu_timestamp_t timestamp) override;
|
||||
virtual void ResetTS(void) override;
|
||||
|
||||
virtual bool RequireNoFrameskip(void) override;
|
||||
virtual pscpu_timestamp_t GPULineHook(const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider) override;
|
||||
|
||||
void SetSubDevice(unsigned int sub_index, InputDevice *device, InputDevice *mc_device);
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
virtual void SetDTR(bool new_dtr) override;
|
||||
virtual bool GetDSR(void) override;
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay) override;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -22,23 +22,22 @@
|
|||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
class InputDevice_neGcon : public InputDevice
|
||||
class InputDevice_neGcon final : public InputDevice
|
||||
{
|
||||
public:
|
||||
|
||||
InputDevice_neGcon(void);
|
||||
virtual ~InputDevice_neGcon();
|
||||
|
||||
virtual void Power(void);
|
||||
virtual int StateAction(StateMem* sm, int load, int data_only, const char* section_name);
|
||||
virtual void UpdateInput(const void *data);
|
||||
virtual void Power(void) override;
|
||||
virtual void UpdateInput(const void *data) override;
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
virtual void SetDTR(bool new_dtr);
|
||||
virtual bool GetDSR(void);
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay);
|
||||
virtual void SetDTR(bool new_dtr) override;
|
||||
virtual bool GetDSR(void) override;
|
||||
virtual bool Clock(bool TxD, int32 &dsr_pulse_delay) override;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -92,42 +91,43 @@ void InputDevice_neGcon::Power(void)
|
|||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
|
||||
int InputDevice_neGcon::StateAction(StateMem* sm, int load, int data_only, const char* section_name)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(dtr),
|
||||
|
||||
SFARRAY(buttons, sizeof(buttons)),
|
||||
SFVAR(twist),
|
||||
SFARRAY(anabuttons, sizeof(anabuttons)),
|
||||
|
||||
SFVAR(command_phase),
|
||||
SFVAR(bitpos),
|
||||
SFVAR(receive_buffer),
|
||||
|
||||
SFVAR(command),
|
||||
|
||||
SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
SFVAR(transmit_pos),
|
||||
SFVAR(transmit_count),
|
||||
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name);
|
||||
|
||||
if(load)
|
||||
{
|
||||
if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
{
|
||||
transmit_pos = 0;
|
||||
transmit_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
//
|
||||
//void InputDevice_neGcon::StateAction(StateMem* sm, const unsigned load, const bool data_only, const char* sname_prefix)
|
||||
//{
|
||||
// SFORMAT StateRegs[] =
|
||||
// {
|
||||
// SFVAR(dtr),
|
||||
//
|
||||
// SFARRAY(buttons, sizeof(buttons)),
|
||||
// SFVAR(twist),
|
||||
// SFARRAY(anabuttons, sizeof(anabuttons)),
|
||||
//
|
||||
// SFVAR(command_phase),
|
||||
// SFVAR(bitpos),
|
||||
// SFVAR(receive_buffer),
|
||||
//
|
||||
// SFVAR(command),
|
||||
//
|
||||
// SFARRAY(transmit_buffer, sizeof(transmit_buffer)),
|
||||
// SFVAR(transmit_pos),
|
||||
// SFVAR(transmit_count),
|
||||
//
|
||||
// SFEND
|
||||
// };
|
||||
// char section_name[32];
|
||||
// trio_snprintf(section_name, sizeof(section_name), "%s_neGcon", sname_prefix);
|
||||
//
|
||||
// if(!MDFNSS_StateAction(sm, load, data_only, StateRegs, section_name, true) && load)
|
||||
// Power();
|
||||
// else if(load)
|
||||
// {
|
||||
// if((transmit_pos + transmit_count) > sizeof(transmit_buffer))
|
||||
// {
|
||||
// transmit_pos = 0;
|
||||
// transmit_count = 0;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
void InputDevice_neGcon::UpdateInput(const void *data)
|
||||
|
@ -137,11 +137,11 @@ void InputDevice_neGcon::UpdateInput(const void *data)
|
|||
buttons[0] = d8[0];
|
||||
buttons[1] = d8[1];
|
||||
|
||||
twist = ((32768 + MDFN_de16lsb((const uint8 *)data + 2) - (((int32)MDFN_de16lsb((const uint8 *)data + 4) * 32768 + 16383) / 32767)) * 255 + 32767) / 65535;
|
||||
twist = ((32768 + MDFN_de16lsb<false>((const uint8 *)data + 2) - (((int32)MDFN_de16lsb<false>((const uint8 *)data + 4) * 32768 + 16383) / 32767)) * 255 + 32767) / 65535;
|
||||
|
||||
anabuttons[0] = (MDFN_de16lsb((const uint8 *)data + 6) * 255 + 16383) / 32767;
|
||||
anabuttons[1] = (MDFN_de16lsb((const uint8 *)data + 8) * 255 + 16383) / 32767;
|
||||
anabuttons[2] = (MDFN_de16lsb((const uint8 *)data + 10) * 255 + 16383) / 32767;
|
||||
anabuttons[0] = (MDFN_de16lsb<false>((const uint8 *)data + 6) * 255 + 16383) / 32767;
|
||||
anabuttons[1] = (MDFN_de16lsb<false>((const uint8 *)data + 8) * 255 + 16383) / 32767;
|
||||
anabuttons[2] = (MDFN_de16lsb<false>((const uint8 *)data + 10) * 255 + 16383) / 32767;
|
||||
|
||||
//printf("%02x %02x %02x %02x\n", twist, anabuttons[0], anabuttons[1], anabuttons[2]);
|
||||
}
|
||||
|
@ -265,33 +265,4 @@ InputDevice *Device_neGcon_Create(void)
|
|||
}
|
||||
|
||||
|
||||
InputDeviceInputInfoStruct Device_neGcon_IDII[21] =
|
||||
{
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ "start", "START", 4, IDIT_BUTTON, NULL },
|
||||
{ "up", "D-Pad UP ↑", 0, IDIT_BUTTON, "down" },
|
||||
{ "right", "D-Pad RIGHT →", 3, IDIT_BUTTON, "left" },
|
||||
{ "down", "D-Pad DOWN ↓", 1, IDIT_BUTTON, "up" },
|
||||
{ "left", "D-Pad LEFT ←", 2, IDIT_BUTTON, "right" },
|
||||
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ "r", "Right Shoulder", 12, IDIT_BUTTON },
|
||||
|
||||
{ "b", "B", 9, IDIT_BUTTON, NULL },
|
||||
{ "a", "A", 10, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
{ NULL, "empty", -1, IDIT_BUTTON, NULL },
|
||||
|
||||
{ "twist_cwise", "Twist ↓|↑ (Analog, Turn Right)", 6, IDIT_BUTTON_ANALOG },
|
||||
{ "twist_ccwise", "Twist ↑|↓ (Analog, Turn Left)", 5, IDIT_BUTTON_ANALOG },
|
||||
{ "i", "I (Analog)", 8, IDIT_BUTTON_ANALOG },
|
||||
{ "ii", "II (Analog)", 7, IDIT_BUTTON_ANALOG },
|
||||
|
||||
{ "l", "Left Shoulder (Analog)", 11, IDIT_BUTTON_ANALOG },
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,5 @@
|
|||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
InputDevice *Device_neGcon_Create(void);
|
||||
extern InputDeviceInputInfoStruct Device_neGcon_IDII[21];
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -29,35 +29,6 @@ static INLINE void Recalc(void)
|
|||
CPU->AssertIRQ(0, (bool)(Status & Mask));
|
||||
}
|
||||
|
||||
void IRQ_Power(void)
|
||||
{
|
||||
Asserted = 0;
|
||||
Status = 0;
|
||||
Mask = 0;
|
||||
|
||||
Recalc();
|
||||
}
|
||||
|
||||
int IRQ_StateAction(StateMem *sm, int load, int data_only)
|
||||
{
|
||||
SFORMAT StateRegs[] =
|
||||
{
|
||||
SFVAR(Asserted),
|
||||
SFVAR(Mask),
|
||||
SFVAR(Status),
|
||||
SFEND
|
||||
};
|
||||
int ret = MDFNSS_StateAction(sm, load, data_only, StateRegs, "IRQ");
|
||||
|
||||
if(load)
|
||||
{
|
||||
Recalc();
|
||||
}
|
||||
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
void IRQ_SyncState(bool isReader, EW::NewState *ns)
|
||||
{
|
||||
NSS(Asserted);
|
||||
|
@ -130,6 +101,14 @@ uint32 IRQ_Read(uint32 A)
|
|||
return(ret);
|
||||
}
|
||||
|
||||
void IRQ_Power(void)
|
||||
{
|
||||
Asserted = 0;
|
||||
Status = 0;
|
||||
Mask = 0;
|
||||
|
||||
Recalc();
|
||||
}
|
||||
|
||||
void IRQ_Reset(void)
|
||||
{
|
||||
|
|
|
@ -19,7 +19,7 @@ enum
|
|||
IRQ_PIO = 10, // Probably
|
||||
};
|
||||
|
||||
void IRQ_Power(void);
|
||||
void IRQ_Power(void) MDFN_COLD;
|
||||
void IRQ_Assert(int which, bool asserted);
|
||||
|
||||
void IRQ_Write(uint32 A, uint32 V);
|
||||
|
@ -36,7 +36,6 @@ enum
|
|||
uint32 IRQ_GetRegister(unsigned int which, char *special, const uint32 special_len);
|
||||
void IRQ_SetRegister(unsigned int which, uint32 value);
|
||||
|
||||
int IRQ_StateAction(StateMem *sm, int load, int data_only);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
#ifndef __MDFN_PSX_MASMEM_H
|
||||
#define __MDFN_PSX_MASMEM_H
|
||||
|
||||
#include "endian.h"
|
||||
|
||||
// address must not be >= size specified by template parameter, and address must be a multiple of the byte-size of the
|
||||
// unit(1,2,4) being read(except for Read/WriteU24, which only needs to be byte-aligned).
|
||||
//
|
||||
|
||||
template<unsigned size, bool big_endian> //, unsigned pre_padding_count, unsigned post_padding_count>
|
||||
struct MultiAccessSizeMem
|
||||
{
|
||||
//uint8 pre_padding[pre_padding_count ? pre_padding_count : 1];
|
||||
|
||||
union
|
||||
{
|
||||
uint8 data8[size];
|
||||
uint64 alignment_dummy[1];
|
||||
};
|
||||
|
||||
//uint8 post_padding[post_padding_count ? post_padding_count : 1];
|
||||
|
||||
template<typename T>
|
||||
INLINE T Read(uint32 address)
|
||||
{
|
||||
return MDFN_deXsb<big_endian, T, true>(data8 + address);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
INLINE void Write(uint32 address, T value)
|
||||
{
|
||||
MDFN_enXsb<big_endian, T, true>(data8 + address, value);
|
||||
}
|
||||
|
||||
INLINE uint8 ReadU8(uint32 address)
|
||||
{
|
||||
return Read<uint8>(address);
|
||||
}
|
||||
|
||||
INLINE uint16 ReadU16(uint32 address)
|
||||
{
|
||||
return Read<uint16>(address);
|
||||
}
|
||||
|
||||
INLINE uint32 ReadU32(uint32 address)
|
||||
{
|
||||
return Read<uint32>(address);
|
||||
}
|
||||
|
||||
INLINE uint32 ReadU24(uint32 address)
|
||||
{
|
||||
uint32 ret;
|
||||
|
||||
if(!big_endian)
|
||||
{
|
||||
ret = ReadU8(address) << 0;
|
||||
ret |= ReadU8(address + 1) << 8;
|
||||
ret |= ReadU8(address + 2) << 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = ReadU8(address) << 16;
|
||||
ret |= ReadU8(address + 1) << 8;
|
||||
ret |= ReadU8(address + 2) << 0;
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
|
||||
INLINE void WriteU8(uint32 address, uint8 value)
|
||||
{
|
||||
Write<uint8>(address, value);
|
||||
}
|
||||
|
||||
INLINE void WriteU16(uint32 address, uint16 value)
|
||||
{
|
||||
Write<uint16>(address, value);
|
||||
}
|
||||
|
||||
INLINE void WriteU32(uint32 address, uint32 value)
|
||||
{
|
||||
Write<uint32>(address, value);
|
||||
}
|
||||
|
||||
INLINE void WriteU24(uint32 address, uint32 value)
|
||||
{
|
||||
if(!big_endian)
|
||||
{
|
||||
WriteU8(address + 0, value >> 0);
|
||||
WriteU8(address + 1, value >> 8);
|
||||
WriteU8(address + 2, value >> 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteU8(address + 0, value >> 16);
|
||||
WriteU8(address + 1, value >> 8);
|
||||
WriteU8(address + 2, value >> 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -407,7 +407,7 @@ static void EncodeImage(const unsigned ybn)
|
|||
|
||||
YCbCr_to_RGB(by[x], cb[x >> 1], cr[x >> 1], r, g, b);
|
||||
|
||||
StoreU16_LE(pix_out, pixel_xor ^ RGB_to_RGB555(r, g, b));
|
||||
MDFN_en16lsb<true>(pix_out, pixel_xor ^ RGB_to_RGB555(r, g, b));
|
||||
pix_out++;
|
||||
}
|
||||
}
|
||||
|
@ -603,7 +603,7 @@ void MDEC_Run(int32 clocks)
|
|||
PixelBufferReadOffset = 0;
|
||||
while(PixelBufferReadOffset != PixelBufferCount32)
|
||||
{
|
||||
MDEC_WRITE_FIFO(LoadU32_LE(&PixelBuffer.pix32[PixelBufferReadOffset++]));
|
||||
MDEC_WRITE_FIFO(MDFN_de32lsb<true>(&PixelBuffer.pix32[PixelBufferReadOffset++]));
|
||||
}
|
||||
} while(InCounter != 0xFFFF);
|
||||
}
|
||||
|
|
|
@ -12,13 +12,12 @@ void MDEC_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
|||
uint32 MDEC_Read(const pscpu_timestamp_t timestamp, uint32 A);
|
||||
|
||||
|
||||
void MDEC_Power(void);
|
||||
void MDEC_Power(void) MDFN_COLD;
|
||||
|
||||
bool MDEC_DMACanWrite(void);
|
||||
bool MDEC_DMACanRead(void);
|
||||
void MDEC_Run(int32 clocks);
|
||||
|
||||
int MDEC_StateAction(StateMem *sm, int load, int data_only);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -40,22 +40,41 @@
|
|||
namespace MDFN_IEN_PSX
|
||||
{
|
||||
|
||||
#if PSX_DBGPRINT_ENABLE
|
||||
static unsigned psx_dbg_level = 0;
|
||||
|
||||
void PSX_DBG(unsigned level, const char *format, ...) throw()
|
||||
{
|
||||
if(psx_dbg_level >= level)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
|
||||
trio_vprintf(format, ap);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned psx_dbg_level = 0;
|
||||
#if PSX_DBGPRINT_ENABLE
|
||||
|
||||
void PSX_DBG_BIOS_PUTC(uint8 c) noexcept
|
||||
{
|
||||
if(psx_dbg_level >= PSX_DBG_BIOS_PRINT)
|
||||
{
|
||||
if(c == 0x1B)
|
||||
return;
|
||||
|
||||
fputc(c, stdout);
|
||||
|
||||
//if(c == '\n')
|
||||
//{
|
||||
// fputc('%', stdout);
|
||||
// fputc(' ', stdout);
|
||||
//}
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
void PSX_DBG(unsigned level, const char *format, ...) noexcept
|
||||
{
|
||||
if(psx_dbg_level >= level)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
|
||||
trio_vprintf(format, ap);
|
||||
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -151,10 +170,10 @@ PS_GPU *GPU = NULL;
|
|||
PS_CDC *CDC = NULL;
|
||||
FrontIO *FIO = NULL;
|
||||
|
||||
static MultiAccessSizeMem<512 * 1024, uint32, false> *BIOSROM = NULL;
|
||||
static MultiAccessSizeMem<65536, uint32, false> *PIOMem = NULL;
|
||||
static MultiAccessSizeMem<512 * 1024, false> *BIOSROM = NULL;
|
||||
static MultiAccessSizeMem<65536, false> *PIOMem = NULL;
|
||||
|
||||
MultiAccessSizeMem<2048 * 1024, uint32, false> MainRAM;
|
||||
MultiAccessSizeMem<2048 * 1024, false> MainRAM;
|
||||
|
||||
static uint32 TextMem_Start;
|
||||
static std::vector<uint8> TextMem;
|
||||
|
@ -731,8 +750,8 @@ template<typename T, bool IsWrite, bool Access24> static INLINE void MemRW(pscpu
|
|||
else switch(sizeof(T))
|
||||
{
|
||||
case 1: V = TextMem[(A & 0x7FFFFF) - 65536]; break;
|
||||
case 2: V = MDFN_de16lsb(&TextMem[(A & 0x7FFFFF) - 65536]); break;
|
||||
case 4: V = MDFN_de32lsb(&TextMem[(A & 0x7FFFFF) - 65536]); break;
|
||||
case 2: V = MDFN_de16lsb<false>(&TextMem[(A & 0x7FFFFF) - 65536]); break;
|
||||
case 4: V = MDFN_de32lsb<false>(&TextMem[(A & 0x7FFFFF) - 65536]); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -921,8 +940,8 @@ template<typename T, bool Access24> static INLINE uint32 MemPeek(pscpu_timestamp
|
|||
else switch(sizeof(T))
|
||||
{
|
||||
case 1: return(TextMem[(A & 0x7FFFFF) - 65536]); break;
|
||||
case 2: return(MDFN_de16lsb(&TextMem[(A & 0x7FFFFF) - 65536])); break;
|
||||
case 4: return(MDFN_de32lsb(&TextMem[(A & 0x7FFFFF) - 65536])); break;
|
||||
case 2: return(MDFN_de16lsb<false>(&TextMem[(A & 0x7FFFFF) - 65536])); break;
|
||||
case 4: return(MDFN_de32lsb<false>(&TextMem[(A & 0x7FFFFF) - 65536])); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -951,11 +970,11 @@ uint32 PSX_MemPeek32(uint32 A)
|
|||
}
|
||||
|
||||
// FIXME: Add PSX_Reset() and FrontIO::Reset() so that emulated input devices don't get power-reset on reset-button reset.
|
||||
static void PSX_Power(void)
|
||||
static void PSX_Power(bool powering_up)
|
||||
{
|
||||
PSX_PRNG.ResetState(); // Should occur first!
|
||||
|
||||
memset(MainRAM.data32, 0, 2048 * 1024);
|
||||
memset(MainRAM.data8, 0, 2048 * 1024);
|
||||
|
||||
for(unsigned i = 0; i < 9; i++)
|
||||
SysControl.Regs[i] = 0;
|
||||
|
@ -968,7 +987,7 @@ static void PSX_Power(void)
|
|||
|
||||
DMA_Power();
|
||||
|
||||
FIO->Power();
|
||||
FIO->Reset(powering_up);
|
||||
SIO_Power();
|
||||
|
||||
MDEC_Power();
|
||||
|
@ -1120,9 +1139,12 @@ struct {
|
|||
return SHOCK_OK;
|
||||
|
||||
case eShockMemcardTransaction_Read:
|
||||
FIO->MCPorts[portnum]->ReadNV((uint8*)transaction->buffer128k,0,128*1024);
|
||||
{
|
||||
const u8* ptr = FIO->MCPorts[portnum]->ReadNV();
|
||||
memcpy(transaction->buffer128k,ptr,128*1024);
|
||||
FIO->MCPorts[portnum]->ResetNVDirtyCount();
|
||||
return SHOCK_OK;
|
||||
}
|
||||
|
||||
case eShockMemcardTransaction_CheckDirty:
|
||||
if(FIO->GetMemcardDirtyCount(portnum))
|
||||
|
@ -1155,20 +1177,20 @@ static void MountCPUAddressSpace()
|
|||
{
|
||||
for(uint32 ma = 0x00000000; ma < 0x00800000; ma += 2048 * 1024)
|
||||
{
|
||||
CPU->SetFastMap(MainRAM.data32, 0x00000000 + ma, 2048 * 1024);
|
||||
CPU->SetFastMap(MainRAM.data32, 0x80000000 + ma, 2048 * 1024);
|
||||
CPU->SetFastMap(MainRAM.data32, 0xA0000000 + ma, 2048 * 1024);
|
||||
CPU->SetFastMap(MainRAM.data8, 0x00000000 + ma, 2048 * 1024);
|
||||
CPU->SetFastMap(MainRAM.data8, 0x80000000 + ma, 2048 * 1024);
|
||||
CPU->SetFastMap(MainRAM.data8, 0xA0000000 + ma, 2048 * 1024);
|
||||
}
|
||||
|
||||
CPU->SetFastMap(BIOSROM->data32, 0x1FC00000, 512 * 1024);
|
||||
CPU->SetFastMap(BIOSROM->data32, 0x9FC00000, 512 * 1024);
|
||||
CPU->SetFastMap(BIOSROM->data32, 0xBFC00000, 512 * 1024);
|
||||
CPU->SetFastMap(BIOSROM->data8, 0x1FC00000, 512 * 1024);
|
||||
CPU->SetFastMap(BIOSROM->data8, 0x9FC00000, 512 * 1024);
|
||||
CPU->SetFastMap(BIOSROM->data8, 0xBFC00000, 512 * 1024);
|
||||
|
||||
if(PIOMem)
|
||||
{
|
||||
CPU->SetFastMap(PIOMem->data32, 0x1F000000, 65536);
|
||||
CPU->SetFastMap(PIOMem->data32, 0x9F000000, 65536);
|
||||
CPU->SetFastMap(PIOMem->data32, 0xBF000000, 65536);
|
||||
CPU->SetFastMap(PIOMem->data8, 0x1F000000, 65536);
|
||||
CPU->SetFastMap(PIOMem->data8, 0x9F000000, 65536);
|
||||
CPU->SetFastMap(PIOMem->data8, 0xBF000000, 65536);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1189,10 +1211,10 @@ EW_EXPORT s32 shock_Create(void** psx, s32 region, void* firmware512k)
|
|||
//PIO Mem: why wouldn't we want this?
|
||||
static const bool WantPIOMem = true;
|
||||
|
||||
BIOSROM = new MultiAccessSizeMem<512 * 1024, uint32, false>();
|
||||
BIOSROM = new MultiAccessSizeMem<512 * 1024, false>();
|
||||
memcpy(BIOSROM->data8, firmware512k, 512 * 1024);
|
||||
|
||||
if(WantPIOMem) PIOMem = new MultiAccessSizeMem<65536, uint32, false>();
|
||||
if(WantPIOMem) PIOMem = new MultiAccessSizeMem<65536, false>();
|
||||
else PIOMem = NULL;
|
||||
|
||||
CPU = new PS_CPU();
|
||||
|
@ -1216,7 +1238,7 @@ EW_EXPORT s32 shock_Create(void** psx, s32 region, void* firmware512k)
|
|||
}
|
||||
|
||||
//these steps can't be done without more information
|
||||
GPU = new PS_GPU(region == REGION_EU, sls, sle);
|
||||
GPU = new PS_GPU(region == REGION_EU, sls, sle, true);
|
||||
|
||||
//fetch video parameters, stash in a simpler format
|
||||
MDFNGI givp;
|
||||
|
@ -1264,7 +1286,7 @@ EW_EXPORT s32 shock_PowerOn(void* psx)
|
|||
{
|
||||
if(s_ShockState.power) return SHOCK_NOCANDO;
|
||||
|
||||
PSX_Power();
|
||||
PSX_Power(true);
|
||||
|
||||
return SHOCK_OK;
|
||||
}
|
||||
|
@ -1321,7 +1343,7 @@ EW_EXPORT s32 shock_Step(void* psx, eShockStep step)
|
|||
SPU->StartFrame(espec.SoundRate, ResampleQuality);
|
||||
|
||||
Running = -1;
|
||||
timestamp = CPU->Run(timestamp, psf_loader != NULL);
|
||||
timestamp = CPU->Run(timestamp, psf_loader == NULL && psx_dbg_level >= PSX_DBG_BIOS_PRINT, psf_loader != NULL);
|
||||
assert(timestamp);
|
||||
|
||||
ForceEventUpdates(timestamp);
|
||||
|
@ -1564,10 +1586,10 @@ static void LoadEXE(const uint8 *data, const uint32 size, bool ignore_pcsp = fal
|
|||
//if(size < 0x800)
|
||||
// throw(MDFN_Error(0, "PS-EXE is too small."));
|
||||
|
||||
PC = MDFN_de32lsb(&data[0x10]);
|
||||
SP = MDFN_de32lsb(&data[0x30]);
|
||||
TextStart = MDFN_de32lsb(&data[0x18]);
|
||||
TextSize = MDFN_de32lsb(&data[0x1C]);
|
||||
PC = MDFN_de32lsb<false>(&data[0x10]);
|
||||
SP = MDFN_de32lsb<false>(&data[0x30]);
|
||||
TextStart = MDFN_de32lsb<false>(&data[0x18]);
|
||||
TextSize = MDFN_de32lsb<false>(&data[0x1C]);
|
||||
|
||||
if(ignore_pcsp)
|
||||
printf("TextStart=0x%08x\nTextSize=0x%08x\n", TextStart, TextSize);
|
||||
|
@ -1627,23 +1649,23 @@ static void LoadEXE(const uint8 *data, const uint32 size, bool ignore_pcsp = fal
|
|||
|
||||
po = &PIOMem->data8[0x0800];
|
||||
|
||||
MDFN_en32lsb(po, (0x0 << 26) | (31 << 21) | (0x8 << 0)); // JR
|
||||
MDFN_en32lsb<false>(po, (0x0 << 26) | (31 << 21) | (0x8 << 0)); // JR
|
||||
po += 4;
|
||||
MDFN_en32lsb(po, 0); // NOP(kinda)
|
||||
MDFN_en32lsb<false>(po, 0); // NOP(kinda)
|
||||
po += 4;
|
||||
|
||||
po = &PIOMem->data8[0x1000];
|
||||
|
||||
// Load cacheable-region target PC into r2
|
||||
MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16) | (0x9F001010 >> 16)); // LUI
|
||||
MDFN_en32lsb<false>(po, (0xF << 26) | (0 << 21) | (1 << 16) | (0x9F001010 >> 16)); // LUI
|
||||
po += 4;
|
||||
MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (2 << 16) | (0x9F001010 & 0xFFFF)); // ORI
|
||||
MDFN_en32lsb<false>(po, (0xD << 26) | (1 << 21) | (2 << 16) | (0x9F001010 & 0xFFFF)); // ORI
|
||||
po += 4;
|
||||
|
||||
// Jump to r2
|
||||
MDFN_en32lsb(po, (0x0 << 26) | (2 << 21) | (0x8 << 0)); // JR
|
||||
MDFN_en32lsb<false>(po, (0x0 << 26) | (2 << 21) | (0x8 << 0)); // JR
|
||||
po += 4;
|
||||
MDFN_en32lsb(po, 0); // NOP(kinda)
|
||||
MDFN_en32lsb<false>(po, 0); // NOP(kinda)
|
||||
po += 4;
|
||||
|
||||
//
|
||||
|
@ -1652,42 +1674,42 @@ static void LoadEXE(const uint8 *data, const uint32 size, bool ignore_pcsp = fal
|
|||
|
||||
// Load source address into r8
|
||||
uint32 sa = 0x9F000000 + 65536;
|
||||
MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16) | (sa >> 16)); // LUI
|
||||
MDFN_en32lsb<false>(po, (0xF << 26) | (0 << 21) | (1 << 16) | (sa >> 16)); // LUI
|
||||
po += 4;
|
||||
MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (8 << 16) | (sa & 0xFFFF)); // ORI
|
||||
MDFN_en32lsb<false>(po, (0xD << 26) | (1 << 21) | (8 << 16) | (sa & 0xFFFF)); // ORI
|
||||
po += 4;
|
||||
|
||||
// Load dest address into r9
|
||||
MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16) | (TextMem_Start >> 16)); // LUI
|
||||
MDFN_en32lsb<false>(po, (0xF << 26) | (0 << 21) | (1 << 16) | (TextMem_Start >> 16)); // LUI
|
||||
po += 4;
|
||||
MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (9 << 16) | (TextMem_Start & 0xFFFF)); // ORI
|
||||
MDFN_en32lsb<false>(po, (0xD << 26) | (1 << 21) | (9 << 16) | (TextMem_Start & 0xFFFF)); // ORI
|
||||
po += 4;
|
||||
|
||||
// Load size into r10
|
||||
MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16) | (TextMem.size() >> 16)); // LUI
|
||||
MDFN_en32lsb<false>(po, (0xF << 26) | (0 << 21) | (1 << 16) | (TextMem.size() >> 16)); // LUI
|
||||
po += 4;
|
||||
MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (10 << 16) | (TextMem.size() & 0xFFFF)); // ORI
|
||||
MDFN_en32lsb<false>(po, (0xD << 26) | (1 << 21) | (10 << 16) | (TextMem.size() & 0xFFFF)); // ORI
|
||||
po += 4;
|
||||
|
||||
//
|
||||
// Loop begin
|
||||
//
|
||||
|
||||
MDFN_en32lsb(po, (0x24 << 26) | (8 << 21) | (1 << 16)); // LBU to r1
|
||||
MDFN_en32lsb<false>(po, (0x24 << 26) | (8 << 21) | (1 << 16)); // LBU to r1
|
||||
po += 4;
|
||||
|
||||
MDFN_en32lsb(po, (0x08 << 26) | (10 << 21) | (10 << 16) | 0xFFFF); // Decrement size
|
||||
MDFN_en32lsb<false>(po, (0x08 << 26) | (10 << 21) | (10 << 16) | 0xFFFF); // Decrement size
|
||||
po += 4;
|
||||
|
||||
MDFN_en32lsb(po, (0x28 << 26) | (9 << 21) | (1 << 16)); // SB from r1
|
||||
MDFN_en32lsb<false>(po, (0x28 << 26) | (9 << 21) | (1 << 16)); // SB from r1
|
||||
po += 4;
|
||||
|
||||
MDFN_en32lsb(po, (0x08 << 26) | (8 << 21) | (8 << 16) | 0x0001); // Increment source addr
|
||||
MDFN_en32lsb<false>(po, (0x08 << 26) | (8 << 21) | (8 << 16) | 0x0001); // Increment source addr
|
||||
po += 4;
|
||||
|
||||
MDFN_en32lsb(po, (0x05 << 26) | (0 << 21) | (10 << 16) | (-5 & 0xFFFF));
|
||||
MDFN_en32lsb<false>(po, (0x05 << 26) | (0 << 21) | (10 << 16) | (-5 & 0xFFFF));
|
||||
po += 4;
|
||||
MDFN_en32lsb(po, (0x08 << 26) | (9 << 21) | (9 << 16) | 0x0001); // Increment dest addr
|
||||
MDFN_en32lsb<false>(po, (0x08 << 26) | (9 << 21) | (9 << 16) | 0x0001); // Increment dest addr
|
||||
po += 4;
|
||||
|
||||
//
|
||||
|
@ -1701,31 +1723,31 @@ static void LoadEXE(const uint8 *data, const uint32 size, bool ignore_pcsp = fal
|
|||
}
|
||||
else
|
||||
{
|
||||
MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16) | (SP >> 16)); // LUI
|
||||
MDFN_en32lsb<false>(po, (0xF << 26) | (0 << 21) | (1 << 16) | (SP >> 16)); // LUI
|
||||
po += 4;
|
||||
MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (29 << 16) | (SP & 0xFFFF)); // ORI
|
||||
MDFN_en32lsb<false>(po, (0xD << 26) | (1 << 21) | (29 << 16) | (SP & 0xFFFF)); // ORI
|
||||
po += 4;
|
||||
|
||||
// Load PC into r2
|
||||
MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16) | ((PC >> 16) | 0x8000)); // LUI
|
||||
MDFN_en32lsb<false>(po, (0xF << 26) | (0 << 21) | (1 << 16) | ((PC >> 16) | 0x8000)); // LUI
|
||||
po += 4;
|
||||
MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (2 << 16) | (PC & 0xFFFF)); // ORI
|
||||
MDFN_en32lsb<false>(po, (0xD << 26) | (1 << 21) | (2 << 16) | (PC & 0xFFFF)); // ORI
|
||||
po += 4;
|
||||
}
|
||||
|
||||
// Half-assed instruction cache flush. ;)
|
||||
for(unsigned i = 0; i < 1024; i++)
|
||||
{
|
||||
MDFN_en32lsb(po, 0);
|
||||
MDFN_en32lsb<false>(po, 0);
|
||||
po += 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Jump to r2
|
||||
MDFN_en32lsb(po, (0x0 << 26) | (2 << 21) | (0x8 << 0)); // JR
|
||||
MDFN_en32lsb<false>(po, (0x0 << 26) | (2 << 21) | (0x8 << 0)); // JR
|
||||
po += 4;
|
||||
MDFN_en32lsb(po, 0); // NOP(kinda)
|
||||
MDFN_en32lsb<false>(po, 0); // NOP(kinda)
|
||||
po += 4;
|
||||
}
|
||||
|
||||
|
@ -1735,7 +1757,7 @@ EW_EXPORT s32 shock_MountEXE(void* psx, void* exebuf, int size)
|
|||
return SHOCK_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef WANT_PSF
|
||||
PSF1Loader::PSF1Loader(MDFNFILE *fp)
|
||||
{
|
||||
|
@ -2162,8 +2184,8 @@ EW_EXPORT s32 shock_AnalyzeDisc(ShockDiscRef* disc, ShockDiscInfo* info)
|
|||
throw "Missing Primary Volume Descriptor";
|
||||
} while(pvd[0] != 0x01);
|
||||
//[156 ... 189], 34 bytes
|
||||
uint32 rdel = MDFN_de32lsb(&pvd[0x9E]);
|
||||
uint32 rdel_len = MDFN_de32lsb(&pvd[0xA6]);
|
||||
uint32 rdel = MDFN_de32lsb<false>(&pvd[0x9E]);
|
||||
uint32 rdel_len = MDFN_de32lsb<false>(&pvd[0xA6]);
|
||||
|
||||
if(rdel_len >= (1024 * 1024 * 10)) // Arbitrary sanity check.
|
||||
throw "Root directory table too large";
|
||||
|
@ -2190,8 +2212,8 @@ EW_EXPORT s32 shock_AnalyzeDisc(ShockDiscRef* disc, ShockDiscInfo* info)
|
|||
|
||||
if(len_fi == 12 && !memcmp(&dr[0x21], "SYSTEM.CNF;1", 12))
|
||||
{
|
||||
uint32 file_lba = MDFN_de32lsb(&dr[0x02]);
|
||||
//uint32 file_len = MDFN_de32lsb(&dr[0x0A]);
|
||||
uint32 file_lba = MDFN_de32lsb<false>(&dr[0x02]);
|
||||
//uint32 file_len = MDFN_de32lsb<false>(&dr[0x0A]);
|
||||
uint8 fb[2048 + 1];
|
||||
char *bootpos;
|
||||
|
||||
|
|
|
@ -33,12 +33,14 @@ namespace MDFN_IEN_PSX
|
|||
#define PSX_DBG_FLOOD 4 // Heavy informational debug messages(GPU commands; TODO).
|
||||
|
||||
#if PSX_DBGPRINT_ENABLE
|
||||
void PSX_DBG(unsigned level, const char *format, ...) throw() MDFN_COLD MDFN_FORMATSTR(gnu_printf, 2, 3);
|
||||
void PSX_DBG(unsigned level, const char *format, ...) noexcept MDFN_COLD MDFN_FORMATSTR(gnu_printf, 2, 3);
|
||||
void PSX_DBG_BIOS_PUTC(uint8 c) noexcept;
|
||||
|
||||
#define PSX_WARNING(format, ...) { PSX_DBG(PSX_DBG_WARNING, format "\n", ## __VA_ARGS__); }
|
||||
#define PSX_DBGINFO(format, ...) { }
|
||||
#else
|
||||
static INLINE void PSX_DBG(unsigned level, const char* format, ...) { }
|
||||
static INLINE void PSX_DBG_BIOS_PUTC(uint8 c) { }
|
||||
static INLINE void PSX_WARNING(const char* format, ...) { }
|
||||
static INLINE void PSX_DBGINFO(const char* format, ...) { }
|
||||
#endif
|
||||
|
@ -110,7 +112,7 @@ namespace MDFN_IEN_PSX
|
|||
extern PS_GPU *GPU;
|
||||
extern PS_CDC *CDC;
|
||||
extern PS_SPU *SPU;
|
||||
extern MultiAccessSizeMem<2048 * 1024, uint32, false> MainRAM;
|
||||
extern MultiAccessSizeMem<2048 * 1024, false> MainRAM;
|
||||
};
|
||||
|
||||
enum eRegion
|
||||
|
|
|
@ -8,8 +8,6 @@ void SIO_Write(pscpu_timestamp_t timestamp, uint32 A, uint32 V);
|
|||
uint32 SIO_Read(pscpu_timestamp_t timestamp, uint32 A);
|
||||
void SIO_Power(void);
|
||||
|
||||
int SIO_StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,66 +1,63 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* 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 of the License, 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
|
||||
*/
|
||||
|
||||
/* TODO:
|
||||
Note to self: Emulating the SPU at more timing accuracy than sample, and emulating the whole SPU RAM write port FIFO thing and hypothetical periodic FIFO commit to
|
||||
SPU RAM(maybe every 32 CPU cycles, with exceptions?) will likely necessitate a much more timing-accurate CPU core, and emulation of the SPU delay register(or at least the
|
||||
effects of the standard value written to it), to avoid game glitches. Probably more trouble than it's worth....
|
||||
|
||||
SPU IRQ emulation isn't totally correct, behavior is kind of complex; run more tests on PS1.
|
||||
|
||||
Test reverb upsampler on the real thing.
|
||||
|
||||
Alter reverb algorithm to process in the pattern of L,R,L,R,L,R on each input sample, instead of doing both L and R on every 2 input samples(make
|
||||
sure the real thing does it this way too, I think it at least runs the downsampler this way); and while we're at it, implement the correct buffer
|
||||
offset(probably either -39 or -40, the latter is what we have now).
|
||||
|
||||
Alter reverb algorithm to perform saturation more often, as occurs on the real thing.
|
||||
|
||||
See if sample flag & 0x8 does anything weird, like suppressing the program-readable block end flag setting.
|
||||
|
||||
Determine the actual purpose of global register 0x2C(is it REALLY an address multiplier? And if so, does it affect the reverb offsets too?)
|
||||
|
||||
For ADSR and volume sweep, should the divider be reset to 0 on &0x8000 == true, or should the upper bit be cleared?
|
||||
|
||||
Should shift occur on all stages of ADPCM sample decoding, or only at the end?
|
||||
|
||||
On the real thing, there's some kind of weirdness with ADSR when you voice on when attack_rate(raw) = 0x7F; the envelope level register is repeatedly
|
||||
reset to 0, which you can see by manual writes to the envelope level register. Normally in the attack phase when attack_rate = 0x7F, enveloping is effectively stuck/paused such that the value you write is sticky and won't be replaced or reset. Note that after you voice on, you can write a new attack_rate < 0x7F, and enveloping will work "normally" again shortly afterwards. You can even write an attack_rate of 0x7F at that point to pause enveloping clocking. I doubt any games rely on this, but it's something to keep in mind if we ever need greater insight as to how the SPU functions at a low-level in order to emulate it at cycle granularity rather than sample granularity, and it may not be a bad idea to investigate this oddity further and emulate it in the future regardless.
|
||||
|
||||
Voice 1 and 3 waveform output writes to SPURAM might not be correct(noted due to problems reading this area of SPU RAM on the real thing
|
||||
based on my expectations of how this should work).
|
||||
*/
|
||||
|
||||
/*
|
||||
Notes:
|
||||
The last half of the noise freq table was confirmed on a real PSX(more or less, number of changes * 0x8000 / samples), but the first half hasn't been yet with sufficient precision.
|
||||
|
||||
All addresses(for 16-bit access, at least) within the SPU address space appear to be fully read/write as if they were RAM, though
|
||||
values at some addresses(like the envelope current value) will be "overwritten" by the sound processing at certain times.
|
||||
|
||||
32-bit and 8-bit reads act as if it were RAM(not tested with all addresses, but a few, assuming the rest are the same), but 8-bit writes
|
||||
to odd addresses appear to be ignored, and 8-bit writes to even addresses are treated as 16-bit writes(most likely, but, need to code custom assembly to
|
||||
fully test the upper 8 bits). NOTE: the preceding information doesn't necessarily cover accesses with side effects, they still need to be tested; and it
|
||||
of course covers reads/writes from the point of view of software running on the CPU.
|
||||
|
||||
It doesn't appear to be possible to enable FM on the first channel/voice(channel/voice 0).
|
||||
|
||||
Lower bit of channel start address appears to be masked out to 0(such that ADPCM block decoding is always 8 16-bit units, 16 bytes, aligned), as far as
|
||||
block-decoding and flag-set program-readable loop address go.
|
||||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* 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 of the License, 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
|
||||
*/
|
||||
|
||||
/* TODO:
|
||||
Note to self: Emulating the SPU at more timing accuracy than sample, and emulating the whole SPU RAM write port FIFO thing and hypothetical periodic FIFO commit to
|
||||
SPU RAM(maybe every 32 CPU cycles, with exceptions?) will likely necessitate a much more timing-accurate CPU core, and emulation of the SPU delay register(or at least the
|
||||
effects of the standard value written to it), to avoid game glitches. Probably more trouble than it's worth....
|
||||
|
||||
SPU IRQ emulation isn't totally correct, behavior is kind of complex; run more tests on PS1.
|
||||
|
||||
Test reverb upsampler on the real thing.
|
||||
|
||||
Alter reverb algorithm to process in the pattern of L,R,L,R,L,R on each input sample, instead of doing both L and R on every 2 input samples(make
|
||||
sure the real thing does it this way too, I think it at least runs the downsampler this way).
|
||||
|
||||
Alter reverb algorithm to perform saturation more often, as occurs on the real thing.
|
||||
|
||||
See if sample flag & 0x8 does anything weird, like suppressing the program-readable block end flag setting.
|
||||
|
||||
Determine the actual purpose of global register 0x2C(is it REALLY an address multiplier? And if so, does it affect the reverb offsets too?)
|
||||
|
||||
For ADSR and volume sweep, should the divider be reset to 0 on &0x8000 == true, or should the upper bit be cleared?
|
||||
|
||||
Should shift occur on all stages of ADPCM sample decoding, or only at the end?
|
||||
|
||||
On the real thing, there's some kind of weirdness with ADSR when you voice on when attack_rate(raw) = 0x7F; the envelope level register is repeatedly
|
||||
reset to 0, which you can see by manual writes to the envelope level register. Normally in the attack phase when attack_rate = 0x7F, enveloping is effectively stuck/paused such that the value you write is sticky and won't be replaced or reset. Note that after you voice on, you can write a new attack_rate < 0x7F, and enveloping will work "normally" again shortly afterwards. You can even write an attack_rate of 0x7F at that point to pause enveloping clocking. I doubt any games rely on this, but it's something to keep in mind if we ever need greater insight as to how the SPU functions at a low-level in order to emulate it at cycle granularity rather than sample granularity, and it may not be a bad idea to investigate this oddity further and emulate it in the future regardless.
|
||||
|
||||
Voice 1 and 3 waveform output writes to SPURAM might not be correct(noted due to problems reading this area of SPU RAM on the real thing
|
||||
based on my expectations of how this should work).
|
||||
*/
|
||||
|
||||
/*
|
||||
Notes:
|
||||
All addresses(for 16-bit access, at least) within the SPU address space appear to be fully read/write as if they were RAM, though
|
||||
values at some addresses(like the envelope current value) will be "overwritten" by the sound processing at certain times.
|
||||
|
||||
32-bit and 8-bit reads act as if it were RAM(not tested with all addresses, but a few, assuming the rest are the same), but 8-bit writes
|
||||
to odd addresses appear to be ignored, and 8-bit writes to even addresses are treated as 16-bit writes(most likely, but, need to code custom assembly to
|
||||
fully test the upper 8 bits). NOTE: the preceding information doesn't necessarily cover accesses with side effects, they still need to be tested; and it
|
||||
of course covers reads/writes from the point of view of software running on the CPU.
|
||||
|
||||
It doesn't appear to be possible to enable FM on the first channel/voice(channel/voice 0).
|
||||
|
||||
Lower bit of channel start address appears to be masked out to 0(such that ADPCM block decoding is always 8 16-bit units, 16 bytes, aligned), as far as
|
||||
block-decoding and flag-set program-readable loop address go.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -95,90 +92,89 @@ PS_SPU::~PS_SPU()
|
|||
{
|
||||
}
|
||||
|
||||
void PS_SPU::Power(void)
|
||||
{
|
||||
clock_divider = 768;
|
||||
|
||||
memset(SPURAM, 0, sizeof(SPURAM));
|
||||
|
||||
for(int i = 0; i < 24; i++)
|
||||
{
|
||||
memset(Voices[i].DecodeBuffer, 0, sizeof(Voices[i].DecodeBuffer));
|
||||
Voices[i].DecodeM2 = 0;
|
||||
Voices[i].DecodeM1 = 0;
|
||||
|
||||
Voices[i].DecodePlayDelay = 0;
|
||||
Voices[i].DecodeWritePos = 0;
|
||||
Voices[i].DecodeReadPos = 0;
|
||||
Voices[i].DecodeAvail = 0;
|
||||
|
||||
Voices[i].DecodeShift = 0;
|
||||
Voices[i].DecodeWeight = 0;
|
||||
Voices[i].DecodeFlags = 0;
|
||||
|
||||
Voices[i].IgnoreSampLA = false;
|
||||
|
||||
Voices[i].Sweep[0].Power();
|
||||
Voices[i].Sweep[1].Power();
|
||||
|
||||
Voices[i].Pitch = 0;
|
||||
Voices[i].CurPhase = 0;
|
||||
|
||||
Voices[i].StartAddr = 0;
|
||||
|
||||
Voices[i].CurAddr = 0;
|
||||
|
||||
Voices[i].ADSRControl = 0;
|
||||
|
||||
Voices[i].LoopAddr = 0;
|
||||
|
||||
Voices[i].PreLRSample = 0;
|
||||
|
||||
memset(&Voices[i].ADSR, 0, sizeof(SPU_ADSR));
|
||||
}
|
||||
|
||||
GlobalSweep[0].Power();
|
||||
GlobalSweep[1].Power();
|
||||
|
||||
NoiseDivider = 0;
|
||||
NoiseCounter = 0;
|
||||
LFSR = 0;
|
||||
|
||||
FM_Mode = 0;
|
||||
Noise_Mode = 0;
|
||||
Reverb_Mode = 0;
|
||||
ReverbWA = 0;
|
||||
|
||||
ReverbVol[0] = ReverbVol[1] = 0;
|
||||
|
||||
CDVol[0] = CDVol[1] = 0;
|
||||
|
||||
ExternVol[0] = ExternVol[1] = 0;
|
||||
|
||||
IRQAddr = 0;
|
||||
|
||||
RWAddr = 0;
|
||||
|
||||
SPUControl = 0;
|
||||
|
||||
VoiceOn = 0;
|
||||
VoiceOff = 0;
|
||||
|
||||
BlockEnd = 0;
|
||||
|
||||
CWA = 0;
|
||||
|
||||
memset(Regs, 0, sizeof(Regs));
|
||||
|
||||
memset(RDSB, 0, sizeof(RDSB));
|
||||
RDSB_WP = 0;
|
||||
|
||||
memset(RUSB, 0, sizeof(RUSB));
|
||||
RUSB_WP = 0;
|
||||
|
||||
ReverbCur = ReverbWA;
|
||||
|
||||
IRQAsserted = false;
|
||||
|
||||
void PS_SPU::Power(void)
|
||||
{
|
||||
clock_divider = 768;
|
||||
|
||||
memset(SPURAM, 0, sizeof(SPURAM));
|
||||
|
||||
for(int i = 0; i < 24; i++)
|
||||
{
|
||||
memset(Voices[i].DecodeBuffer, 0, sizeof(Voices[i].DecodeBuffer));
|
||||
Voices[i].DecodeM2 = 0;
|
||||
Voices[i].DecodeM1 = 0;
|
||||
|
||||
Voices[i].DecodePlayDelay = 0;
|
||||
Voices[i].DecodeWritePos = 0;
|
||||
Voices[i].DecodeReadPos = 0;
|
||||
Voices[i].DecodeAvail = 0;
|
||||
|
||||
Voices[i].DecodeShift = 0;
|
||||
Voices[i].DecodeWeight = 0;
|
||||
Voices[i].DecodeFlags = 0;
|
||||
|
||||
Voices[i].IgnoreSampLA = false;
|
||||
|
||||
Voices[i].Sweep[0].Power();
|
||||
Voices[i].Sweep[1].Power();
|
||||
|
||||
Voices[i].Pitch = 0;
|
||||
Voices[i].CurPhase = 0;
|
||||
|
||||
Voices[i].StartAddr = 0;
|
||||
|
||||
Voices[i].CurAddr = 0;
|
||||
|
||||
Voices[i].ADSRControl = 0;
|
||||
|
||||
Voices[i].LoopAddr = 0;
|
||||
|
||||
Voices[i].PreLRSample = 0;
|
||||
|
||||
memset(&Voices[i].ADSR, 0, sizeof(SPU_ADSR));
|
||||
}
|
||||
|
||||
GlobalSweep[0].Power();
|
||||
GlobalSweep[1].Power();
|
||||
|
||||
NoiseDivider = 0;
|
||||
NoiseCounter = 0;
|
||||
LFSR = 0;
|
||||
|
||||
FM_Mode = 0;
|
||||
Noise_Mode = 0;
|
||||
Reverb_Mode = 0;
|
||||
ReverbWA = 0;
|
||||
|
||||
ReverbVol[0] = ReverbVol[1] = 0;
|
||||
|
||||
CDVol[0] = CDVol[1] = 0;
|
||||
|
||||
ExternVol[0] = ExternVol[1] = 0;
|
||||
|
||||
IRQAddr = 0;
|
||||
|
||||
RWAddr = 0;
|
||||
|
||||
SPUControl = 0;
|
||||
|
||||
VoiceOn = 0;
|
||||
VoiceOff = 0;
|
||||
|
||||
BlockEnd = 0;
|
||||
|
||||
CWA = 0;
|
||||
|
||||
memset(Regs, 0, sizeof(Regs));
|
||||
|
||||
memset(RDSB, 0, sizeof(RDSB));
|
||||
memset(RUSB, 0, sizeof(RUSB));
|
||||
RvbResPos = 0;
|
||||
|
||||
ReverbCur = ReverbWA;
|
||||
|
||||
IRQAsserted = false;
|
||||
}
|
||||
|
||||
static INLINE void CalcVCDelta(const uint8 zs, uint8 speed, bool log_mode, bool dec_mode, bool inv_increment, int16 Current, int &increment, int &divinco)
|
||||
|
@ -190,8 +186,8 @@ static INLINE void CalcVCDelta(const uint8 zs, uint8 speed, bool log_mode, bool
|
|||
|
||||
divinco = 32768;
|
||||
|
||||
if(speed < 0x2C)
|
||||
increment <<= (0x2F - speed) >> 2;
|
||||
if(speed < 0x2C)
|
||||
increment = (unsigned)increment << ((0x2F - speed) >> 2);
|
||||
|
||||
if(speed >= 0x30)
|
||||
divinco >>= (speed - 0x2C) >> 2;
|
||||
|
@ -328,10 +324,20 @@ void PS_SPU::RunDecoder(SPU_Voice *voice)
|
|||
return;
|
||||
}
|
||||
|
||||
if((voice->CurAddr & 0x7) == 0)
|
||||
{
|
||||
// Handle delayed flags from the previously-decoded block.
|
||||
if(voice->DecodeFlags & 0x1)
|
||||
|
||||
if((voice->CurAddr & 0x7) == 0)
|
||||
{
|
||||
//
|
||||
// Handle delayed flags from the previously-decoded block.
|
||||
//
|
||||
// NOTE: The timing of setting the BlockEnd bit here, and forcing ADSR envelope volume to 0, is a bit late. (And I'm not sure if it should be done once
|
||||
// per decoded block, or more than once, but that's probably not something games would rely on, but we should test it anyway).
|
||||
//
|
||||
// Correctish timing can be achieved by moving this block of code up above voice->DecodeAvail >= 11, and sticking it inside an: if(voice->DecodeAvail <= 12),
|
||||
// though more tests are needed on the ADPCM decoding process as a whole before we should actually make such a change. Additionally, we'd probably
|
||||
// have to separate the CurAddr = LoopAddr logic, so we don't generate spurious early SPU IRQs.
|
||||
//
|
||||
if(voice->DecodeFlags & 0x1)
|
||||
{
|
||||
voice->CurAddr = voice->LoopAddr & ~0x7;
|
||||
|
||||
|
@ -386,37 +392,50 @@ void PS_SPU::RunDecoder(SPU_Voice *voice)
|
|||
voice->CurAddr = (voice->CurAddr + 1) & 0x3FFFF;
|
||||
}
|
||||
|
||||
//
|
||||
// Don't else this block; we need to ALWAYS decode 4 samples per call to RunDecoder() if DecodeAvail < 11, or else sample playback
|
||||
// at higher rates will fail horribly.
|
||||
//
|
||||
{
|
||||
const uint16 CV = SPURAM[voice->CurAddr];
|
||||
const unsigned shift = voice->DecodeShift;
|
||||
const int32 weight_m1 = Weights[voice->DecodeWeight][0];
|
||||
const int32 weight_m2 = Weights[voice->DecodeWeight][1];
|
||||
uint32 coded = (uint32)CV << 12;
|
||||
int16 *tb = &voice->DecodeBuffer[voice->DecodeWritePos];
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
int32 sample = (int16)(coded & 0xF000) >> shift;
|
||||
|
||||
sample += ((voice->DecodeM2 * weight_m2) >> 6);
|
||||
sample += ((voice->DecodeM1 * weight_m1) >> 6);
|
||||
|
||||
clamp(&sample, -32768, 32767);
|
||||
|
||||
tb[i] = sample;
|
||||
voice->DecodeM2 = voice->DecodeM1;
|
||||
voice->DecodeM1 = sample;
|
||||
coded >>= 4;
|
||||
}
|
||||
voice->DecodeWritePos = (voice->DecodeWritePos + 4) & 0x1F;
|
||||
voice->DecodeAvail += 4;
|
||||
voice->CurAddr = (voice->CurAddr + 1) & 0x3FFFF;
|
||||
}
|
||||
}
|
||||
//
|
||||
// Don't else this block; we need to ALWAYS decode 4 samples per call to RunDecoder() if DecodeAvail < 11, or else sample playback
|
||||
// at higher rates will fail horribly.
|
||||
//
|
||||
{
|
||||
const int32 weight_m1 = Weights[voice->DecodeWeight][0];
|
||||
const int32 weight_m2 = Weights[voice->DecodeWeight][1];
|
||||
uint16 CV;
|
||||
unsigned shift;
|
||||
uint32 coded;
|
||||
int16 *tb = &voice->DecodeBuffer[voice->DecodeWritePos];
|
||||
|
||||
CV = SPURAM[voice->CurAddr];
|
||||
shift = voice->DecodeShift;
|
||||
|
||||
if(MDFN_UNLIKELY(shift > 12))
|
||||
{
|
||||
//PSX_DBG(PSX_DBG_FLOOD, "[SPU] Buggy/Illegal ADPCM block shift value on voice %u: %u\n", (unsigned)(voice - Voices), shift);
|
||||
|
||||
shift = 8;
|
||||
CV &= 0x8888;
|
||||
}
|
||||
|
||||
coded = (uint32)CV << 12;
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
int32 sample = (int16)(coded & 0xF000) >> shift;
|
||||
|
||||
sample += ((voice->DecodeM2 * weight_m2) >> 6);
|
||||
sample += ((voice->DecodeM1 * weight_m1) >> 6);
|
||||
|
||||
clamp(&sample, -32768, 32767);
|
||||
|
||||
tb[i] = sample;
|
||||
voice->DecodeM2 = voice->DecodeM1;
|
||||
voice->DecodeM1 = sample;
|
||||
coded >>= 4;
|
||||
}
|
||||
voice->DecodeWritePos = (voice->DecodeWritePos + 4) & 0x1F;
|
||||
voice->DecodeAvail += 4;
|
||||
voice->CurAddr = (voice->CurAddr + 1) & 0x3FFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PS_SPU::CacheEnvelope(SPU_Voice *voice)
|
||||
|
@ -592,23 +611,21 @@ int32 PS_SPU::UpdateFromCDC(int32 clocks)
|
|||
sample_clocks++;
|
||||
}
|
||||
|
||||
while(sample_clocks > 0)
|
||||
{
|
||||
// Accumulated normal sound output.
|
||||
int32 accum_l = 0;
|
||||
int32 accum_r = 0;
|
||||
|
||||
// Accumulated sound output for reverb input
|
||||
int32 accum_fv_l = 0;
|
||||
int32 accum_fv_r = 0;
|
||||
|
||||
// Output of reverb processing.
|
||||
int32 reverb_l = 0;
|
||||
int32 reverb_r = 0;
|
||||
|
||||
// Final output.
|
||||
int32 output_l = 0;
|
||||
int32 output_r = 0;
|
||||
while(sample_clocks > 0)
|
||||
{
|
||||
// xxx[0] = left, xxx[1] = right
|
||||
|
||||
// Accumulated sound output.
|
||||
int32 accum[2] = { 0, 0 };
|
||||
|
||||
// Accumulated sound output for reverb input
|
||||
int32 accum_fv[2] = { 0, 0 };
|
||||
|
||||
// Output of reverb processing.
|
||||
int32 reverb[2] = { 0, 0 };
|
||||
|
||||
// Final output.
|
||||
int32 output[2] = { 0, 0 };
|
||||
|
||||
const uint32 PhaseModCache = FM_Mode & ~ 1;
|
||||
/*
|
||||
|
@ -690,14 +707,14 @@ int32 PS_SPU::UpdateFromCDC(int32 clocks)
|
|||
l = (voice_pvs * voice->Sweep[0].ReadVolume()) >> 15;
|
||||
r = (voice_pvs * voice->Sweep[1].ReadVolume()) >> 15;
|
||||
|
||||
accum_l += l;
|
||||
accum_r += r;
|
||||
|
||||
if(Reverb_Mode & (1 << voice_num))
|
||||
{
|
||||
accum_fv_l += l;
|
||||
accum_fv_r += r;
|
||||
}
|
||||
accum[0] += l;
|
||||
accum[1] += r;
|
||||
|
||||
if(Reverb_Mode & (1 << voice_num))
|
||||
{
|
||||
accum_fv[0] += l;
|
||||
accum_fv[1] += r;
|
||||
}
|
||||
|
||||
// Run sweep
|
||||
for(int lr = 0; lr < 2; lr++)
|
||||
|
@ -782,10 +799,10 @@ int32 PS_SPU::UpdateFromCDC(int32 clocks)
|
|||
// TODO: If we add sub-sample timing accuracy, see if it's checked for every channel at different times, or just once.
|
||||
if(!(SPUControl & 0x4000))
|
||||
{
|
||||
accum_l = 0;
|
||||
accum_r = 0;
|
||||
accum_fv_l = 0;
|
||||
accum_fv_r = 0;
|
||||
accum[0] = 0;
|
||||
accum[1] = 0;
|
||||
accum_fv[0] = 0;
|
||||
accum_fv[1] = 0;
|
||||
}
|
||||
|
||||
// Get CD-DA
|
||||
|
@ -802,66 +819,54 @@ int32 PS_SPU::UpdateFromCDC(int32 clocks)
|
|||
for(unsigned i = 0; i < 2; i++)
|
||||
cdav[i] = (cda_raw[i] * CDVol[i]) >> 15;
|
||||
|
||||
if(SPUControl & 0x0001)
|
||||
{
|
||||
accum_l += cdav[0];
|
||||
accum_r += cdav[1];
|
||||
|
||||
if(SPUControl & 0x0004) // TODO: Test this bit(and see if it is really dependent on bit0)
|
||||
{
|
||||
accum_fv_l += cdav[0];
|
||||
accum_fv_r += cdav[1];
|
||||
}
|
||||
}
|
||||
if(SPUControl & 0x0001)
|
||||
{
|
||||
accum[0] += cdav[0];
|
||||
accum[1] += cdav[1];
|
||||
|
||||
if(SPUControl & 0x0004) // TODO: Test this bit(and see if it is really dependent on bit0)
|
||||
{
|
||||
accum_fv[0] += cdav[0];
|
||||
accum_fv[1] += cdav[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CWA = (CWA + 1) & 0x1FF;
|
||||
|
||||
RunNoise();
|
||||
|
||||
clamp(&accum_l, -32768, 32767);
|
||||
clamp(&accum_r, -32768, 32767);
|
||||
clamp(&accum_fv_l, -32768, 32767);
|
||||
clamp(&accum_fv_r, -32768, 32767);
|
||||
|
||||
#if 0
|
||||
accum_l = 0;
|
||||
accum_r = 0;
|
||||
//accum_fv_l = (short)(rand());
|
||||
//accum_fv_r = (short)(rand());
|
||||
#endif
|
||||
|
||||
RunReverb(accum_fv_l, accum_fv_r, reverb_l, reverb_r);
|
||||
|
||||
//MDFN_DispMessage("%d %d\n", MainVol[0], MainVol[1], ReverbVol[0], ReverbVol[1]);
|
||||
|
||||
// FIXME: Dry volume versus everything else
|
||||
output_l = (((accum_l * GlobalSweep[0].ReadVolume()) >> 16) + ((reverb_l * ReverbVol[0]) >> 15));
|
||||
output_r = (((accum_r * GlobalSweep[1].ReadVolume()) >> 16) + ((reverb_r * ReverbVol[1]) >> 15));
|
||||
|
||||
//output_l = reverb_l;
|
||||
//output_r = reverb_r;
|
||||
|
||||
clamp(&output_l, -32768, 32767);
|
||||
clamp(&output_r, -32768, 32767);
|
||||
|
||||
if(IntermediateBufferPos < 4096) // Overflow might occur in some debugger use cases.
|
||||
{
|
||||
IntermediateBuffer[IntermediateBufferPos][0] = output_l;
|
||||
IntermediateBuffer[IntermediateBufferPos][1] = output_r;
|
||||
IntermediateBufferPos++;
|
||||
}
|
||||
|
||||
sample_clocks--;
|
||||
|
||||
// Clock global sweep
|
||||
for(int lr = 0; lr < 2; lr++)
|
||||
GlobalSweep[lr].Clock();
|
||||
}
|
||||
|
||||
//assert(clock_divider < 768);
|
||||
|
||||
return clock_divider;
|
||||
RunNoise();
|
||||
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
clamp(&accum_fv[lr], -32768, 32767);
|
||||
|
||||
RunReverb(accum_fv, reverb);
|
||||
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
{
|
||||
accum[lr] += ((reverb[lr] * ReverbVol[lr]) >> 15);
|
||||
clamp(&accum[lr], -32768, 32767);
|
||||
output[lr] = (accum[lr] * GlobalSweep[lr].ReadVolume()) >> 15;
|
||||
}
|
||||
|
||||
if(IntermediateBufferPos < 4096) // Overflow might occur in some debugger use cases.
|
||||
{
|
||||
// 75%, for some (resampling) headroom.
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
IntermediateBuffer[IntermediateBufferPos][lr] = (output[lr] * 3 + 2) >> 2;
|
||||
|
||||
IntermediateBufferPos++;
|
||||
}
|
||||
|
||||
sample_clocks--;
|
||||
|
||||
// Clock global sweep
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
GlobalSweep[lr].Clock();
|
||||
}
|
||||
|
||||
//assert(clock_divider < 768);
|
||||
|
||||
return clock_divider;
|
||||
}
|
||||
|
||||
void PS_SPU::WriteDMA(uint32 V)
|
||||
|
@ -1216,10 +1221,9 @@ SYNCFUNC(PS_SPU)
|
|||
NSS(AuxRegs);
|
||||
|
||||
NSS(RDSB);
|
||||
NSS(RDSB_WP);
|
||||
|
||||
NSS(RUSB);
|
||||
NSS(RUSB_WP);
|
||||
NSS(RvbResPos);
|
||||
|
||||
|
||||
NSS(ReverbCur);
|
||||
NSS(IRQAsserted);
|
||||
|
@ -1234,6 +1238,9 @@ SYNCFUNC(PS_SPU)
|
|||
//and another thing like this, which I think makes no sense. I really need to test these.
|
||||
IRQ_Assert(IRQ_SPU, IRQAsserted);
|
||||
//}
|
||||
|
||||
//sanity check this
|
||||
//RvbResPos &= 0x3F;
|
||||
}
|
||||
|
||||
uint16 PS_SPU::PeekSPURAM(uint32 address)
|
||||
|
@ -1323,27 +1330,27 @@ uint32 PS_SPU::GetRegister(unsigned int which, char *special, const uint32 speci
|
|||
ret = (uint16)CDVol[1];
|
||||
break;
|
||||
|
||||
case GSREG_DRYVOL_CTRL_L:
|
||||
case GSREG_MAINVOL_CTRL_L:
|
||||
ret = Regs[0xC0];
|
||||
break;
|
||||
|
||||
case GSREG_DRYVOL_CTRL_R:
|
||||
case GSREG_MAINVOL_CTRL_R:
|
||||
ret = Regs[0xC1];
|
||||
break;
|
||||
|
||||
case GSREG_DRYVOL_L:
|
||||
case GSREG_MAINVOL_L:
|
||||
ret = GlobalSweep[0].ReadVolume() & 0xFFFF;
|
||||
break;
|
||||
|
||||
case GSREG_DRYVOL_R:
|
||||
case GSREG_MAINVOL_R:
|
||||
ret = GlobalSweep[1].ReadVolume() & 0xFFFF;
|
||||
break;
|
||||
|
||||
case GSREG_WETVOL_L:
|
||||
case GSREG_RVBVOL_L:
|
||||
ret = (uint16)ReverbVol[0];
|
||||
break;
|
||||
|
||||
case GSREG_WETVOL_R:
|
||||
case GSREG_RVBVOL_R:
|
||||
ret = (uint16)ReverbVol[1];
|
||||
break;
|
||||
|
||||
|
@ -1405,7 +1412,7 @@ uint32 PS_SPU::GetRegister(unsigned int which, char *special, const uint32 speci
|
|||
case GSREG_MIX_DEST_B1:
|
||||
case GSREG_IN_COEF_L:
|
||||
case GSREG_IN_COEF_R:
|
||||
ret = ReverbRegs[which - GSREG_FB_SRC_A] & 0xFFFF;
|
||||
ret = ReverbRegs[which - GSREG_FB_SRC_A];
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1441,31 +1448,31 @@ void PS_SPU::SetRegister(unsigned int which, uint32 value)
|
|||
CDVol[1] = (int16)value;
|
||||
break;
|
||||
|
||||
case GSREG_DRYVOL_CTRL_L:
|
||||
case GSREG_MAINVOL_CTRL_L:
|
||||
Regs[0xC0] = value;
|
||||
GlobalSweep[0].WriteControl(value);
|
||||
//GlobalSweep[0].Control = value;
|
||||
break;
|
||||
|
||||
case GSREG_DRYVOL_CTRL_R:
|
||||
case GSREG_MAINVOL_CTRL_R:
|
||||
Regs[0xC1] = value;
|
||||
GlobalSweep[1].WriteControl(value);
|
||||
//GlobalSweep[1].Control = value;
|
||||
break;
|
||||
|
||||
case GSREG_DRYVOL_L:
|
||||
case GSREG_MAINVOL_L:
|
||||
GlobalSweep[0].WriteVolume(value);
|
||||
break;
|
||||
|
||||
case GSREG_DRYVOL_R:
|
||||
case GSREG_MAINVOL_R:
|
||||
GlobalSweep[1].WriteVolume(value);
|
||||
break;
|
||||
|
||||
case GSREG_WETVOL_L:
|
||||
case GSREG_RVBVOL_L:
|
||||
ReverbVol[0] = (int16)value;
|
||||
break;
|
||||
|
||||
case GSREG_WETVOL_R:
|
||||
case GSREG_RVBVOL_R:
|
||||
ReverbVol[1] = (int16)value;
|
||||
break;
|
||||
|
||||
|
@ -1494,6 +1501,42 @@ void PS_SPU::SetRegister(unsigned int which, uint32 value)
|
|||
break;
|
||||
|
||||
|
||||
//case GSREG_FB_SRC_A ... GSREG_IN_COEF_R:
|
||||
case GSREG_FB_SRC_A:
|
||||
case GSREG_FB_SRC_B:
|
||||
case GSREG_IIR_ALPHA:
|
||||
case GSREG_ACC_COEF_A:
|
||||
case GSREG_ACC_COEF_B:
|
||||
case GSREG_ACC_COEF_C:
|
||||
case GSREG_ACC_COEF_D:
|
||||
case GSREG_IIR_COEF:
|
||||
case GSREG_FB_ALPHA:
|
||||
case GSREG_FB_X:
|
||||
case GSREG_IIR_DEST_A0:
|
||||
case GSREG_IIR_DEST_A1:
|
||||
case GSREG_ACC_SRC_A0:
|
||||
case GSREG_ACC_SRC_A1:
|
||||
case GSREG_ACC_SRC_B0:
|
||||
case GSREG_ACC_SRC_B1:
|
||||
case GSREG_IIR_SRC_A0:
|
||||
case GSREG_IIR_SRC_A1:
|
||||
case GSREG_IIR_DEST_B0:
|
||||
case GSREG_IIR_DEST_B1:
|
||||
case GSREG_ACC_SRC_C0:
|
||||
case GSREG_ACC_SRC_C1:
|
||||
case GSREG_ACC_SRC_D0:
|
||||
case GSREG_ACC_SRC_D1:
|
||||
case GSREG_IIR_SRC_B1:
|
||||
case GSREG_IIR_SRC_B0:
|
||||
case GSREG_MIX_DEST_A0:
|
||||
case GSREG_MIX_DEST_A1:
|
||||
case GSREG_MIX_DEST_B0:
|
||||
case GSREG_MIX_DEST_B1:
|
||||
case GSREG_IN_COEF_L:
|
||||
case GSREG_IN_COEF_R:
|
||||
ReverbRegs[which - GSREG_FB_SRC_A] = value;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -96,14 +96,12 @@ class PS_SPU
|
|||
{
|
||||
public:
|
||||
|
||||
PS_SPU();
|
||||
~PS_SPU();
|
||||
PS_SPU() MDFN_COLD;
|
||||
~PS_SPU() MDFN_COLD;
|
||||
|
||||
template<bool isReader>void SyncState(EW::NewState *ns);
|
||||
|
||||
int StateAction(StateMem *sm, int load, int data_only);
|
||||
|
||||
void Power(void);
|
||||
void Power(void) MDFN_COLD;
|
||||
void Write(pscpu_timestamp_t timestamp, uint32 A, uint16 V);
|
||||
uint16 Read(pscpu_timestamp_t timestamp, uint32 A);
|
||||
|
||||
|
@ -132,7 +130,7 @@ private:
|
|||
void RunEnvelope(SPU_Voice *voice);
|
||||
|
||||
|
||||
void RunReverb(int32 in_l, int32 in_r, int32 &out_l, int32 &out_r);
|
||||
void RunReverb(const int32* in, int32* out);
|
||||
void RunNoise(void);
|
||||
bool GetCDAudio(int32 &l, int32 &r);
|
||||
|
||||
|
@ -146,7 +144,7 @@ private:
|
|||
uint32 Noise_Mode;
|
||||
uint32 Reverb_Mode;
|
||||
|
||||
int32 ReverbWA;
|
||||
uint32 ReverbWA;
|
||||
|
||||
SPU_Sweep GlobalSweep[2]; // Doesn't affect reverb volume!
|
||||
|
||||
|
@ -184,163 +182,161 @@ private:
|
|||
uint16 _Global1[0x08];
|
||||
};
|
||||
};
|
||||
union
|
||||
{
|
||||
int16 ReverbRegs[0x20];
|
||||
|
||||
struct
|
||||
{
|
||||
int16 FB_SRC_A;
|
||||
int16 FB_SRC_B;
|
||||
int16 IIR_ALPHA;
|
||||
int16 ACC_COEF_A;
|
||||
int16 ACC_COEF_B;
|
||||
int16 ACC_COEF_C;
|
||||
int16 ACC_COEF_D;
|
||||
int16 IIR_COEF;
|
||||
int16 FB_ALPHA;
|
||||
int16 FB_X;
|
||||
int16 IIR_DEST_A0;
|
||||
int16 IIR_DEST_A1;
|
||||
int16 ACC_SRC_A0;
|
||||
int16 ACC_SRC_A1;
|
||||
int16 ACC_SRC_B0;
|
||||
int16 ACC_SRC_B1;
|
||||
int16 IIR_SRC_A0;
|
||||
int16 IIR_SRC_A1;
|
||||
int16 IIR_DEST_B0;
|
||||
int16 IIR_DEST_B1;
|
||||
int16 ACC_SRC_C0;
|
||||
int16 ACC_SRC_C1;
|
||||
int16 ACC_SRC_D0;
|
||||
int16 ACC_SRC_D1;
|
||||
int16 IIR_SRC_B1;
|
||||
int16 IIR_SRC_B0;
|
||||
int16 MIX_DEST_A0;
|
||||
int16 MIX_DEST_A1;
|
||||
int16 MIX_DEST_B0;
|
||||
int16 MIX_DEST_B1;
|
||||
int16 IN_COEF_L;
|
||||
int16 IN_COEF_R;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
uint16 AuxRegs[0x10];
|
||||
|
||||
int16 RDSB[2][128]; // [40]
|
||||
int32 RDSB_WP;
|
||||
|
||||
int16 RUSB[2][128];
|
||||
int32 RUSB_WP;
|
||||
|
||||
int32 ReverbCur;
|
||||
|
||||
int32 Get_Reverb_Offset(int32 offset);
|
||||
int32 RD_RVB(int16 raw_offs);
|
||||
void WR_RVB(int16 raw_offs, int32 sample, int32 extra_offs = 0);
|
||||
|
||||
bool IRQAsserted;
|
||||
|
||||
//pscpu_timestamp_t lastts;
|
||||
int32 clock_divider;
|
||||
|
||||
int last_rate;
|
||||
uint32 last_quality;
|
||||
|
||||
// Buffers 44.1KHz samples, should have enough for two(worst-case scenario) video frames(2* ~735 frames NTSC, 2* ~882 PAL) plus jitter plus enough for the resampler leftovers.
|
||||
// We'll just go with 4096 because powers of 2 are AWESOME and such.
|
||||
uint32 IntermediateBufferPos;
|
||||
int16 IntermediateBuffer[4096][2];
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
GSREG_SPUCONTROL = 0,
|
||||
|
||||
GSREG_FM_ON,
|
||||
GSREG_NOISE_ON,
|
||||
GSREG_REVERB_ON,
|
||||
|
||||
GSREG_CDVOL_L,
|
||||
GSREG_CDVOL_R,
|
||||
|
||||
GSREG_DRYVOL_CTRL_L,
|
||||
GSREG_DRYVOL_CTRL_R,
|
||||
|
||||
GSREG_DRYVOL_L,
|
||||
GSREG_DRYVOL_R,
|
||||
|
||||
GSREG_WETVOL_L,
|
||||
GSREG_WETVOL_R,
|
||||
|
||||
GSREG_RWADDR,
|
||||
|
||||
GSREG_IRQADDR,
|
||||
|
||||
GSREG_REVERBWA,
|
||||
|
||||
GSREG_VOICEON,
|
||||
GSREG_VOICEOFF,
|
||||
GSREG_BLOCKEND,
|
||||
|
||||
// Note: the order of these should match the reverb reg array
|
||||
GSREG_FB_SRC_A,
|
||||
GSREG_FB_SRC_B,
|
||||
GSREG_IIR_ALPHA,
|
||||
GSREG_ACC_COEF_A,
|
||||
GSREG_ACC_COEF_B,
|
||||
GSREG_ACC_COEF_C,
|
||||
GSREG_ACC_COEF_D,
|
||||
GSREG_IIR_COEF,
|
||||
GSREG_FB_ALPHA,
|
||||
GSREG_FB_X,
|
||||
GSREG_IIR_DEST_A0,
|
||||
GSREG_IIR_DEST_A1,
|
||||
GSREG_ACC_SRC_A0,
|
||||
GSREG_ACC_SRC_A1,
|
||||
GSREG_ACC_SRC_B0,
|
||||
GSREG_ACC_SRC_B1,
|
||||
GSREG_IIR_SRC_A0,
|
||||
GSREG_IIR_SRC_A1,
|
||||
GSREG_IIR_DEST_B0,
|
||||
GSREG_IIR_DEST_B1,
|
||||
GSREG_ACC_SRC_C0,
|
||||
GSREG_ACC_SRC_C1,
|
||||
GSREG_ACC_SRC_D0,
|
||||
GSREG_ACC_SRC_D1,
|
||||
GSREG_IIR_SRC_B1,
|
||||
GSREG_IIR_SRC_B0,
|
||||
GSREG_MIX_DEST_A0,
|
||||
GSREG_MIX_DEST_A1,
|
||||
GSREG_MIX_DEST_B0,
|
||||
GSREG_MIX_DEST_B1,
|
||||
GSREG_IN_COEF_L,
|
||||
GSREG_IN_COEF_R,
|
||||
|
||||
|
||||
// Multiply v * 256 for each extra voice
|
||||
GSREG_V0_VOL_CTRL_L = 0x8000,
|
||||
GSREG_V0_VOL_CTRL_R,
|
||||
GSREG_V0_VOL_L,
|
||||
GSREG_V0_VOL_R,
|
||||
GSREG_V0_PITCH,
|
||||
GSREG_V0_STARTADDR,
|
||||
GSREG_V0_ADSR_CTRL,
|
||||
GSREG_V0_ADSR_LEVEL,
|
||||
GSREG_V0_LOOP_ADDR,
|
||||
GSREG_V0_READ_ADDR
|
||||
};
|
||||
|
||||
uint32 GetRegister(unsigned int which, char *special, const uint32 special_len);
|
||||
void SetRegister(unsigned int which, uint32 value);
|
||||
|
||||
uint16 PeekSPURAM(uint32 address);
|
||||
void PokeSPURAM(uint32 address, uint16 value);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
union
|
||||
{
|
||||
uint16 ReverbRegs[0x20];
|
||||
|
||||
struct
|
||||
{
|
||||
uint16 FB_SRC_A;
|
||||
uint16 FB_SRC_B;
|
||||
int16 IIR_ALPHA;
|
||||
int16 ACC_COEF_A;
|
||||
int16 ACC_COEF_B;
|
||||
int16 ACC_COEF_C;
|
||||
int16 ACC_COEF_D;
|
||||
int16 IIR_COEF;
|
||||
int16 FB_ALPHA;
|
||||
int16 FB_X;
|
||||
uint16 IIR_DEST_A0;
|
||||
uint16 IIR_DEST_A1;
|
||||
uint16 ACC_SRC_A0;
|
||||
uint16 ACC_SRC_A1;
|
||||
uint16 ACC_SRC_B0;
|
||||
uint16 ACC_SRC_B1;
|
||||
uint16 IIR_SRC_A0;
|
||||
uint16 IIR_SRC_A1;
|
||||
uint16 IIR_DEST_B0;
|
||||
uint16 IIR_DEST_B1;
|
||||
uint16 ACC_SRC_C0;
|
||||
uint16 ACC_SRC_C1;
|
||||
uint16 ACC_SRC_D0;
|
||||
uint16 ACC_SRC_D1;
|
||||
uint16 IIR_SRC_B1;
|
||||
uint16 IIR_SRC_B0;
|
||||
uint16 MIX_DEST_A0;
|
||||
uint16 MIX_DEST_A1;
|
||||
uint16 MIX_DEST_B0;
|
||||
uint16 MIX_DEST_B1;
|
||||
int16 IN_COEF_L;
|
||||
int16 IN_COEF_R;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
uint16 AuxRegs[0x10];
|
||||
|
||||
int16 RDSB[2][128];
|
||||
int16 RUSB[2][64];
|
||||
int32 RvbResPos;
|
||||
|
||||
uint32 ReverbCur;
|
||||
|
||||
uint32 Get_Reverb_Offset(uint32 offset);
|
||||
int16 RD_RVB(uint16 raw_offs, int32 extra_offs = 0);
|
||||
void WR_RVB(uint16 raw_offs, int16 sample);
|
||||
|
||||
bool IRQAsserted;
|
||||
|
||||
//pscpu_timestamp_t lastts;
|
||||
int32 clock_divider;
|
||||
|
||||
int last_rate;
|
||||
uint32 last_quality;
|
||||
|
||||
// Buffers 44.1KHz samples, should have enough for two(worst-case scenario) video frames(2* ~735 frames NTSC, 2* ~882 PAL) plus jitter plus enough for the resampler leftovers.
|
||||
// We'll just go with 4096 because powers of 2 are AWESOME and such.
|
||||
uint32 IntermediateBufferPos;
|
||||
int16 IntermediateBuffer[4096][2];
|
||||
|
||||
public:
|
||||
enum
|
||||
{
|
||||
GSREG_SPUCONTROL = 0,
|
||||
|
||||
GSREG_FM_ON,
|
||||
GSREG_NOISE_ON,
|
||||
GSREG_REVERB_ON,
|
||||
|
||||
GSREG_CDVOL_L,
|
||||
GSREG_CDVOL_R,
|
||||
|
||||
GSREG_MAINVOL_CTRL_L,
|
||||
GSREG_MAINVOL_CTRL_R,
|
||||
|
||||
GSREG_MAINVOL_L,
|
||||
GSREG_MAINVOL_R,
|
||||
|
||||
GSREG_RVBVOL_L,
|
||||
GSREG_RVBVOL_R,
|
||||
|
||||
GSREG_RWADDR,
|
||||
|
||||
GSREG_IRQADDR,
|
||||
|
||||
GSREG_REVERBWA,
|
||||
|
||||
GSREG_VOICEON,
|
||||
GSREG_VOICEOFF,
|
||||
GSREG_BLOCKEND,
|
||||
|
||||
// Note: the order of these should match the reverb reg array
|
||||
GSREG_FB_SRC_A,
|
||||
GSREG_FB_SRC_B,
|
||||
GSREG_IIR_ALPHA,
|
||||
GSREG_ACC_COEF_A,
|
||||
GSREG_ACC_COEF_B,
|
||||
GSREG_ACC_COEF_C,
|
||||
GSREG_ACC_COEF_D,
|
||||
GSREG_IIR_COEF,
|
||||
GSREG_FB_ALPHA,
|
||||
GSREG_FB_X,
|
||||
GSREG_IIR_DEST_A0,
|
||||
GSREG_IIR_DEST_A1,
|
||||
GSREG_ACC_SRC_A0,
|
||||
GSREG_ACC_SRC_A1,
|
||||
GSREG_ACC_SRC_B0,
|
||||
GSREG_ACC_SRC_B1,
|
||||
GSREG_IIR_SRC_A0,
|
||||
GSREG_IIR_SRC_A1,
|
||||
GSREG_IIR_DEST_B0,
|
||||
GSREG_IIR_DEST_B1,
|
||||
GSREG_ACC_SRC_C0,
|
||||
GSREG_ACC_SRC_C1,
|
||||
GSREG_ACC_SRC_D0,
|
||||
GSREG_ACC_SRC_D1,
|
||||
GSREG_IIR_SRC_B1,
|
||||
GSREG_IIR_SRC_B0,
|
||||
GSREG_MIX_DEST_A0,
|
||||
GSREG_MIX_DEST_A1,
|
||||
GSREG_MIX_DEST_B0,
|
||||
GSREG_MIX_DEST_B1,
|
||||
GSREG_IN_COEF_L,
|
||||
GSREG_IN_COEF_R,
|
||||
|
||||
|
||||
// Multiply v * 256 for each extra voice
|
||||
GSREG_V0_VOL_CTRL_L = 0x8000,
|
||||
GSREG_V0_VOL_CTRL_R,
|
||||
GSREG_V0_VOL_L,
|
||||
GSREG_V0_VOL_R,
|
||||
GSREG_V0_PITCH,
|
||||
GSREG_V0_STARTADDR,
|
||||
GSREG_V0_ADSR_CTRL,
|
||||
GSREG_V0_ADSR_LEVEL,
|
||||
GSREG_V0_LOOP_ADDR,
|
||||
GSREG_V0_READ_ADDR
|
||||
};
|
||||
|
||||
uint32 GetRegister(unsigned int which, char *special, const uint32 special_len);
|
||||
void SetRegister(unsigned int which, uint32 value);
|
||||
|
||||
uint16 PeekSPURAM(uint32 address);
|
||||
void PokeSPURAM(uint32 address, uint16 value);
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,227 +11,185 @@ static INLINE int16 ReverbSat(int32 samp)
|
|||
}
|
||||
|
||||
|
||||
INLINE int32 PS_SPU::Get_Reverb_Offset(int32 in_offset)
|
||||
INLINE uint32 PS_SPU::Get_Reverb_Offset(uint32 in_offset)
|
||||
{
|
||||
int32 offset = in_offset & 0x3FFFF;
|
||||
int32 wa_size = 0x40000 - ReverbWA;
|
||||
uint32 offset = ReverbCur + (in_offset & 0x3FFFF);
|
||||
|
||||
if(offset & 0x20000)
|
||||
{
|
||||
offset -= ReverbWA;
|
||||
offset += ReverbWA & ((int32)(offset << 13) >> 31);
|
||||
offset &= 0x3FFFF;
|
||||
|
||||
if(offset < 0)
|
||||
{
|
||||
offset = 0;
|
||||
//PSX_WARNING("[SPU] A reverb offset is broken(-).");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(offset >= wa_size)
|
||||
{
|
||||
offset = wa_size - 1;
|
||||
//PSX_WARNING("[SPU] A reverb offset is broken(+): WASize=0x%04x, 0x%04x.", wa_size >> 2, in_offset >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
offset += ReverbCur;
|
||||
|
||||
if(offset >= 0x40000)
|
||||
offset = (offset & 0x3FFFF) + ReverbWA;
|
||||
|
||||
assert(offset >= ReverbWA && offset < 0x40000);
|
||||
// For debugging in case there are any problems with games misprogramming the reverb registers in a race-conditiony manner that
|
||||
// causes important data in SPU RAM to be trashed:
|
||||
//if(offset < ReverbWA)
|
||||
// printf("BARF: offset=%05x reverbwa=%05x reverbcur=%05x in_offset=%05x\n", offset, ReverbWA, ReverbCur, in_offset & 0x3FFFF);
|
||||
|
||||
return(offset);
|
||||
}
|
||||
|
||||
int32 PS_SPU::RD_RVB(int16 raw_offs)
|
||||
int16 NO_INLINE PS_SPU::RD_RVB(uint16 raw_offs, int32 extra_offs)
|
||||
{
|
||||
//raw_offs = rand() & 0xFFFF;
|
||||
|
||||
return((int16)SPURAM[Get_Reverb_Offset(raw_offs << 2)]);
|
||||
return ReadSPURAM(Get_Reverb_Offset((raw_offs << 2) + extra_offs));
|
||||
}
|
||||
|
||||
void PS_SPU::WR_RVB(int16 raw_offs, int32 sample, int32 extra_offs)
|
||||
void NO_INLINE PS_SPU::WR_RVB(uint16 raw_offs, int16 sample)
|
||||
{
|
||||
//raw_offs = rand() & 0xFFFF;
|
||||
|
||||
SPURAM[Get_Reverb_Offset((raw_offs << 2) + extra_offs)] = ReverbSat(sample);
|
||||
WriteSPURAM(Get_Reverb_Offset(raw_offs << 2), sample);
|
||||
}
|
||||
|
||||
//
|
||||
// Zeroes optimized out; middle removed too(it's 16384)
|
||||
static const int16 ResampTable[20] =
|
||||
{
|
||||
-1, 2, -10, 35, -103, 266, -616, 1332, -2960, 10246, 10246, -2960, 1332, -616, 266, -103, 35, -10, 2, -1,
|
||||
};
|
||||
|
||||
static INLINE int32 Reverb4422(const int16 *src)
|
||||
{
|
||||
static const int16 ResampTable[40] =
|
||||
{
|
||||
(int16)0xffff,
|
||||
(int16)0x0000,
|
||||
(int16)0x0002,
|
||||
(int16)0x0000,
|
||||
(int16)0xfff6,
|
||||
(int16)0x0000,
|
||||
(int16)0x0023,
|
||||
(int16)0x0000,
|
||||
(int16)0xff99,
|
||||
(int16)0x0000,
|
||||
(int16)0x010a,
|
||||
(int16)0x0000,
|
||||
(int16)0xfd98,
|
||||
(int16)0x0000,
|
||||
(int16)0x0534,
|
||||
(int16)0x0000,
|
||||
(int16)0xf470,
|
||||
(int16)0x0000,
|
||||
(int16)0x2806,
|
||||
(int16)0x4000,
|
||||
(int16)0x2806,
|
||||
(int16)0x0000,
|
||||
(int16)0xf470,
|
||||
(int16)0x0000,
|
||||
(int16)0x0534,
|
||||
(int16)0x0000,
|
||||
(int16)0xfd98,
|
||||
(int16)0x0000,
|
||||
(int16)0x010a,
|
||||
(int16)0x0000,
|
||||
(int16)0xff99,
|
||||
(int16)0x0000,
|
||||
(int16)0x0023,
|
||||
(int16)0x0000,
|
||||
(int16)0xfff6,
|
||||
(int16)0x0000,
|
||||
(int16)0x0002,
|
||||
(int16)0x0000,
|
||||
(int16)0xffff,
|
||||
(int16)0x0000,
|
||||
};
|
||||
int32 out = 0; // 32-bits is adequate(it won't overflow)
|
||||
|
||||
for(int i = 0; i < 40; i += 2)
|
||||
out += ResampTable[i] * src[i];
|
||||
for(unsigned i = 0; i < 20; i++)
|
||||
out += ResampTable[i] * src[i * 2];
|
||||
|
||||
// Middle non-zero
|
||||
out += 0x4000 * src[19];
|
||||
|
||||
out >>= 15;
|
||||
|
||||
if(out < -32768)
|
||||
out = -32768;
|
||||
|
||||
if(out > 32767)
|
||||
out = 32767;
|
||||
clamp(&out, -32768, 32767);
|
||||
|
||||
return(out);
|
||||
}
|
||||
|
||||
void PS_SPU::RunReverb(int32 in_l, int32 in_r, int32 &out_l, int32 &out_r)
|
||||
template<bool phase>
|
||||
static INLINE int32 Reverb2244(const int16 *src)
|
||||
{
|
||||
int32 out; // 32-bits is adequate(it won't overflow)
|
||||
|
||||
if(phase)
|
||||
{
|
||||
// Middle non-zero
|
||||
out = src[9];
|
||||
}
|
||||
else
|
||||
{
|
||||
out = 0;
|
||||
|
||||
for(unsigned i = 0; i < 20; i++)
|
||||
out += ResampTable[i] * src[i];
|
||||
|
||||
out >>= 14;
|
||||
|
||||
clamp(&out, -32768, 32767);
|
||||
}
|
||||
|
||||
return(out);
|
||||
}
|
||||
|
||||
static int32 IIASM(const int16 IIR_ALPHA, const int16 insamp)
|
||||
{
|
||||
if(MDFN_UNLIKELY(IIR_ALPHA == -32768))
|
||||
{
|
||||
//const int32 iai_adj = sign_x_to_s32(17, (32768 - IIR_ALPHA));
|
||||
//tmp = iai_adj * in_samp
|
||||
|
||||
if(insamp == -32768)
|
||||
return 0;
|
||||
else
|
||||
return insamp * -65536;
|
||||
}
|
||||
else
|
||||
return insamp * (32768 - IIR_ALPHA);
|
||||
}
|
||||
|
||||
//
|
||||
// Take care to thoroughly test the reverb resampling code when modifying anything that uses RvbResPos.
|
||||
//
|
||||
void PS_SPU::RunReverb(const int32* in, int32* out)
|
||||
{
|
||||
int32 upsampled[2] = { 0, 0 };
|
||||
|
||||
RDSB[0][RDSB_WP] = in_l;
|
||||
RDSB[1][RDSB_WP] = in_r;
|
||||
RDSB[0][RDSB_WP | 0x40] = in_l; // So we don't have to &/bounds check in our MAC loop
|
||||
RDSB[1][RDSB_WP | 0x40] = in_r;
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
{
|
||||
RDSB[lr][RvbResPos | 0x00] = in[lr];
|
||||
RDSB[lr][RvbResPos | 0x40] = in[lr]; // So we don't have to &/bounds check in our MAC loop
|
||||
}
|
||||
|
||||
RDSB_WP = (RDSB_WP + 1) & 0x3F;
|
||||
|
||||
if(!(RDSB_WP & 1))
|
||||
if(RvbResPos & 1)
|
||||
{
|
||||
int32 downsampled[2];
|
||||
|
||||
for(int lr = 0; lr < 2; lr++)
|
||||
downsampled[lr] = Reverb4422(&RDSB[lr][(RDSB_WP - 40) & 0x3F]);
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
downsampled[lr] = Reverb4422(&RDSB[lr][(RvbResPos - 39) & 0x3F]);
|
||||
|
||||
//
|
||||
// Run algorithm
|
||||
///
|
||||
if(SPUControl & 0x80)
|
||||
{
|
||||
int32 IIR_INPUT_A0;
|
||||
int32 IIR_INPUT_A1;
|
||||
int32 IIR_INPUT_B0;
|
||||
int32 IIR_INPUT_B1;
|
||||
int32 IIR_A0, IIR_A1, IIR_B0, IIR_B1;
|
||||
int32 ACC0, ACC1;
|
||||
int32 FB_A0, FB_A1, FB_B0, FB_B1;
|
||||
if(SPUControl & 0x80)
|
||||
{
|
||||
int16 IIR_INPUT_A0, IIR_INPUT_A1, IIR_INPUT_B0, IIR_INPUT_B1;
|
||||
int16 IIR_A0, IIR_A1, IIR_B0, IIR_B1;
|
||||
int16 ACC0, ACC1;
|
||||
int16 FB_A0, FB_A1, FB_B0, FB_B1;
|
||||
|
||||
IIR_INPUT_A0 = ((RD_RVB(IIR_SRC_A0) * IIR_COEF) >> 15) + ((downsampled[0] * IN_COEF_L) >> 15);
|
||||
IIR_INPUT_A1 = ((RD_RVB(IIR_SRC_A1) * IIR_COEF) >> 15) + ((downsampled[1] * IN_COEF_R) >> 15);
|
||||
IIR_INPUT_B0 = ((RD_RVB(IIR_SRC_B0) * IIR_COEF) >> 15) + ((downsampled[0] * IN_COEF_L) >> 15);
|
||||
IIR_INPUT_B1 = ((RD_RVB(IIR_SRC_B1) * IIR_COEF) >> 15) + ((downsampled[1] * IN_COEF_R) >> 15);
|
||||
IIR_INPUT_A0 = ReverbSat(((RD_RVB(IIR_SRC_A0) * IIR_COEF) >> 15) + ((downsampled[0] * IN_COEF_L) >> 15));
|
||||
IIR_INPUT_A1 = ReverbSat(((RD_RVB(IIR_SRC_A1) * IIR_COEF) >> 15) + ((downsampled[1] * IN_COEF_R) >> 15));
|
||||
IIR_INPUT_B0 = ReverbSat(((RD_RVB(IIR_SRC_B0) * IIR_COEF) >> 15) + ((downsampled[0] * IN_COEF_L) >> 15));
|
||||
IIR_INPUT_B1 = ReverbSat(((RD_RVB(IIR_SRC_B1) * IIR_COEF) >> 15) + ((downsampled[1] * IN_COEF_R) >> 15));
|
||||
|
||||
IIR_A0 = ReverbSat((((IIR_INPUT_A0 * IIR_ALPHA) >> 14) + (IIASM(IIR_ALPHA, RD_RVB(IIR_DEST_A0, -1)) >> 14)) >> 1);
|
||||
IIR_A1 = ReverbSat((((IIR_INPUT_A1 * IIR_ALPHA) >> 14) + (IIASM(IIR_ALPHA, RD_RVB(IIR_DEST_A1, -1)) >> 14)) >> 1);
|
||||
IIR_B0 = ReverbSat((((IIR_INPUT_B0 * IIR_ALPHA) >> 14) + (IIASM(IIR_ALPHA, RD_RVB(IIR_DEST_B0, -1)) >> 14)) >> 1);
|
||||
IIR_B1 = ReverbSat((((IIR_INPUT_B1 * IIR_ALPHA) >> 14) + (IIASM(IIR_ALPHA, RD_RVB(IIR_DEST_B1, -1)) >> 14)) >> 1);
|
||||
|
||||
IIR_A0 = (((int64)IIR_INPUT_A0 * IIR_ALPHA) >> 15) + ((RD_RVB(IIR_DEST_A0) * (32768 - IIR_ALPHA)) >> 15);
|
||||
IIR_A1 = (((int64)IIR_INPUT_A1 * IIR_ALPHA) >> 15) + ((RD_RVB(IIR_DEST_A1) * (32768 - IIR_ALPHA)) >> 15);
|
||||
IIR_B0 = (((int64)IIR_INPUT_B0 * IIR_ALPHA) >> 15) + ((RD_RVB(IIR_DEST_B0) * (32768 - IIR_ALPHA)) >> 15);
|
||||
IIR_B1 = (((int64)IIR_INPUT_B1 * IIR_ALPHA) >> 15) + ((RD_RVB(IIR_DEST_B1) * (32768 - IIR_ALPHA)) >> 15);
|
||||
WR_RVB(IIR_DEST_A0, IIR_A0);
|
||||
WR_RVB(IIR_DEST_A1, IIR_A1);
|
||||
WR_RVB(IIR_DEST_B0, IIR_B0);
|
||||
WR_RVB(IIR_DEST_B1, IIR_B1);
|
||||
|
||||
WR_RVB(IIR_DEST_A0, IIR_A0, 1);
|
||||
WR_RVB(IIR_DEST_A1, IIR_A1, 1);
|
||||
WR_RVB(IIR_DEST_B0, IIR_B0, 1);
|
||||
WR_RVB(IIR_DEST_B1, IIR_B1, 1);
|
||||
ACC0 = ReverbSat((((RD_RVB(ACC_SRC_A0) * ACC_COEF_A) >> 14) +
|
||||
((RD_RVB(ACC_SRC_B0) * ACC_COEF_B) >> 14) +
|
||||
((RD_RVB(ACC_SRC_C0) * ACC_COEF_C) >> 14) +
|
||||
((RD_RVB(ACC_SRC_D0) * ACC_COEF_D) >> 14)) >> 1);
|
||||
|
||||
#if 0
|
||||
ACC0 = ((RD_RVB(ACC_SRC_A0) * ACC_COEF_A) >> 15) +
|
||||
((RD_RVB(ACC_SRC_B0) * ACC_COEF_B) >> 15) +
|
||||
((RD_RVB(ACC_SRC_C0) * ACC_COEF_C) >> 15) +
|
||||
((RD_RVB(ACC_SRC_D0) * ACC_COEF_D) >> 15);
|
||||
ACC1 = ReverbSat((((RD_RVB(ACC_SRC_A1) * ACC_COEF_A) >> 14) +
|
||||
((RD_RVB(ACC_SRC_B1) * ACC_COEF_B) >> 14) +
|
||||
((RD_RVB(ACC_SRC_C1) * ACC_COEF_C) >> 14) +
|
||||
((RD_RVB(ACC_SRC_D1) * ACC_COEF_D) >> 14)) >> 1);
|
||||
|
||||
ACC1 = ((RD_RVB(ACC_SRC_A1) * ACC_COEF_A) >> 15) +
|
||||
((RD_RVB(ACC_SRC_B1) * ACC_COEF_B) >> 15) +
|
||||
((RD_RVB(ACC_SRC_C1) * ACC_COEF_C) >> 15) +
|
||||
((RD_RVB(ACC_SRC_D1) * ACC_COEF_D) >> 15);
|
||||
#endif
|
||||
FB_A0 = RD_RVB(MIX_DEST_A0 - FB_SRC_A);
|
||||
FB_A1 = RD_RVB(MIX_DEST_A1 - FB_SRC_A);
|
||||
FB_B0 = RD_RVB(MIX_DEST_B0 - FB_SRC_B);
|
||||
FB_B1 = RD_RVB(MIX_DEST_B1 - FB_SRC_B);
|
||||
|
||||
ACC0 = ((int64)(RD_RVB(ACC_SRC_A0) * ACC_COEF_A) +
|
||||
(RD_RVB(ACC_SRC_B0) * ACC_COEF_B) +
|
||||
(RD_RVB(ACC_SRC_C0) * ACC_COEF_C) +
|
||||
(RD_RVB(ACC_SRC_D0) * ACC_COEF_D)) >> 15;
|
||||
WR_RVB(MIX_DEST_A0, ReverbSat(ACC0 - ((FB_A0 * FB_ALPHA) >> 15)));
|
||||
WR_RVB(MIX_DEST_A1, ReverbSat(ACC1 - ((FB_A1 * FB_ALPHA) >> 15)));
|
||||
|
||||
|
||||
ACC1 = ((int64)(RD_RVB(ACC_SRC_A1) * ACC_COEF_A) +
|
||||
(RD_RVB(ACC_SRC_B1) * ACC_COEF_B) +
|
||||
(RD_RVB(ACC_SRC_C1) * ACC_COEF_C) +
|
||||
(RD_RVB(ACC_SRC_D1) * ACC_COEF_D)) >> 15;
|
||||
|
||||
|
||||
FB_A0 = RD_RVB(MIX_DEST_A0 - FB_SRC_A);
|
||||
FB_A1 = RD_RVB(MIX_DEST_A1 - FB_SRC_A);
|
||||
FB_B0 = RD_RVB(MIX_DEST_B0 - FB_SRC_B);
|
||||
FB_B1 = RD_RVB(MIX_DEST_B1 - FB_SRC_B);
|
||||
|
||||
WR_RVB(MIX_DEST_A0, ACC0 - ((FB_A0 * FB_ALPHA) >> 15));
|
||||
WR_RVB(MIX_DEST_A1, ACC1 - ((FB_A1 * FB_ALPHA) >> 15));
|
||||
|
||||
WR_RVB(MIX_DEST_B0, (((int64)FB_ALPHA * ACC0) >> 15) - ((FB_A0 * (int16)(0x8000 ^ FB_ALPHA)) >> 15) - ((FB_B0 * FB_X) >> 15));
|
||||
WR_RVB(MIX_DEST_B1, (((int64)FB_ALPHA * ACC1) >> 15) - ((FB_A1 * (int16)(0x8000 ^ FB_ALPHA)) >> 15) - ((FB_B1 * FB_X) >> 15));
|
||||
}
|
||||
WR_RVB(MIX_DEST_B0, ReverbSat(((FB_ALPHA * ACC0) >> 15) - ((FB_A0 * (int16)(0x8000 ^ FB_ALPHA)) >> 15) - ((FB_B0 * FB_X) >> 15)));
|
||||
WR_RVB(MIX_DEST_B1, ReverbSat(((FB_ALPHA * ACC1) >> 15) - ((FB_A1 * (int16)(0x8000 ^ FB_ALPHA)) >> 15) - ((FB_B1 * FB_X) >> 15)));
|
||||
}
|
||||
|
||||
//
|
||||
// Get output samples
|
||||
//
|
||||
// RUSB[0][RUSB_WP | 0x40] = RUSB[0][RUSB_WP] = (short)rand();
|
||||
// RUSB[1][RUSB_WP | 0x40] = RUSB[1][RUSB_WP] = (short)rand();
|
||||
RUSB[0][RUSB_WP | 0x40] = RUSB[0][RUSB_WP] = (RD_RVB(MIX_DEST_A0) + RD_RVB(MIX_DEST_B0)) >> 1;
|
||||
RUSB[1][RUSB_WP | 0x40] = RUSB[1][RUSB_WP] = (RD_RVB(MIX_DEST_A1) + RD_RVB(MIX_DEST_B1)) >> 1;
|
||||
|
||||
RUSB_WP = (RUSB_WP + 1) & 0x3F;
|
||||
RUSB[0][(RvbResPos >> 1) | 0x20] = RUSB[0][RvbResPos >> 1] = (RD_RVB(MIX_DEST_A0) + RD_RVB(MIX_DEST_B0)) >> 1;
|
||||
RUSB[1][(RvbResPos >> 1) | 0x20] = RUSB[1][RvbResPos >> 1] = (RD_RVB(MIX_DEST_A1) + RD_RVB(MIX_DEST_B1)) >> 1;
|
||||
|
||||
ReverbCur = (ReverbCur + 1) & 0x3FFFF;
|
||||
if(!ReverbCur)
|
||||
ReverbCur = ReverbWA;
|
||||
}
|
||||
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
upsampled[lr] = Reverb2244<true>(&RUSB[lr][((RvbResPos - 39) & 0x3F) >> 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
RUSB[0][RUSB_WP | 0x40] = RUSB[0][RUSB_WP] = 0;
|
||||
RUSB[1][RUSB_WP | 0x40] = RUSB[1][RUSB_WP] = 0;
|
||||
|
||||
RUSB_WP = (RUSB_WP + 1) & 0x3F;
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
upsampled[lr] = Reverb2244<false>(&RUSB[lr][((RvbResPos - 39) & 0x3F) >> 1]);
|
||||
}
|
||||
|
||||
for(int lr = 0; lr < 2; lr++)
|
||||
upsampled[lr] = Reverb4422(&RUSB[lr][(RUSB_WP - 40) & 0x3F]);
|
||||
RvbResPos = (RvbResPos + 1) & 0x3F;
|
||||
|
||||
out_l = upsampled[0];
|
||||
out_r = upsampled[1];
|
||||
for(unsigned lr = 0; lr < 2; lr++)
|
||||
out[lr] = upsampled[lr];
|
||||
}
|
||||
|
||||
|
|
|
@ -34,8 +34,7 @@ void TIMER_SetVBlank(bool status);
|
|||
pscpu_timestamp_t TIMER_Update(const pscpu_timestamp_t);
|
||||
void TIMER_ResetTS(void);
|
||||
|
||||
void TIMER_Power(void);
|
||||
int TIMER_StateAction(StateMem *sm, int load, int data_only);
|
||||
void TIMER_Power(void) MDFN_COLD;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,897 +0,0 @@
|
|||
/* Mednafen - Multi-system Emulator
|
||||
*
|
||||
* 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 of the License, 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 <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <map>
|
||||
|
||||
#include "octoshock.h"
|
||||
|
||||
#include "state.h"
|
||||
#include "video.h"
|
||||
//#include "video/resize.h"
|
||||
#include "endian.h"
|
||||
|
||||
static int SaveStateStatus[10];
|
||||
|
||||
#define RLSB MDFNSTATE_RLSB //0x80000000
|
||||
|
||||
int32 smem_read(StateMem *st, void *buffer, uint32 len)
|
||||
{
|
||||
if((len + st->loc) > st->len)
|
||||
return(0);
|
||||
|
||||
memcpy(buffer, st->data + st->loc, len);
|
||||
st->loc += len;
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
int32 smem_write(StateMem *st, void *buffer, uint32 len)
|
||||
{
|
||||
if((len + st->loc) > st->malloced)
|
||||
{
|
||||
uint32 newsize = (st->malloced >= 32768) ? st->malloced : (st->initial_malloc ? st->initial_malloc : 32768);
|
||||
|
||||
while(newsize < (len + st->loc))
|
||||
newsize *= 2;
|
||||
st->data = (uint8 *)realloc(st->data, newsize);
|
||||
st->malloced = newsize;
|
||||
}
|
||||
memcpy(st->data + st->loc, buffer, len);
|
||||
st->loc += len;
|
||||
|
||||
if(st->loc > st->len) st->len = st->loc;
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
int32 smem_putc(StateMem *st, int value)
|
||||
{
|
||||
uint8 tmpval = value;
|
||||
if(smem_write(st, &tmpval, 1) != 1)
|
||||
return(-1);
|
||||
return(1);
|
||||
}
|
||||
|
||||
int32 smem_tell(StateMem *st)
|
||||
{
|
||||
return(st->loc);
|
||||
}
|
||||
|
||||
int32 smem_seek(StateMem *st, uint32 offset, int whence)
|
||||
{
|
||||
switch(whence)
|
||||
{
|
||||
case SEEK_SET: st->loc = offset; break;
|
||||
case SEEK_END: st->loc = st->len - offset; break;
|
||||
case SEEK_CUR: st->loc += offset; break;
|
||||
}
|
||||
|
||||
if(st->loc > st->len)
|
||||
{
|
||||
st->loc = st->len;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if(st->loc < 0)
|
||||
{
|
||||
st->loc = 0;
|
||||
return(-1);
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
int smem_write32le(StateMem *st, uint32 b)
|
||||
{
|
||||
uint8 s[4];
|
||||
s[0]=b;
|
||||
s[1]=b>>8;
|
||||
s[2]=b>>16;
|
||||
s[3]=b>>24;
|
||||
return((smem_write(st, s, 4)<4)?0:4);
|
||||
}
|
||||
|
||||
int smem_read32le(StateMem *st, uint32 *b)
|
||||
{
|
||||
uint8 s[4];
|
||||
|
||||
if(smem_read(st, s, 4) < 4)
|
||||
return(0);
|
||||
|
||||
*b = s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24);
|
||||
|
||||
return(4);
|
||||
}
|
||||
|
||||
|
||||
static bool ValidateSFStructure(SFORMAT *sf)
|
||||
{
|
||||
SFORMAT *saved_sf = sf;
|
||||
|
||||
while(sf->size || sf->name)
|
||||
{
|
||||
SFORMAT *sub_sf = saved_sf;
|
||||
while(sub_sf->size || sub_sf->name)
|
||||
{
|
||||
if(sf != sub_sf)
|
||||
{
|
||||
if(!strncmp(sf->name, sub_sf->name, 32))
|
||||
{
|
||||
printf("Duplicate state variable name: %.32s\n", sf->name);
|
||||
}
|
||||
}
|
||||
sub_sf++;
|
||||
}
|
||||
|
||||
sf++;
|
||||
}
|
||||
return(1);
|
||||
}
|
||||
|
||||
|
||||
static bool SubWrite(StateMem *st, SFORMAT *sf, int data_only, const char *name_prefix = NULL)
|
||||
{
|
||||
// FIXME? It's kind of slow, and we definitely don't want it on with state rewinding...
|
||||
if(!data_only)
|
||||
ValidateSFStructure(sf);
|
||||
|
||||
while(sf->size || sf->name) // Size can sometimes be zero, so also check for the text name. These two should both be zero only at the end of a struct.
|
||||
{
|
||||
if(!sf->size || !sf->v)
|
||||
{
|
||||
sf++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(sf->size == (uint32)~0) /* Link to another struct. */
|
||||
{
|
||||
if(!SubWrite(st, (SFORMAT *)sf->v, data_only, name_prefix))
|
||||
return(0);
|
||||
|
||||
sf++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 bytesize = sf->size;
|
||||
|
||||
// If we're only saving the raw data, and we come across a bool type, we save it as it is in memory, rather than converting it to
|
||||
// 1-byte. In the SFORMAT structure, the size member for bool entries is the number of bool elements, not the total in-memory size,
|
||||
// so we adjust it here.
|
||||
if(data_only && (sf->flags & MDFNSTATE_BOOL))
|
||||
{
|
||||
bytesize *= sizeof(bool);
|
||||
}
|
||||
|
||||
if(!data_only)
|
||||
{
|
||||
char nameo[1 + 256];
|
||||
int slen;
|
||||
|
||||
slen = snprintf(nameo + 1, 256, "%s%s", name_prefix ? name_prefix : "", sf->name);
|
||||
nameo[0] = slen;
|
||||
|
||||
if(slen >= 255)
|
||||
{
|
||||
printf("Warning: state variable name possibly too long: %s %s %s %d\n", sf->name, name_prefix, nameo, slen);
|
||||
slen = 255;
|
||||
}
|
||||
|
||||
smem_write(st, nameo, 1 + nameo[0]);
|
||||
smem_write32le(st, bytesize);
|
||||
|
||||
/* Flip the byte order... */
|
||||
if(sf->flags & MDFNSTATE_BOOL)
|
||||
{
|
||||
|
||||
}
|
||||
else if(sf->flags & MDFNSTATE_RLSB64)
|
||||
Endian_A64_NE_to_LE(sf->v, bytesize / sizeof(uint64));
|
||||
else if(sf->flags & MDFNSTATE_RLSB32)
|
||||
Endian_A32_NE_to_LE(sf->v, bytesize / sizeof(uint32));
|
||||
else if(sf->flags & MDFNSTATE_RLSB16)
|
||||
Endian_A16_NE_to_LE(sf->v, bytesize / sizeof(uint16));
|
||||
else if(sf->flags & RLSB)
|
||||
Endian_V_NE_to_LE(sf->v, bytesize);
|
||||
}
|
||||
|
||||
// Special case for the evil bool type, to convert bool to 1-byte elements.
|
||||
// Don't do it if we're only saving the raw data.
|
||||
if((sf->flags & MDFNSTATE_BOOL) && !data_only)
|
||||
{
|
||||
for(int32 bool_monster = 0; bool_monster < bytesize; bool_monster++)
|
||||
{
|
||||
uint8 tmp_bool = ((bool *)sf->v)[bool_monster];
|
||||
//printf("Bool write: %.31s\n", sf->name);
|
||||
smem_write(st, &tmp_bool, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
smem_write(st, (uint8 *)sf->v, bytesize);
|
||||
|
||||
if(!data_only)
|
||||
{
|
||||
/* Now restore the original byte order. */
|
||||
if(sf->flags & MDFNSTATE_BOOL)
|
||||
{
|
||||
|
||||
}
|
||||
else if(sf->flags & MDFNSTATE_RLSB64)
|
||||
Endian_A64_LE_to_NE(sf->v, bytesize / sizeof(uint64));
|
||||
else if(sf->flags & MDFNSTATE_RLSB32)
|
||||
Endian_A32_LE_to_NE(sf->v, bytesize / sizeof(uint32));
|
||||
else if(sf->flags & MDFNSTATE_RLSB16)
|
||||
Endian_A16_LE_to_NE(sf->v, bytesize / sizeof(uint16));
|
||||
else if(sf->flags & RLSB)
|
||||
Endian_V_LE_to_NE(sf->v, bytesize);
|
||||
}
|
||||
sf++;
|
||||
}
|
||||
|
||||
return TRUE_1;
|
||||
}
|
||||
|
||||
static int WriteStateChunk(StateMem *st, const char *sname, SFORMAT *sf, int data_only)
|
||||
{
|
||||
int32 data_start_pos;
|
||||
int32 end_pos;
|
||||
|
||||
if(!data_only)
|
||||
{
|
||||
uint8 sname_tmp[32];
|
||||
|
||||
memset(sname_tmp, 0, sizeof(sname_tmp));
|
||||
strncpy((char *)sname_tmp, sname, 32);
|
||||
|
||||
if(strlen(sname) > 32)
|
||||
printf("Warning: section name is too long: %s\n", sname);
|
||||
|
||||
smem_write(st, sname_tmp, 32);
|
||||
|
||||
smem_write32le(st, 0); // We'll come back and write this later.
|
||||
}
|
||||
|
||||
data_start_pos = smem_tell(st);
|
||||
|
||||
if(!SubWrite(st, sf, data_only))
|
||||
return(0);
|
||||
|
||||
end_pos = smem_tell(st);
|
||||
|
||||
if(!data_only)
|
||||
{
|
||||
smem_seek(st, data_start_pos - 4, SEEK_SET);
|
||||
smem_write32le(st, end_pos - data_start_pos);
|
||||
smem_seek(st, end_pos, SEEK_SET);
|
||||
}
|
||||
|
||||
return(end_pos - data_start_pos);
|
||||
}
|
||||
|
||||
struct compare_cstr
|
||||
{
|
||||
bool operator()(const char *s1, const char *s2) const
|
||||
{
|
||||
return(strcmp(s1, s2) < 0);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<const char *, SFORMAT *, compare_cstr> SFMap_t;
|
||||
|
||||
static void MakeSFMap(SFORMAT *sf, SFMap_t &sfmap)
|
||||
{
|
||||
while(sf->size || sf->name) // Size can sometimes be zero, so also check for the text name. These two should both be zero only at the end of a struct.
|
||||
{
|
||||
if(!sf->size || !sf->v)
|
||||
{
|
||||
sf++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(sf->size == (uint32)~0) /* Link to another SFORMAT structure. */
|
||||
MakeSFMap((SFORMAT *)sf->v, sfmap);
|
||||
else
|
||||
{
|
||||
assert(sf->name);
|
||||
|
||||
if(sfmap.find(sf->name) != sfmap.end())
|
||||
printf("Duplicate save state variable in internal emulator structures(CLUB THE PROGRAMMERS WITH BREADSTICKS): %s\n", sf->name);
|
||||
|
||||
sfmap[sf->name] = sf;
|
||||
}
|
||||
|
||||
sf++;
|
||||
}
|
||||
}
|
||||
|
||||
// Fast raw chunk reader
|
||||
static void DOReadChunk(StateMem *st, SFORMAT *sf)
|
||||
{
|
||||
while(sf->size || sf->name) // Size can sometimes be zero, so also check for the text name.
|
||||
// These two should both be zero only at the end of a struct.
|
||||
{
|
||||
if(!sf->size || !sf->v)
|
||||
{
|
||||
sf++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(sf->size == (uint32) ~0) // Link to another SFORMAT struct
|
||||
{
|
||||
DOReadChunk(st, (SFORMAT *)sf->v);
|
||||
sf++;
|
||||
continue;
|
||||
}
|
||||
|
||||
int32 bytesize = sf->size;
|
||||
|
||||
// Loading raw data, bool types are stored as they appear in memory, not as single bytes in the full state format.
|
||||
// In the SFORMAT structure, the size member for bool entries is the number of bool elements, not the total in-memory size,
|
||||
// so we adjust it here.
|
||||
if(sf->flags & MDFNSTATE_BOOL)
|
||||
bytesize *= sizeof(bool);
|
||||
|
||||
smem_read(st, (uint8 *)sf->v, bytesize);
|
||||
sf++;
|
||||
}
|
||||
}
|
||||
|
||||
static int ReadStateChunk(StateMem *st, SFORMAT *sf, int size, int data_only)
|
||||
{
|
||||
int temp;
|
||||
|
||||
if(data_only)
|
||||
{
|
||||
DOReadChunk(st, sf);
|
||||
}
|
||||
else
|
||||
{
|
||||
SFMap_t sfmap;
|
||||
SFMap_t sfmap_found; // Used for identifying variables that are missing in the save state.
|
||||
|
||||
MakeSFMap(sf, sfmap);
|
||||
|
||||
temp = smem_tell(st);
|
||||
while(smem_tell(st) < (temp + size))
|
||||
{
|
||||
uint32 recorded_size; // In bytes
|
||||
uint8 toa[1 + 256]; // Don't change to char unless cast toa[0] to unsigned to smem_read() and other places.
|
||||
|
||||
if(smem_read(st, toa, 1) != 1)
|
||||
{
|
||||
puts("Unexpected EOF");
|
||||
return(0);
|
||||
}
|
||||
|
||||
if(smem_read(st, toa + 1, toa[0]) != toa[0])
|
||||
{
|
||||
puts("Unexpected EOF?");
|
||||
return 0;
|
||||
}
|
||||
|
||||
toa[1 + toa[0]] = 0;
|
||||
|
||||
smem_read32le(st, &recorded_size);
|
||||
|
||||
SFMap_t::iterator sfmit;
|
||||
|
||||
sfmit = sfmap.find((char *)toa + 1);
|
||||
|
||||
if(sfmit != sfmap.end())
|
||||
{
|
||||
SFORMAT *tmp = sfmit->second;
|
||||
uint32 expected_size = tmp->size; // In bytes
|
||||
|
||||
if(recorded_size != expected_size)
|
||||
{
|
||||
printf("Variable in save state wrong size: %s. Need: %u, got: %u\n", toa + 1, expected_size, recorded_size);
|
||||
if(smem_seek(st, recorded_size, SEEK_CUR) < 0)
|
||||
{
|
||||
puts("Seek error");
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sfmap_found[tmp->name] = tmp;
|
||||
|
||||
smem_read(st, (uint8 *)tmp->v, expected_size);
|
||||
|
||||
if(tmp->flags & MDFNSTATE_BOOL)
|
||||
{
|
||||
// Converting downwards is necessary for the case of sizeof(bool) > 1
|
||||
for(int32 bool_monster = expected_size - 1; bool_monster >= 0; bool_monster--)
|
||||
{
|
||||
((bool *)tmp->v)[bool_monster] = ((uint8 *)tmp->v)[bool_monster];
|
||||
}
|
||||
}
|
||||
if(tmp->flags & MDFNSTATE_RLSB64)
|
||||
Endian_A64_LE_to_NE(tmp->v, expected_size / sizeof(uint64));
|
||||
else if(tmp->flags & MDFNSTATE_RLSB32)
|
||||
Endian_A32_LE_to_NE(tmp->v, expected_size / sizeof(uint32));
|
||||
else if(tmp->flags & MDFNSTATE_RLSB16)
|
||||
Endian_A16_LE_to_NE(tmp->v, expected_size / sizeof(uint16));
|
||||
else if(tmp->flags & RLSB)
|
||||
Endian_V_LE_to_NE(tmp->v, expected_size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Unknown variable in save state: %s\n", toa + 1);
|
||||
if(smem_seek(st, recorded_size, SEEK_CUR) < 0)
|
||||
{
|
||||
puts("Seek error");
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
} // while(...)
|
||||
|
||||
for(SFMap_t::const_iterator it = sfmap.begin(); it != sfmap.end(); it++)
|
||||
{
|
||||
if(sfmap_found.find(it->second->name) == sfmap_found.end())
|
||||
{
|
||||
printf("Variable missing from save state: %s\n", it->second->name);
|
||||
}
|
||||
}
|
||||
|
||||
assert(smem_tell(st) == (temp + size));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int CurrentState = 0;
|
||||
static int RecentlySavedState = -1;
|
||||
|
||||
/* This function is called by the game driver(NES, GB, GBA) to save a state. */
|
||||
int MDFNSS_StateAction(StateMem *st, int load, int data_only, std::vector <SSDescriptor> §ions)
|
||||
{
|
||||
std::vector<SSDescriptor>::iterator section;
|
||||
|
||||
if(load)
|
||||
{
|
||||
if(data_only)
|
||||
{
|
||||
for(section = sections.begin(); section != sections.end(); section++)
|
||||
{
|
||||
ReadStateChunk(st, section->sf, ~0, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char sname[32];
|
||||
|
||||
for(section = sections.begin(); section != sections.end(); section++)
|
||||
{
|
||||
int found = 0;
|
||||
uint32 tmp_size;
|
||||
uint32 total = 0;
|
||||
|
||||
while(smem_read(st, (uint8 *)sname, 32) == 32)
|
||||
{
|
||||
if(smem_read32le(st, &tmp_size) != 4)
|
||||
return(0);
|
||||
|
||||
total += tmp_size + 32 + 4;
|
||||
|
||||
// Yay, we found the section
|
||||
if(!strncmp(sname, section->name, 32))
|
||||
{
|
||||
if(!ReadStateChunk(st, section->sf, tmp_size, 0))
|
||||
{
|
||||
printf("Error reading chunk: %s\n", section->name);
|
||||
return(0);
|
||||
}
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// puts("SEEK");
|
||||
if(smem_seek(st, tmp_size, SEEK_CUR) < 0)
|
||||
{
|
||||
puts("Chunk seek failure");
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(smem_seek(st, -total, SEEK_CUR) < 0)
|
||||
{
|
||||
puts("Reverse seek error");
|
||||
return(0);
|
||||
}
|
||||
if(!found && !section->optional) // Not found. We are sad!
|
||||
{
|
||||
printf("Section missing: %.32s\n", section->name);
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for(section = sections.begin(); section != sections.end(); section++)
|
||||
{
|
||||
if(!WriteStateChunk(st, section->name, section->sf, data_only))
|
||||
return(0);
|
||||
}
|
||||
}
|
||||
|
||||
return(1);
|
||||
}
|
||||
|
||||
int MDFNSS_StateAction(StateMem *st, int load, int data_only, SFORMAT *sf, const char *name, bool optional)
|
||||
{
|
||||
std::vector <SSDescriptor> love;
|
||||
|
||||
love.push_back(SSDescriptor(sf, name, optional));
|
||||
return(MDFNSS_StateAction(st, load, data_only, love));
|
||||
}
|
||||
|
||||
//int MDFNSS_SaveSM(StateMem *st, int wantpreview_and_ts, int data_only, const MDFN_Surface *surface, const MDFN_Rect *DisplayRect, const int32 *LineWidths)
|
||||
//{
|
||||
// static const char *header_magic = "MDFNSVST";
|
||||
// uint8 header[32];
|
||||
// int neowidth = 0, neoheight = 0;
|
||||
//
|
||||
// memset(header, 0, sizeof(header));
|
||||
//
|
||||
// if(wantpreview_and_ts)
|
||||
// {
|
||||
// bool is_multires = false;
|
||||
//
|
||||
// // We'll want to use the nominal width if the source rectangle is > 25% off on either axis, or the source image has
|
||||
// // multiple horizontal resolutions.
|
||||
// neowidth = MDFNGameInfo->nominal_width;
|
||||
// neoheight = MDFNGameInfo->nominal_height;
|
||||
//
|
||||
// if(LineWidths[0] != ~0)
|
||||
// {
|
||||
// uint32 first_w = LineWidths[DisplayRect->y];
|
||||
//
|
||||
// for(int y = 0; y < DisplayRect->h; y++)
|
||||
// if(LineWidths[DisplayRect->y + y] != first_w)
|
||||
// {
|
||||
// puts("Multires!");
|
||||
// is_multires = TRUE;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if(!is_multires)
|
||||
// {
|
||||
// if(((double)DisplayRect->w / MDFNGameInfo->nominal_width) > 0.75 && ((double)DisplayRect->w / MDFNGameInfo->nominal_width) < 1.25)
|
||||
// neowidth = DisplayRect->w;
|
||||
//
|
||||
// if(((double)DisplayRect->h / MDFNGameInfo->nominal_height) > 0.75 && ((double)DisplayRect->h / MDFNGameInfo->nominal_height) < 1.25)
|
||||
// neoheight = DisplayRect->h;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if(!data_only)
|
||||
// {
|
||||
// memcpy(header, header_magic, 8);
|
||||
//
|
||||
// if(wantpreview_and_ts)
|
||||
// MDFN_en64lsb(header + 8, time(NULL));
|
||||
//
|
||||
// MDFN_en32lsb(header + 16, MEDNAFEN_VERSION_NUMERIC);
|
||||
// MDFN_en32lsb(header + 24, neowidth);
|
||||
// MDFN_en32lsb(header + 28, neoheight);
|
||||
// smem_write(st, header, 32);
|
||||
// }
|
||||
//
|
||||
// if(wantpreview_and_ts)
|
||||
// {
|
||||
// uint8 *previewbuffer = (uint8 *)malloc(4 * neowidth * neoheight);
|
||||
// MDFN_Surface *dest_surface = new MDFN_Surface((uint32 *)previewbuffer, neowidth, neoheight, neowidth, surface->format);
|
||||
// MDFN_Rect dest_rect;
|
||||
//
|
||||
// dest_rect.x = 0;
|
||||
// dest_rect.y = 0;
|
||||
// dest_rect.w = neowidth;
|
||||
// dest_rect.h = neoheight;
|
||||
//
|
||||
// MDFN_ResizeSurface(surface, DisplayRect, LineWidths, dest_surface, &dest_rect);
|
||||
//
|
||||
// {
|
||||
// uint32 a, b = 0;
|
||||
// for(a = 0; a < neowidth * neoheight * 4; a+=4)
|
||||
// {
|
||||
// uint32 c = *(uint32 *)&previewbuffer[a];
|
||||
// int nr, ng, nb;
|
||||
//
|
||||
// surface->DecodeColor(c, nr, ng, nb);
|
||||
//
|
||||
// previewbuffer[b + 0] = nr;
|
||||
// previewbuffer[b + 1] = ng;
|
||||
// previewbuffer[b + 2] = nb;
|
||||
// b += 3;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// smem_write(st, previewbuffer, 3 * neowidth * neoheight);
|
||||
//
|
||||
// free(previewbuffer);
|
||||
// delete dest_surface;
|
||||
// }
|
||||
//
|
||||
// // State rewinding code path hack, FIXME
|
||||
// if(data_only)
|
||||
// {
|
||||
// if(!MDFN_RawInputStateAction(st, 0, data_only))
|
||||
// return(0);
|
||||
// }
|
||||
//
|
||||
// if(!MDFNGameInfo->StateAction(st, 0, data_only))
|
||||
// return(0);
|
||||
//
|
||||
// if(!data_only)
|
||||
// {
|
||||
// uint32 sizy = smem_tell(st);
|
||||
// smem_seek(st, 16 + 4, SEEK_SET);
|
||||
// smem_write32le(st, sizy);
|
||||
// }
|
||||
// return(1);
|
||||
//}
|
||||
|
||||
int MDFNSS_Save(const char *fname, const char *suffix, const MDFN_Surface *surface, const MDFN_Rect *DisplayRect, const int32 *LineWidths)
|
||||
{
|
||||
StateMem st;
|
||||
|
||||
memset(&st, 0, sizeof(StateMem));
|
||||
|
||||
|
||||
//if(!MDFNGameInfo->StateAction)
|
||||
//{
|
||||
// MDFN_DispMessage(_("Module \"%s\" doesn't support save states."), MDFNGameInfo->shortname);
|
||||
// return(0);
|
||||
//}
|
||||
|
||||
//if(!MDFNSS_SaveSM(&st, (DisplayRect && LineWidths), 0, surface, DisplayRect, LineWidths))
|
||||
//{
|
||||
// if(st.data)
|
||||
// free(st.data);
|
||||
// if(!fname && !suffix)
|
||||
// MDFN_DispMessage(_("State %d save error."), CurrentState);
|
||||
// return(0);
|
||||
//}
|
||||
|
||||
/*if(!MDFN_DumpToFile(fname ? fname : MDFN_MakeFName(MDFNMKF_STATE,CurrentState,suffix).c_str(), 6, st.data, st.len))
|
||||
{
|
||||
SaveStateStatus[CurrentState] = 0;
|
||||
free(st.data);
|
||||
|
||||
if(!fname && !suffix)
|
||||
MDFN_DispMessage(_("State %d save error."),CurrentState);
|
||||
|
||||
return(0);
|
||||
}*/
|
||||
|
||||
//free(st.data);
|
||||
|
||||
//if(!fname && !suffix)
|
||||
// MDFN_DispMessage(_("State %d saved."),CurrentState);
|
||||
|
||||
return(1);
|
||||
}
|
||||
//
|
||||
//
|
||||
//int MDFNSS_LoadSM(StateMem *st, int haspreview, int data_only)
|
||||
//{
|
||||
// uint8 header[32];
|
||||
// uint32 stateversion;
|
||||
//
|
||||
// if(data_only)
|
||||
// {
|
||||
// stateversion = MEDNAFEN_VERSION_NUMERIC;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// smem_read(st, header, 32);
|
||||
//
|
||||
// if(memcmp(header, "MEDNAFENSVESTATE", 16) && memcmp(header, "MDFNSVST", 8))
|
||||
// return(0);
|
||||
//
|
||||
// stateversion = MDFN_de32lsb(header + 16);
|
||||
//
|
||||
// if((int)stateversion < 0x900) // Ensuring that (int)stateversion is > 0 is the most important part.
|
||||
// return(0);
|
||||
//
|
||||
// if(haspreview)
|
||||
// {
|
||||
// uint32 width = MDFN_de32lsb(header + 24);
|
||||
// uint32 height = MDFN_de32lsb(header + 28);
|
||||
// uint32 psize;
|
||||
//
|
||||
// psize = width * height * 3;
|
||||
// smem_seek(st, psize, SEEK_CUR); // Skip preview
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // State rewinding code path hack, FIXME
|
||||
// if(data_only)
|
||||
// {
|
||||
// if(!MDFN_RawInputStateAction(st, stateversion, data_only))
|
||||
// return(0);
|
||||
// }
|
||||
//
|
||||
// return(MDFNGameInfo->StateAction(st, stateversion, data_only));
|
||||
//}
|
||||
//
|
||||
//int MDFNSS_LoadFP(gzFile fp)
|
||||
//{
|
||||
// uint8 header[32];
|
||||
// StateMem st;
|
||||
//
|
||||
// memset(&st, 0, sizeof(StateMem));
|
||||
//
|
||||
// if(gzread(fp, header, 32) != 32)
|
||||
// {
|
||||
// return(0);
|
||||
// }
|
||||
// st.len = MDFN_de32lsb(header + 16 + 4);
|
||||
//
|
||||
// if(st.len < 32)
|
||||
// return(0);
|
||||
//
|
||||
// if(!(st.data = (uint8 *)malloc(st.len)))
|
||||
// return(0);
|
||||
//
|
||||
// memcpy(st.data, header, 32);
|
||||
// if(gzread(fp, st.data + 32, st.len - 32) != ((int32)st.len - 32))
|
||||
// {
|
||||
// free(st.data);
|
||||
// return(0);
|
||||
// }
|
||||
// if(!MDFNSS_LoadSM(&st, 1, 0))
|
||||
// {
|
||||
// free(st.data);
|
||||
// return(0);
|
||||
// }
|
||||
// free(st.data);
|
||||
// return(1);
|
||||
//}
|
||||
|
||||
int MDFNSS_Load(const char *fname, const char *suffix)
|
||||
{
|
||||
//gzFile st;
|
||||
|
||||
// if(!MDFNGameInfo->StateAction)
|
||||
// {
|
||||
// MDFN_DispMessage(_("Module \"%s\" doesn't support save states."), MDFNGameInfo->shortname);
|
||||
// return(0);
|
||||
// }
|
||||
|
||||
// if(fname)
|
||||
// st=gzopen(fname, "rb");
|
||||
// else
|
||||
// {
|
||||
// st=gzopen(MDFN_MakeFName(MDFNMKF_STATE,CurrentState,suffix).c_str(),"rb");
|
||||
//}
|
||||
|
||||
//if(st == NULL)
|
||||
//{
|
||||
// if(!fname && !suffix)
|
||||
// {
|
||||
// MDFN_DispMessage(_("State %d load error."),CurrentState);
|
||||
// SaveStateStatus[CurrentState]=0;
|
||||
// }
|
||||
// return(0);
|
||||
//}
|
||||
|
||||
//if(MDFNSS_LoadFP(st))
|
||||
//{
|
||||
// if(!fname && !suffix)
|
||||
// {
|
||||
// SaveStateStatus[CurrentState]=1;
|
||||
// MDFN_DispMessage(_("State %d loaded."),CurrentState);
|
||||
// SaveStateStatus[CurrentState]=1;
|
||||
// }
|
||||
// gzclose(st);
|
||||
// return(1);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// SaveStateStatus[CurrentState]=1;
|
||||
// MDFN_DispMessage(_("State %d read error!"),CurrentState);
|
||||
// gzclose(st);
|
||||
// return(0);
|
||||
// }
|
||||
|
||||
return 0;
|
||||
}
|
||||
//
|
||||
//void MDFNSS_GetStateInfo(const char *filename, StateStatusStruct *status)
|
||||
//{
|
||||
// gzFile fp;
|
||||
// uint32 StateShowPBWidth;
|
||||
// uint32 StateShowPBHeight;
|
||||
// uint8 *previewbuffer = NULL;
|
||||
//
|
||||
// fp = gzopen(filename, "rb");
|
||||
// if(fp)
|
||||
// {
|
||||
// uint8 header[32];
|
||||
//
|
||||
// gzread(fp, header, 32);
|
||||
// uint32 width = MDFN_de32lsb(header + 24);
|
||||
// uint32 height = MDFN_de32lsb(header + 28);
|
||||
//
|
||||
// if(width > 1024) width = 1024;
|
||||
// if(height > 1024) height = 1024;
|
||||
//
|
||||
// if(!(previewbuffer = (uint8 *)MDFN_malloc(3 * width * height, _("Save state preview buffer"))))
|
||||
// {
|
||||
// StateShowPBWidth = 0;
|
||||
// StateShowPBHeight = 0;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// gzread(fp, previewbuffer, 3 * width * height);
|
||||
//
|
||||
// StateShowPBWidth = width;
|
||||
// StateShowPBHeight = height;
|
||||
// }
|
||||
// gzclose(fp);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// StateShowPBWidth = MDFNGameInfo->nominal_width;
|
||||
// StateShowPBHeight = MDFNGameInfo->nominal_height;
|
||||
// }
|
||||
//
|
||||
// status->gfx = previewbuffer;
|
||||
// status->w = StateShowPBWidth;
|
||||
// status->h = StateShowPBHeight;
|
||||
//}
|
||||
|
||||
|
||||
void MDFNI_SaveState(const char *fname, const char *suffix, const MDFN_Surface *surface, const MDFN_Rect *DisplayRect, const int32 *LineWidths)
|
||||
{
|
||||
//if(!MDFNGameInfo->StateAction)
|
||||
// return;
|
||||
|
||||
//if(MDFNnetplay && (MDFNGameInfo->SaveStateAltersState == true))
|
||||
//{
|
||||
// char sb[256];
|
||||
// trio_snprintf(sb, sizeof(sb), _("Module %s is not compatible with manual state saving during netplay."), MDFNGameInfo->shortname);
|
||||
// MDFND_NetplayText((const uint8*)sb, false);
|
||||
// return;
|
||||
//}
|
||||
|
||||
MDFNSS_Save(fname, suffix, surface, DisplayRect, LineWidths);
|
||||
}
|
||||
|
||||
void MDFNI_LoadState(const char *fname, const char *suffix)
|
||||
{
|
||||
//if(!MDFNGameInfo->StateAction)
|
||||
// return;
|
||||
|
||||
/* For network play and movies, be load the state locally, and then save the state to a temporary buffer,
|
||||
and send or record that. This ensures that if an older state is loaded that is missing some
|
||||
information expected in newer save states, desynchronization won't occur(at least not
|
||||
from this ;)).
|
||||
*/
|
||||
if(MDFNSS_Load(fname, suffix))
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "octoshock.h"
|
||||
#include "video/surface.h"
|
||||
//#include "state-common.h"
|
||||
#include <stdio.h>
|
||||
|
||||
//void MDFNSS_GetStateInfo(const char *filename, StateStatusStruct *status);
|
||||
|
||||
int MDFNSS_Save(const char *, const char *suffix, const MDFN_Surface *surface = (MDFN_Surface *)NULL, const MDFN_Rect *DisplayRect = (MDFN_Rect*)NULL, const int32 *LineWidths = (int32*)NULL);
|
||||
int MDFNSS_Load(const char *, const char *suffix);
|
||||
int MDFNSS_SaveFP(FILE* fp, const MDFN_Surface *surface = (MDFN_Surface *)NULL, const MDFN_Rect *DisplayRect = (MDFN_Rect*)NULL, const int32 *LineWidths = (int32*)NULL);
|
||||
int MDFNSS_LoadFP(FILE fp);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8 *data;
|
||||
uint32 loc;
|
||||
uint32 len;
|
||||
|
||||
uint32 malloced;
|
||||
|
||||
uint32 initial_malloc; // A setting!
|
||||
} StateMem;
|
||||
|
||||
// Eh, we abuse the smem_* in-memory stream code
|
||||
// in a few other places. :)
|
||||
int32 smem_read(StateMem *st, void *buffer, uint32 len);
|
||||
int32 smem_write(StateMem *st, void *buffer, uint32 len);
|
||||
int32 smem_putc(StateMem *st, int value);
|
||||
int32 smem_tell(StateMem *st);
|
||||
int32 smem_seek(StateMem *st, uint32 offset, int whence);
|
||||
int smem_write32le(StateMem *st, uint32 b);
|
||||
int smem_read32le(StateMem *st, uint32 *b);
|
||||
|
||||
int MDFNSS_SaveSM(StateMem *st, int wantpreview_and_ts, int data_only, const MDFN_Surface *surface = (MDFN_Surface *)NULL, const MDFN_Rect *DisplayRect = (MDFN_Rect*)NULL, const int32 *LineWidths = (int32*)NULL);
|
||||
int MDFNSS_LoadSM(StateMem *st, int haspreview, int data_only);
|
||||
|
||||
void MDFNSS_CheckStates(void);
|
||||
|
||||
// Flag for a single, >= 1 byte native-endian variable
|
||||
#define MDFNSTATE_RLSB 0x80000000
|
||||
|
||||
// 32-bit native-endian elements
|
||||
#define MDFNSTATE_RLSB32 0x40000000
|
||||
|
||||
// 16-bit native-endian elements
|
||||
#define MDFNSTATE_RLSB16 0x20000000
|
||||
|
||||
// 64-bit native-endian elements
|
||||
#define MDFNSTATE_RLSB64 0x10000000
|
||||
|
||||
#define MDFNSTATE_BOOL 0x08000000
|
||||
|
||||
|
||||
//// Array of structures
|
||||
//#define MDFNSTATE_ARRAYOFS 0x04000000
|
||||
|
||||
typedef struct {
|
||||
void *v; // Pointer to the variable/array
|
||||
uint32 size; // Length, in bytes, of the data to be saved EXCEPT:
|
||||
// In the case of MDFNSTATE_BOOL, it is the number of bool elements to save(bool is not always 1-byte).
|
||||
// If 0, the subchunk isn't saved.
|
||||
uint32 flags; // Flags
|
||||
const char *name; // Name
|
||||
//uint32 struct_size; // Only used for MDFNSTATE_ARRAYOFS, sizeof(struct) that members of the linked SFORMAT struct are in.
|
||||
} SFORMAT;
|
||||
|
||||
INLINE bool SF_IS_BOOL(bool *) { return(1); }
|
||||
INLINE bool SF_IS_BOOL(void *) { return(0); }
|
||||
|
||||
INLINE uint32 SF_FORCE_AB(bool *) { return(0); }
|
||||
|
||||
INLINE uint32 SF_FORCE_A8(int8 *) { return(0); }
|
||||
INLINE uint32 SF_FORCE_A8(uint8 *) { return(0); }
|
||||
|
||||
INLINE uint32 SF_FORCE_A16(int16 *) { return(0); }
|
||||
INLINE uint32 SF_FORCE_A16(uint16 *) { return(0); }
|
||||
|
||||
INLINE uint32 SF_FORCE_A32(int32 *) { return(0); }
|
||||
INLINE uint32 SF_FORCE_A32(uint32 *) { return(0); }
|
||||
|
||||
INLINE uint32 SF_FORCE_A64(int64 *) { return(0); }
|
||||
INLINE uint32 SF_FORCE_A64(uint64 *) { return(0); }
|
||||
|
||||
INLINE uint32 SF_FORCE_D(double *) { return(0); }
|
||||
|
||||
#define SFVARN(x, n) { &(x), SF_IS_BOOL(&(x)) ? 1U : (uint32)sizeof(x), MDFNSTATE_RLSB | (SF_IS_BOOL(&(x)) ? MDFNSTATE_BOOL : 0), n }
|
||||
#define SFVAR(x) SFVARN((x), #x)
|
||||
|
||||
#define SFARRAYN(x, l, n) { (x), (uint32)(l), 0 | SF_FORCE_A8(x), n }
|
||||
#define SFARRAY(x, l) SFARRAYN((x), (l), #x)
|
||||
|
||||
#define SFARRAYBN(x, l, n) { (x), (uint32)(l), MDFNSTATE_BOOL | SF_FORCE_AB(x), n }
|
||||
#define SFARRAYB(x, l) SFARRAYBN((x), (l), #x)
|
||||
|
||||
#define SFARRAY16N(x, l, n) { (x), (uint32)((l) * sizeof(uint16)), MDFNSTATE_RLSB16 | SF_FORCE_A16(x), n }
|
||||
#define SFARRAY16(x, l) SFARRAY16N((x), (l), #x)
|
||||
|
||||
#define SFARRAY32N(x, l, n) { (x), (uint32)((l) * sizeof(uint32)), MDFNSTATE_RLSB32 | SF_FORCE_A32(x), n }
|
||||
#define SFARRAY32(x, l) SFARRAY32N((x), (l), #x)
|
||||
|
||||
#define SFARRAY64N(x, l, n) { (x), (uint32)((l) * sizeof(uint64)), MDFNSTATE_RLSB64 | SF_FORCE_A64(x), n }
|
||||
#define SFARRAY64(x, l) SFARRAY64N((x), (l), #x)
|
||||
|
||||
#if SIZEOF_DOUBLE != 8
|
||||
#error "sizeof(double) != 8"
|
||||
#endif
|
||||
|
||||
#define SFARRAYDN(x, l, n) { (x), (uint32)((l) * 8), MDFNSTATE_RLSB64 | SF_FORCE_D(x), n }
|
||||
#define SFARRAYD(x, l) SFARRAYDN((x), (l), #x)
|
||||
|
||||
#define SFEND { 0, 0, 0, 0 }
|
||||
|
||||
#include <vector>
|
||||
|
||||
// State-Section Descriptor
|
||||
class SSDescriptor
|
||||
{
|
||||
public:
|
||||
SSDescriptor(SFORMAT *n_sf, const char *n_name, bool n_optional = 0)
|
||||
{
|
||||
sf = n_sf;
|
||||
name = n_name;
|
||||
optional = n_optional;
|
||||
}
|
||||
~SSDescriptor(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
SFORMAT *sf;
|
||||
const char *name;
|
||||
bool optional;
|
||||
};
|
||||
|
||||
int MDFNSS_StateAction(StateMem *st, int load, int data_only, std::vector <SSDescriptor> §ions);
|
||||
int MDFNSS_StateAction(StateMem *st, int load, int data_only, SFORMAT *sf, const char *name, bool optional = 0);
|
||||
|
||||
void MDFN_StateEvilFlushMovieLove(void);
|
||||
bool MDFN_StateEvilIsRunning(void);
|
||||
void MDFN_StateEvilBegin(void);
|
||||
void MDFN_StateEvilEnd(void);
|
||||
int MDFN_StateEvil(int);
|
||||
|
|
@ -148,7 +148,7 @@ void MDFN_Surface::Init(void *const p_pixels, const uint32 p_width, const uint32
|
|||
{
|
||||
//ErrnoHolder ene(errno);
|
||||
|
||||
throw "OLD ERROR";
|
||||
throw "OLD ERROR";
|
||||
//throw(MDFN_Error(ene.Errno(), "%s", ene.StrError()));
|
||||
}
|
||||
}
|
||||
|
@ -225,6 +225,16 @@ void MDFN_Surface::SetFormat(const MDFN_PixelFormat &nf, bool convert)
|
|||
{
|
||||
if(format.bpp == 8)
|
||||
{
|
||||
uint16 palconv[256];
|
||||
|
||||
for(unsigned i = 0; i < 256; i++)
|
||||
{
|
||||
uint8 r, g, b;
|
||||
|
||||
format.DecodePColor(palette[i], r, g, b);
|
||||
palconv[i] = nf.MakeColor(r, g, b, 0);
|
||||
}
|
||||
|
||||
puts("8bpp to 16bpp convert");
|
||||
for(int y = 0; y < h; y++)
|
||||
{
|
||||
|
@ -233,9 +243,7 @@ void MDFN_Surface::SetFormat(const MDFN_PixelFormat &nf, bool convert)
|
|||
|
||||
for(int x = 0; x < w; x++)
|
||||
{
|
||||
const MDFN_PaletteEntry &p = palette[srow[x]];
|
||||
|
||||
drow[x] = nf.MakeColor(p.r, p.g, p.b, 0);
|
||||
drow[x] = palconv[srow[x]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -267,6 +275,16 @@ void MDFN_Surface::SetFormat(const MDFN_PixelFormat &nf, bool convert)
|
|||
{
|
||||
if(format.bpp == 8)
|
||||
{
|
||||
uint32 palconv[256];
|
||||
|
||||
for(unsigned i = 0; i < 256; i++)
|
||||
{
|
||||
uint8 r, g, b;
|
||||
|
||||
format.DecodePColor(palette[i], r, g, b);
|
||||
palconv[i] = nf.MakeColor(r, g, b, 0);
|
||||
}
|
||||
|
||||
puts("8bpp to 32bpp convert");
|
||||
for(int y = 0; y < h; y++)
|
||||
{
|
||||
|
@ -275,9 +293,7 @@ void MDFN_Surface::SetFormat(const MDFN_PixelFormat &nf, bool convert)
|
|||
|
||||
for(int x = 0; x < w; x++)
|
||||
{
|
||||
const MDFN_PaletteEntry &p = palette[srow[x]];
|
||||
|
||||
drow[x] = nf.MakeColor(p.r, p.g, p.b, 0);
|
||||
drow[x] = palconv[srow[x]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,11 @@ enum
|
|||
//MDFN_COLORSPACE_YUV = 2, // TODO, maybe.
|
||||
};
|
||||
|
||||
struct MDFN_PaletteEntry
|
||||
{
|
||||
uint8 r, g, b;
|
||||
};
|
||||
|
||||
class MDFN_PixelFormat
|
||||
{
|
||||
public:
|
||||
|
@ -86,6 +91,24 @@ class MDFN_PixelFormat
|
|||
}
|
||||
}
|
||||
|
||||
INLINE MDFN_PaletteEntry MakePColor(uint8 r, uint8 g, uint8 b) const
|
||||
{
|
||||
MDFN_PaletteEntry ret;
|
||||
|
||||
ret.r = ((r * ((1 << Rprec) - 1) + 127) / 255) << Rshift;
|
||||
ret.g = ((g * ((1 << Gprec) - 1) + 127) / 255) << Gshift;
|
||||
ret.b = ((b * ((1 << Bprec) - 1) + 127) / 255) << Bshift;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
INLINE void DecodePColor(const MDFN_PaletteEntry& pe, uint8 &r, uint8 &g, uint8 &b) const
|
||||
{
|
||||
r = ((pe.r >> Rshift) & ((1 << Rprec) - 1)) * 255 / ((1 << Rprec) - 1);
|
||||
g = ((pe.g >> Gshift) & ((1 << Gprec) - 1)) * 255 / ((1 << Gprec) - 1);
|
||||
b = ((pe.b >> Bshift) & ((1 << Bprec) - 1)) * 255 / ((1 << Bprec) - 1);
|
||||
}
|
||||
|
||||
// Gets the R/G/B/A values for the passed 32-bit surface pixel value
|
||||
INLINE void DecodeColor(uint32 value, int &r, int &g, int &b, int &a) const
|
||||
{
|
||||
|
@ -149,11 +172,6 @@ class MDFN_PixelFormat
|
|||
}
|
||||
}; // MDFN_PixelFormat;
|
||||
|
||||
struct MDFN_PaletteEntry
|
||||
{
|
||||
uint8 r, g, b;
|
||||
};
|
||||
|
||||
// Supports 32-bit RGBA
|
||||
// 16-bit is WIP
|
||||
// 8-bit is even WIPier.
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#include "../mednafen.h"
|
||||
#include "../video.h"
|
||||
#include "../general.h"
|
||||
#include "../driver.h"
|
||||
|
Loading…
Reference in New Issue