mirror of
https://github.com/hch12907/orbum.git
synced 2024-06-02 19:38:16 -04:00
Merge branch 'sio2-sio0-impl' into master-merge-sio-impl
This commit is contained in:
commit
6451b92db4
|
@ -229,9 +229,12 @@ set(COMMON_SRC_FILES
|
|||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Sio0/Sio0Registers.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Sio2/RSio2.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Sio2/RSio2.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Sio2/Sio2Packet.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Sio2/Sio2Registers.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Sio2/Sio2Registers.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Sio2/Sio2PortRegisters.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Sio2/Sio2PortRegisters.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Sio2/Sio2Ports.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Sio2/Sio2Ports.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Timers/IopTimersUnitRegisters.cpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Timers/IopTimersUnitRegisters.hpp"
|
||||
"${CMAKE_SOURCE_DIR}/liborbum/src/Resources/Iop/Timers/IopTimersUnits.cpp"
|
||||
|
|
|
@ -268,6 +268,7 @@ struct Constants
|
|||
|
||||
struct SIO2
|
||||
{
|
||||
static constexpr int NUMBER_PORTS = 16;
|
||||
static constexpr double SIO2_CLK_SPEED = 2000000.0; // 2 MHz. From here: https://en.wikipedia.org/wiki/PlayStation_2_technical_specifications.
|
||||
};
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ void CEeTimers::tick_timer(const ControllerEvent::Type ce_type)
|
|||
{
|
||||
auto& r = core->get_resources();
|
||||
|
||||
// Update the timers which are set to count based on the type of event recieved.
|
||||
// Update the timers which are set to count based on the type of event received.
|
||||
for (auto& unit : r.ee.timers.units)
|
||||
{
|
||||
auto _lock = unit.mode->scope_lock();
|
||||
|
|
|
@ -343,26 +343,22 @@ std::optional<uptr> CIopCore::translate_address_data(const uptr virtual_address,
|
|||
{
|
||||
auto& r = core->get_resources();
|
||||
|
||||
#if 0 //defined(BUILD_DEBUG)
|
||||
static const std::pair<uptr, uptr> DEBUG_VA_BREAKPOINT_RANGES[] =
|
||||
{
|
||||
std::make_pair(0xFFFFFFFF, 0xFFFFFFFF)
|
||||
//std::make_pair(0xBF801040, 0xBF801050),
|
||||
//std::make_pair(0xBF808200, 0xBF808300)
|
||||
};
|
||||
#if defined(BUILD_DEBUG)
|
||||
static const std::pair<uptr, uptr> DEBUG_VA_BREAKPOINT_RANGES[] =
|
||||
{
|
||||
std::make_pair(0xBF808200, 0xBF80825C)};
|
||||
|
||||
for (const auto& range : DEBUG_VA_BREAKPOINT_RANGES)
|
||||
{
|
||||
if (virtual_address >= range.first && virtual_address <= range.second)
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) <<
|
||||
boost::format("IOP MMU data breakpoint hit @ cycle = 0x%llX, PC = 0x%08X, VA = 0x%08X (%s).")
|
||||
% DEBUG_LOOP_COUNTER
|
||||
% r.iop.core.r3000.pc.read_uword()
|
||||
% virtual_address
|
||||
% ((rw_access == READ) ? "READ" : "WRITE");
|
||||
}
|
||||
}
|
||||
for (const auto& range : DEBUG_VA_BREAKPOINT_RANGES)
|
||||
{
|
||||
if (virtual_address >= range.first && virtual_address <= range.second)
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << boost::format("IOP MMU data breakpoint hit @ cycle = 0x%llX, PC = 0x%08X, VA = 0x%08X (%s).")
|
||||
% DEBUG_LOOP_COUNTER
|
||||
% r.iop.core.r3000.pc.read_uword()
|
||||
% virtual_address
|
||||
% ((rw_access == READ) ? "READ" : "WRITE");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check if a write is being performed with isolate cache turned on - don't run through the cache.
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include <boost/format.hpp>
|
||||
|
||||
#include "Controller/Iop/Sio0/CSio0.hpp"
|
||||
|
||||
#include "Core.hpp"
|
||||
|
@ -45,10 +47,17 @@ int CSio0::time_to_ticks(const double time_us)
|
|||
|
||||
int CSio0::time_step(const int ticks_available)
|
||||
{
|
||||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.iop.sio0.ctrl;
|
||||
|
||||
// CTRL locked for entire duration.
|
||||
auto _ctrl_lock = ctrl.scope_lock();
|
||||
|
||||
handle_reset_check();
|
||||
handle_irq_check();
|
||||
handle_transfer();
|
||||
|
||||
return ticks_available;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSio0::handle_reset_check()
|
||||
|
@ -56,17 +65,22 @@ void CSio0::handle_reset_check()
|
|||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.iop.sio0.ctrl;
|
||||
auto& stat = r.iop.sio0.stat;
|
||||
auto& data = r.iop.sio0.data;
|
||||
|
||||
// Perform SIO0 reset (all relevant bits).
|
||||
auto _stat_lock = stat.scope_lock();
|
||||
|
||||
// Perform SIO0 reset (all relevant bits and FIFO).
|
||||
if (ctrl.extract_field(Sio0Register_Ctrl::SIO_RESET))
|
||||
{
|
||||
auto _ctrl_lock = ctrl.scope_lock();
|
||||
auto _stat_lock = stat.scope_lock();
|
||||
|
||||
ctrl.insert_field(Sio0Register_Ctrl::RESET_IRQ, 0);
|
||||
stat.insert_field(Sio0Register_Stat::TX_RDY, 1);
|
||||
stat.insert_field(Sio0Register_Stat::TX_EMPTY, 1);
|
||||
stat.insert_field(Sio0Register_Stat::IRQ, 0);
|
||||
|
||||
data.initialise();
|
||||
|
||||
// Now ready to transmit data.
|
||||
// TODO: not totally correct...
|
||||
stat.insert_field(Sio0Register_Stat::TX_RDY1, 1);
|
||||
ctrl.insert_field(Sio0Register_Ctrl::SIO_RESET, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,20 +90,54 @@ void CSio0::handle_irq_check()
|
|||
auto& ctrl = r.iop.sio0.ctrl;
|
||||
auto& stat = r.iop.sio0.stat;
|
||||
|
||||
auto _ctrl_lock = ctrl.scope_lock();
|
||||
auto _stat_lock = stat.scope_lock();
|
||||
|
||||
// Reset IRQ bit if it has been acknowledged by IOP.
|
||||
if (ctrl.extract_field(Sio0Register_Ctrl::RESET_IRQ))
|
||||
{
|
||||
auto _ctrl_lock = ctrl.scope_lock();
|
||||
auto _stat_lock = stat.scope_lock();
|
||||
|
||||
stat.insert_field(Sio0Register_Stat::IRQ, 0);
|
||||
ctrl.insert_field(Sio0Register_Ctrl::RESET_IRQ, 0);
|
||||
}
|
||||
|
||||
// Raise IRQ on the ACK line going high (if enabled).
|
||||
if (ctrl.extract_field(Sio0Register_Ctrl::ACK_INT_EN) && stat.extract_field(Sio0Register_Stat::DSR))
|
||||
stat.insert_field(Sio0Register_Stat::IRQ, 1);
|
||||
|
||||
// Raise IOP INTC IRQ if requested.
|
||||
if (stat.extract_field(Sio0Register_Stat::IRQ))
|
||||
{
|
||||
auto _lock = r.iop.intc.stat.scope_lock();
|
||||
auto _stat_lock = r.iop.intc.stat.scope_lock();
|
||||
r.iop.intc.stat.insert_field(IopIntcRegister_Stat::SIO0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
void CSio0::handle_transfer()
|
||||
{
|
||||
auto& r = core->get_resources();
|
||||
auto& command_queue = r.iop.sio0.data.command_queue;
|
||||
auto& response_queue = r.iop.sio0.data.response_queue;
|
||||
auto& stat = r.iop.sio0.stat;
|
||||
|
||||
// Sequential command/response action.
|
||||
|
||||
auto _stat_lock = stat.scope_lock();
|
||||
|
||||
// Check there is data to send first, TX_RDY2 changes depending on this.
|
||||
if (!command_queue.has_read_available(1))
|
||||
{
|
||||
stat.insert_field(Sio0Register_Stat::TX_RDY2, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response_queue.has_write_available(1))
|
||||
return;
|
||||
|
||||
ubyte cmd = command_queue.read_ubyte();
|
||||
BOOST_LOG(Core::get_logger()) << str(boost::format("~~~~~ SIO0 received cmd: 0x%02X") % cmd);
|
||||
|
||||
// TODO: properly implement, for now just send back 0x00 for all commands received.
|
||||
response_queue.write_ubyte(0);
|
||||
BOOST_LOG(Core::get_logger()) << "~~~~~ SIO0 sent response: 0x00 (not connected)";
|
||||
|
||||
stat.insert_field(Sio0Register_Stat::RX_NONEMPTY, 1);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ public:
|
|||
/// Converts a time duration into the number of ticks that would have occurred.
|
||||
int time_to_ticks(const double time_us);
|
||||
|
||||
/// Steps through the SIO0 state.
|
||||
int time_step(const int ticks_available);
|
||||
|
||||
/// Performs a reset if needed.
|
||||
|
@ -19,4 +20,7 @@ public:
|
|||
|
||||
/// Handles raising IRQ's with the IOP and resetting the state.
|
||||
void handle_irq_check();
|
||||
|
||||
/// Performs a send/receive of a command queued.
|
||||
void handle_transfer();
|
||||
};
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
#include <stdexcept>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include "Controller/Iop/Sio2/CSio2.hpp"
|
||||
|
||||
#include "Common/Constants.hpp"
|
||||
#include "Core.hpp"
|
||||
#include "Resources/RResources.hpp"
|
||||
|
||||
using Direction = Sio2Register_Ctrl::Direction;
|
||||
|
||||
CSio2::CSio2(Core* core) :
|
||||
CController(core)
|
||||
{
|
||||
|
@ -50,50 +55,213 @@ int CSio2::time_step(const int ticks_available)
|
|||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.iop.sio2.ctrl;
|
||||
|
||||
// Lock whole SIO2 operation while processing.
|
||||
auto _ctrl_lock = ctrl.scope_lock();
|
||||
|
||||
if (ctrl.write_latch)
|
||||
handle_ctrl_check();
|
||||
|
||||
handle_port_trasnfer();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSio2::handle_ctrl_check()
|
||||
{
|
||||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.iop.sio2.ctrl;
|
||||
|
||||
if (ctrl.write_latch && !ctrl.transfer_started)
|
||||
{
|
||||
if (ctrl.extract_field(Sio2Register_Ctrl::RESET_DIR) > 0)
|
||||
{
|
||||
// Receive packet. Value should be 0x3BD.
|
||||
if (ctrl.read_uword() != 0x3BD)
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << boost::format("Careful, SIO2 ctrl recv value not normal: 0x%08X")
|
||||
% ctrl.read_uword();
|
||||
}
|
||||
// Reset SIO2 port state and start transfer.
|
||||
ctrl.transfer_port = 0;
|
||||
ctrl.transfer_port_count = 0;
|
||||
ctrl.transfer_started = true;
|
||||
ctrl.transfer_direction = ctrl.get_direction();
|
||||
|
||||
// Clear the direction bit (no idea why... seems to be required).
|
||||
ctrl.insert_field(Sio2Register_Ctrl::RESET_DIR, 0);
|
||||
|
||||
// Raise IOP IRQ.
|
||||
auto _lock = r.iop.intc.stat.scope_lock();
|
||||
r.iop.intc.stat.insert_field(IopIntcRegister_Stat::SIO2, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Send packet. Value should be 0x3BC.
|
||||
if (ctrl.read_uword() != 0x3BC)
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << boost::format("Careful, SIO2 ctrl send value not normal: 0x%08X")
|
||||
% ctrl.read_uword();
|
||||
}
|
||||
|
||||
// Perform SIO0 reset.
|
||||
// Perform SIO0 reset before initiating transfer if in the TX direction.
|
||||
if (ctrl.transfer_direction == Direction::TX)
|
||||
handle_sio0_reset();
|
||||
}
|
||||
|
||||
// Reset direction bits - they are write only (see register description).
|
||||
ctrl.insert_field(Sio2Register_Ctrl::DIRECTION, 0);
|
||||
|
||||
ctrl.write_latch = false;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CSio2::handle_sio0_reset()
|
||||
{
|
||||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.iop.sio0.ctrl;
|
||||
auto _lock = ctrl.scope_lock();
|
||||
|
||||
auto _lock = ctrl.scope_lock();
|
||||
ctrl.insert_field(Sio0Register_Ctrl::SIO_RESET, 1);
|
||||
}
|
||||
|
||||
void CSio2::handle_port_trasnfer()
|
||||
{
|
||||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.iop.sio2.ctrl;
|
||||
auto& ports = r.iop.sio2.ports;
|
||||
auto& port = ports[ctrl.transfer_port];
|
||||
|
||||
if (!ctrl.transfer_started)
|
||||
return;
|
||||
|
||||
auto _port_ctrl3_lock = port.ctrl_3->scope_lock();
|
||||
|
||||
if (!port.ctrl_3->port_transfer_started)
|
||||
{
|
||||
static bool warned = false;
|
||||
|
||||
if (!port.ctrl_3->write_latch)
|
||||
{
|
||||
if (!warned)
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "~~~~~~ SIO2 TX/RX stalled: ctrl3 needs to be set by IOP first (write latch not set)";
|
||||
warned = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
warned = false;
|
||||
|
||||
port.ctrl_3->port_transfer_started = true;
|
||||
port.ctrl_3->write_latch = false;
|
||||
BOOST_LOG(Core::get_logger()) << str(boost::format("~~~~~~ SIO2 TX port %d started") % ctrl.transfer_port);
|
||||
}
|
||||
|
||||
switch (ctrl.transfer_direction)
|
||||
{
|
||||
case Direction::TX:
|
||||
{
|
||||
transfer_data_tx();
|
||||
break;
|
||||
}
|
||||
case Direction::RX:
|
||||
{
|
||||
transfer_data_rx();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Could not determine SIO2 transfer direction.");
|
||||
}
|
||||
}
|
||||
|
||||
void CSio2::transfer_data_tx()
|
||||
{
|
||||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.iop.sio2.ctrl;
|
||||
auto& ports = r.iop.sio2.ports;
|
||||
auto& data_in = r.fifo_tosio2;
|
||||
auto& sio0_data = r.iop.sio0.data;
|
||||
auto& sio0_ctrl = r.iop.sio0.ctrl;
|
||||
auto& sio0_stat = r.iop.sio0.stat;
|
||||
|
||||
auto& port = ports[ctrl.transfer_port];
|
||||
|
||||
size_t cmd_length = port.ctrl_3->extract_field(Sio2PortRegister_Ctrl3::CMDLEN);
|
||||
|
||||
// Send data to the SIO0.
|
||||
if (ctrl.transfer_port_count != cmd_length)
|
||||
{
|
||||
if (!sio0_stat.extract_field(Sio0Register_Stat::TX_RDY1))
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "~~~~~~ SIO2 TX stalled: SIO0 stat.TX_RDY1 not set";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data_in.has_read_available(1))
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "~~~~~~ SIO2 TX stalled: no FIFO data";
|
||||
return;
|
||||
}
|
||||
|
||||
// If it's the first byte, perform some initialisation.
|
||||
if (ctrl.transfer_port_count == 0)
|
||||
{
|
||||
// Set the SIO0 pad port first when the SIO0 is ready.
|
||||
if (!sio0_stat.extract_field(Sio0Register_Stat::TX_RDY2))
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "~~~~~~ SIO2 TX stalled (init): SIO0 stat.TX_RDY2 not set";
|
||||
return;
|
||||
}
|
||||
|
||||
uhword sio0_padport = port.ctrl_3->extract_field(Sio2PortRegister_Ctrl3::PADPORT);
|
||||
|
||||
auto _sio0_ctrl_lock = sio0_ctrl.scope_lock();
|
||||
sio0_ctrl.insert_field(Sio0Register_Ctrl::PORT, sio0_padport);
|
||||
}
|
||||
|
||||
ubyte data = data_in.read_ubyte();
|
||||
sio0_data.write_ubyte(data);
|
||||
ctrl.transfer_port_count += 1;
|
||||
}
|
||||
|
||||
// Finished with this port, move on to next.
|
||||
if (ctrl.transfer_port_count == cmd_length)
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << str(boost::format("~~~~~~ SIO2 TX port %d finished") % ctrl.transfer_port);
|
||||
ctrl.transfer_port += 1;
|
||||
ctrl.transfer_port_count = 0;
|
||||
}
|
||||
|
||||
// All data sent, stop transfering.
|
||||
if (ctrl.transfer_port == Constants::IOP::SIO2::NUMBER_PORTS)
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "~~~~~~ SIO2 TX all ports finished";
|
||||
ctrl.transfer_started = false;
|
||||
}
|
||||
}
|
||||
|
||||
void CSio2::transfer_data_rx()
|
||||
{
|
||||
auto& r = core->get_resources();
|
||||
auto& ctrl = r.iop.sio2.ctrl;
|
||||
auto& ports = r.iop.sio2.ports;
|
||||
auto& data_out = r.fifo_fromsio2;
|
||||
auto& sio0_data = r.iop.sio0.data;
|
||||
auto& sio0_stat = r.iop.sio0.stat;
|
||||
|
||||
auto& port = ports[ctrl.transfer_port];
|
||||
size_t response_length = port.ctrl_3->extract_field(Sio2PortRegister_Ctrl3::BUFSZ);
|
||||
|
||||
// Receive data from the SIO0.
|
||||
if (ctrl.transfer_port_count != response_length)
|
||||
{
|
||||
if (!data_out.has_write_available(1))
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "~~~~~~ SIO2 RX stalled: FIFO full";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sio0_stat.extract_field(Sio0Register_Stat::RX_NONEMPTY))
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "~~~~~~ SIO2 RX stalled: SIO0 stat.RX_NONEMPTY not set";
|
||||
return;
|
||||
}
|
||||
|
||||
ubyte data = sio0_data.read_ubyte();
|
||||
data_out.write_ubyte(data);
|
||||
ctrl.transfer_port_count += 1;
|
||||
}
|
||||
|
||||
// Finished with this port, move on to next.
|
||||
if (ctrl.transfer_port_count == response_length)
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << str(boost::format("~~~~~~ SIO2 RX port %d finished") % ctrl.transfer_port);
|
||||
ctrl.transfer_port += 1;
|
||||
ctrl.transfer_port_count = 0;
|
||||
}
|
||||
|
||||
// All data received, stop transfering, raise IRQ on RX finish.
|
||||
if (ctrl.transfer_port == Constants::IOP::SIO2::NUMBER_PORTS)
|
||||
{
|
||||
BOOST_LOG(Core::get_logger()) << "~~~~~~ SIO2 RX all ports finished";
|
||||
ctrl.transfer_started = false;
|
||||
|
||||
auto& intc_stat = r.iop.intc.stat;
|
||||
auto _intc_lock = intc_stat.scope_lock();
|
||||
intc_stat.insert_field(IopIntcRegister_Stat::SIO2, 1);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
#include "Controller/CController.hpp"
|
||||
|
||||
/// SIO2 controller.
|
||||
/// Basic operation from BIOS perspective:
|
||||
/// 1. Set CTRL register value, for writing.
|
||||
/// 2. For each port, set CTRL 1/2 registers (unused within emulator).
|
||||
/// 3. For each port, set CTRL 3 registers.
|
||||
/// 4. Send command data to the data_in FIFO.
|
||||
/// 5. Turn on the SIO2 DMA channels for reading and writing.
|
||||
/// 6. Set CTRL register value, for reading (based upon result from reading CTRL).
|
||||
/// 7. Repeat.
|
||||
class CSio2 : public CController
|
||||
{
|
||||
public:
|
||||
|
@ -12,8 +21,22 @@ public:
|
|||
/// Converts a time duration into the number of ticks that would have occurred.
|
||||
int time_to_ticks(const double time_us);
|
||||
|
||||
/// Steps through the SIO2 state, initiating transfers with
|
||||
/// the SIO0 for each port enabled.
|
||||
int time_step(const int ticks_available);
|
||||
|
||||
void handle_ctrl_check();
|
||||
|
||||
/// Sets the SIO0 up for a reset.
|
||||
void handle_sio0_reset();
|
||||
|
||||
void handle_port_trasnfer();
|
||||
|
||||
void transfer_data_tx();
|
||||
|
||||
void transfer_data_rx();
|
||||
|
||||
void transfer_set_next_port();
|
||||
|
||||
void transfer_stop(const bool raise_irq);
|
||||
};
|
||||
|
|
|
@ -53,7 +53,7 @@ void CIopTimers::tick_timer(const ControllerEvent::Type ce_type)
|
|||
{
|
||||
auto& r = core->get_resources();
|
||||
|
||||
// Update the timers which are set to count based on the type of event recieved.
|
||||
// Update the timers which are set to count based on the type of event received.
|
||||
for (auto& unit : r.iop.timers.units)
|
||||
{
|
||||
auto _lock = unit->mode.scope_lock();
|
||||
|
|
|
@ -130,7 +130,7 @@ int CSpu2::transfer_data_adma_write(Spu2Core_Base& spu2_core)
|
|||
{
|
||||
// Set 'no data available' magic values for SPU2 registers (done on each try).
|
||||
spu2_core.admas.set_adma_running(false);
|
||||
spu2_core.statx.insert_field(Spu2CoreRegister_Statx::NEEDDATA, 1);
|
||||
spu2_core.statx.insert_field(Spu2CoreRegister_Statx::DREQ, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,7 @@ int CSpu2::transfer_data_adma_write(Spu2Core_Base& spu2_core)
|
|||
uhword data;
|
||||
spu2_core.dma_fifo_queue->read(reinterpret_cast<ubyte*>(&data), NUMBER_BYTES_IN_HWORD);
|
||||
spu2_core.admas.set_adma_running(true);
|
||||
spu2_core.statx.insert_field(Spu2CoreRegister_Statx::NEEDDATA, 0);
|
||||
spu2_core.statx.insert_field(Spu2CoreRegister_Statx::DREQ, 0);
|
||||
|
||||
// Depending on the current transfer count, we are in the left or right sound channel data block (from SPU2-X/Dma.cpp).
|
||||
// Data incoming is in a striped pattern with 0x100 hwords for the left channel, followed by 0x100 hwords for the right channel, repeated.
|
||||
|
@ -186,14 +186,14 @@ int CSpu2::transfer_data_mdma_write(Spu2Core_Base& spu2_core)
|
|||
if (!spu2_core.dma_fifo_queue->has_read_available(NUMBER_BYTES_IN_HWORD))
|
||||
{
|
||||
// Set 'no data available' magic values for SPU2 registers.
|
||||
spu2_core.statx.insert_field(Spu2CoreRegister_Statx::NEEDDATA, 1);
|
||||
spu2_core.statx.insert_field(Spu2CoreRegister_Statx::DREQ, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read in data and set 'data available' magic values for SPU2 registers.
|
||||
uhword data;
|
||||
spu2_core.dma_fifo_queue->read(reinterpret_cast<ubyte*>(&data), NUMBER_BYTES_IN_HWORD);
|
||||
spu2_core.statx.insert_field(Spu2CoreRegister_Statx::NEEDDATA, 0);
|
||||
spu2_core.statx.insert_field(Spu2CoreRegister_Statx::DREQ, 0);
|
||||
|
||||
// Calculate write address. Make sure address is not outside 2MB limit (remember, we are addressing by hwords).
|
||||
uhword tsal_addr_lo = spu2_core.tsal.read_uhword();
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
/// SPU2 system logic.
|
||||
/// 2 steps involved:
|
||||
/// 1. Check through the DMA channels, and send/recieve data as necessary.
|
||||
/// 1. Check through the DMA channels, and send/receive data as necessary.
|
||||
/// 2. Process audio and play samples at 44.1 or 48.0 kHz.
|
||||
class CSpu2 : public CController
|
||||
{
|
||||
|
|
|
@ -48,7 +48,7 @@ CoreOptions CoreOptions::make_default()
|
|||
"",
|
||||
"",
|
||||
"",
|
||||
200,
|
||||
10,
|
||||
4, //std::thread::hardware_concurrency() - 1,
|
||||
|
||||
1.0,
|
||||
|
|
|
@ -44,8 +44,8 @@ struct EeDmacConstants
|
|||
"GIF",
|
||||
"fromIPU",
|
||||
"toIPU",
|
||||
"SIF0",
|
||||
"SIF1",
|
||||
"SIF0 (from IOP)",
|
||||
"SIF1 (to IOP)",
|
||||
"SIF2",
|
||||
"fromSPR",
|
||||
"toSPR"};
|
||||
|
|
|
@ -15,8 +15,8 @@ struct IopDmacConstants
|
|||
"OTClear",
|
||||
"SPU2c1",
|
||||
"DEV9",
|
||||
"SIF0",
|
||||
"SIF1",
|
||||
"SIF0 (to EE)",
|
||||
"SIF1 (from EE)",
|
||||
"toSIO2",
|
||||
"fromSIO2",
|
||||
"Undefined"};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
/// Responsible for communication with controllers and memory cards.
|
||||
struct RSio0
|
||||
{
|
||||
SizedHwordRegister data;
|
||||
Sio0Register_Data data; // Hybrid FIFO port - can read and write to this port simultaneously.
|
||||
Sio0Register_Stat stat;
|
||||
SizedHwordRegister mode;
|
||||
Sio0Register_Ctrl ctrl;
|
||||
|
|
|
@ -10,4 +10,36 @@ void Sio0Register_Stat::byte_bus_write_uhword(const BusContext context, const us
|
|||
{
|
||||
auto _lock = scope_lock();
|
||||
SizedHwordRegister::byte_bus_write_uhword(context, offset, value);
|
||||
}
|
||||
}
|
||||
|
||||
Sio0Register_Data::Sio0Register_Data() :
|
||||
stat(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void Sio0Register_Data::initialise()
|
||||
{
|
||||
command_queue.initialise();
|
||||
response_queue.initialise();
|
||||
}
|
||||
|
||||
ubyte Sio0Register_Data::read_ubyte()
|
||||
{
|
||||
ubyte data = response_queue.read_ubyte();
|
||||
|
||||
// Toggle the SIO0 FIFO empty bit.
|
||||
auto _lock = stat->scope_lock();
|
||||
stat->insert_field(Sio0Register_Stat::RX_NONEMPTY, response_queue.has_read_available(1) ? 1 : 0);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void Sio0Register_Data::write_ubyte(const ubyte data)
|
||||
{
|
||||
command_queue.write_ubyte(data);
|
||||
|
||||
// Toggle the SIO0 FIFO full bit. Also set unset the TX_RDY2 (ie: transfer not finished bit).
|
||||
auto _lock = stat->scope_lock();
|
||||
stat->insert_field(Sio0Register_Stat::TX_RDY1, command_queue.has_write_available(1) ? 1 : 0); // TODO: this is not right...
|
||||
stat->insert_field(Sio0Register_Stat::TX_RDY2, 0);
|
||||
}
|
||||
|
|
|
@ -1,40 +1,67 @@
|
|||
#include "Common/Types/Bitfield.hpp"
|
||||
#include "Common/Types/FifoQueue/DmaFifoQueue.hpp"
|
||||
#include "Common/Types/Register/ByteRegister.hpp"
|
||||
#include "Common/Types/Register/SizedHwordRegister.hpp"
|
||||
#include "Common/Types/ScopeLock.hpp"
|
||||
|
||||
/// SIO0 ctrl register.
|
||||
/// Needs to be accessed atomically by the SIO0 and SIO2 within emulator.
|
||||
/// Assumed to be similar to the PSX SIO (pad/mc), consult no$psx docs.
|
||||
class Sio0Register_Ctrl : public SizedHwordRegister, public ScopeLock
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield TX_PERM = Bitfield(0, 1);
|
||||
static constexpr Bitfield DTR = Bitfield(1, 1);
|
||||
static constexpr Bitfield RX_PERM = Bitfield(2, 1);
|
||||
static constexpr Bitfield BREAK = Bitfield(3, 1);
|
||||
static constexpr Bitfield RESET_IRQ = Bitfield(4, 1); // This is like a "master controller has acknowledged IRQ" bit, don't be confused with SIO_RESET.
|
||||
static constexpr Bitfield RTS = Bitfield(5, 1);
|
||||
static constexpr Bitfield SIO_RESET = Bitfield(6, 1); // This is like a global SIO reset (see no$psx docs).
|
||||
static constexpr Bitfield TXEN = Bitfield(0, 1); // Enable SIO0 transmit.
|
||||
static constexpr Bitfield DTR = Bitfield(1, 1); // (Data terminal ready) SIO is ready to receive data from pad/mc selected by PORT.
|
||||
static constexpr Bitfield RXEN = Bitfield(2, 1); // 0 means normal operation, RXEN is always enabled otherwise? (not sure what 1: "force" means)
|
||||
static constexpr Bitfield RESET_IRQ = Bitfield(4, 1); // This is like a "master controller has acknowledged IRQ" bit, don't be confused with SIO_RESET.
|
||||
static constexpr Bitfield SIO_RESET = Bitfield(6, 1); // This is like a global SIO reset (see no$psx docs).
|
||||
static constexpr Bitfield RX_INT_MODE = Bitfield(8, 2); // Receive buffer interrupt mode, used with bit 11.
|
||||
static constexpr Bitfield TX_INT_EN = Bitfield(10, 1); // Transmit interrupt enable.
|
||||
static constexpr Bitfield RX_INT_EN = Bitfield(11, 1); // receive interrupt enable, interrupt when RX_INT_MODE bytes have been received.
|
||||
static constexpr Bitfield ACK_INT_EN = Bitfield(12, 1); // Pad/mc acknowledge line interrupt enable (used with STAT.DSR).
|
||||
static constexpr Bitfield PORT = Bitfield(13, 1); // Currently selected port (0/1).
|
||||
|
||||
/// Scope locked bus writes.
|
||||
void byte_bus_write_uhword(const BusContext context, const usize offset, const uhword value) override;
|
||||
};
|
||||
|
||||
/// SIO0 ctrl register.
|
||||
/// Needs to be accessed atomically by the SIO2 within emulator.
|
||||
/// SIO0 stat register.
|
||||
/// Needs to be accessed atomically by the SIO0 and SIO2 within emulator.
|
||||
/// Assumed to be similar to the PSX SIO (pad/mc), consult no$psx docs.
|
||||
class Sio0Register_Stat : public SizedHwordRegister, public ScopeLock
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield TX_RDY = Bitfield(0, 1);
|
||||
static constexpr Bitfield RX_RDY = Bitfield(1, 1);
|
||||
static constexpr Bitfield TX_EMPTY = Bitfield(2, 1);
|
||||
static constexpr Bitfield TX_RDY1 = Bitfield(0, 1);
|
||||
static constexpr Bitfield RX_NONEMPTY = Bitfield(1, 1); // RX FIFO non-empty.
|
||||
static constexpr Bitfield TX_RDY2 = Bitfield(2, 1);
|
||||
static constexpr Bitfield PARITY_ERR = Bitfield(3, 1);
|
||||
static constexpr Bitfield RX_OVERRUN = Bitfield(4, 1);
|
||||
static constexpr Bitfield FRAMING_ERR = Bitfield(5, 1);
|
||||
static constexpr Bitfield SYNC_DETECT = Bitfield(6, 1);
|
||||
static constexpr Bitfield DSR = Bitfield(7, 1);
|
||||
static constexpr Bitfield CTS = Bitfield(8, 1);
|
||||
static constexpr Bitfield IRQ = Bitfield(9, 1);
|
||||
static constexpr Bitfield DSR = Bitfield(7, 1); // (Data set ready) Acknowledge input line level by pad/mc.
|
||||
static constexpr Bitfield IRQ = Bitfield(9, 1); // IRQ line, setting this causes an interrupt to fire.
|
||||
|
||||
/// Scope locked bus writes.
|
||||
void byte_bus_write_uhword(const BusContext context, const usize offset, const uhword value) override;
|
||||
};
|
||||
};
|
||||
/// SIO0 data "register".
|
||||
/// This is a hybrid FIFO port, where writing and reading access 2 different
|
||||
/// FIFO queues. Tx direction means from SIO2 to SIO0, Rx direction means from
|
||||
/// SIO0 to SIO2.
|
||||
class Sio0Register_Data : public ByteRegister
|
||||
{
|
||||
public:
|
||||
Sio0Register_Data();
|
||||
|
||||
void initialise() override;
|
||||
|
||||
ubyte read_ubyte() override;
|
||||
void write_ubyte(const ubyte value) override;
|
||||
|
||||
/// FIFO queues.
|
||||
/// These queues are meant to be accessed directly from the SIO0 only, for
|
||||
/// all other components, this is meant to be accessed as a register.
|
||||
DmaFifoQueue<> command_queue;
|
||||
DmaFifoQueue<> response_queue;
|
||||
|
||||
/// Reference to the SIO0 stat register, needed to change status bits
|
||||
/// depending on the different FIFO queue states (tx full/rx empty).
|
||||
Sio0Register_Stat* stat;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "Resources/Iop/Sio2/RSio2.hpp"
|
||||
|
||||
RSio2::RSio2() :
|
||||
recv1(0x1D100),
|
||||
recv2(0xF, true)
|
||||
{
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "Common/Constants.hpp"
|
||||
#include "Common/Types/FifoQueue/DmaFifoQueue.hpp"
|
||||
#include "Common/Types/Register/SizedWordRegister.hpp"
|
||||
#include "Resources/Iop/Sio2/Sio2Ports.hpp"
|
||||
#include "Resources/Iop/Sio2/Sio2Registers.hpp"
|
||||
|
||||
/// SIO2 resources.
|
||||
|
@ -12,37 +14,28 @@ struct RSio2
|
|||
{
|
||||
RSio2();
|
||||
|
||||
DmaFifoQueue<> data_fifo; // Fifo queue used for sending and receiving data (can change direction).
|
||||
/// SIO2 ports (16 total).
|
||||
Sio2Port_Full port_0;
|
||||
Sio2Port_Full port_1;
|
||||
Sio2Port_Full port_2;
|
||||
Sio2Port_Full port_3;
|
||||
Sio2Port_Slim port_4;
|
||||
Sio2Port_Slim port_5;
|
||||
Sio2Port_Slim port_6;
|
||||
Sio2Port_Slim port_7;
|
||||
Sio2Port_Slim port_8;
|
||||
Sio2Port_Slim port_9;
|
||||
Sio2Port_Slim port_a;
|
||||
Sio2Port_Slim port_b;
|
||||
Sio2Port_Slim port_c;
|
||||
Sio2Port_Slim port_d;
|
||||
Sio2Port_Slim port_e;
|
||||
Sio2Port_Slim port_f;
|
||||
Sio2Port ports[Constants::IOP::SIO2::NUMBER_PORTS];
|
||||
|
||||
SizedWordRegister port0_ctrl3; // TODO: figure out these names properly.
|
||||
SizedWordRegister port1_ctrl3;
|
||||
SizedWordRegister port2_ctrl3;
|
||||
SizedWordRegister port3_ctrl3;
|
||||
SizedWordRegister port4_ctrl3;
|
||||
SizedWordRegister port5_ctrl3;
|
||||
SizedWordRegister port6_ctrl3;
|
||||
SizedWordRegister port7_ctrl3;
|
||||
SizedWordRegister port8_ctrl3;
|
||||
SizedWordRegister port9_ctrl3;
|
||||
SizedWordRegister porta_ctrl3;
|
||||
SizedWordRegister portb_ctrl3;
|
||||
SizedWordRegister portc_ctrl3;
|
||||
SizedWordRegister portd_ctrl3;
|
||||
SizedWordRegister porte_ctrl3;
|
||||
SizedWordRegister portf_ctrl3;
|
||||
|
||||
SizedWordRegister port0_ctrl1;
|
||||
SizedWordRegister port0_ctrl2;
|
||||
SizedWordRegister port1_ctrl1;
|
||||
SizedWordRegister port1_ctrl2;
|
||||
SizedWordRegister port2_ctrl1;
|
||||
SizedWordRegister port2_ctrl2;
|
||||
SizedWordRegister port3_ctrl1;
|
||||
SizedWordRegister port3_ctrl2;
|
||||
Sio2Register_Data data_in;
|
||||
Sio2Register_Data data_out;
|
||||
/// Common registers.
|
||||
Sio2Register_Ctrl ctrl;
|
||||
SizedWordRegister recv1;
|
||||
SizedWordRegister recv1; // TODO: for now, returns device unplugged magic value (0x1D100).
|
||||
SizedWordRegister recv2; // Constant 0xF value.
|
||||
SizedWordRegister recv3;
|
||||
SizedWordRegister register_8278;
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
Describes a DMA argument type within a SIO2 packet.
|
||||
For both in and out arguments.
|
||||
TODO: document properly after I have figured it out.
|
||||
*/
|
||||
// struct SIO2DmaArgument_t
|
||||
// {
|
||||
// uword mAddress;
|
||||
// size_t mSize;
|
||||
// size_t mCount;
|
||||
// };
|
||||
|
||||
/*
|
||||
Describes a SIO2 packet.
|
||||
See IopSio2.h and also the PS2SDK: https://github.com/ps2dev/ps2sdk/blob/master/iop/system/sio2log/include/sio2man.h.
|
||||
TODO: document properly after I have figured it out.
|
||||
*/
|
||||
// struct SIO2Packet_t
|
||||
// {
|
||||
// uword mRecieveValue0;
|
||||
// uword mSendValues0[4];
|
||||
// uword mSendValues1[4];
|
||||
// uword mRecieveValue1;
|
||||
// uword mSendValues2[16];
|
||||
// uword mRecieveValue2;
|
||||
// size_t mSendSize;
|
||||
// size_t mRecieveSize;
|
||||
// uptr mSendAddress;
|
||||
// uptr mRecieveAddress;
|
||||
|
||||
// SIO2DmaArgument_t mInDmaArgument;
|
||||
// SIO2DmaArgument_t mOutDmaArgument;
|
||||
// };
|
21
liborbum/src/Resources/Iop/Sio2/Sio2PortRegisters.cpp
Normal file
21
liborbum/src/Resources/Iop/Sio2/Sio2PortRegisters.cpp
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include "Resources/Iop/Sio2/Sio2PortRegisters.hpp"
|
||||
|
||||
#include "Core.hpp"
|
||||
|
||||
Sio2PortRegister_Ctrl3::Sio2PortRegister_Ctrl3() :
|
||||
write_latch(false),
|
||||
port_transfer_started(false)
|
||||
{
|
||||
}
|
||||
|
||||
void Sio2PortRegister_Ctrl3::byte_bus_write_uword(const BusContext context, const usize offset, const uword value)
|
||||
{
|
||||
auto _lock = scope_lock();
|
||||
|
||||
if (write_latch)
|
||||
BOOST_LOG(Core::get_logger()) << "SIO2 CTRL3 write latch was already set - please check (might be ok)!";
|
||||
|
||||
write_uword(value);
|
||||
|
||||
write_latch = true;
|
||||
}
|
36
liborbum/src/Resources/Iop/Sio2/Sio2PortRegisters.hpp
Normal file
36
liborbum/src/Resources/Iop/Sio2/Sio2PortRegisters.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#include "Common/Types/Bitfield.hpp"
|
||||
#include "Common/Types/Register/SizedWordRegister.hpp"
|
||||
#include "Common/Types/ScopeLock.hpp"
|
||||
|
||||
class Sio2PortRegister_Ctrl1 : public SizedWordRegister
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
class Sio2PortRegister_Ctrl2 : public SizedWordRegister
|
||||
{
|
||||
public:
|
||||
};
|
||||
|
||||
// Infomation used from PCSX2: Sio.cpp and IopSio2.cpp.
|
||||
class Sio2PortRegister_Ctrl3 : public SizedWordRegister, public ScopeLock
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield PADPORT = Bitfield(0, 1); // Port 0/1 that the SIO should be configured for.
|
||||
static constexpr Bitfield CMDLEN = Bitfield(8, 9); // Pad/mc command length (send to SIO0 length)
|
||||
static constexpr Bitfield BUFSZ = Bitfield(18, 9); // Pad/mc response length (receive from SIO0 length) (TODO: assumed to always interrupt after).
|
||||
// Note that PCSX2 appears to not use this properly, so maybe this isn't correct.
|
||||
|
||||
Sio2PortRegister_Ctrl3();
|
||||
|
||||
/// Write latch, used to signal when a port transfer should start.
|
||||
/// Set upon bus writes.
|
||||
bool write_latch;
|
||||
|
||||
/// Port transfer status, used to signify if a transfer has started on this
|
||||
/// port. Changed by the SIO2 controller.
|
||||
bool port_transfer_started;
|
||||
|
||||
/// Scope locked bus writes.
|
||||
void byte_bus_write_uword(const BusContext context, const usize offset, const uword value) override;
|
||||
};
|
8
liborbum/src/Resources/Iop/Sio2/Sio2Ports.cpp
Normal file
8
liborbum/src/Resources/Iop/Sio2/Sio2Ports.cpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include "Resources/Iop/Sio2/Sio2Ports.hpp"
|
||||
|
||||
Sio2Port::Sio2Port() :
|
||||
ctrl_1(nullptr),
|
||||
ctrl_2(nullptr),
|
||||
ctrl_3(nullptr)
|
||||
{
|
||||
}
|
28
liborbum/src/Resources/Iop/Sio2/Sio2Ports.hpp
Normal file
28
liborbum/src/Resources/Iop/Sio2/Sio2Ports.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "Resources/Iop/Sio2/Sio2PortRegisters.hpp"
|
||||
|
||||
/// Describes an abstract SIO2 port.
|
||||
class Sio2Port
|
||||
{
|
||||
public:
|
||||
Sio2Port();
|
||||
|
||||
Sio2PortRegister_Ctrl1* ctrl_1;
|
||||
Sio2PortRegister_Ctrl2* ctrl_2;
|
||||
Sio2PortRegister_Ctrl3* ctrl_3;
|
||||
};
|
||||
|
||||
/// Port that contains all 3 CTRL registers.
|
||||
class Sio2Port_Full
|
||||
{
|
||||
public:
|
||||
Sio2PortRegister_Ctrl1 ctrl_1;
|
||||
Sio2PortRegister_Ctrl2 ctrl_2;
|
||||
Sio2PortRegister_Ctrl3 ctrl_3;
|
||||
};
|
||||
|
||||
/// Port that contains only 1 CTRL register (ctrl_3).
|
||||
class Sio2Port_Slim
|
||||
{
|
||||
public:
|
||||
Sio2PortRegister_Ctrl3 ctrl_3;
|
||||
};
|
|
@ -1,12 +1,35 @@
|
|||
#include <boost/format.hpp>
|
||||
|
||||
#include "Resources/Iop/Sio2/Sio2Registers.hpp"
|
||||
|
||||
#include "Core.hpp"
|
||||
|
||||
using Direction = Sio2Register_Ctrl::Direction;
|
||||
|
||||
Sio2Register_Ctrl::Sio2Register_Ctrl() :
|
||||
transfer_started(false),
|
||||
transfer_port(0),
|
||||
transfer_port_count(0),
|
||||
write_latch(false)
|
||||
{
|
||||
}
|
||||
|
||||
Direction Sio2Register_Ctrl::get_direction()
|
||||
{
|
||||
auto value = extract_field(DIRECTION);
|
||||
|
||||
switch (value & 0xF)
|
||||
{
|
||||
case 0x1:
|
||||
case 0xD:
|
||||
return Direction::RX;
|
||||
case 0xC:
|
||||
return Direction::TX;
|
||||
default:
|
||||
throw std::runtime_error(str(boost::format("Unknown SIO2 ctrl value: 0x%08X.") % value));
|
||||
}
|
||||
}
|
||||
|
||||
void Sio2Register_Ctrl::byte_bus_write_uword(const BusContext context, const usize offset, const uword value)
|
||||
{
|
||||
auto _lock = scope_lock();
|
||||
|
@ -18,23 +41,3 @@ void Sio2Register_Ctrl::byte_bus_write_uword(const BusContext context, const usi
|
|||
|
||||
write_latch = true;
|
||||
}
|
||||
|
||||
Sio2Register_Data::Sio2Register_Data() :
|
||||
data_fifo(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void Sio2Register_Data::initialise()
|
||||
{
|
||||
data_fifo->initialise();
|
||||
}
|
||||
|
||||
ubyte Sio2Register_Data::read_ubyte()
|
||||
{
|
||||
return data_fifo->read_ubyte();
|
||||
}
|
||||
|
||||
void Sio2Register_Data::write_ubyte(const ubyte value)
|
||||
{
|
||||
data_fifo->write_ubyte(value);
|
||||
}
|
||||
|
|
|
@ -6,14 +6,23 @@
|
|||
#include "Common/Types/ScopeLock.hpp"
|
||||
|
||||
/// SIO2 CTRL Register.
|
||||
/// RESET_DIR is a reset SIO2 or SIO flag in TX/RX direction (?).
|
||||
/// This seems like a bit of magic to me, no documentation really.
|
||||
/// Looking into the bios, it changes between OR'ing 0xC or 0x1 with 0x3BC, which works with what PCSX2 says.
|
||||
/// (ie: 0xC OR'd has no effect, 0x1 OR'd makes it 0x3BD.)
|
||||
/// Bits 0, 2, 3 appear to be write only that toggle the send or receive mode.
|
||||
/// Not sure about bit 1, but assuming it is also a write only bit (not used
|
||||
/// elsewhere). If bits 2 & 3 are set (ie: byte 0xC written), this is TX
|
||||
/// (to SIO0) mode. If bit 0 is set (ie: byte 0x1 or 0xD written), this is RX
|
||||
/// (from SIO0) mode. 0xC, 0x1 and 0xD are the normal values written by the IOP.
|
||||
class Sio2Register_Ctrl : public SizedWordRegister, public ScopeLock
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield RESET_DIR = Bitfield(0, 1);
|
||||
/// SIO2 transfer direction to/from SIO0.
|
||||
enum class Direction
|
||||
{
|
||||
TX,
|
||||
RX,
|
||||
};
|
||||
|
||||
static constexpr Bitfield DIRECTION = Bitfield(0, 4); // Direction bits - not exactly sure what every bit means.
|
||||
// One of the bits is probably the reset SIO0 bit.
|
||||
|
||||
Sio2Register_Ctrl();
|
||||
|
||||
|
@ -21,24 +30,15 @@ public:
|
|||
/// Sets the write latch on bus write, throws error when it has already been set.
|
||||
void byte_bus_write_uword(const BusContext context, const usize offset, const uword value) override;
|
||||
|
||||
/// Write latch.
|
||||
/// Set to true on write, cleared by the system logic when the command has been processed.
|
||||
/// Extracts the set direction from the register.
|
||||
Direction get_direction();
|
||||
|
||||
/// Current transfer status to/from SIO0.
|
||||
bool transfer_started;
|
||||
size_t transfer_port;
|
||||
size_t transfer_port_count;
|
||||
Direction transfer_direction;
|
||||
|
||||
/// Write latch, set to true on bus write, cleared by the controller.
|
||||
bool write_latch;
|
||||
};
|
||||
|
||||
/// Data fifo port register.
|
||||
/// Used as an interface by the SIO2 to transmit data.
|
||||
class Sio2Register_Data : public ByteRegister
|
||||
{
|
||||
public:
|
||||
Sio2Register_Data();
|
||||
|
||||
void initialise() override;
|
||||
|
||||
/// Reads and writes to the data fifo queue.
|
||||
ubyte read_ubyte() override;
|
||||
void write_ubyte(const ubyte value) override;
|
||||
|
||||
/// Reference to the data fifo queue.
|
||||
DmaFifoQueue<>* data_fifo;
|
||||
};
|
||||
|
|
|
@ -1642,32 +1642,32 @@ void initialise_iop(RResources* r)
|
|||
r->iop.bus.map(0x1F80104C, &r->iop.sio0.ctrl);
|
||||
|
||||
// SIO2 Registers.
|
||||
r->iop.bus.map(0x1F808200, &r->iop.sio2.port0_ctrl3);
|
||||
r->iop.bus.map(0x1F808204, &r->iop.sio2.port1_ctrl3);
|
||||
r->iop.bus.map(0x1F808208, &r->iop.sio2.port2_ctrl3);
|
||||
r->iop.bus.map(0x1F80820C, &r->iop.sio2.port3_ctrl3);
|
||||
r->iop.bus.map(0x1F808210, &r->iop.sio2.port4_ctrl3);
|
||||
r->iop.bus.map(0x1F808214, &r->iop.sio2.port5_ctrl3);
|
||||
r->iop.bus.map(0x1F808218, &r->iop.sio2.port6_ctrl3);
|
||||
r->iop.bus.map(0x1F80821C, &r->iop.sio2.port7_ctrl3);
|
||||
r->iop.bus.map(0x1F808220, &r->iop.sio2.port8_ctrl3);
|
||||
r->iop.bus.map(0x1F808224, &r->iop.sio2.port9_ctrl3);
|
||||
r->iop.bus.map(0x1F808228, &r->iop.sio2.porta_ctrl3);
|
||||
r->iop.bus.map(0x1F80822C, &r->iop.sio2.portb_ctrl3);
|
||||
r->iop.bus.map(0x1F808230, &r->iop.sio2.portc_ctrl3);
|
||||
r->iop.bus.map(0x1F808234, &r->iop.sio2.portd_ctrl3);
|
||||
r->iop.bus.map(0x1F808238, &r->iop.sio2.porte_ctrl3);
|
||||
r->iop.bus.map(0x1F80823C, &r->iop.sio2.portf_ctrl3);
|
||||
r->iop.bus.map(0x1F808240, &r->iop.sio2.port0_ctrl1);
|
||||
r->iop.bus.map(0x1F808244, &r->iop.sio2.port0_ctrl2);
|
||||
r->iop.bus.map(0x1F808248, &r->iop.sio2.port1_ctrl1);
|
||||
r->iop.bus.map(0x1F80824C, &r->iop.sio2.port1_ctrl2);
|
||||
r->iop.bus.map(0x1F808250, &r->iop.sio2.port2_ctrl1);
|
||||
r->iop.bus.map(0x1F808254, &r->iop.sio2.port2_ctrl2);
|
||||
r->iop.bus.map(0x1F808258, &r->iop.sio2.port3_ctrl1);
|
||||
r->iop.bus.map(0x1F80825C, &r->iop.sio2.port3_ctrl2);
|
||||
r->iop.bus.map(0x1F808260, &r->iop.sio2.data_in);
|
||||
r->iop.bus.map(0x1F808264, &r->iop.sio2.data_out);
|
||||
r->iop.bus.map(0x1F808200, &r->iop.sio2.port_0.ctrl_3);
|
||||
r->iop.bus.map(0x1F808204, &r->iop.sio2.port_1.ctrl_3);
|
||||
r->iop.bus.map(0x1F808208, &r->iop.sio2.port_2.ctrl_3);
|
||||
r->iop.bus.map(0x1F80820C, &r->iop.sio2.port_3.ctrl_3);
|
||||
r->iop.bus.map(0x1F808210, &r->iop.sio2.port_4.ctrl_3);
|
||||
r->iop.bus.map(0x1F808214, &r->iop.sio2.port_5.ctrl_3);
|
||||
r->iop.bus.map(0x1F808218, &r->iop.sio2.port_6.ctrl_3);
|
||||
r->iop.bus.map(0x1F80821C, &r->iop.sio2.port_7.ctrl_3);
|
||||
r->iop.bus.map(0x1F808220, &r->iop.sio2.port_8.ctrl_3);
|
||||
r->iop.bus.map(0x1F808224, &r->iop.sio2.port_9.ctrl_3);
|
||||
r->iop.bus.map(0x1F808228, &r->iop.sio2.port_a.ctrl_3);
|
||||
r->iop.bus.map(0x1F80822C, &r->iop.sio2.port_b.ctrl_3);
|
||||
r->iop.bus.map(0x1F808230, &r->iop.sio2.port_c.ctrl_3);
|
||||
r->iop.bus.map(0x1F808234, &r->iop.sio2.port_d.ctrl_3);
|
||||
r->iop.bus.map(0x1F808238, &r->iop.sio2.port_e.ctrl_3);
|
||||
r->iop.bus.map(0x1F80823C, &r->iop.sio2.port_f.ctrl_3);
|
||||
r->iop.bus.map(0x1F808240, &r->iop.sio2.port_0.ctrl_1);
|
||||
r->iop.bus.map(0x1F808244, &r->iop.sio2.port_0.ctrl_2);
|
||||
r->iop.bus.map(0x1F808248, &r->iop.sio2.port_1.ctrl_1);
|
||||
r->iop.bus.map(0x1F80824C, &r->iop.sio2.port_1.ctrl_2);
|
||||
r->iop.bus.map(0x1F808250, &r->iop.sio2.port_2.ctrl_1);
|
||||
r->iop.bus.map(0x1F808254, &r->iop.sio2.port_2.ctrl_2);
|
||||
r->iop.bus.map(0x1F808258, &r->iop.sio2.port_3.ctrl_1);
|
||||
r->iop.bus.map(0x1F80825C, &r->iop.sio2.port_3.ctrl_2);
|
||||
r->iop.bus.map(0x1F808260, &r->fifo_tosio2);
|
||||
r->iop.bus.map(0x1F808264, &r->fifo_fromsio2);
|
||||
r->iop.bus.map(0x1F808268, &r->iop.sio2.ctrl);
|
||||
r->iop.bus.map(0x1F80826C, &r->iop.sio2.recv1);
|
||||
r->iop.bus.map(0x1F808270, &r->iop.sio2.recv2);
|
||||
|
@ -1725,8 +1725,39 @@ void initialise_iop_timers(RResources* r)
|
|||
|
||||
void initialise_iop_sio2(RResources* r)
|
||||
{
|
||||
r->iop.sio2.data_in.data_fifo = &r->iop.sio2.data_fifo;
|
||||
r->iop.sio2.data_out.data_fifo = &r->iop.sio2.data_fifo;
|
||||
r->iop.sio2.ports[0].ctrl_1 = &r->iop.sio2.port_0.ctrl_1;
|
||||
r->iop.sio2.ports[0].ctrl_2 = &r->iop.sio2.port_0.ctrl_2;
|
||||
r->iop.sio2.ports[0].ctrl_3 = &r->iop.sio2.port_0.ctrl_3;
|
||||
|
||||
r->iop.sio2.ports[1].ctrl_1 = &r->iop.sio2.port_1.ctrl_1;
|
||||
r->iop.sio2.ports[1].ctrl_2 = &r->iop.sio2.port_1.ctrl_2;
|
||||
r->iop.sio2.ports[1].ctrl_3 = &r->iop.sio2.port_1.ctrl_3;
|
||||
|
||||
r->iop.sio2.ports[2].ctrl_1 = &r->iop.sio2.port_2.ctrl_1;
|
||||
r->iop.sio2.ports[2].ctrl_2 = &r->iop.sio2.port_2.ctrl_2;
|
||||
r->iop.sio2.ports[2].ctrl_3 = &r->iop.sio2.port_2.ctrl_3;
|
||||
|
||||
r->iop.sio2.ports[3].ctrl_1 = &r->iop.sio2.port_3.ctrl_1;
|
||||
r->iop.sio2.ports[3].ctrl_2 = &r->iop.sio2.port_3.ctrl_2;
|
||||
r->iop.sio2.ports[3].ctrl_3 = &r->iop.sio2.port_3.ctrl_3;
|
||||
|
||||
r->iop.sio2.ports[4].ctrl_3 = &r->iop.sio2.port_4.ctrl_3;
|
||||
r->iop.sio2.ports[5].ctrl_3 = &r->iop.sio2.port_5.ctrl_3;
|
||||
r->iop.sio2.ports[6].ctrl_3 = &r->iop.sio2.port_6.ctrl_3;
|
||||
r->iop.sio2.ports[7].ctrl_3 = &r->iop.sio2.port_7.ctrl_3;
|
||||
r->iop.sio2.ports[8].ctrl_3 = &r->iop.sio2.port_8.ctrl_3;
|
||||
r->iop.sio2.ports[9].ctrl_3 = &r->iop.sio2.port_9.ctrl_3;
|
||||
r->iop.sio2.ports[10].ctrl_3 = &r->iop.sio2.port_a.ctrl_3;
|
||||
r->iop.sio2.ports[11].ctrl_3 = &r->iop.sio2.port_b.ctrl_3;
|
||||
r->iop.sio2.ports[12].ctrl_3 = &r->iop.sio2.port_c.ctrl_3;
|
||||
r->iop.sio2.ports[13].ctrl_3 = &r->iop.sio2.port_d.ctrl_3;
|
||||
r->iop.sio2.ports[14].ctrl_3 = &r->iop.sio2.port_e.ctrl_3;
|
||||
r->iop.sio2.ports[15].ctrl_3 = &r->iop.sio2.port_f.ctrl_3;
|
||||
}
|
||||
|
||||
void initialise_iop_sio0(RResources* r)
|
||||
{
|
||||
r->iop.sio0.data.stat = &r->iop.sio0.stat;
|
||||
}
|
||||
|
||||
void initialise_ee_core(RResources* r)
|
||||
|
@ -1785,6 +1816,7 @@ void initialise_resources(const std::unique_ptr<RResources>& r)
|
|||
initialise_iop_dmac(r.get());
|
||||
initialise_iop_timers(r.get());
|
||||
initialise_iop_sio2(r.get());
|
||||
initialise_iop_sio0(r.get());
|
||||
|
||||
initialise_cdvd(r.get());
|
||||
|
||||
|
|
|
@ -96,11 +96,16 @@ public:
|
|||
};
|
||||
|
||||
/// SPU2 Core STATX register.
|
||||
/// TODO: find out more, not much to go on.
|
||||
/// Lots of good info can be found by looking at where the IOP BIOS SPU2 debug
|
||||
/// strings are used. BIOS will detect the SPU2 has timed out if many of these
|
||||
/// bits are not set correctly, which it tries for 256 (0x100) / 3841 (0xF01)
|
||||
/// times depending on the bit.
|
||||
class Spu2CoreRegister_Statx : public SizedHwordRegister
|
||||
{
|
||||
public:
|
||||
static constexpr Bitfield NEEDDATA = Bitfield(7, 1);
|
||||
static constexpr Bitfield UNKNOWN4 = Bitfield(4, 2); // Unknown, referenced from BIOS.
|
||||
static constexpr Bitfield DREQ = Bitfield(7, 1); // Request for more data (confirmed with BIOS).
|
||||
static constexpr Bitfield WRDY_M = Bitfield(10, 1); // (write?) ready flag? BIOS waits for this to be 0, otherwise it says SPU2 has timed out.
|
||||
};
|
||||
|
||||
/// SPU2 Core ADMAS register.
|
||||
|
|
Loading…
Reference in a new issue