mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-11-04 18:05:06 +01:00
This commit is contained in:
commit
e5a1184a51
@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
|
* Copyright (C) 2007-2013 Eke-Eke (Genesis Plus GX)
|
||||||
*
|
*
|
||||||
* Most cartridge protections were initially documented by Haze
|
* Many cartridge protections were initially documented by Haze
|
||||||
* (http://haze.mameworld.info/)
|
* (http://haze.mameworld.info/)
|
||||||
*
|
*
|
||||||
* Realtec mapper was documented by TascoDeluxe
|
* Realtec mapper was documented by TascoDeluxe
|
||||||
@ -42,6 +42,7 @@
|
|||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
|
|
||||||
#include "shared.h"
|
#include "shared.h"
|
||||||
|
#include "eeprom_i2c.h"
|
||||||
#include "eeprom_spi.h"
|
#include "eeprom_spi.h"
|
||||||
#include "gamepad.h"
|
#include "gamepad.h"
|
||||||
|
|
||||||
@ -356,6 +357,24 @@ void md_cart_init(void)
|
|||||||
BACKUP MEMORY
|
BACKUP MEMORY
|
||||||
***********************************************/
|
***********************************************/
|
||||||
sram_init();
|
sram_init();
|
||||||
|
eeprom_i2c_init();
|
||||||
|
|
||||||
|
/* external SRAM */
|
||||||
|
if (sram.on && !sram.custom)
|
||||||
|
{
|
||||||
|
/* disabled on startup if ROM is mapped in same area */
|
||||||
|
if (cart.romsize <= sram.start)
|
||||||
|
{
|
||||||
|
/* initialize m68k bus handlers */
|
||||||
|
m68k.memory_map[sram.start >> 16].base = sram.sram;
|
||||||
|
m68k.memory_map[sram.start >> 16].read8 = sram_read_byte;
|
||||||
|
m68k.memory_map[sram.start >> 16].read16 = sram_read_word;
|
||||||
|
m68k.memory_map[sram.start >> 16].write8 = sram_write_byte;
|
||||||
|
m68k.memory_map[sram.start >> 16].write16 = sram_write_word;
|
||||||
|
zbank_memory_map[sram.start >> 16].read = sram_read_byte;
|
||||||
|
zbank_memory_map[sram.start >> 16].write = sram_write_byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**********************************************
|
/**********************************************
|
||||||
SVP CHIP
|
SVP CHIP
|
||||||
@ -492,7 +511,7 @@ void md_cart_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**********************************************
|
/**********************************************
|
||||||
Cartridge Extra Hardware
|
CARTRIDGE EXTRA HARDWARE
|
||||||
***********************************************/
|
***********************************************/
|
||||||
memset(&cart.hw, 0, sizeof(cart.hw));
|
memset(&cart.hw, 0, sizeof(cart.hw));
|
||||||
|
|
||||||
@ -607,7 +626,7 @@ void md_cart_init(void)
|
|||||||
m68k.memory_map[i].base = cart.rom + ((i & 0x03) << 16);
|
m68k.memory_map[i].base = cart.rom + ((i & 0x03) << 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 32K SRAM is initially disabled at $200000-$2FFFFF */
|
/* 32K static RAM is mapped to $200000-$2FFFFF (disabled on startup) */
|
||||||
for (i=0x20; i<0x30; i++)
|
for (i=0x20; i<0x30; i++)
|
||||||
{
|
{
|
||||||
m68k.memory_map[i].base = sram.sram;
|
m68k.memory_map[i].base = sram.sram;
|
||||||
@ -789,10 +808,28 @@ int md_cart_context_load(uint8 *state)
|
|||||||
if (offset == 0xff)
|
if (offset == 0xff)
|
||||||
{
|
{
|
||||||
/* SRAM */
|
/* SRAM */
|
||||||
m68k.memory_map[i].base = sram.sram;
|
m68k.memory_map[i].base = sram.sram;
|
||||||
|
m68k.memory_map[i].read8 = sram_read_byte;
|
||||||
|
m68k.memory_map[i].read16 = sram_read_word;
|
||||||
|
m68k.memory_map[i].write8 = sram_write_byte;
|
||||||
|
m68k.memory_map[i].write16 = sram_write_word;
|
||||||
|
zbank_memory_map[i].read = sram_read_byte;
|
||||||
|
zbank_memory_map[i].write = sram_write_byte;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
/* check if SRAM was mapped there before loading state */
|
||||||
|
if (m68k.memory_map[i].base == sram.sram)
|
||||||
|
{
|
||||||
|
m68k.memory_map[i].read8 = NULL;
|
||||||
|
m68k.memory_map[i].read16 = NULL;
|
||||||
|
m68k.memory_map[i].write8 = m68k_unused_8_w;
|
||||||
|
m68k.memory_map[i].write16 = m68k_unused_16_w;
|
||||||
|
zbank_memory_map[i].read = NULL;
|
||||||
|
zbank_memory_map[i].write = zbank_unused_w;
|
||||||
|
}
|
||||||
|
|
||||||
/* ROM */
|
/* ROM */
|
||||||
m68k.memory_map[i].base = cart.rom + (offset << 16);
|
m68k.memory_map[i].base = cart.rom + (offset << 16);
|
||||||
}
|
}
|
||||||
@ -829,9 +866,9 @@ static void mapper_sega_w(uint32 data)
|
|||||||
{
|
{
|
||||||
/* Backup RAM mapped to $200000-$20ffff (normally mirrored up to $3fffff but this breaks Sonic Megamix and no game need it) */
|
/* Backup RAM mapped to $200000-$20ffff (normally mirrored up to $3fffff but this breaks Sonic Megamix and no game need it) */
|
||||||
m68k.memory_map[0x20].base = sram.sram;
|
m68k.memory_map[0x20].base = sram.sram;
|
||||||
m68k.memory_map[0x20].write8 = NULL;
|
m68k.memory_map[0x20].read8 = sram_read_byte;
|
||||||
m68k.memory_map[0x20].write16 = NULL;
|
m68k.memory_map[0x20].read16 = sram_read_word;
|
||||||
zbank_memory_map[0x20].write = NULL;
|
zbank_memory_map[0x20].read = sram_read_byte;
|
||||||
|
|
||||||
/* Backup RAM write protection */
|
/* Backup RAM write protection */
|
||||||
if (data & 2)
|
if (data & 2)
|
||||||
@ -840,6 +877,12 @@ static void mapper_sega_w(uint32 data)
|
|||||||
m68k.memory_map[0x20].write16 = m68k_unused_16_w;
|
m68k.memory_map[0x20].write16 = m68k_unused_16_w;
|
||||||
zbank_memory_map[0x20].write = zbank_unused_w;
|
zbank_memory_map[0x20].write = zbank_unused_w;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m68k.memory_map[0x20].write8 = sram_write_byte;
|
||||||
|
m68k.memory_map[0x20].write16 = sram_write_word;
|
||||||
|
zbank_memory_map[0x20].write = sram_write_byte;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* S&K lock-on chip */
|
/* S&K lock-on chip */
|
||||||
@ -857,10 +900,13 @@ static void mapper_sega_w(uint32 data)
|
|||||||
/* cartridge ROM mapped to $200000-$3fffff */
|
/* cartridge ROM mapped to $200000-$3fffff */
|
||||||
for (i=0x20; i<0x40; i++)
|
for (i=0x20; i<0x40; i++)
|
||||||
{
|
{
|
||||||
m68k.memory_map[i].base = cart.rom + ((i<<16) & cart.mask);
|
m68k.memory_map[i].base = cart.rom + ((i<<16) & cart.mask);
|
||||||
m68k.memory_map[i].write8 = m68k_unused_8_w;
|
m68k.memory_map[i].read8 = NULL;
|
||||||
m68k.memory_map[i].write16 = m68k_unused_16_w;
|
m68k.memory_map[i].read16 = NULL;
|
||||||
zbank_memory_map[i].write = zbank_unused_w;
|
zbank_memory_map[i].read = NULL;
|
||||||
|
m68k.memory_map[i].write8 = m68k_unused_8_w;
|
||||||
|
m68k.memory_map[i].write16 = m68k_unused_16_w;
|
||||||
|
zbank_memory_map[i].write = zbank_unused_w;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -907,10 +953,10 @@ static void mapper_sf001_w(uint32 address, uint32 data)
|
|||||||
m68k.memory_map[i].base = cart.rom + (i << 16);
|
m68k.memory_map[i].base = cart.rom + (i << 16);
|
||||||
m68k.memory_map[i].read8 = m68k_read_bus_8;
|
m68k.memory_map[i].read8 = m68k_read_bus_8;
|
||||||
m68k.memory_map[i].read16 = m68k_read_bus_16;
|
m68k.memory_map[i].read16 = m68k_read_bus_16;
|
||||||
m68k.memory_map[i].write8 = m68k_unused_8_w;
|
m68k.memory_map[i].write8 = (i > 0x00) ? m68k_unused_8_w : mapper_sf001_w;
|
||||||
m68k.memory_map[i].write16 = m68k_unused_16_w;
|
m68k.memory_map[i].write16 = (i > 0x00) ? m68k_unused_16_w : mapper_sf001_w;
|
||||||
zbank_memory_map[i].read = zbank_unused_r;
|
zbank_memory_map[i].read = zbank_unused_r;
|
||||||
zbank_memory_map[i].write = m68k_unused_8_w;
|
zbank_memory_map[i].write = (i > 0x00) ? m68k_unused_8_w : mapper_sf001_w;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -939,12 +985,12 @@ static void mapper_sf001_w(uint32 address, uint32 data)
|
|||||||
while (i<0x40)
|
while (i<0x40)
|
||||||
{
|
{
|
||||||
m68k.memory_map[i].base = sram.sram;
|
m68k.memory_map[i].base = sram.sram;
|
||||||
m68k.memory_map[i].read8 = NULL;
|
m68k.memory_map[i].read8 = sram_read_byte;
|
||||||
m68k.memory_map[i].read16 = NULL;
|
m68k.memory_map[i].read16 = sram_read_word;
|
||||||
m68k.memory_map[i].write8 = NULL;
|
m68k.memory_map[i].write8 = sram_write_byte;
|
||||||
m68k.memory_map[i].write16 = NULL;
|
m68k.memory_map[i].write16 = sram_write_word;
|
||||||
zbank_memory_map[i].read = NULL;
|
zbank_memory_map[i].read = sram_read_byte;
|
||||||
zbank_memory_map[i].write = NULL;
|
zbank_memory_map[i].write = sram_write_byte;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -956,10 +1002,10 @@ static void mapper_sf001_w(uint32 address, uint32 data)
|
|||||||
m68k.memory_map[i].base = cart.rom + (i << 16);
|
m68k.memory_map[i].base = cart.rom + (i << 16);
|
||||||
m68k.memory_map[i].read8 = NULL;
|
m68k.memory_map[i].read8 = NULL;
|
||||||
m68k.memory_map[i].read16 = NULL;
|
m68k.memory_map[i].read16 = NULL;
|
||||||
m68k.memory_map[i].write8 = m68k_unused_8_w;
|
m68k.memory_map[i].write8 = (i > 0x00) ? m68k_unused_8_w : mapper_sf001_w;
|
||||||
m68k.memory_map[i].write16 = m68k_unused_16_w;
|
m68k.memory_map[i].write16 = (i > 0x00) ? m68k_unused_16_w : mapper_sf001_w;
|
||||||
zbank_memory_map[i].read = NULL;
|
zbank_memory_map[i].read = NULL;
|
||||||
zbank_memory_map[i].write = m68k_unused_8_w;
|
zbank_memory_map[i].write = (i > 0x00) ? m68k_unused_8_w : mapper_sf001_w;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1023,12 +1069,12 @@ static void mapper_sf004_w(uint32 address, uint32 data)
|
|||||||
/* 32KB static RAM mirrored into $200000-$2FFFFF (odd bytes only) */
|
/* 32KB static RAM mirrored into $200000-$2FFFFF (odd bytes only) */
|
||||||
for (i=0x20; i<0x30; i++)
|
for (i=0x20; i<0x30; i++)
|
||||||
{
|
{
|
||||||
m68k.memory_map[i].read8 = NULL;
|
m68k.memory_map[i].read8 = sram_read_byte;
|
||||||
m68k.memory_map[i].read16 = NULL;
|
m68k.memory_map[i].read16 = sram_read_word;
|
||||||
m68k.memory_map[i].write8 = NULL;
|
m68k.memory_map[i].write8 = sram_write_byte;
|
||||||
m68k.memory_map[i].write16 = NULL;
|
m68k.memory_map[i].write16 = sram_write_word;
|
||||||
zbank_memory_map[i].read = NULL;
|
zbank_memory_map[i].read = sram_read_byte;
|
||||||
zbank_memory_map[i].write = NULL;
|
zbank_memory_map[i].write = sram_write_byte;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -37,15 +37,9 @@
|
|||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
|
|
||||||
#include "shared.h"
|
#include "shared.h"
|
||||||
#include "eeprom_i2c.h"
|
|
||||||
|
|
||||||
T_SRAM sram;
|
T_SRAM sram;
|
||||||
|
|
||||||
static unsigned int sram_read_byte(unsigned int address);
|
|
||||||
static unsigned int sram_read_word(unsigned int address);
|
|
||||||
static void sram_write_byte(unsigned int address, unsigned int data);
|
|
||||||
static void sram_write_word(unsigned int address, unsigned int data);
|
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* A quick guide to external RAM on the Genesis
|
* A quick guide to external RAM on the Genesis
|
||||||
*
|
*
|
||||||
@ -194,44 +188,25 @@ void sram_init()
|
|||||||
sram.on = 1;
|
sram.on = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* autodetect games with serial EEPROM */
|
|
||||||
eeprom_i2c_init();
|
|
||||||
|
|
||||||
/* initialize m68k bus handlers (if not already done) */
|
|
||||||
if (sram.on && !sram.custom)
|
|
||||||
{
|
|
||||||
/* disabled on startup if ROM is mapped in same area */
|
|
||||||
if (cart.romsize <= sram.start)
|
|
||||||
{
|
|
||||||
m68k.memory_map[sram.start >> 16].base = sram.sram;
|
|
||||||
m68k.memory_map[sram.start >> 16].read8 = sram_read_byte;
|
|
||||||
m68k.memory_map[sram.start >> 16].read16 = sram_read_word;
|
|
||||||
m68k.memory_map[sram.start >> 16].write8 = sram_write_byte;
|
|
||||||
m68k.memory_map[sram.start >> 16].write16 = sram_write_word;
|
|
||||||
zbank_memory_map[sram.start >> 16].read = sram_read_byte;
|
|
||||||
zbank_memory_map[sram.start >> 16].write = sram_write_byte;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int sram_read_byte(unsigned int address)
|
unsigned int sram_read_byte(unsigned int address)
|
||||||
{
|
{
|
||||||
return sram.sram[address & 0xffff];
|
return sram.sram[address & 0xffff];
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int sram_read_word(unsigned int address)
|
unsigned int sram_read_word(unsigned int address)
|
||||||
{
|
{
|
||||||
address &= 0xfffe;
|
address &= 0xfffe;
|
||||||
return (sram.sram[address + 1] | (sram.sram[address] << 8));
|
return (sram.sram[address + 1] | (sram.sram[address] << 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sram_write_byte(unsigned int address, unsigned int data)
|
void sram_write_byte(unsigned int address, unsigned int data)
|
||||||
{
|
{
|
||||||
sram.sram[address & 0xffff] = data;
|
sram.sram[address & 0xffff] = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sram_write_word(unsigned int address, unsigned int data)
|
void sram_write_word(unsigned int address, unsigned int data)
|
||||||
{
|
{
|
||||||
address &= 0xfffe;
|
address &= 0xfffe;
|
||||||
sram.sram[address] = data >> 8;
|
sram.sram[address] = data >> 8;
|
||||||
|
@ -52,6 +52,10 @@ typedef struct
|
|||||||
|
|
||||||
/* Function prototypes */
|
/* Function prototypes */
|
||||||
extern void sram_init();
|
extern void sram_init();
|
||||||
|
extern unsigned int sram_read_byte(unsigned int address);
|
||||||
|
extern unsigned int sram_read_word(unsigned int address);
|
||||||
|
extern void sram_write_byte(unsigned int address, unsigned int data);
|
||||||
|
extern void sram_write_word(unsigned int address, unsigned int data);
|
||||||
|
|
||||||
/* global variables */
|
/* global variables */
|
||||||
extern T_SRAM sram;
|
extern T_SRAM sram;
|
||||||
|
@ -129,14 +129,25 @@ void lightgun_refresh(int port)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* External Interrupt ? */
|
/* External Interrupt enabled ? */
|
||||||
if (reg[11] & 0x08)
|
if (reg[11] & 0x08)
|
||||||
{
|
{
|
||||||
m68k_update_irq(2);
|
m68k_update_irq(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HACK: force HV Counter latch (some games does not lock HV Counter but instead use larger offset value) */
|
/* HVC latch enabled ? */
|
||||||
hvc_latch = 0x10000 | (y << 8);
|
if (reg[0] & 0x02)
|
||||||
|
{
|
||||||
|
/* line accurate V-Counter value */
|
||||||
|
hvc_latch = 0x10000 | (y << 8);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* HACK: force HVC latch even when disabled (a few games does not lock HV Counter but instead applies larger offset value) */
|
||||||
|
hvc_latch = 0x20000 | (y << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pixel accurate H-Counter value */
|
||||||
if (reg[12] & 1)
|
if (reg[12] & 1)
|
||||||
{
|
{
|
||||||
hvc_latch |= hc_320[((x / 2) + input.x_offset) % 210];
|
hvc_latch |= hc_320[((x / 2) + input.x_offset) % 210];
|
||||||
@ -147,6 +158,11 @@ void lightgun_refresh(int port)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (hvc_latch & 0x20000)
|
||||||
|
{
|
||||||
|
/* HVC should be free-running on other lines when latch is disabled (fixes "Gunfight - 3 in 1" randomization) */
|
||||||
|
hvc_latch = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1362,42 +1362,34 @@ unsigned int vdp_hvc_r(unsigned int cycles)
|
|||||||
int vc;
|
int vc;
|
||||||
unsigned int data = hvc_latch;
|
unsigned int data = hvc_latch;
|
||||||
|
|
||||||
/* Check if HVC is frozen */
|
/* Check if HVC latch is enabled */
|
||||||
if (!data)
|
if (data)
|
||||||
{
|
{
|
||||||
/* Cycle-accurate HCounter (Striker, Mickey Mania, Skitchin, Road Rash I,II,III, Sonic 3D Blast...) */
|
/* Mode 5: HV-counters are frozen (cf. lightgun games, Sunset Riders logo) */
|
||||||
data = hctab[cycles % MCYCLES_PER_LINE];
|
if (reg[1] & 0x04)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Mode 5: HV counters are frozen (cf. lightgun games & Sunset Riders) */
|
|
||||||
if (reg[1] & 4)
|
|
||||||
{
|
{
|
||||||
/* check if HVC is really locked or is read within INT2 callback (HVC latch is forced for lightgun emulation, cf. lightgun.c) */
|
|
||||||
if ((reg[0] & 0x02) || (m68k.int_mask == 0x0200))
|
|
||||||
{
|
|
||||||
#ifdef LOGVDP
|
#ifdef LOGVDP
|
||||||
error("[%d(%d)][%d(%d)] HVC read -> 0x%x (%x)\n", v_counter, (cycles/MCYCLES_PER_LINE-1)%lines_per_frame, cycles, cycles%MCYCLES_PER_LINE, data & 0xffff, m68k_get_reg(M68K_REG_PC));
|
error("[%d(%d)][%d(%d)] HVC latch read -> 0x%x (%x)\n", v_counter, (cycles/MCYCLES_PER_LINE-1)%lines_per_frame, cycles, cycles%MCYCLES_PER_LINE, data & 0xffff, m68k_get_reg(M68K_REG_PC));
|
||||||
#endif
|
#endif
|
||||||
/* return latched HVC value */
|
/* return latched HVC value */
|
||||||
return (data & 0xffff);
|
return (data & 0xffff);
|
||||||
}
|
|
||||||
|
|
||||||
/* HV counters should be free-running (fixes "Gunfight - 3 in 1" randomization when Justifiers are enabled) */
|
|
||||||
hvc_latch = 0;
|
|
||||||
data = hctab[cycles % MCYCLES_PER_LINE];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Mode 4: V counter runs normally, H counter is frozen */
|
/* Mode 4: by default, V-counter runs normally & H counter is frozen */
|
||||||
data &= 0xff;
|
data &= 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Cycle-accurate H-Counter (Striker, Mickey Mania, Skitchin, Road Rash I,II,III, Sonic 3D Blast...) */
|
||||||
|
data = hctab[cycles % MCYCLES_PER_LINE];
|
||||||
|
}
|
||||||
|
|
||||||
/* Cycle-accurate VCounter (cycle counter starts from line -1) */
|
/* Cycle-accurate V-Counter (cycle counter starts from line -1) */
|
||||||
vc = (cycles / MCYCLES_PER_LINE) - 1;
|
vc = (cycles / MCYCLES_PER_LINE) - 1;
|
||||||
|
|
||||||
/* VCounter overflow */
|
/* V-Counter overflow */
|
||||||
if (vc > vc_max)
|
if (vc > vc_max)
|
||||||
{
|
{
|
||||||
vc -= lines_per_frame;
|
vc -= lines_per_frame;
|
||||||
@ -1413,6 +1405,7 @@ unsigned int vdp_hvc_r(unsigned int cycles)
|
|||||||
vc = (vc & ~1) | ((vc >> 8) & 1);
|
vc = (vc & ~1) | ((vc >> 8) & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* return H-Counter in LSB & V-Counter in MSB */
|
||||||
data |= ((vc & 0xff) << 8);
|
data |= ((vc & 0xff) << 8);
|
||||||
|
|
||||||
#ifdef LOGVDP
|
#ifdef LOGVDP
|
||||||
|
Loading…
Reference in New Issue
Block a user