mirror of
https://github.com/holub/mame
synced 2025-10-04 08:28:39 +03:00
framework for adding 'gamebooster' (need to figure out how it actually works / maps tho) (#3134)
* fix/tidy tvboy driver (nw) * missed file (nw) * framework for adding 'gamebooster' (need to figure out how it actually works / maps tho) (nw) (code based on zx spectrum expansion port code) * (nw) * lost a line (nw) * allow it to run (nw) * continued work (nw) * mame64 psj -parallel gamebooster -cart tetris now works * rm outdated (nw) * remove unneeded code (nw) * limit accesses, log unexpected ones, might have custom banking (nw) * write bytes in an order that keeps the gb code happier , sml boots (nw)
This commit is contained in:
parent
9560cc97d9
commit
ff61c2c50d
@ -3092,6 +3092,20 @@ if (BUSES["PSX_CONTROLLER"]~=null) then
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/bus/psx/parallel.h,BUSES["PSX_PARALLEL"] = true
|
||||
---------------------------------------------------
|
||||
|
||||
if (BUSES["PSX_PARALLEL"]~=null) then
|
||||
files {
|
||||
MAME_DIR .. "src/devices/bus/psx/parallel.cpp",
|
||||
MAME_DIR .. "src/devices/bus/psx/parallel.h",
|
||||
MAME_DIR .. "src/devices/bus/psx/gamebooster.cpp",
|
||||
MAME_DIR .. "src/devices/bus/psx/gamebooster.h",
|
||||
}
|
||||
end
|
||||
|
||||
---------------------------------------------------
|
||||
--
|
||||
--@src/devices/bus/nasbus/nasbus.h,BUSES["NASBUS"] = true
|
||||
|
@ -721,6 +721,7 @@ BUSES["PLUS4"] = true
|
||||
BUSES["POFO"] = true
|
||||
BUSES["PSI_KEYBOARD"] = true
|
||||
BUSES["PSX_CONTROLLER"] = true
|
||||
BUSES["PSX_PARALLEL"] = true
|
||||
BUSES["QL"] = true
|
||||
BUSES["RS232"] = true
|
||||
BUSES["S100"] = true
|
||||
|
158
src/devices/bus/psx/gamebooster.cpp
Normal file
158
src/devices/bus/psx/gamebooster.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:David Haywood
|
||||
/**********************************************************************
|
||||
|
||||
Datel Game Booster for Playstation 1
|
||||
|
||||
Gameboy emulator with Gameboy cartridge slot
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "gamebooster.h"
|
||||
|
||||
//**************************************************************************
|
||||
// DEVICE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
DEFINE_DEVICE_TYPE(PSX_GAMEBOOSTER, psx_gamebooster_device, "psxgboost", "Datel Game Booster for Playstation")
|
||||
|
||||
//-------------------------------------------------
|
||||
// ROM( psxgboost )
|
||||
//-------------------------------------------------
|
||||
|
||||
ROM_START( psxgboost )
|
||||
ROM_REGION(0x40000, "rom", 0)
|
||||
ROM_LOAD("Game Booster.rom", 0x0000, 0x40000, CRC(c8e459b8) SHA1(c20ab073f61242f37665f12199b95cfa3a83e9fc) )
|
||||
ROM_END
|
||||
|
||||
//-------------------------------------------------
|
||||
// rom_region - device-specific ROM region
|
||||
//-------------------------------------------------
|
||||
|
||||
const tiny_rom_entry *psx_gamebooster_device::device_rom_region() const
|
||||
{
|
||||
return ROM_NAME( psxgboost );
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// LIVE DEVICE
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// psx_gamebooster_device - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
psx_gamebooster_device::psx_gamebooster_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: device_t(mconfig, PSX_GAMEBOOSTER, tag, owner, clock)
|
||||
, psx_parallel_interface(mconfig, *this)
|
||||
, m_rom(*this, "rom")
|
||||
, m_cartslot(*this, "gbslot")
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void psx_gamebooster_device::device_start()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_reset - device-specific reset
|
||||
//-------------------------------------------------
|
||||
|
||||
void psx_gamebooster_device::device_reset()
|
||||
{
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// IMPLEMENTATION
|
||||
//**************************************************************************
|
||||
|
||||
READ16_MEMBER(psx_gamebooster_device::exp_r)
|
||||
{
|
||||
if (offset < 0x20000)
|
||||
{
|
||||
return m_rom->base()[(offset * 2) & 0x3ffff] | (m_rom->base()[((offset * 2) + 1) & 0x3ffff] << 8);
|
||||
}
|
||||
else if (offset < 0x24000)
|
||||
{
|
||||
offset -= 0x20000;
|
||||
uint16_t retval = 0;;
|
||||
|
||||
if (mem_mask & 0xff00) retval |= (m_cartslot->read_rom(space, (offset*2)+1))<<8;
|
||||
if (mem_mask & 0x00ff) retval |= m_cartslot->read_rom(space, (offset*2)+0);
|
||||
|
||||
return retval;
|
||||
}
|
||||
else
|
||||
{
|
||||
logerror("%s: psx_gamebooster_device::exp_r %04x\n", machine().describe_context(), offset*2);
|
||||
}
|
||||
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
WRITE16_MEMBER(psx_gamebooster_device::exp_w)
|
||||
{
|
||||
|
||||
if (offset < 0x20000)
|
||||
{
|
||||
logerror("%s: psx_gamebooster_device::exp_w %04x %04x\n", machine().describe_context(), offset*2, data);
|
||||
}
|
||||
else if (offset < 0x24000)
|
||||
{
|
||||
offset -= 0x20000;
|
||||
logerror("%s: psx_gamebooster_device::exp_w %04x %04x\n", machine().describe_context(), offset*2, data);
|
||||
|
||||
if (mem_mask & 0xff00) m_cartslot->write_bank(space, (offset*2)+1, data>>8);
|
||||
if (mem_mask & 0x00ff) m_cartslot->write_bank(space, (offset*2)+0, data); // send this 2nd or it erases the bank with the above
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
logerror("%s: psx_gamebooster_device::exp_w %04x %04x\n", machine().describe_context(), offset*2, data);
|
||||
}
|
||||
}
|
||||
|
||||
static SLOT_INTERFACE_START(gb_cart)
|
||||
SLOT_INTERFACE_INTERNAL("rom", GB_STD_ROM)
|
||||
SLOT_INTERFACE_INTERNAL("rom_mbc1", GB_ROM_MBC1)
|
||||
SLOT_INTERFACE_INTERNAL("rom_mbc1col", GB_ROM_MBC1)
|
||||
SLOT_INTERFACE_INTERNAL("rom_mbc2", GB_ROM_MBC2)
|
||||
SLOT_INTERFACE_INTERNAL("rom_mbc3", GB_ROM_MBC3)
|
||||
SLOT_INTERFACE_INTERNAL("rom_huc1", GB_ROM_MBC3)
|
||||
SLOT_INTERFACE_INTERNAL("rom_huc3", GB_ROM_MBC3)
|
||||
SLOT_INTERFACE_INTERNAL("rom_mbc5", GB_ROM_MBC5)
|
||||
SLOT_INTERFACE_INTERNAL("rom_mbc6", GB_ROM_MBC6)
|
||||
SLOT_INTERFACE_INTERNAL("rom_mbc7", GB_ROM_MBC7)
|
||||
SLOT_INTERFACE_INTERNAL("rom_tama5", GB_ROM_TAMA5)
|
||||
SLOT_INTERFACE_INTERNAL("rom_mmm01", GB_ROM_MMM01)
|
||||
SLOT_INTERFACE_INTERNAL("rom_m161", GB_ROM_M161)
|
||||
SLOT_INTERFACE_INTERNAL("rom_sachen1", GB_ROM_SACHEN1)
|
||||
SLOT_INTERFACE_INTERNAL("rom_sachen2", GB_ROM_SACHEN2)
|
||||
SLOT_INTERFACE_INTERNAL("rom_wisdom", GB_ROM_WISDOM)
|
||||
SLOT_INTERFACE_INTERNAL("rom_yong", GB_ROM_YONG)
|
||||
SLOT_INTERFACE_INTERNAL("rom_lasama", GB_ROM_LASAMA)
|
||||
SLOT_INTERFACE_INTERNAL("rom_atvrac", GB_ROM_ATVRAC)
|
||||
SLOT_INTERFACE_INTERNAL("rom_camera", GB_STD_ROM)
|
||||
SLOT_INTERFACE_INTERNAL("rom_188in1", GB_ROM_188IN1)
|
||||
SLOT_INTERFACE_INTERNAL("rom_sintax", GB_ROM_SINTAX)
|
||||
SLOT_INTERFACE_INTERNAL("rom_chong", GB_ROM_CHONGWU)
|
||||
SLOT_INTERFACE_INTERNAL("rom_licheng", GB_ROM_LICHENG)
|
||||
SLOT_INTERFACE_INTERNAL("rom_digimon", GB_ROM_DIGIMON)
|
||||
SLOT_INTERFACE_INTERNAL("rom_rock8", GB_ROM_ROCKMAN8)
|
||||
SLOT_INTERFACE_INTERNAL("rom_sm3sp", GB_ROM_SM3SP)
|
||||
// SLOT_INTERFACE_INTERNAL("rom_dkong5", GB_ROM_DKONG5)
|
||||
// SLOT_INTERFACE_INTERNAL("rom_unk01", GB_ROM_UNK01)
|
||||
SLOT_INTERFACE_END
|
||||
|
||||
MACHINE_CONFIG_START(psx_gamebooster_device::device_add_mconfig)
|
||||
/* cartslot */
|
||||
MCFG_GB_CARTRIDGE_ADD("gbslot", gb_cart, nullptr)
|
||||
|
||||
MCFG_SOFTWARE_LIST_ADD("cart_list","gameboy")
|
||||
MCFG_SOFTWARE_LIST_COMPATIBLE_ADD("gbc_list","gbcolor")
|
||||
MACHINE_CONFIG_END
|
49
src/devices/bus/psx/gamebooster.h
Normal file
49
src/devices/bus/psx/gamebooster.h
Normal file
@ -0,0 +1,49 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:David Haywood
|
||||
|
||||
#ifndef MAME_BUS_PSX_GAMEBOOSTER_H
|
||||
#define MAME_BUS_PSX_GAMEBOOSTER_H
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "parallel.h"
|
||||
#include "bus/gameboy/rom.h"
|
||||
#include "bus/gameboy/mbc.h"
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
// ======================> psx_gamebooster_device
|
||||
|
||||
class psx_gamebooster_device :
|
||||
public device_t,
|
||||
public psx_parallel_interface
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
psx_gamebooster_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
|
||||
protected:
|
||||
// device-level overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
// optional information overrides
|
||||
virtual const tiny_rom_entry *device_rom_region() const override;
|
||||
virtual void device_add_mconfig(machine_config &config) override;
|
||||
|
||||
virtual DECLARE_READ16_MEMBER(exp_r) override;
|
||||
virtual DECLARE_WRITE16_MEMBER(exp_w) override;
|
||||
|
||||
private:
|
||||
required_memory_region m_rom;
|
||||
required_device<gb_cart_slot_device> m_cartslot;
|
||||
};
|
||||
|
||||
|
||||
// device type definition
|
||||
DECLARE_DEVICE_TYPE(PSX_GAMEBOOSTER, psx_gamebooster_device)
|
||||
|
||||
#endif // MAME_BUS_PSX_GAMEBOOSTER_H
|
114
src/devices/bus/psx/parallel.cpp
Normal file
114
src/devices/bus/psx/parallel.cpp
Normal file
@ -0,0 +1,114 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:David Haywood
|
||||
/**********************************************************************
|
||||
|
||||
Sony Playstation 'Parallel' slot
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include "emu.h"
|
||||
#include "parallel.h"
|
||||
|
||||
//**************************************************************************
|
||||
// DEVICE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
DEFINE_DEVICE_TYPE(PSX_PARALLEL_SLOT, psx_parallel_slot_device, "psx_parallel_slot", "Playstation Parallel Slot")
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// DEVICE SPECTRUM_EXPANSION CARD INTERFACE
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// psx_parallel_interface - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
psx_parallel_interface::psx_parallel_interface(const machine_config &mconfig, device_t &device)
|
||||
: device_slot_card_interface(mconfig, device)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// ~psx_parallel_interface - destructor
|
||||
//-------------------------------------------------
|
||||
|
||||
psx_parallel_interface::~psx_parallel_interface()
|
||||
{
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// LIVE DEVICE
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// psx_parallel_slot_device - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
psx_parallel_slot_device::psx_parallel_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
|
||||
device_t(mconfig, PSX_PARALLEL_SLOT, tag, owner, clock),
|
||||
device_slot_interface(mconfig, *this),
|
||||
m_card(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// expansion_slot_device - destructor
|
||||
//-------------------------------------------------
|
||||
|
||||
psx_parallel_slot_device::~psx_parallel_slot_device()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void psx_parallel_slot_device::device_start()
|
||||
{
|
||||
m_card = dynamic_cast<psx_parallel_interface *>(get_card_device());
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_reset - device-specific reset
|
||||
//-------------------------------------------------
|
||||
|
||||
void psx_parallel_slot_device::device_reset()
|
||||
{
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// exp_r
|
||||
//-------------------------------------------------
|
||||
|
||||
READ16_MEMBER(psx_parallel_slot_device::exp_r)
|
||||
{
|
||||
if (m_card)
|
||||
return m_card->exp_r(space, offset);
|
||||
else
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// exp_w
|
||||
//-------------------------------------------------
|
||||
|
||||
WRITE16_MEMBER(psx_parallel_slot_device::exp_w)
|
||||
{
|
||||
if (m_card)
|
||||
m_card->exp_w(space, offset, data);
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// SLOT_INTERFACE( spectrum_expansion_devices )
|
||||
//-------------------------------------------------
|
||||
|
||||
|
||||
// slot devices
|
||||
#include "gamebooster.h"
|
||||
|
||||
SLOT_INTERFACE_START( psx_parallel_devices )
|
||||
SLOT_INTERFACE("gamebooster", PSX_GAMEBOOSTER)
|
||||
SLOT_INTERFACE_END
|
||||
|
74
src/devices/bus/psx/parallel.h
Normal file
74
src/devices/bus/psx/parallel.h
Normal file
@ -0,0 +1,74 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:David Haywood
|
||||
|
||||
#ifndef MAME_BUS_PSX_PARALLEL_H
|
||||
#define MAME_BUS_PSX_PARALLEL_H
|
||||
|
||||
#pragma once
|
||||
|
||||
//**************************************************************************
|
||||
// INTERFACE CONFIGURATION MACROS
|
||||
//**************************************************************************
|
||||
|
||||
#define MCFG_PSX_PARALLEL_SLOT_ADD(_tag, _slot_intf, _def_slot) \
|
||||
MCFG_DEVICE_ADD(_tag, PSX_PARALLEL_SLOT, 0) \
|
||||
MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false)
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
// ======================> psx_parallel_slot_device
|
||||
|
||||
class psx_parallel_interface;
|
||||
|
||||
class psx_parallel_slot_device : public device_t, public device_slot_interface
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
psx_parallel_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
|
||||
virtual ~psx_parallel_slot_device();
|
||||
|
||||
DECLARE_READ16_MEMBER(exp_r);
|
||||
DECLARE_WRITE16_MEMBER(exp_w);
|
||||
|
||||
const bool hascard() const
|
||||
{
|
||||
return bool(m_card);
|
||||
};
|
||||
|
||||
protected:
|
||||
// device-level overrides
|
||||
virtual void device_start() override;
|
||||
virtual void device_reset() override;
|
||||
|
||||
psx_parallel_interface *m_card;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
// ======================> psx_parallel_interface
|
||||
|
||||
class psx_parallel_interface : public device_slot_card_interface
|
||||
{
|
||||
public:
|
||||
// construction/destruction
|
||||
virtual ~psx_parallel_interface();
|
||||
|
||||
// reading and writing
|
||||
virtual DECLARE_READ16_MEMBER(exp_r) { return 0xff; }
|
||||
virtual DECLARE_WRITE16_MEMBER(exp_w) { }
|
||||
|
||||
protected:
|
||||
psx_parallel_interface(const machine_config &mconfig, device_t &device);
|
||||
};
|
||||
|
||||
|
||||
// device type definition
|
||||
DECLARE_DEVICE_TYPE(PSX_PARALLEL_SLOT, psx_parallel_slot_device)
|
||||
|
||||
SLOT_INTERFACE_EXTERN( psx_parallel_devices );
|
||||
|
||||
|
||||
#endif // MAME_BUS_PSX_PARALLEL_H
|
@ -12,6 +12,7 @@
|
||||
#include "emu.h"
|
||||
|
||||
#include "bus/psx/ctlrport.h"
|
||||
#include "bus/psx/parallel.h"
|
||||
#include "cpu/m6805/m6805.h"
|
||||
#include "cpu/psx/psx.h"
|
||||
#include "imagedev/chd_cd.h"
|
||||
@ -35,7 +36,8 @@ public:
|
||||
psx1_state(const machine_config &mconfig, device_type type, const char *tag)
|
||||
: driver_device(mconfig, type, tag),
|
||||
m_maincpu(*this, "maincpu"),
|
||||
m_ram(*this, "maincpu:ram")
|
||||
m_ram(*this, "maincpu:ram"),
|
||||
m_parallel(*this, "parallel")
|
||||
{
|
||||
}
|
||||
|
||||
@ -66,6 +68,9 @@ public:
|
||||
void pse(machine_config &config);
|
||||
void psu(machine_config &config);
|
||||
void psj(machine_config &config);
|
||||
|
||||
private:
|
||||
required_device<psx_parallel_slot_device> m_parallel;
|
||||
};
|
||||
|
||||
|
||||
@ -407,6 +412,13 @@ int psx1_state::load_psf(std::vector<uint8_t> buffer)
|
||||
|
||||
READ16_MEMBER(psx1_state::parallel_r)
|
||||
{
|
||||
if (m_parallel->hascard())
|
||||
{
|
||||
uint16_t dat = m_parallel->exp_r(space,offset,mem_mask);
|
||||
return dat;
|
||||
}
|
||||
|
||||
// all this could probably be a fake parallel device instead?
|
||||
const uint16_t bootloader[] =
|
||||
{
|
||||
0x00b0, 0x1f00, 0x694c, 0x6563, 0x736e, 0x6465, 0x6220, 0x2079,
|
||||
@ -431,6 +443,12 @@ READ16_MEMBER(psx1_state::parallel_r)
|
||||
|
||||
WRITE16_MEMBER(psx1_state::parallel_w)
|
||||
{
|
||||
if (m_parallel->hascard())
|
||||
{
|
||||
m_parallel->exp_w(space,offset,data, mem_mask);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_exe_buffer.size() != 0)
|
||||
{
|
||||
if (load_psxexe(m_exe_buffer) ||
|
||||
@ -484,7 +502,7 @@ void psx1_state::cd_dma_write( uint32_t *p_n_psxram, uint32_t n_address, int32_t
|
||||
}
|
||||
|
||||
static ADDRESS_MAP_START( psx_map, AS_PROGRAM, 32, psx1_state )
|
||||
AM_RANGE(0x1f000000, 0x1f0001ff) AM_READWRITE16(parallel_r, parallel_w, 0xffffffff)
|
||||
AM_RANGE(0x1f000000, 0x1f07ffff) AM_READWRITE16(parallel_r, parallel_w, 0xffffffff)
|
||||
ADDRESS_MAP_END
|
||||
|
||||
static ADDRESS_MAP_START( subcpu_map, AS_PROGRAM, 8, psx1_state )
|
||||
@ -523,6 +541,8 @@ MACHINE_CONFIG_START(psx1_state::psj)
|
||||
|
||||
MCFG_SOFTWARE_LIST_ADD("cd_list","psx")
|
||||
|
||||
MCFG_PSX_PARALLEL_SLOT_ADD("parallel", psx_parallel_devices, nullptr)
|
||||
|
||||
MCFG_DEVICE_MODIFY( "maincpu" )
|
||||
MCFG_PSX_CD_READ_HANDLER( DEVREAD8( PSXCD_TAG, psxcd_device, read ) )
|
||||
MCFG_PSX_CD_WRITE_HANDLER( DEVWRITE8( PSXCD_TAG, psxcd_device, write ) )
|
||||
@ -571,6 +591,8 @@ MACHINE_CONFIG_START(psx1_state::pse)
|
||||
|
||||
MCFG_SOFTWARE_LIST_ADD("cd_list","psx")
|
||||
|
||||
MCFG_PSX_PARALLEL_SLOT_ADD("parallel", psx_parallel_devices, nullptr)
|
||||
|
||||
MCFG_DEVICE_MODIFY( "maincpu" )
|
||||
MCFG_PSX_CD_READ_HANDLER( DEVREAD8( PSXCD_TAG, psxcd_device, read ) )
|
||||
MCFG_PSX_CD_WRITE_HANDLER( DEVWRITE8( PSXCD_TAG, psxcd_device, write ) )
|
||||
|
Loading…
Reference in New Issue
Block a user