1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 02:32:36 +01:00

PPUThread refactoring

`CallbackManager` removed, added _gcm_intr_thread for cellGcmSys
`PPUThread` renamed to `ppu_thread`, inheritance allowed
Added lightweight command queue for `ppu_thread`
Implemented call stack dump for PPU
`get_current_thread_mutex` removed
`thread_ctrl::spawn`: minor initialization fix
`thread_ctrl::wait_for` added
`named_thread`: some methods added
`cpu_thread::run` added
Some bugs fixes, including SPU channels
This commit is contained in:
Nekotekina 2016-07-28 00:43:22 +03:00
parent 33c59fa51b
commit f8719c1230
99 changed files with 4480 additions and 4592 deletions

View File

@ -143,22 +143,34 @@ struct atomic_storage
return atomic_storage<T>::sub_fetch(dest, 1);
}
static inline bool test_and_set(T& dest, T mask)
{
return (atomic_storage<T>::fetch_or(dest, mask) & mask) != 0;
}
static inline bool test_and_reset(T& dest, T mask)
{
return (atomic_storage<T>::fetch_and(dest, ~mask) & mask) != 0;
}
static inline bool test_and_complement(T& dest, T mask)
{
return (atomic_storage<T>::fetch_xor(dest, mask) & mask) != 0;
}
static inline bool bts(T& dest, uint bit)
{
const T mask = static_cast<T>(1) << bit;
return (atomic_storage<T>::fetch_or(dest, mask) & mask) != 0;
return atomic_storage<T>::test_and_set(dest, static_cast<T>(1) << bit);
}
static inline bool btr(T& dest, uint bit)
{
const T mask = static_cast<T>(1) << bit;
return (atomic_storage<T>::fetch_and(dest, ~mask) & mask) != 0;
return atomic_storage<T>::test_and_reset(dest, static_cast<T>(1) << bit);
}
static inline bool btc(T& dest, uint bit)
{
const T mask = static_cast<T>(1) << bit;
return (atomic_storage<T>::fetch_xor(dest, mask) & mask) != 0;
return atomic_storage<T>::test_and_complement(dest, static_cast<T>(1) << bit);
}
};
@ -637,14 +649,9 @@ struct atomic_test_and_set
template<typename T1, typename T2>
struct atomic_test_and_set<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
{
static inline bool op(T1& lhs, const T2& rhs)
{
return (atomic_storage<T1>::fetch_or(lhs, rhs) & rhs) != 0;
}
static constexpr auto fetch_op = &op;
static constexpr auto op_fetch = &op;
static constexpr auto atomic_op = &op;
static constexpr auto fetch_op = &atomic_storage<T1>::test_and_set;
static constexpr auto op_fetch = &atomic_storage<T1>::test_and_set;
static constexpr auto atomic_op = &atomic_storage<T1>::test_and_set;
};
template<typename T1, typename T2, typename>
@ -659,14 +666,9 @@ struct atomic_test_and_reset
template<typename T1, typename T2>
struct atomic_test_and_reset<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
{
static inline bool op(T1& lhs, const T2& rhs)
{
return (atomic_storage<T1>::fetch_and(lhs, ~rhs) & rhs) != 0;
}
static constexpr auto fetch_op = &op;
static constexpr auto op_fetch = &op;
static constexpr auto atomic_op = &op;
static constexpr auto fetch_op = &atomic_storage<T1>::test_and_reset;
static constexpr auto op_fetch = &atomic_storage<T1>::test_and_reset;
static constexpr auto atomic_op = &atomic_storage<T1>::test_and_reset;
};
template<typename T1, typename T2, typename>
@ -681,14 +683,9 @@ struct atomic_test_and_complement
template<typename T1, typename T2>
struct atomic_test_and_complement<T1, T2, std::enable_if_t<std::is_integral<T1>::value && std::is_convertible<T2, T1>::value>>
{
static inline bool op(T1& lhs, const T2& rhs)
{
return (atomic_storage<T1>::fetch_xor(lhs, rhs) & rhs) != 0;
}
static constexpr auto fetch_op = &op;
static constexpr auto op_fetch = &op;
static constexpr auto atomic_op = &op;
static constexpr auto fetch_op = &atomic_storage<T1>::test_and_complement;
static constexpr auto op_fetch = &atomic_storage<T1>::test_and_complement;
static constexpr auto atomic_op = &atomic_storage<T1>::test_and_complement;
};
// Atomic type with lock-free and standard layout guarantees (and appropriate limitations)

View File

@ -12,7 +12,7 @@ template<typename T> using sleep_queue = std::deque<T*>;
// Sleep is called in the constructor (if not null)
// Awake is called in the destructor (if not null)
// Sleep queue is actually std::deque with pointers, be careful about the lifetime
template<typename T, void(T::*Sleep)() = &T::sleep, void(T::*Awake)() = &T::awake>
template<typename T, void(T::*Sleep)() = nullptr, void(T::*Awake)() = nullptr>
class sleep_entry final
{
sleep_queue<T>& m_queue;

View File

@ -1830,11 +1830,6 @@ struct thread_ctrl::internal
thread_local thread_ctrl::internal* g_tls_internal = nullptr;
extern std::mutex& get_current_thread_mutex()
{
return g_tls_internal->mutex;
}
extern std::condition_variable& get_current_thread_cv()
{
return g_tls_internal->cond;
@ -2244,7 +2239,7 @@ void named_thread::start_thread(const std::shared_ptr<void>& _this)
ENSURES(_this.get() == this);
// Run thread
m_thread = thread_ctrl::spawn(get_name(), [this, _this]()
thread_ctrl::spawn(m_thread, get_name(), [this, _this]()
{
try
{

View File

@ -233,9 +233,12 @@ public:
// Wait until pred(). Abortable, may throw. Thread must be locked.
// Timeout in microseconds (zero means infinite).
template<typename F>
static inline auto wait(u64 useconds, F&& pred)
static inline auto wait_for(u64 useconds, F&& pred)
{
g_tls_this_thread->wait_start(useconds);
if (useconds)
{
g_tls_this_thread->wait_start(useconds);
}
while (true)
{
@ -252,6 +255,26 @@ public:
}
}
// Wait once. Abortable, may throw. Thread must be locked.
// Timeout in microseconds (zero means infinite).
static inline bool wait_for(u64 useconds = 0)
{
if (useconds)
{
g_tls_this_thread->wait_start(useconds);
}
g_tls_this_thread->test();
if (!g_tls_this_thread->wait_wait(useconds) && useconds)
{
return false;
}
g_tls_this_thread->test();
return true;
}
// Wait until pred(). Abortable, may throw. Thread must be locked.
template<typename F>
static inline auto wait(F&& pred)
@ -269,7 +292,7 @@ public:
}
}
// Wait once. Thread must be locked.
// Wait once. Abortable, may throw. Thread must be locked.
static inline void wait()
{
g_tls_this_thread->test();
@ -277,7 +300,7 @@ public:
g_tls_this_thread->test();
}
// Wait unconditionally until aborted. Thread must be locked.
// Wait eternally. Abortable, may throw. Thread must be locked.
[[noreturn]] static inline void eternalize()
{
while (true)
@ -302,13 +325,20 @@ public:
// Named thread factory
template<typename N, typename F>
static inline std::shared_ptr<thread_ctrl> spawn(N&& name, F&& func)
static inline void spawn(N&& name, F&& func)
{
auto ctrl = std::make_shared<thread_ctrl>(std::forward<N>(name));
auto&& out = std::make_shared<thread_ctrl>(std::forward<N>(name));
thread_ctrl::start(ctrl, std::forward<F>(func));
thread_ctrl::start(out, std::forward<F>(func));
}
return ctrl;
// Named thread factory
template<typename N, typename F>
static inline void spawn(std::shared_ptr<thread_ctrl>& out, N&& name, F&& func)
{
out = std::make_shared<thread_ctrl>(std::forward<N>(name));
thread_ctrl::start(out, std::forward<F>(func));
}
};
@ -356,6 +386,31 @@ public:
{
return m_thread.get();
}
void join() const
{
return m_thread->join();
}
void lock() const
{
return m_thread->lock();
}
void unlock() const
{
return m_thread->unlock();
}
void lock_notify() const
{
return m_thread->lock_notify();
}
void notify() const
{
return m_thread->notify();
}
};
// Simple thread mutex locker
@ -429,8 +484,8 @@ class scope_thread final
public:
template<typename N, typename F>
scope_thread(N&& name, F&& func)
: m_thread(thread_ctrl::spawn(std::forward<N>(name), std::forward<F>(func)))
{
thread_ctrl::spawn(m_thread, std::forward<N>(name), std::forward<F>(func));
}
// Deleted copy/move constructors + copy/move operators
@ -441,4 +496,10 @@ public:
{
m_thread->join();
}
// Access thread_ctrl
thread_ctrl* operator->() const
{
return m_thread.get();
}
};

View File

@ -3,13 +3,9 @@
#include "CPUThread.h"
#include <mutex>
#include <condition_variable>
thread_local cpu_thread* g_tls_current_cpu_thread = nullptr;
extern std::mutex& get_current_thread_mutex();
extern std::condition_variable& get_current_thread_cv();
void cpu_thread::on_task()
{
state -= cpu_state::exit;
@ -18,7 +14,7 @@ void cpu_thread::on_task()
Emu.SendDbgCommand(DID_CREATE_THREAD, this);
std::unique_lock<std::mutex> lock(get_current_thread_mutex());
std::unique_lock<named_thread> lock(*this);
// Check thread status
while (!(state & cpu_state::exit))
@ -54,14 +50,14 @@ void cpu_thread::on_task()
continue;
}
get_current_thread_cv().wait(lock);
thread_ctrl::wait();
}
}
void cpu_thread::on_stop()
{
state += cpu_state::exit;
(*this)->lock_notify();
lock_notify();
}
cpu_thread::~cpu_thread()
@ -73,9 +69,9 @@ cpu_thread::cpu_thread(cpu_type type)
{
}
bool cpu_thread::check_status()
bool cpu_thread::check_state()
{
std::unique_lock<std::mutex> lock(get_current_thread_mutex(), std::defer_lock);
std::unique_lock<named_thread> lock(*this, std::defer_lock);
while (true)
{
@ -86,7 +82,7 @@ bool cpu_thread::check_status()
return true;
}
if (!state.test(cpu_state_pause) && !state.test(cpu_state::interrupt))
if (!state.test(cpu_state_pause))
{
break;
}
@ -97,12 +93,7 @@ bool cpu_thread::check_status()
continue;
}
if (!state.test(cpu_state_pause) && state & cpu_state::interrupt && handle_interrupt())
{
continue;
}
get_current_thread_cv().wait(lock);
thread_ctrl::wait();
}
const auto state_ = state.load();
@ -120,3 +111,9 @@ bool cpu_thread::check_status()
return false;
}
void cpu_thread::run()
{
state -= cpu_state::stop;
lock_notify();
}

View File

@ -3,7 +3,7 @@
#include "../Utilities/Thread.h"
#include "../Utilities/BitSet.h"
// CPU Thread Type
// CPU Thread Type (TODO: probably remove, use id and idm to classify threads)
enum class cpu_type : u8
{
ppu, // PPU Thread
@ -11,7 +11,7 @@ enum class cpu_type : u8
arm, // ARMv7 Thread
};
// CPU Thread State flags
// CPU Thread State flags (TODO: use u32 once cpu_type is removed)
enum struct cpu_state : u16
{
stop, // Thread not running (HLE, initial state)
@ -19,7 +19,6 @@ enum struct cpu_state : u16
suspend, // Thread paused
ret, // Callback return requested
signal, // Thread received a signal (HLE)
interrupt, // Thread interrupted
dbg_global_pause, // Emulation paused
dbg_global_stop, // Emulation stopped
@ -43,9 +42,6 @@ public:
cpu_thread(cpu_type type);
// Public recursive sleep state counter
atomic_t<u8> sleep_counter{};
// Public thread state
atomic_t<bitset_t<cpu_state>> state{ cpu_state::stop };
@ -53,25 +49,14 @@ public:
atomic_t<void*> owner{};
// Process thread state, return true if the checker must return
bool check_status();
bool check_state();
// Increse sleep counter
void sleep()
{
if (!sleep_counter++) return; //handle_interrupt();
}
// Run thread
void run();
// Decrese sleep counter
void awake()
{
if (!--sleep_counter) owner = nullptr;
}
// Print CPU state
virtual std::string dump() const = 0;
virtual void cpu_init() {}
virtual std::string dump() const = 0; // Print CPU state
virtual void cpu_init() {} // Obsolete, must be removed
virtual void cpu_task() = 0;
virtual bool handle_interrupt() { return false; }
};
inline cpu_thread* get_current_cpu_thread() noexcept
@ -80,27 +65,3 @@ inline cpu_thread* get_current_cpu_thread() noexcept
return g_tls_current_cpu_thread;
}
// Helper for cpu_thread.
// 1) Calls sleep() and locks the thread in the constructor.
// 2) Calls awake() and unlocks the thread in the destructor.
class cpu_thread_lock final
{
cpu_thread& m_thread;
public:
cpu_thread_lock(const cpu_thread_lock&) = delete;
cpu_thread_lock(cpu_thread& thread)
: m_thread(thread)
{
m_thread.sleep();
m_thread->lock();
}
~cpu_thread_lock()
{
m_thread.awake();
m_thread->unlock();
}
};

View File

@ -3,8 +3,6 @@
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUModule.h"
extern std::mutex g_mutex_avcodec_open2;
extern "C"
{
#include "libavcodec/avcodec.h"
@ -15,97 +13,391 @@ extern "C"
#include "cellPamf.h"
#include "cellAdec.h"
#include <mutex>
#include <thread>
extern std::mutex g_mutex_avcodec_open2;
logs::channel cellAdec("cellAdec", logs::level::notice);
AudioDecoder::AudioDecoder(s32 type, u32 addr, u32 size, vm::ptr<CellAdecCbMsg> func, u32 arg)
: type(type)
, memAddr(addr)
, memSize(size)
, memBias(0)
, cbFunc(func)
, cbArg(arg)
, is_closed(false)
, is_finished(false)
, just_started(false)
, just_finished(false)
, codec(nullptr)
, input_format(nullptr)
, ctx(nullptr)
, fmt(nullptr)
class AudioDecoder : public ppu_thread
{
av_register_all();
avcodec_register_all();
public:
squeue_t<AdecTask> job;
volatile bool is_closed;
volatile bool is_finished;
bool just_started;
bool just_finished;
switch (type)
{
case CELL_ADEC_TYPE_ATRACX:
case CELL_ADEC_TYPE_ATRACX_2CH:
case CELL_ADEC_TYPE_ATRACX_6CH:
case CELL_ADEC_TYPE_ATRACX_8CH:
{
codec = avcodec_find_decoder(AV_CODEC_ID_ATRAC3P);
input_format = av_find_input_format("oma");
break;
}
case CELL_ADEC_TYPE_MP3:
{
codec = avcodec_find_decoder(AV_CODEC_ID_MP3);
input_format = av_find_input_format("mp3");
break;
}
default:
{
throw EXCEPTION("Unknown type (0x%x)", type);
}
}
AVCodec* codec;
AVInputFormat* input_format;
AVCodecContext* ctx;
AVFormatContext* fmt;
u8* io_buf;
if (!codec)
struct AudioReader
{
throw EXCEPTION("avcodec_find_decoder() failed");
}
if (!input_format)
{
throw EXCEPTION("av_find_input_format() failed");
}
fmt = avformat_alloc_context();
if (!fmt)
{
throw EXCEPTION("avformat_alloc_context() failed");
}
io_buf = (u8*)av_malloc(4096);
fmt->pb = avio_alloc_context(io_buf, 256, 0, this, adecRead, NULL, NULL);
if (!fmt->pb)
{
throw EXCEPTION("avio_alloc_context() failed");
}
}
u32 addr;
u32 size;
bool init;
bool has_ats;
AudioDecoder::~AudioDecoder()
{
// TODO: check finalization
AdecFrame af;
while (frames.try_pop(af))
{
av_frame_unref(af.data);
av_frame_free(&af.data);
}
if (ctx)
{
avcodec_close(ctx);
avformat_close_input(&fmt);
}
if (fmt)
{
if (io_buf)
AudioReader()
: init(false)
{
av_free(io_buf);
}
if (fmt->pb) av_free(fmt->pb);
avformat_free_context(fmt);
} reader;
squeue_t<AdecFrame> frames;
const s32 type;
const u32 memAddr;
const u32 memSize;
const vm::ptr<CellAdecCbMsg> cbFunc;
const u32 cbArg;
u32 memBias;
AdecTask task;
u64 last_pts, first_pts;
u32 ch_out;
u32 ch_cfg;
u32 frame_size;
u32 sample_rate;
bool use_ats_headers;
AudioDecoder(s32 type, u32 addr, u32 size, vm::ptr<CellAdecCbMsg> func, u32 arg)
: ppu_thread("HLE Audio Decoder")
, type(type)
, memAddr(addr)
, memSize(size)
, memBias(0)
, cbFunc(func)
, cbArg(arg)
, is_closed(false)
, is_finished(false)
, just_started(false)
, just_finished(false)
, codec(nullptr)
, input_format(nullptr)
, ctx(nullptr)
, fmt(nullptr)
{
av_register_all();
avcodec_register_all();
switch (type)
{
case CELL_ADEC_TYPE_ATRACX:
case CELL_ADEC_TYPE_ATRACX_2CH:
case CELL_ADEC_TYPE_ATRACX_6CH:
case CELL_ADEC_TYPE_ATRACX_8CH:
{
codec = avcodec_find_decoder(AV_CODEC_ID_ATRAC3P);
input_format = av_find_input_format("oma");
break;
}
case CELL_ADEC_TYPE_MP3:
{
codec = avcodec_find_decoder(AV_CODEC_ID_MP3);
input_format = av_find_input_format("mp3");
break;
}
default:
{
throw EXCEPTION("Unknown type (0x%x)", type);
}
}
if (!codec)
{
throw EXCEPTION("avcodec_find_decoder() failed");
}
if (!input_format)
{
throw EXCEPTION("av_find_input_format() failed");
}
fmt = avformat_alloc_context();
if (!fmt)
{
throw EXCEPTION("avformat_alloc_context() failed");
}
io_buf = (u8*)av_malloc(4096);
fmt->pb = avio_alloc_context(io_buf, 256, 0, this, adecRead, NULL, NULL);
if (!fmt->pb)
{
throw EXCEPTION("avio_alloc_context() failed");
}
}
}
~AudioDecoder()
{
// TODO: check finalization
AdecFrame af;
while (frames.try_pop(af))
{
av_frame_unref(af.data);
av_frame_free(&af.data);
}
if (ctx)
{
avcodec_close(ctx);
avformat_close_input(&fmt);
}
if (fmt)
{
if (io_buf)
{
av_free(io_buf);
}
if (fmt->pb) av_free(fmt->pb);
avformat_free_context(fmt);
}
}
virtual void cpu_task() override
{
while (true)
{
if (Emu.IsStopped() || is_closed)
{
break;
}
if (!job.pop(task, &is_closed))
{
break;
}
switch (task.type)
{
case adecStartSeq:
{
// TODO: reset data
cellAdec.warning("adecStartSeq:");
reader.addr = 0;
reader.size = 0;
reader.init = false;
reader.has_ats = false;
just_started = true;
if (adecIsAtracX(type))
{
ch_cfg = task.at3p.channel_config;
ch_out = task.at3p.channels;
frame_size = task.at3p.frame_size;
sample_rate = task.at3p.sample_rate;
use_ats_headers = task.at3p.ats_header == 1;
}
break;
}
case adecEndSeq:
{
// TODO: finalize
cellAdec.warning("adecEndSeq:");
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_SEQDONE, CELL_OK, cbArg);
just_finished = true;
break;
}
case adecDecodeAu:
{
int err = 0;
reader.addr = task.au.addr;
reader.size = task.au.size;
reader.has_ats = use_ats_headers;
//LOG_NOTICE(HLE, "Audio AU: size = 0x%x, pts = 0x%llx", task.au.size, task.au.pts);
if (just_started)
{
first_pts = task.au.pts;
last_pts = task.au.pts;
if (adecIsAtracX(type)) last_pts -= 0x10000; // hack
}
struct AVPacketHolder : AVPacket
{
AVPacketHolder(u32 size)
{
av_init_packet(this);
if (size)
{
data = (u8*)av_calloc(1, size + FF_INPUT_BUFFER_PADDING_SIZE);
this->size = size + FF_INPUT_BUFFER_PADDING_SIZE;
}
else
{
data = NULL;
size = 0;
}
}
~AVPacketHolder()
{
av_free(data);
}
} au(0);
if (just_started && just_finished)
{
avcodec_flush_buffers(ctx);
reader.init = true; // wrong
just_finished = false;
just_started = false;
}
else if (just_started) // deferred initialization
{
AVDictionary* opts = nullptr;
av_dict_set(&opts, "probesize", "96", 0);
err = avformat_open_input(&fmt, NULL, input_format, &opts);
if (err || opts)
{
throw EXCEPTION("avformat_open_input() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
}
//err = avformat_find_stream_info(fmt, NULL);
//if (err || !fmt->nb_streams)
//{
// ADEC_ERROR("adecDecodeAu: avformat_find_stream_info() failed (err=0x%x, nb_streams=%d)", err, fmt->nb_streams);
//}
if (!avformat_new_stream(fmt, codec))
{
throw EXCEPTION("avformat_new_stream() failed");
}
ctx = fmt->streams[0]->codec; // TODO: check data
opts = nullptr;
av_dict_set(&opts, "refcounted_frames", "1", 0);
{
std::lock_guard<std::mutex> lock(g_mutex_avcodec_open2);
// not multithread-safe (???)
err = avcodec_open2(ctx, codec, &opts);
}
if (err || opts)
{
throw EXCEPTION("avcodec_open2() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
}
just_started = false;
}
bool last_frame = false;
while (true)
{
if (Emu.IsStopped() || is_closed)
{
if (Emu.IsStopped()) cellAdec.warning("adecDecodeAu: aborted");
break;
}
last_frame = av_read_frame(fmt, &au) < 0;
if (last_frame)
{
//break;
av_free(au.data);
au.data = NULL;
au.size = 0;
}
struct AdecFrameHolder : AdecFrame
{
AdecFrameHolder()
{
data = av_frame_alloc();
}
~AdecFrameHolder()
{
if (data)
{
av_frame_unref(data);
av_frame_free(&data);
}
}
} frame;
if (!frame.data)
{
throw EXCEPTION("av_frame_alloc() failed");
}
int got_frame = 0;
int decode = avcodec_decode_audio4(ctx, frame.data, &got_frame, &au);
if (decode <= 0)
{
if (decode < 0)
{
cellAdec.error("adecDecodeAu: AU decoding error(0x%x)", decode);
}
if (!got_frame && reader.size == 0) break;
}
if (got_frame)
{
//u64 ts = av_frame_get_best_effort_timestamp(frame.data);
//if (ts != AV_NOPTS_VALUE)
//{
// frame.pts = ts/* - first_pts*/;
// last_pts = frame.pts;
//}
last_pts += ((u64)frame.data->nb_samples) * 90000 / frame.data->sample_rate;
frame.pts = last_pts;
s32 nbps = av_get_bytes_per_sample((AVSampleFormat)frame.data->format);
switch (frame.data->format)
{
case AV_SAMPLE_FMT_FLTP: break;
case AV_SAMPLE_FMT_S16P: break;
default:
{
throw EXCEPTION("Unsupported frame format(%d)", frame.data->format);
}
}
frame.auAddr = task.au.addr;
frame.auSize = task.au.size;
frame.userdata = task.au.userdata;
frame.size = frame.data->nb_samples * frame.data->channels * nbps;
//LOG_NOTICE(HLE, "got audio frame (pts=0x%llx, nb_samples=%d, ch=%d, sample_rate=%d, nbps=%d)",
//frame.pts, frame.data->nb_samples, frame.data->channels, frame.data->sample_rate, nbps);
if (frames.push(frame, &is_closed))
{
frame.data = nullptr; // to prevent destruction
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_PCMOUT, CELL_OK, cbArg);
}
}
}
cbFunc(*this, id, CELL_ADEC_MSG_TYPE_AUDONE, task.au.auInfo_addr, cbArg);
break;
}
case adecClose:
{
break;
}
default:
{
throw EXCEPTION("Unknown task(%d)", task.type);
}
}
}
is_finished = true;
}
};
int adecRead(void* opaque, u8* buf, int buf_size)
{
@ -171,7 +463,7 @@ next:
buf_size -= adec.reader.size;
res += adec.reader.size;
adec.cbFunc(*adec.adecCb, adec.id, CELL_ADEC_MSG_TYPE_AUDONE, adec.task.au.auInfo_addr, adec.cbArg);
adec.cbFunc(adec, adec.id, CELL_ADEC_MSG_TYPE_AUDONE, adec.task.au.auInfo_addr, adec.cbArg);
adec.job.pop(adec.task);
@ -212,267 +504,6 @@ next:
}
}
void adecOpen(u32 adec_id) // TODO: call from the constructor
{
const auto sptr = idm::get<AudioDecoder>(adec_id);
AudioDecoder& adec = *sptr;
adec.id = adec_id;
adec.adecCb = idm::make_ptr<PPUThread>(fmt::format("Demuxer[0x%x] Thread", adec_id));
adec.adecCb->prio = 1001;
adec.adecCb->stack_size = 0x10000;
adec.adecCb->custom_task = [sptr](PPUThread& CPU)
{
AudioDecoder& adec = *sptr;
AdecTask& task = adec.task;
while (true)
{
if (Emu.IsStopped() || adec.is_closed)
{
break;
}
if (!adec.job.pop(task, &adec.is_closed))
{
break;
}
switch (task.type)
{
case adecStartSeq:
{
// TODO: reset data
cellAdec.warning("adecStartSeq:");
adec.reader.addr = 0;
adec.reader.size = 0;
adec.reader.init = false;
adec.reader.has_ats = false;
adec.just_started = true;
if (adecIsAtracX(adec.type))
{
adec.ch_cfg = task.at3p.channel_config;
adec.ch_out = task.at3p.channels;
adec.frame_size = task.at3p.frame_size;
adec.sample_rate = task.at3p.sample_rate;
adec.use_ats_headers = task.at3p.ats_header == 1;
}
break;
}
case adecEndSeq:
{
// TODO: finalize
cellAdec.warning("adecEndSeq:");
adec.cbFunc(CPU, adec.id, CELL_ADEC_MSG_TYPE_SEQDONE, CELL_OK, adec.cbArg);
adec.just_finished = true;
break;
}
case adecDecodeAu:
{
int err = 0;
adec.reader.addr = task.au.addr;
adec.reader.size = task.au.size;
adec.reader.has_ats = adec.use_ats_headers;
//LOG_NOTICE(HLE, "Audio AU: size = 0x%x, pts = 0x%llx", task.au.size, task.au.pts);
if (adec.just_started)
{
adec.first_pts = task.au.pts;
adec.last_pts = task.au.pts;
if (adecIsAtracX(adec.type)) adec.last_pts -= 0x10000; // hack
}
struct AVPacketHolder : AVPacket
{
AVPacketHolder(u32 size)
{
av_init_packet(this);
if (size)
{
data = (u8*)av_calloc(1, size + FF_INPUT_BUFFER_PADDING_SIZE);
this->size = size + FF_INPUT_BUFFER_PADDING_SIZE;
}
else
{
data = NULL;
size = 0;
}
}
~AVPacketHolder()
{
av_free(data);
}
} au(0);
if (adec.just_started && adec.just_finished)
{
avcodec_flush_buffers(adec.ctx);
adec.reader.init = true; // wrong
adec.just_finished = false;
adec.just_started = false;
}
else if (adec.just_started) // deferred initialization
{
AVDictionary* opts = nullptr;
av_dict_set(&opts, "probesize", "96", 0);
err = avformat_open_input(&adec.fmt, NULL, adec.input_format, &opts);
if (err || opts)
{
throw EXCEPTION("avformat_open_input() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
}
//err = avformat_find_stream_info(adec.fmt, NULL);
//if (err || !adec.fmt->nb_streams)
//{
// ADEC_ERROR("adecDecodeAu: avformat_find_stream_info() failed (err=0x%x, nb_streams=%d)", err, adec.fmt->nb_streams);
//}
if (!avformat_new_stream(adec.fmt, adec.codec))
{
throw EXCEPTION("avformat_new_stream() failed");
}
adec.ctx = adec.fmt->streams[0]->codec; // TODO: check data
opts = nullptr;
av_dict_set(&opts, "refcounted_frames", "1", 0);
{
std::lock_guard<std::mutex> lock(g_mutex_avcodec_open2);
// not multithread-safe (???)
err = avcodec_open2(adec.ctx, adec.codec, &opts);
}
if (err || opts)
{
throw EXCEPTION("avcodec_open2() failed (err=0x%x, opts=%d)", err, opts ? 1 : 0);
}
adec.just_started = false;
}
bool last_frame = false;
while (true)
{
if (Emu.IsStopped() || adec.is_closed)
{
if (Emu.IsStopped()) cellAdec.warning("adecDecodeAu: aborted");
break;
}
last_frame = av_read_frame(adec.fmt, &au) < 0;
if (last_frame)
{
//break;
av_free(au.data);
au.data = NULL;
au.size = 0;
}
struct AdecFrameHolder : AdecFrame
{
AdecFrameHolder()
{
data = av_frame_alloc();
}
~AdecFrameHolder()
{
if (data)
{
av_frame_unref(data);
av_frame_free(&data);
}
}
} frame;
if (!frame.data)
{
throw EXCEPTION("av_frame_alloc() failed");
}
int got_frame = 0;
int decode = avcodec_decode_audio4(adec.ctx, frame.data, &got_frame, &au);
if (decode <= 0)
{
if (decode < 0)
{
cellAdec.error("adecDecodeAu: AU decoding error(0x%x)", decode);
}
if (!got_frame && adec.reader.size == 0) break;
}
if (got_frame)
{
//u64 ts = av_frame_get_best_effort_timestamp(frame.data);
//if (ts != AV_NOPTS_VALUE)
//{
// frame.pts = ts/* - adec.first_pts*/;
// adec.last_pts = frame.pts;
//}
adec.last_pts += ((u64)frame.data->nb_samples) * 90000 / frame.data->sample_rate;
frame.pts = adec.last_pts;
s32 nbps = av_get_bytes_per_sample((AVSampleFormat)frame.data->format);
switch (frame.data->format)
{
case AV_SAMPLE_FMT_FLTP: break;
case AV_SAMPLE_FMT_S16P: break;
default:
{
throw EXCEPTION("Unsupported frame format(%d)", frame.data->format);
}
}
frame.auAddr = task.au.addr;
frame.auSize = task.au.size;
frame.userdata = task.au.userdata;
frame.size = frame.data->nb_samples * frame.data->channels * nbps;
//LOG_NOTICE(HLE, "got audio frame (pts=0x%llx, nb_samples=%d, ch=%d, sample_rate=%d, nbps=%d)",
//frame.pts, frame.data->nb_samples, frame.data->channels, frame.data->sample_rate, nbps);
if (adec.frames.push(frame, &adec.is_closed))
{
frame.data = nullptr; // to prevent destruction
adec.cbFunc(CPU, adec.id, CELL_ADEC_MSG_TYPE_PCMOUT, CELL_OK, adec.cbArg);
}
}
}
adec.cbFunc(CPU, adec.id, CELL_ADEC_MSG_TYPE_AUDONE, task.au.auInfo_addr, adec.cbArg);
break;
}
case adecClose:
{
break;
}
default:
{
throw EXCEPTION("Unknown task(%d)", task.type);
}
}
}
adec.is_finished = true;
};
adec.adecCb->cpu_init();
adec.adecCb->state -= cpu_state::stop;
(*adec.adecCb)->lock_notify();
}
bool adecCheckType(s32 type)
{
switch (type)
@ -527,7 +558,11 @@ s32 cellAdecOpen(vm::ptr<CellAdecType> type, vm::ptr<CellAdecResource> res, vm::
return CELL_ADEC_ERROR_ARG;
}
adecOpen(*handle = idm::make<AudioDecoder>(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg));
auto&& adec = std::make_shared<AudioDecoder>(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg);
*handle = idm::import_existing<ppu_thread>(adec);
adec->run();
return CELL_OK;
}
@ -541,7 +576,11 @@ s32 cellAdecOpenEx(vm::ptr<CellAdecType> type, vm::ptr<CellAdecResourceEx> res,
return CELL_ADEC_ERROR_ARG;
}
adecOpen(*handle = idm::make<AudioDecoder>(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg));
auto&& adec = std::make_shared<AudioDecoder>(type->audioCodecType, res->startAddr, res->totalMemSize, cb->cbFunc, cb->cbArg);
*handle = idm::import_existing<ppu_thread>(adec);
adec->run();
return CELL_OK;
}
@ -574,8 +613,7 @@ s32 cellAdecClose(u32 handle)
std::this_thread::sleep_for(1ms); // hack
}
idm::remove<PPUThread>(adec->adecCb->id);
idm::remove<AudioDecoder>(handle);
idm::remove<ppu_thread>(handle);
return CELL_OK;
}

View File

@ -1043,7 +1043,7 @@ struct AdecTask
struct AdecFrame
{
AVFrame* data;
struct AVFrame* data;
u64 pts;
u64 userdata;
u32 auAddr;
@ -1096,58 +1096,3 @@ struct OMAHeader // OMA Header
};
CHECK_SIZE(OMAHeader, 96);
class AudioDecoder
{
public:
squeue_t<AdecTask> job;
u32 id;
volatile bool is_closed;
volatile bool is_finished;
bool just_started;
bool just_finished;
AVCodec* codec;
AVInputFormat* input_format;
AVCodecContext* ctx;
AVFormatContext* fmt;
u8* io_buf;
struct AudioReader
{
u32 addr;
u32 size;
bool init;
bool has_ats;
AudioReader()
: init(false)
{
}
} reader;
squeue_t<AdecFrame> frames;
const s32 type;
const u32 memAddr;
const u32 memSize;
const vm::ptr<CellAdecCbMsg> cbFunc;
const u32 cbArg;
u32 memBias;
AdecTask task;
u64 last_pts, first_pts;
u32 ch_out;
u32 ch_cfg;
u32 frame_size;
u32 sample_rate;
bool use_ats_headers;
std::shared_ptr<PPUThread> adecCb;
AudioDecoder(s32 type, u32 addr, u32 size, vm::ptr<CellAdecCbMsg> func, u32 arg);
~AudioDecoder();
};

File diff suppressed because it is too large Load Diff

View File

@ -275,196 +275,3 @@ struct CellDmuxAuInfoEx
CellCodecTimeStamp pts;
CellCodecTimeStamp dts;
};
/* Demuxer Thread Classes */
enum
{
/* http://dvd.sourceforge.net/dvdinfo/mpeghdrs.html */
PACKET_START_CODE_MASK = 0xffffff00,
PACKET_START_CODE_PREFIX = 0x00000100,
PACK_START_CODE = 0x000001ba,
SYSTEM_HEADER_START_CODE = 0x000001bb,
PRIVATE_STREAM_1 = 0x000001bd,
PADDING_STREAM = 0x000001be,
PRIVATE_STREAM_2 = 0x000001bf,
};
struct DemuxerStream
{
u32 addr;
u32 size;
u64 userdata;
bool discontinuity;
template<typename T>
bool get(T& out)
{
if (sizeof(T) > size) return false;
out = vm::_ref<T>(addr);
addr += sizeof(T);
size -= sizeof(T);
return true;
}
template<typename T>
bool peek(T& out, u32 shift = 0)
{
if (sizeof(T) + shift > size) return false;
out = vm::_ref<T>(addr + shift);
return true;
}
void skip(u32 count)
{
addr += count;
size = size > count ? size - count : 0;
}
bool check(u32 count) const
{
return count <= size;
}
u64 get_ts(u8 c)
{
u8 v[4]; get((u32&)v);
return
(((u64)c & 0x0e) << 29) |
(((u64)v[0]) << 21) |
(((u64)v[1] & 0x7e) << 15) |
(((u64)v[2]) << 7) | ((u64)v[3] >> 1);
}
};
struct PesHeader
{
u64 pts;
u64 dts;
u8 size;
bool has_ts;
bool is_ok;
PesHeader(DemuxerStream& stream);
};
class ElementaryStream;
enum DemuxerJobType
{
dmuxSetStream,
dmuxResetStream,
dmuxResetStreamAndWaitDone,
dmuxEnableEs,
dmuxDisableEs,
dmuxResetEs,
dmuxFlushEs,
dmuxClose,
};
struct DemuxerTask
{
DemuxerJobType type;
union
{
DemuxerStream stream;
struct
{
u32 es;
u32 auInfo_ptr_addr;
u32 auSpec_ptr_addr;
ElementaryStream* es_ptr;
} es;
};
DemuxerTask()
{
}
DemuxerTask(DemuxerJobType type)
: type(type)
{
}
};
class Demuxer
{
public:
squeue_t<DemuxerTask, 32> job;
const u32 memAddr;
const u32 memSize;
const vm::ptr<CellDmuxCbMsg> cbFunc;
const u32 cbArg;
u32 id;
volatile bool is_finished;
volatile bool is_closed;
atomic_t<bool> is_running;
atomic_t<bool> is_working;
std::shared_ptr<PPUThread> dmuxCb;
Demuxer(u32 addr, u32 size, vm::ptr<CellDmuxCbMsg> func, u32 arg)
: is_finished(false)
, is_closed(false)
, is_running(false)
, is_working(false)
, memAddr(addr)
, memSize(size)
, cbFunc(func)
, cbArg(arg)
{
}
};
class ElementaryStream
{
std::mutex m_mutex;
squeue_t<u32> entries; // AU starting addresses
u32 put_count; // number of AU written
u32 got_count; // number of AU obtained by GetAu(Ex)
u32 released; // number of AU released
u32 put; // AU that is being written now
bool is_full(u32 space);
public:
ElementaryStream(Demuxer* dmux, u32 addr, u32 size, u32 fidMajor, u32 fidMinor, u32 sup1, u32 sup2, vm::ptr<CellDmuxCbEsMsg> cbFunc, u32 cbArg, u32 spec);
Demuxer* dmux;
const id_value<> id{};
const u32 memAddr;
const u32 memSize;
const u32 fidMajor;
const u32 fidMinor;
const u32 sup1;
const u32 sup2;
const vm::ptr<CellDmuxCbEsMsg> cbFunc;
const u32 cbArg;
const u32 spec; //addr
std::vector<u8> raw_data; // demultiplexed data stream (managed by demuxer thread)
size_t raw_pos; // should be <= raw_data.size()
u64 last_dts;
u64 last_pts;
void push(DemuxerStream& stream, u32 size); // called by demuxer thread (not multithread-safe)
bool isfull(u32 space);
void push_au(u32 size, u64 dts, u64 pts, u64 userdata, bool rap, u32 specific);
bool release();
bool peek(u32& out_data, bool no_ex, u32& out_spec, bool update_index);
void reset();
};

View File

@ -82,7 +82,7 @@ s32 cellFontOpenFontFile(vm::ptr<CellFontLibrary> library, vm::cptr<char> fontPa
return ret;
}
s32 cellFontOpenFontset(PPUThread& ppu, vm::ptr<CellFontLibrary> library, vm::ptr<CellFontType> fontType, vm::ptr<CellFont> font)
s32 cellFontOpenFontset(ppu_thread& ppu, vm::ptr<CellFontLibrary> library, vm::ptr<CellFontType> fontType, vm::ptr<CellFont> font)
{
cellFont.warning("cellFontOpenFontset(library=*0x%x, fontType=*0x%x, font=*0x%x)", library, fontType, font);
@ -423,7 +423,7 @@ s32 cellFontGraphicsSetFontRGBA()
return CELL_OK;
}
s32 cellFontOpenFontsetOnMemory(PPUThread& ppu, vm::ptr<CellFontLibrary> library, vm::ptr<CellFontType> fontType, vm::ptr<CellFont> font)
s32 cellFontOpenFontsetOnMemory(ppu_thread& ppu, vm::ptr<CellFontLibrary> library, vm::ptr<CellFontType> fontType, vm::ptr<CellFont> font)
{
cellFont.todo("cellFontOpenFontsetOnMemory(library=*0x%x, fontType=*0x%x, font=*0x%x)", library, fontType, font);

View File

@ -8,6 +8,8 @@
#include "Utilities/StrUtil.h"
#include <mutex>
logs::channel cellFs("cellFs", logs::level::notice);
s32 cellFsOpen(vm::cptr<char> path, s32 flags, vm::ptr<u32> fd, vm::cptr<void> arg, u64 size)
@ -691,38 +693,58 @@ struct lv2_fs_mount_point
std::mutex mutex;
};
void fsAio(vm::ptr<CellFsAio> aio, bool write, s32 xid, fs_aio_cb_t func)
struct fs_aio_thread : ppu_thread
{
cellFs.notice("FS AIO Request(%d): fd=%d, offset=0x%llx, buf=*0x%x, size=0x%llx, user_data=0x%llx", xid, aio->fd, aio->offset, aio->buf, aio->size, aio->user_data);
using ppu_thread::ppu_thread;
s32 error = CELL_OK;
u64 result = 0;
const auto file = idm::get<lv2_file>(aio->fd);
if (!file || (!write && file->flags & CELL_FS_O_WRONLY) || (write && !(file->flags & CELL_FS_O_ACCMODE)))
virtual void cpu_task() override
{
error = CELL_EBADF;
while (ppu_cmd cmd = cmd_wait())
{
const u32 type = cmd.arg1<u32>();
const s32 xid = cmd.arg2<s32>();
const ppu_cmd cmd2 = cmd_queue[cmd_queue.peek() + 1];
const auto aio = cmd2.arg1<vm::ptr<CellFsAio>>();
const auto func = cmd2.arg2<fs_aio_cb_t>();
cmd_pop(1);
s32 error = CELL_OK;
u64 result = 0;
const auto file = idm::get<lv2_file>(aio->fd);
if (!file || (type == 1 && file->flags & CELL_FS_O_WRONLY) || (type == 2 && !(file->flags & CELL_FS_O_ACCMODE)))
{
error = CELL_EBADF;
}
else
{
std::lock_guard<std::mutex> lock(file->mp->mutex);
const auto old_pos = file->file.pos(); file->file.seek(aio->offset);
result = type == 2
? file->op_write(aio->buf, aio->size)
: file->op_read(aio->buf, aio->size);
file->file.seek(old_pos);
}
func(*this, aio, error, xid, result);
}
}
else
};
struct fs_aio_manager
{
std::shared_ptr<fs_aio_thread> t = std::make_shared<fs_aio_thread>("FS AIO Thread", 500);
fs_aio_manager()
{
std::lock_guard<std::mutex> lock(file->mp->mutex);
const auto old_pos = file->file.pos(); file->file.seek(aio->offset);
result = write
? file->op_write(aio->buf, aio->size)
: file->op_read(aio->buf, aio->size);
file->file.seek(old_pos);
idm::import_existing<ppu_thread>(t);
t->run();
}
// should be executed directly by FS AIO thread
Emu.GetCallbackManager().Async([=](PPUThread& ppu)
{
func(ppu, aio, error, xid, result);
});
}
};
s32 cellFsAioInit(vm::cptr<char> mount_point)
{
@ -730,6 +752,7 @@ s32 cellFsAioInit(vm::cptr<char> mount_point)
cellFs.warning("*** mount_point = '%s'", mount_point.get_ptr());
// TODO: create AIO thread (if not exists) for specified mount point
fxm::get_always<fs_aio_manager>();
return CELL_OK;
}
@ -754,7 +777,15 @@ s32 cellFsAioRead(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
const s32 xid = (*id = ++g_fs_aio_id);
thread_ctrl::spawn("FS AIO Read Thread", COPY_EXPR(fsAio(aio, false, xid, func)));
const auto m = fxm::get_always<fs_aio_manager>();
m->t->cmd_list
({
{ 1, xid },
{ aio, func },
});
m->t->lock_notify();
return CELL_OK;
}
@ -767,14 +798,22 @@ s32 cellFsAioWrite(vm::ptr<CellFsAio> aio, vm::ptr<s32> id, fs_aio_cb_t func)
const s32 xid = (*id = ++g_fs_aio_id);
thread_ctrl::spawn("FS AIO Write Thread", COPY_EXPR(fsAio(aio, true, xid, func)));
const auto m = fxm::get_always<fs_aio_manager>();
m->t->cmd_list
({
{ 2, xid },
{ aio, func },
});
m->t->lock_notify();
return CELL_OK;
}
s32 cellFsAioCancel(s32 id)
{
cellFs.warning("cellFsAioCancel(id=%d) -> CELL_EINVAL", id);
cellFs.todo("cellFsAioCancel(id=%d) -> CELL_EINVAL", id);
// TODO: cancelled requests return CELL_ECANCELED through their own callbacks

View File

@ -50,7 +50,7 @@ struct content_permission final
}
};
s32 cellHddGameCheck(PPUThread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, vm::ptr<CellHddGameStatCallback> funcStat, u32 container)
s32 cellHddGameCheck(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, vm::ptr<CellHddGameStatCallback> funcStat, u32 container)
{
cellGame.error("cellHddGameCheck(version=%d, dirName=*0x%x, errDialog=%d, funcStat=*0x%x, container=%d)", version, dirName, errDialog, funcStat, container);
@ -332,7 +332,7 @@ ppu_error_code cellGameContentPermit(vm::ptr<char[CELL_GAME_PATH_MAX]> contentIn
return CELL_OK;
}
ppu_error_code cellGameDataCheckCreate2(PPUThread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, vm::ptr<CellGameDataStatCallback> funcStat, u32 container)
ppu_error_code cellGameDataCheckCreate2(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, vm::ptr<CellGameDataStatCallback> funcStat, u32 container)
{
cellGame.error("cellGameDataCheckCreate2(version=0x%x, dirName=*0x%x, errDialog=0x%x, funcStat=*0x%x, container=%d)", version, dirName, errDialog, funcStat, container);
@ -420,7 +420,7 @@ ppu_error_code cellGameDataCheckCreate2(PPUThread& ppu, u32 version, vm::cptr<ch
}
}
s32 cellGameDataCheckCreate(PPUThread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, vm::ptr<CellGameDataStatCallback> funcStat, u32 container)
s32 cellGameDataCheckCreate(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, vm::ptr<CellGameDataStatCallback> funcStat, u32 container)
{
cellGame.warning("cellGameDataCheckCreate(version=0x%x, dirName=*0x%x, errDialog=0x%x, funcStat=*0x%x, container=%d)", version, dirName, errDialog, funcStat, container);

View File

@ -396,6 +396,8 @@ s32 _cellGcmInitBody(vm::pptr<CellGcmContextData> context, u32 cmdSize, u32 ioSi
ctrl.ref = -1;
const auto render = fxm::get<GSRender>();
render->intr_thread = idm::make_ptr<ppu_thread>("_gcm_intr_thread", 1, 0x4000);
render->intr_thread->run();
render->ctxt_addr = context.addr();
render->gcm_buffers.set(vm::alloc(sizeof(CellGcmDisplayInfo) * 8, vm::main));
render->zculls_addr = vm::alloc(sizeof(CellGcmZcullInfo) * 8, vm::main);
@ -496,7 +498,7 @@ void cellGcmSetFlipStatus()
fxm::get<GSRender>()->flip_status = 0;
}
s32 cellGcmSetPrepareFlip(PPUThread& ppu, vm::ptr<CellGcmContextData> ctxt, u32 id)
s32 cellGcmSetPrepareFlip(ppu_thread& ppu, vm::ptr<CellGcmContextData> ctxt, u32 id)
{
cellGcmSys.trace("cellGcmSetPrepareFlip(ctx=*0x%x, id=0x%x)", ctxt, id);
@ -525,7 +527,7 @@ s32 cellGcmSetPrepareFlip(PPUThread& ppu, vm::ptr<CellGcmContextData> ctxt, u32
return id;
}
s32 cellGcmSetFlip(PPUThread& ppu, vm::ptr<CellGcmContextData> ctxt, u32 id)
s32 cellGcmSetFlip(ppu_thread& ppu, vm::ptr<CellGcmContextData> ctxt, u32 id)
{
cellGcmSys.trace("cellGcmSetFlip(ctxt=*0x%x, id=0x%x)", ctxt, id);
@ -1141,14 +1143,14 @@ s32 cellGcmSetDefaultCommandBufferAndSegmentWordSize()
// Other
//------------------------------------------------------------------------
s32 _cellGcmSetFlipCommand(PPUThread& ppu, vm::ptr<CellGcmContextData> ctx, u32 id)
s32 _cellGcmSetFlipCommand(ppu_thread& ppu, vm::ptr<CellGcmContextData> ctx, u32 id)
{
cellGcmSys.trace("cellGcmSetFlipCommand(ctx=*0x%x, id=0x%x)", ctx, id);
return cellGcmSetPrepareFlip(ppu, ctx, id);
}
s32 _cellGcmSetFlipCommandWithWaitLabel(PPUThread& ppu, vm::ptr<CellGcmContextData> ctx, u32 id, u32 label_index, u32 label_value)
s32 _cellGcmSetFlipCommandWithWaitLabel(ppu_thread& ppu, vm::ptr<CellGcmContextData> ctx, u32 id, u32 label_index, u32 label_value)
{
cellGcmSys.trace("cellGcmSetFlipCommandWithWaitLabel(ctx=*0x%x, id=0x%x, label_index=0x%x, label_value=0x%x)", ctx, id, label_index, label_value);

View File

@ -9,6 +9,7 @@
typedef int HostCode;
#else
#include <iconv.h>
#include <errno.h>
typedef const char *HostCode;
#endif

View File

@ -92,7 +92,7 @@ s32 cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialog
{
if (callback)
{
Emu.GetCallbackManager().Register([=](PPUThread& ppu) -> s32
sysutil_register_cb([=](ppu_thread& ppu) -> s32
{
callback(ppu, status, userData);
return CELL_OK;
@ -121,7 +121,7 @@ s32 cellMsgDialogOpen2(u32 type, vm::cptr<char> msgString, vm::ptr<CellMsgDialog
return CELL_OK;
}
s32 cellMsgDialogOpenErrorCode(PPUThread& ppu, u32 errorCode, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam)
s32 cellMsgDialogOpenErrorCode(ppu_thread& ppu, u32 errorCode, vm::ptr<CellMsgDialogCallback> callback, vm::ptr<void> userData, vm::ptr<void> extParam)
{
cellSysutil.warning("cellMsgDialogOpenErrorCode(errorCode=0x%x, callback=*0x%x, userData=*0x%x, extParam=*0x%x)", errorCode, callback, userData, extParam);

View File

@ -4,6 +4,7 @@
#include "Emu/Cell/PPUModule.h"
#include "cellMusic.h"
#include "cellSysutil.h"
logs::channel cellMusic("cellMusic", logs::level::notice);
@ -122,7 +123,7 @@ s32 cellMusicInitialize2(s32 mode, s32 spuPriority, vm::ptr<CellMusic2Callback>
music->func = func;
music->userData = userData;
Emu.GetCallbackManager().Register([=](PPUThread& ppu) -> s32
sysutil_register_cb([=](ppu_thread& ppu) -> s32
{
func(ppu, CELL_MUSIC2_EVENT_INITIALIZE_RESULT, vm::make_var<s32>(CELL_OK), userData);
return CELL_OK;

View File

@ -101,8 +101,8 @@ s32 cellNetCtlNetStartDialogLoadAsync(vm::ptr<CellNetCtlNetStartDialogParam> par
// TODO: Actually sign into PSN or an emulated network similar to PSN (ESN)
// TODO: Properly open the dialog prompt for sign in
sysutilSendSystemCommand(CELL_SYSUTIL_NET_CTL_NETSTART_LOADED, 0);
sysutilSendSystemCommand(CELL_SYSUTIL_NET_CTL_NETSTART_FINISHED, 0);
sysutil_send_system_cmd(CELL_SYSUTIL_NET_CTL_NETSTART_LOADED, 0);
sysutil_send_system_cmd(CELL_SYSUTIL_NET_CTL_NETSTART_FINISHED, 0);
return CELL_OK;
}
@ -119,7 +119,7 @@ s32 cellNetCtlNetStartDialogUnloadAsync(vm::ptr<CellNetCtlNetStartDialogResult>
cellNetCtl.warning("cellNetCtlNetStartDialogUnloadAsync(result=*0x%x)", result);
result->result = CELL_NET_CTL_ERROR_DIALOG_CANCELED;
sysutilSendSystemCommand(CELL_SYSUTIL_NET_CTL_NETSTART_UNLOADED, 0);
sysutil_send_system_cmd(CELL_SYSUTIL_NET_CTL_NETSTART_UNLOADED, 0);
return CELL_OK;
}

View File

@ -244,7 +244,7 @@ be_t<u32> pngDecGetChunkInformation(PStream stream, bool IDAT = false)
return chunk_information;
}
s32 pngDecCreate(PPUThread& ppu, PPHandle png_handle, PThreadInParam thread_in_param, PThreadOutParam thread_out_param, PExtThreadInParam extra_thread_in_param = vm::null, PExtThreadOutParam extra_thread_out_param = vm::null)
s32 pngDecCreate(ppu_thread& ppu, PPHandle png_handle, PThreadInParam thread_in_param, PThreadOutParam thread_out_param, PExtThreadInParam extra_thread_in_param = vm::null, PExtThreadOutParam extra_thread_out_param = vm::null)
{
// Check if partial image decoding is used
if (extra_thread_out_param)
@ -277,7 +277,7 @@ s32 pngDecCreate(PPUThread& ppu, PPHandle png_handle, PThreadInParam thread_in_p
return CELL_OK;
}
s32 pngDecDestroy(PPUThread& ppu, PHandle handle)
s32 pngDecDestroy(ppu_thread& ppu, PHandle handle)
{
// Deallocate the decoder handle memory
if (handle->free(ppu, handle, handle->free_arg) != 0)
@ -289,7 +289,7 @@ s32 pngDecDestroy(PPUThread& ppu, PHandle handle)
return CELL_OK;
}
s32 pngDecOpen(PPUThread& ppu, PHandle handle, PPStream png_stream, PSrc source, POpenInfo open_info, PCbControlStream control_stream = vm::null, POpenParam open_param = vm::null)
s32 pngDecOpen(ppu_thread& ppu, PHandle handle, PPStream png_stream, PSrc source, POpenInfo open_info, PCbControlStream control_stream = vm::null, POpenParam open_param = vm::null)
{
// Check if partial image decoding is used
if (control_stream || open_param)
@ -442,7 +442,7 @@ s32 pngDecOpen(PPUThread& ppu, PHandle handle, PPStream png_stream, PSrc source,
return CELL_OK;
}
s32 pngDecClose(PPUThread& ppu, PHandle handle, PStream stream)
s32 pngDecClose(ppu_thread& ppu, PHandle handle, PStream stream)
{
// Remove the file descriptor, if a file descriptor was used for decoding
if (stream->buffer->file)
@ -555,7 +555,7 @@ s32 pngDecSetParameter(PStream stream, PInParam in_param, POutParam out_param, P
return CELL_OK;
}
s32 pngDecodeData(PPUThread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam data_control_param, PDataOutInfo data_out_info, PCbControlDisp cb_control_disp = vm::null, PDispParam disp_param = vm::null)
s32 pngDecodeData(ppu_thread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam data_control_param, PDataOutInfo data_out_info, PCbControlDisp cb_control_disp = vm::null, PDispParam disp_param = vm::null)
{
if (cb_control_disp || disp_param)
{
@ -703,37 +703,37 @@ s32 pngDecodeData(PPUThread& ppu, PHandle handle, PStream stream, vm::ptr<u8> da
return CELL_OK;
}
s32 cellPngDecCreate(PPUThread& ppu, PPHandle handle, PThreadInParam threadInParam, PThreadOutParam threadOutParam)
s32 cellPngDecCreate(ppu_thread& ppu, PPHandle handle, PThreadInParam threadInParam, PThreadOutParam threadOutParam)
{
cellPngDec.warning("cellPngDecCreate(handle=**0x%x, threadInParam=*0x%x, threadOutParam=*0x%x)", handle, threadInParam, threadOutParam);
return pngDecCreate(ppu, handle, threadInParam, threadOutParam);
}
s32 cellPngDecExtCreate(PPUThread& ppu, PPHandle handle, PThreadInParam threadInParam, PThreadOutParam threadOutParam, PExtThreadInParam extThreadInParam, PExtThreadOutParam extThreadOutParam)
s32 cellPngDecExtCreate(ppu_thread& ppu, PPHandle handle, PThreadInParam threadInParam, PThreadOutParam threadOutParam, PExtThreadInParam extThreadInParam, PExtThreadOutParam extThreadOutParam)
{
cellPngDec.warning("cellPngDecCreate(mainHandle=**0x%x, threadInParam=*0x%x, threadOutParam=*0x%x, extThreadInParam=*0x%x, extThreadOutParam=*0x%x)", handle, threadInParam, threadOutParam, extThreadInParam, extThreadOutParam);
return pngDecCreate(ppu, handle, threadInParam, threadOutParam, extThreadInParam, extThreadOutParam);
}
s32 cellPngDecDestroy(PPUThread& ppu, PHandle handle)
s32 cellPngDecDestroy(ppu_thread& ppu, PHandle handle)
{
cellPngDec.warning("cellPngDecDestroy(mainHandle=*0x%x)", handle);
return pngDecDestroy(ppu, handle);
}
s32 cellPngDecOpen(PPUThread& ppu, PHandle handle, PPStream stream, PSrc src, POpenInfo openInfo)
s32 cellPngDecOpen(ppu_thread& ppu, PHandle handle, PPStream stream, PSrc src, POpenInfo openInfo)
{
cellPngDec.warning("cellPngDecOpen(handle=*0x%x, stream=**0x%x, src=*0x%x, openInfo=*0x%x)", handle, stream, src, openInfo);
return pngDecOpen(ppu, handle, stream, src, openInfo);
}
s32 cellPngDecExtOpen(PPUThread& ppu, PHandle handle, PPStream stream, PSrc src, POpenInfo openInfo, PCbControlStream cbCtrlStrm, POpenParam opnParam)
s32 cellPngDecExtOpen(ppu_thread& ppu, PHandle handle, PPStream stream, PSrc src, POpenInfo openInfo, PCbControlStream cbCtrlStrm, POpenParam opnParam)
{
cellPngDec.warning("cellPngDecExtOpen(handle=*0x%x, stream=**0x%x, src=*0x%x, openInfo=*0x%x, cbCtrlStrm=*0x%x, opnParam=*0x%x)", handle, stream, src, openInfo, cbCtrlStrm, opnParam);
return pngDecOpen(ppu, handle, stream, src, openInfo, cbCtrlStrm, opnParam);
}
s32 cellPngDecClose(PPUThread& ppu, PHandle handle, PStream stream)
s32 cellPngDecClose(ppu_thread& ppu, PHandle handle, PStream stream)
{
cellPngDec.warning("cellPngDecClose(handle=*0x%x, stream=*0x%x)", handle, stream);
return pngDecClose(ppu, handle, stream);
@ -763,13 +763,13 @@ s32 cellPngDecExtSetParameter(PHandle handle, PStream stream, PInParam inParam,
return pngDecSetParameter(stream, inParam, outParam, extInParam, extOutParam);
}
s32 cellPngDecDecodeData(PPUThread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam dataCtrlParam, PDataOutInfo dataOutInfo)
s32 cellPngDecDecodeData(ppu_thread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam dataCtrlParam, PDataOutInfo dataOutInfo)
{
cellPngDec.warning("cellPngDecDecodeData(handle=*0x%x, stream=*0x%x, data=*0x%x, dataCtrlParam=*0x%x, dataOutInfo=*0x%x)", handle, stream, data, dataCtrlParam, dataOutInfo);
return pngDecodeData(ppu, handle, stream, data, dataCtrlParam, dataOutInfo);
}
s32 cellPngDecExtDecodeData(PPUThread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam dataCtrlParam, PDataOutInfo dataOutInfo, PCbControlDisp cbCtrlDisp, PDispParam dispParam)
s32 cellPngDecExtDecodeData(ppu_thread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam dataCtrlParam, PDataOutInfo dataOutInfo, PCbControlDisp cbCtrlDisp, PDispParam dispParam)
{
cellPngDec.warning("cellPngDecExtDecodeData(handle=*0x%x, stream=*0x%x, data=*0x%x, dataCtrlParam=*0x%x, dataOutInfo=*0x%x, cbCtrlDisp=*0x%x, dispParam=*0x%x)", handle, stream, data, dataCtrlParam, dataOutInfo, cbCtrlDisp, dispParam);
return pngDecodeData(ppu, handle, stream, data, dataCtrlParam, dataOutInfo, cbCtrlDisp, dispParam);

View File

@ -83,7 +83,7 @@ s32 cellRescSetSrc(s32 idx, vm::ptr<CellRescSrc> src)
return CELL_OK;
}
s32 cellRescSetConvertAndFlip(PPUThread& ppu, vm::ptr<CellGcmContextData> cntxt, s32 idx)
s32 cellRescSetConvertAndFlip(ppu_thread& ppu, vm::ptr<CellGcmContextData> cntxt, s32 idx)
{
cellResc.todo("cellRescSetConvertAndFlip(cntxt=*0x%x, idx=0x%x)", cntxt, idx);

View File

@ -10,8 +10,8 @@ logs::channel cellRudp("cellRudp", logs::level::notice);
struct rudp_t
{
// allocator functions
std::function<vm::ptr<void>(PPUThread& ppu, u32 size)> malloc;
std::function<void(PPUThread& ppu, vm::ptr<void> ptr)> free;
std::function<vm::ptr<void>(ppu_thread& ppu, u32 size)> malloc;
std::function<void(ppu_thread& ppu, vm::ptr<void> ptr)> free;
// event handler function
vm::ptr<CellRudpEventHandler> handler = vm::null;
@ -36,12 +36,12 @@ s32 cellRudpInit(vm::ptr<CellRudpAllocator> allocator)
}
else
{
rudp->malloc = [](PPUThread& ppu, u32 size)
rudp->malloc = [](ppu_thread& ppu, u32 size)
{
return vm::ptr<void>::make(vm::alloc(size, vm::main));
};
rudp->free = [](PPUThread& ppu, vm::ptr<void> ptr)
rudp->free = [](ppu_thread& ppu, vm::ptr<void> ptr)
{
if (!vm::dealloc(ptr.addr(), vm::main))
{

View File

@ -7,28 +7,6 @@
logs::channel cellSail("cellSail", logs::level::notice);
void playerBoot(vm::ptr<CellSailPlayer> pSelf, u64 userParam)
{
Emu.GetCallbackManager().Async([=](PPUThread& ppu)
{
CellSailEvent event;
event.u32x2.major = CELL_SAIL_EVENT_PLAYER_STATE_CHANGED;
event.u32x2.minor = 0;
pSelf->callback(ppu, pSelf->callbackArg, event, CELL_SAIL_PLAYER_STATE_BOOT_TRANSITION, 0);
});
// TODO: Do stuff here
pSelf->booted = true;
Emu.GetCallbackManager().Async([=](PPUThread& ppu)
{
CellSailEvent event;
event.u32x2.major = CELL_SAIL_EVENT_PLAYER_CALL_COMPLETED;
event.u32x2.minor = CELL_SAIL_PLAYER_CALL_BOOT;
pSelf->callback(ppu, pSelf->callbackArg, event, 0, 0);
});
}
s32 cellSailMemAllocatorInitialize(vm::ptr<CellSailMemAllocator> pSelf, vm::ptr<CellSailMemAllocatorFuncs> pCallbacks)
{
cellSail.warning("cellSailMemAllocatorInitialize(pSelf=*0x%x, pCallbacks=*0x%x)", pSelf, pCallbacks);
@ -611,7 +589,7 @@ s32 cellSailPlayerInitialize()
return CELL_OK;
}
s32 cellSailPlayerInitialize2(
s32 cellSailPlayerInitialize2(ppu_thread& ppu,
vm::ptr<CellSailPlayer> pSelf,
vm::ptr<CellSailMemAllocator> pAllocator,
vm::ptr<CellSailPlayerFuncNotified> pCallback,
@ -630,13 +608,12 @@ s32 cellSailPlayerInitialize2(
pSelf->booted = false;
pSelf->paused = true;
Emu.GetCallbackManager().Async([=](PPUThread& ppu)
{
CellSailEvent event;
event.u32x2.major = CELL_SAIL_EVENT_PLAYER_STATE_CHANGED;
event.u32x2.minor = 0;
pSelf->callback(ppu, pSelf->callbackArg, event, CELL_SAIL_PLAYER_STATE_INITIALIZED, 0);
});
};
return CELL_OK;
}
@ -765,11 +742,26 @@ s32 cellSailPlayerReplaceEventHandler()
return CELL_OK;
}
s32 cellSailPlayerBoot(PPUThread& ppu, vm::ptr<CellSailPlayer> pSelf, u64 userParam)
s32 cellSailPlayerBoot(ppu_thread& ppu, vm::ptr<CellSailPlayer> pSelf, u64 userParam)
{
cellSail.warning("cellSailPlayerBoot(pSelf=*0x%x, userParam=%d)", pSelf, userParam);
playerBoot(pSelf, userParam);
{
CellSailEvent event;
event.u32x2.major = CELL_SAIL_EVENT_PLAYER_STATE_CHANGED;
event.u32x2.minor = 0;
pSelf->callback(ppu, pSelf->callbackArg, event, CELL_SAIL_PLAYER_STATE_BOOT_TRANSITION, 0);
};
// TODO: Do stuff here
pSelf->booted = true;
{
CellSailEvent event;
event.u32x2.major = CELL_SAIL_EVENT_PLAYER_CALL_COMPLETED;
event.u32x2.minor = CELL_SAIL_PLAYER_CALL_BOOT;
pSelf->callback(ppu, pSelf->callbackArg, event, 0, 0);
};
return CELL_OK;
}

View File

@ -7,6 +7,7 @@
#include "Loader/PSF.h"
#include "Utilities/StrUtil.h"
#include <mutex>
#include <algorithm>
logs::channel cellSaveData("cellSaveData", logs::level::notice);
@ -36,7 +37,7 @@ enum : u32
std::mutex g_savedata_mutex;
static never_inline s32 savedata_op(PPUThread& ppu, u32 operation, u32 version, vm::cptr<char> dirName,
static never_inline s32 savedata_op(ppu_thread& ppu, u32 operation, u32 version, vm::cptr<char> dirName,
u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncFixed funcFixed, PFuncStat funcStat,
PFuncFile funcFile, u32 container, u32 unknown, vm::ptr<void> userdata, u32 userId, PFuncDone funcDone)
{
@ -618,7 +619,7 @@ static never_inline s32 savedata_op(PPUThread& ppu, u32 operation, u32 version,
}
// Functions
s32 cellSaveDataListSave2(PPUThread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
s32 cellSaveDataListSave2(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.warning("cellSaveDataListSave2(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
@ -627,7 +628,7 @@ s32 cellSaveDataListSave2(PPUThread& ppu, u32 version, PSetList setList, PSetBuf
return savedata_op(ppu, SAVEDATA_OP_LIST_SAVE, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, userdata, 0, vm::null);
}
s32 cellSaveDataListLoad2(PPUThread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
s32 cellSaveDataListLoad2(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.warning("cellSaveDataListLoad2(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
@ -636,7 +637,7 @@ s32 cellSaveDataListLoad2(PPUThread& ppu, u32 version, PSetList setList, PSetBuf
return savedata_op(ppu, SAVEDATA_OP_LIST_LOAD, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, userdata, 0, vm::null);
}
s32 cellSaveDataListSave(PPUThread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
s32 cellSaveDataListSave(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
PFuncStat funcStat, PFuncFile funcFile, u32 container)
{
cellSaveData.warning("cellSaveDataListSave(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
@ -645,7 +646,7 @@ s32 cellSaveDataListSave(PPUThread& ppu, u32 version, PSetList setList, PSetBuf
return savedata_op(ppu, SAVEDATA_OP_LIST_SAVE, version, vm::null, 1, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
}
s32 cellSaveDataListLoad(PPUThread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
s32 cellSaveDataListLoad(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncList funcList,
PFuncStat funcStat, PFuncFile funcFile, u32 container)
{
cellSaveData.warning("cellSaveDataListLoad(version=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
@ -655,7 +656,7 @@ s32 cellSaveDataListLoad(PPUThread& ppu, u32 version, PSetList setList, PSetBuf
}
s32 cellSaveDataFixedSave2(PPUThread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
s32 cellSaveDataFixedSave2(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.warning("cellSaveDataFixedSave2(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
@ -664,7 +665,7 @@ s32 cellSaveDataFixedSave2(PPUThread& ppu, u32 version, PSetList setList, PSetBu
return savedata_op(ppu, SAVEDATA_OP_FIXED_SAVE, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, userdata, 0, vm::null);
}
s32 cellSaveDataFixedLoad2(PPUThread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
s32 cellSaveDataFixedLoad2(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.warning("cellSaveDataFixedLoad2(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
@ -673,7 +674,7 @@ s32 cellSaveDataFixedLoad2(PPUThread& ppu, u32 version, PSetList setList, PSetBu
return savedata_op(ppu, SAVEDATA_OP_FIXED_LOAD, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, userdata, 0, vm::null);
}
s32 cellSaveDataFixedSave(PPUThread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
s32 cellSaveDataFixedSave(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
PFuncStat funcStat, PFuncFile funcFile, u32 container)
{
cellSaveData.warning("cellSaveDataFixedSave(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
@ -683,7 +684,7 @@ s32 cellSaveDataFixedSave(PPUThread& ppu, u32 version, PSetList setList, PSetBuf
}
s32 cellSaveDataFixedLoad(PPUThread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
s32 cellSaveDataFixedLoad(ppu_thread& ppu, u32 version, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed,
PFuncStat funcStat, PFuncFile funcFile, u32 container)
{
cellSaveData.warning("cellSaveDataFixedLoad(version=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
@ -692,7 +693,7 @@ s32 cellSaveDataFixedLoad(PPUThread& ppu, u32 version, PSetList setList, PSetBuf
return savedata_op(ppu, SAVEDATA_OP_FIXED_LOAD, version, vm::null, 1, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
}
s32 cellSaveDataAutoSave2(PPUThread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
s32 cellSaveDataAutoSave2(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.warning("cellSaveDataAutoSave2(version=%d, dirName=*0x%x, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
@ -701,7 +702,7 @@ s32 cellSaveDataAutoSave2(PPUThread& ppu, u32 version, vm::cptr<char> dirName, u
return savedata_op(ppu, SAVEDATA_OP_AUTO_SAVE, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, userdata, 0, vm::null);
}
s32 cellSaveDataAutoLoad2(PPUThread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
s32 cellSaveDataAutoLoad2(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.warning("cellSaveDataAutoLoad2(version=%d, dirName=*0x%x, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
@ -710,7 +711,7 @@ s32 cellSaveDataAutoLoad2(PPUThread& ppu, u32 version, vm::cptr<char> dirName, u
return savedata_op(ppu, SAVEDATA_OP_AUTO_LOAD, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, userdata, 0, vm::null);
}
s32 cellSaveDataAutoSave(PPUThread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
s32 cellSaveDataAutoSave(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
PFuncStat funcStat, PFuncFile funcFile, u32 container)
{
cellSaveData.warning("cellSaveDataAutoSave(version=%d, dirName=*0x%x, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
@ -719,7 +720,7 @@ s32 cellSaveDataAutoSave(PPUThread& ppu, u32 version, vm::cptr<char> dirName, u3
return savedata_op(ppu, SAVEDATA_OP_AUTO_SAVE, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
}
s32 cellSaveDataAutoLoad(PPUThread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
s32 cellSaveDataAutoLoad(ppu_thread& ppu, u32 version, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf,
PFuncStat funcStat, PFuncFile funcFile, u32 container)
{
cellSaveData.warning("cellSaveDataAutoLoad(version=%d, dirName=*0x%x, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x)",
@ -728,7 +729,7 @@ s32 cellSaveDataAutoLoad(PPUThread& ppu, u32 version, vm::cptr<char> dirName, u3
return savedata_op(ppu, SAVEDATA_OP_AUTO_LOAD, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 2, vm::null, 0, vm::null);
}
s32 cellSaveDataListAutoSave(PPUThread& ppu, u32 version, u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataListAutoSave(ppu_thread& ppu, u32 version, u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.warning("cellSaveDataListAutoSave(version=%d, errDialog=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, errDialog, setList, setBuf, funcFixed, funcStat, funcFile, container, userdata);
@ -736,7 +737,7 @@ s32 cellSaveDataListAutoSave(PPUThread& ppu, u32 version, u32 errDialog, PSetLis
return savedata_op(ppu, SAVEDATA_OP_LIST_AUTO_SAVE, version, vm::null, errDialog, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 0, userdata, 0, vm::null);
}
s32 cellSaveDataListAutoLoad(PPUThread& ppu, u32 version, u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataListAutoLoad(ppu_thread& ppu, u32 version, u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.warning("cellSaveDataListAutoLoad(version=%d, errDialog=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, errDialog, setList, setBuf, funcFixed, funcStat, funcFile, container, userdata);
@ -758,7 +759,7 @@ s32 cellSaveDataDelete(u32 container)
return CELL_SAVEDATA_RET_CANCEL;
}
s32 cellSaveDataFixedDelete(PPUThread& ppu, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataFixedDelete(ppu_thread& ppu, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
cellSaveData.todo("cellSaveDataFixedDelete(setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x)",
setList, setBuf, funcFixed, funcDone, container, userdata);
@ -766,7 +767,7 @@ s32 cellSaveDataFixedDelete(PPUThread& ppu, PSetList setList, PSetBuf setBuf, PF
return CELL_OK;
}
s32 cellSaveDataUserListSave(PPUThread& ppu, u32 version, u32 userId, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserListSave(ppu_thread& ppu, u32 version, u32 userId, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.error("cellSaveDataUserListSave(version=%d, userId=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, userId, setList, setBuf, funcList, funcStat, funcFile, container, userdata);
@ -774,7 +775,7 @@ s32 cellSaveDataUserListSave(PPUThread& ppu, u32 version, u32 userId, PSetList s
return savedata_op(ppu, SAVEDATA_OP_LIST_SAVE, version, vm::null, 0, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 6, userdata, userId, vm::null);
}
s32 cellSaveDataUserListLoad(PPUThread& ppu, u32 version, u32 userId, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserListLoad(ppu_thread& ppu, u32 version, u32 userId, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.error("cellSaveDataUserListLoad(version=%d, userId=%d, setList=*0x%x, setBuf=*0x%x, funcList=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, userId, setList, setBuf, funcList, funcStat, funcFile, container, userdata);
@ -782,7 +783,7 @@ s32 cellSaveDataUserListLoad(PPUThread& ppu, u32 version, u32 userId, PSetList s
return savedata_op(ppu, SAVEDATA_OP_LIST_LOAD, version, vm::null, 0, setList, setBuf, funcList, vm::null, funcStat, funcFile, container, 6, userdata, userId, vm::null);
}
s32 cellSaveDataUserFixedSave(PPUThread& ppu, u32 version, u32 userId, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserFixedSave(ppu_thread& ppu, u32 version, u32 userId, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.error("cellSaveDataUserFixedSave(version=%d, userId=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, userId, setList, setBuf, funcFixed, funcStat, funcFile, container, userdata);
@ -790,7 +791,7 @@ s32 cellSaveDataUserFixedSave(PPUThread& ppu, u32 version, u32 userId, PSetList
return savedata_op(ppu, SAVEDATA_OP_FIXED_SAVE, version, vm::null, 0, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 6, userdata, userId, vm::null);
}
s32 cellSaveDataUserFixedLoad(PPUThread& ppu, u32 version, u32 userId, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserFixedLoad(ppu_thread& ppu, u32 version, u32 userId, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.error("cellSaveDataUserFixedLoad(version=%d, userId=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, userId, setList, setBuf, funcFixed, funcStat, funcFile, container, userdata);
@ -798,7 +799,7 @@ s32 cellSaveDataUserFixedLoad(PPUThread& ppu, u32 version, u32 userId, PSetList
return savedata_op(ppu, SAVEDATA_OP_FIXED_LOAD, version, vm::null, 0, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 6, userdata, userId, vm::null);
}
s32 cellSaveDataUserAutoSave(PPUThread& ppu, u32 version, u32 userId, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserAutoSave(ppu_thread& ppu, u32 version, u32 userId, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.error("cellSaveDataUserAutoSave(version=%d, userId=%d, dirName=*0x%x, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, userId, dirName, errDialog, setBuf, funcStat, funcFile, container, userdata);
@ -806,7 +807,7 @@ s32 cellSaveDataUserAutoSave(PPUThread& ppu, u32 version, u32 userId, vm::cptr<c
return savedata_op(ppu, SAVEDATA_OP_AUTO_SAVE, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 6, userdata, userId, vm::null);
}
s32 cellSaveDataUserAutoLoad(PPUThread& ppu, u32 version, u32 userId, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserAutoLoad(ppu_thread& ppu, u32 version, u32 userId, vm::cptr<char> dirName, u32 errDialog, PSetBuf setBuf, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.error("cellSaveDataUserAutoLoad(version=%d, userId=%d, dirName=*0x%x, errDialog=%d, setBuf=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, userId, dirName, errDialog, setBuf, funcStat, funcFile, container, userdata);
@ -814,7 +815,7 @@ s32 cellSaveDataUserAutoLoad(PPUThread& ppu, u32 version, u32 userId, vm::cptr<c
return savedata_op(ppu, SAVEDATA_OP_AUTO_LOAD, version, dirName, errDialog, vm::null, setBuf, vm::null, vm::null, funcStat, funcFile, container, 6, userdata, userId, vm::null);
}
s32 cellSaveDataUserListAutoSave(PPUThread& ppu, u32 version, u32 userId, u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserListAutoSave(ppu_thread& ppu, u32 version, u32 userId, u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.error("cellSaveDataUserListAutoSave(version=%d, userId=%d, errDialog=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, userId, errDialog, setList, setBuf, funcFixed, funcStat, funcFile, container, userdata);
@ -822,7 +823,7 @@ s32 cellSaveDataUserListAutoSave(PPUThread& ppu, u32 version, u32 userId, u32 er
return savedata_op(ppu, SAVEDATA_OP_LIST_AUTO_SAVE, version, vm::null, errDialog, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 6, userdata, userId, vm::null);
}
s32 cellSaveDataUserListAutoLoad(PPUThread& ppu, u32 version, u32 userId, u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserListAutoLoad(ppu_thread& ppu, u32 version, u32 userId, u32 errDialog, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncStat funcStat, PFuncFile funcFile, u32 container, vm::ptr<void> userdata)
{
cellSaveData.error("cellSaveDataUserListAutoLoad(version=%d, userId=%d, errDialog=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcStat=*0x%x, funcFile=*0x%x, container=0x%x, userdata=*0x%x)",
version, userId, errDialog, setList, setBuf, funcFixed, funcStat, funcFile, container, userdata);
@ -830,7 +831,7 @@ s32 cellSaveDataUserListAutoLoad(PPUThread& ppu, u32 version, u32 userId, u32 er
return savedata_op(ppu, SAVEDATA_OP_LIST_AUTO_LOAD, version, vm::null, errDialog, setList, setBuf, vm::null, funcFixed, funcStat, funcFile, container, 6, userdata, userId, vm::null);
}
s32 cellSaveDataUserFixedDelete(PPUThread& ppu, u32 userId, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserFixedDelete(ppu_thread& ppu, u32 userId, PSetList setList, PSetBuf setBuf, PFuncFixed funcFixed, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
cellSaveData.todo("cellSaveDataUserFixedDelete(userId=%d, setList=*0x%x, setBuf=*0x%x, funcFixed=*0x%x, funcDone=*0x%x, container=0x%x, userdata=*0x%x)",
userId, setList, setBuf, funcFixed, funcDone, container, userdata);
@ -847,35 +848,35 @@ void cellSaveDataEnableOverlay(s32 enable)
// Functions (Extensions)
s32 cellSaveDataListDelete(PPUThread& ppu, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataListDelete(ppu_thread& ppu, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
UNIMPLEMENTED_FUNC(cellSaveData);
return CELL_OK;
}
s32 cellSaveDataListImport(PPUThread& ppu, PSetList setList, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataListImport(ppu_thread& ppu, PSetList setList, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
UNIMPLEMENTED_FUNC(cellSaveData);
return CELL_OK;
}
s32 cellSaveDataListExport(PPUThread& ppu, PSetList setList, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataListExport(ppu_thread& ppu, PSetList setList, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
UNIMPLEMENTED_FUNC(cellSaveData);
return CELL_OK;
}
s32 cellSaveDataFixedImport(PPUThread& ppu, vm::cptr<char> dirName, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataFixedImport(ppu_thread& ppu, vm::cptr<char> dirName, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
UNIMPLEMENTED_FUNC(cellSaveData);
return CELL_OK;
}
s32 cellSaveDataFixedExport(PPUThread& ppu, vm::cptr<char> dirName, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataFixedExport(ppu_thread& ppu, vm::cptr<char> dirName, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
UNIMPLEMENTED_FUNC(cellSaveData);
@ -889,35 +890,35 @@ s32 cellSaveDataGetListItem(vm::cptr<char> dirName, vm::ptr<CellSaveDataDirStat>
return CELL_OK;
}
s32 cellSaveDataUserListDelete(PPUThread& ppu, u32 userId, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserListDelete(ppu_thread& ppu, u32 userId, PSetList setList, PSetBuf setBuf, PFuncList funcList, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
UNIMPLEMENTED_FUNC(cellSaveData);
return CELL_OK;
}
s32 cellSaveDataUserListImport(PPUThread& ppu, u32 userId, PSetList setList, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserListImport(ppu_thread& ppu, u32 userId, PSetList setList, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
UNIMPLEMENTED_FUNC(cellSaveData);
return CELL_OK;
}
s32 cellSaveDataUserListExport(PPUThread& ppu, u32 userId, PSetList setList, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserListExport(ppu_thread& ppu, u32 userId, PSetList setList, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
UNIMPLEMENTED_FUNC(cellSaveData);
return CELL_OK;
}
s32 cellSaveDataUserFixedImport(PPUThread& ppu, u32 userId, vm::cptr<char> dirName, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserFixedImport(ppu_thread& ppu, u32 userId, vm::cptr<char> dirName, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
UNIMPLEMENTED_FUNC(cellSaveData);
return CELL_OK;
}
s32 cellSaveDataUserFixedExport(PPUThread& ppu, u32 userId, vm::cptr<char> dirName, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
s32 cellSaveDataUserFixedExport(ppu_thread& ppu, u32 userId, vm::cptr<char> dirName, u32 maxSizeKB, PFuncDone funcDone, u32 container, vm::ptr<void> userdata)
{
UNIMPLEMENTED_FUNC(cellSaveData);

View File

@ -30,28 +30,6 @@ struct cell_error_t
#define CHECK_SUCCESS(expr) if (cell_error_t error{expr}) throw fmt::exception("Failure: %s -> 0x%x" HERE, #expr, error.value)
static u32 ppu_thread_create(u32 entry, u64 arg, s32 prio, u32 stacksize, const std::string& name, std::function<void(PPUThread&)> task)
{
const auto ppu = idm::make_ptr<PPUThread>(name);
ppu->prio = prio;
ppu->stack_size = stacksize;
ppu->custom_task = std::move(task);
ppu->cpu_init();
if (entry)
{
ppu->pc = vm::read32(entry);
ppu->GPR[2] = vm::read32(entry + 4); // rtoc
}
ppu->GPR[3] = arg;
ppu->state -= cpu_state::stop;
(*ppu)->lock_notify();
return ppu->id;
}
//----------------------------------------------------------------------------
// Function prototypes
//----------------------------------------------------------------------------
@ -68,34 +46,34 @@ namespace _spurs
bool is_libprof_loaded();
// Create an LV2 event queue and attach it to the SPURS instance
s32 create_lv2_eq(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<u32> queueId, vm::ptr<u8> port, s32 size, const sys_event_queue_attribute_t& name);
s32 create_lv2_eq(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<u32> queueId, vm::ptr<u8> port, s32 size, const sys_event_queue_attribute_t& name);
// Attach an LV2 event queue to the SPURS instance
s32 attach_lv2_eq(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 queue, vm::ptr<u8> port, s32 isDynamic, bool spursCreated);
s32 attach_lv2_eq(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 queue, vm::ptr<u8> port, s32 isDynamic, bool spursCreated);
// Detach an LV2 event queue from the SPURS instance
s32 detach_lv2_eq(vm::ptr<CellSpurs> spurs, u8 spuPort, bool spursCreated);
// Wait until a workload in the SPURS instance becomes ready
void handler_wait_ready(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
void handler_wait_ready(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
// Entry point of the SPURS handler thread. This thread is responsible for starting the SPURS SPU thread group.
void handler_entry(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
void handler_entry(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
// Create the SPURS handler thread
s32 create_handler(vm::ptr<CellSpurs> spurs, u32 ppuPriority);
// Invoke event handlers
s32 invoke_event_handlers(PPUThread& ppu, vm::ptr<CellSpurs::EventPortMux> eventPortMux);
s32 invoke_event_handlers(ppu_thread& ppu, vm::ptr<CellSpurs::EventPortMux> eventPortMux);
// Invoke workload shutdown completion callbacks
s32 wakeup_shutdown_completion_waiter(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 wid);
s32 wakeup_shutdown_completion_waiter(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 wid);
// Entry point of the SPURS event helper thread
void event_helper_entry(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
void event_helper_entry(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
// Create the SPURS event helper thread
s32 create_event_helper(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 ppuPriority);
s32 create_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 ppuPriority);
// Initialise the event port multiplexor structure
void init_event_port_mux(vm::ptr<CellSpurs::EventPortMux> eventPortMux, u8 spuPort, u32 eventPort, u32 unknown);
@ -107,25 +85,25 @@ namespace _spurs
s32 finalize_spu(vm::ptr<CellSpurs> spurs);
// Stop the event helper thread
s32 stop_event_helper(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
s32 stop_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
// Signal to the SPURS handler thread
s32 signal_to_handler_thread(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
s32 signal_to_handler_thread(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
// Join the SPURS handler thread
s32 join_handler_thread(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
s32 join_handler_thread(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
// Initialise SPURS
s32 initialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 revision, u32 sdkVersion, s32 nSpus, s32 spuPriority, s32 ppuPriority, u32 flags, vm::cptr<char> prefix, u32 prefixSize, u32 container, vm::cptr<u8> swlPriority, u32 swlMaxSpu, u32 swlIsPreem);
s32 initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision, u32 sdkVersion, s32 nSpus, s32 spuPriority, s32 ppuPriority, u32 flags, vm::cptr<char> prefix, u32 prefixSize, u32 container, vm::cptr<u8> swlPriority, u32 swlMaxSpu, u32 swlIsPreem);
}
//
// SPURS Core Functions
//
//s32 cellSpursInitialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, s32 nSpus, s32 spuPriority, s32 ppuPriority, b8 exitIfNoWork);
//s32 cellSpursInitializeWithAttribute(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::cptr<CellSpursAttribute> attr);
//s32 cellSpursInitializeWithAttribute2(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::cptr<CellSpursAttribute> attr);
//s32 cellSpursInitialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, s32 nSpus, s32 spuPriority, s32 ppuPriority, b8 exitIfNoWork);
//s32 cellSpursInitializeWithAttribute(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::cptr<CellSpursAttribute> attr);
//s32 cellSpursInitializeWithAttribute2(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::cptr<CellSpursAttribute> attr);
//s32 _cellSpursAttributeInitialize(vm::ptr<CellSpursAttribute> attr, u32 revision, u32 sdkVersion, u32 nSpus, s32 spuPriority, s32 ppuPriority, b8 exitIfNoWork);
//s32 cellSpursAttributeSetMemoryContainerForSpuThread(vm::ptr<CellSpursAttribute> attr, u32 container);
//s32 cellSpursAttributeSetNamePrefix(vm::ptr<CellSpursAttribute> attr, vm::cptr<char> prefix, u32 size);
@ -139,7 +117,7 @@ namespace _spurs
//s32 cellSpursSetMaxContention(vm::ptr<CellSpurs> spurs, u32 wid, u32 maxContention);
//s32 cellSpursSetPriorities(vm::ptr<CellSpurs> spurs, u32 wid, vm::cptr<u8> priorities);
//s32 cellSpursSetPreemptionVictimHints(vm::ptr<CellSpurs> spurs, vm::cptr<b8> isPreemptible);
//s32 cellSpursAttachLv2EventQueue(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 queue, vm::ptr<u8> port, s32 isDynamic);
//s32 cellSpursAttachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 queue, vm::ptr<u8> port, s32 isDynamic);
//s32 cellSpursDetachLv2EventQueue(vm::ptr<CellSpurs> spurs, u8 port);
// Enable the SPU exception event handler
@ -162,22 +140,22 @@ s32 cellSpursEnableExceptionEventHandler(vm::ptr<CellSpurs> spurs, b8 flag);
namespace _spurs
{
// Signal SPUs to update trace status
void trace_status_update(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
void trace_status_update(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
// Initialize SPURS trace
s32 trace_initialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTraceInfo> buffer, u32 size, u32 mode, u32 updateStatus);
s32 trace_initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTraceInfo> buffer, u32 size, u32 mode, u32 updateStatus);
// Start SPURS trace
s32 trace_start(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 updateStatus);
s32 trace_start(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 updateStatus);
// Stop SPURS trace
s32 trace_stop(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 updateStatus);
s32 trace_stop(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 updateStatus);
}
//s32 cellSpursTraceInitialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTraceInfo> buffer, u32 size, u32 mode);
//s32 cellSpursTraceFinalize(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
//s32 cellSpursTraceStart(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
//s32 cellSpursTraceStop(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
//s32 cellSpursTraceInitialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTraceInfo> buffer, u32 size, u32 mode);
//s32 cellSpursTraceFinalize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
//s32 cellSpursTraceStart(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
//s32 cellSpursTraceStop(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
//
// SPURS policy module functions
@ -199,7 +177,7 @@ namespace _spurs
//s32 cellSpursRemoveWorkload();
// Activate the SPURS kernel
s32 cellSpursWakeUp(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
s32 cellSpursWakeUp(ppu_thread& ppu, vm::ptr<CellSpurs> spurs);
//s32 cellSpursSendWorkloadSignal(vm::ptr<CellSpurs> spurs, u32 wid);
//s32 cellSpursGetWorkloadFlag(vm::ptr<CellSpurs> spurs, vm::pptr<CellSpursWorkloadFlag> flag);
@ -222,11 +200,11 @@ s32 cellSpursWakeUp(PPUThread& ppu, vm::ptr<CellSpurs> spurs);
namespace _spurs
{
// Create taskset
s32 create_taskset(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, u64 args, vm::cptr<u8[8]> priority, u32 max_contention, vm::cptr<char> name, u32 size, s32 enable_clear_ls);
s32 create_taskset(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, u64 args, vm::cptr<u8[8]> priority, u32 max_contention, vm::cptr<char> name, u32 size, s32 enable_clear_ls);
}
//s32 cellSpursCreateTasksetWithAttribute(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, vm::ptr<CellSpursTasksetAttribute> attr);
//s32 cellSpursCreateTaskset(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, u64 args, vm::cptr<u8[8]> priority, u32 maxContention);
//s32 cellSpursCreateTasksetWithAttribute(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, vm::ptr<CellSpursTasksetAttribute> attr);
//s32 cellSpursCreateTaskset(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, u64 args, vm::cptr<u8[8]> priority, u32 maxContention);
//s32 cellSpursJoinTaskset(vm::ptr<CellSpursTaskset> taskset);
//s32 cellSpursGetTasksetId(vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> wid);
//s32 cellSpursShutdownTaskset(vm::ptr<CellSpursTaskset> taskset);
@ -234,13 +212,13 @@ namespace _spurs
//s32 cellSpursTasksetAttributeSetTasksetSize(vm::ptr<CellSpursTasksetAttribute> attr, u32 size);
//s32 cellSpursTasksetAttributeEnableClearLS(vm::ptr<CellSpursTasksetAttribute> attr, s32 enable);
//s32 _cellSpursTasksetAttribute2Initialize(vm::ptr<CellSpursTasksetAttribute2> attribute, u32 revision);
//s32 cellSpursCreateTaskset2(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, vm::ptr<CellSpursTasksetAttribute2> attr);
//s32 cellSpursCreateTaskset2(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, vm::ptr<CellSpursTasksetAttribute2> attr);
//s32 cellSpursDestroyTaskset2();
//s32 cellSpursTasksetSetExceptionEventHandler(vm::ptr<CellSpursTaskset> taskset, vm::ptr<CellSpursTasksetExceptionEventHandler> handler, vm::ptr<u64> arg);
//s32 cellSpursTasksetUnsetExceptionEventHandler(vm::ptr<CellSpursTaskset> taskset);
// Get taskset instance from the workload ID
s32 cellSpursLookUpTasksetAddress(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::pptr<CellSpursTaskset> taskset, u32 id);
s32 cellSpursLookUpTasksetAddress(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::pptr<CellSpursTaskset> taskset, u32 id);
//s32 cellSpursTasksetGetSpursAddress(vm::cptr<CellSpursTaskset> taskset, vm::ptr<u32> spurs);
//s32 cellSpursGetTasksetInfo();
@ -256,13 +234,13 @@ namespace _spurs
s32 create_task(vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> task_id, vm::cptr<void> elf, vm::cptr<void> context, u32 size, vm::ptr<CellSpursTaskLsPattern> ls_pattern, vm::ptr<CellSpursTaskArgument> arg);
// Start task
s32 task_start(PPUThread& ppu, vm::ptr<CellSpursTaskset> taskset, u32 taskId);
s32 task_start(ppu_thread& ppu, vm::ptr<CellSpursTaskset> taskset, u32 taskId);
}
//s32 cellSpursCreateTask(PPUThread& ppu, vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> taskId, vm::cptr<void> elf, vm::cptr<void> context, u32 size, vm::ptr<CellSpursTaskLsPattern> lsPattern, vm::ptr<CellSpursTaskArgument> argument);
//s32 cellSpursCreateTask(ppu_thread& ppu, vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> taskId, vm::cptr<void> elf, vm::cptr<void> context, u32 size, vm::ptr<CellSpursTaskLsPattern> lsPattern, vm::ptr<CellSpursTaskArgument> argument);
// Sends a signal to the task
s32 _cellSpursSendSignal(PPUThread& ppu, vm::ptr<CellSpursTaskset> taskset, u32 taskId);
s32 _cellSpursSendSignal(ppu_thread& ppu, vm::ptr<CellSpursTaskset> taskset, u32 taskId);
//s32 cellSpursCreateTaskWithAttribute();
//s32 cellSpursTaskExitCodeGet();
@ -287,15 +265,15 @@ s32 _cellSpursSendSignal(PPUThread& ppu, vm::ptr<CellSpursTaskset> taskset, u32
namespace _spurs
{
// Wait for SPURS event flag
s32 event_flag_wait(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode, u32 block);
s32 event_flag_wait(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode, u32 block);
}
//s32 _cellSpursEventFlagInitialize(vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, vm::ptr<CellSpursEventFlag> eventFlag, u32 flagClearMode, u32 flagDirection);
//s32 cellSpursEventFlagClear(vm::ptr<CellSpursEventFlag> eventFlag, u16 bits);
//s32 cellSpursEventFlagSet(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, u16 bits);
//s32 cellSpursEventFlagWait(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode);
//s32 cellSpursEventFlagTryWait(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode);
//s32 cellSpursEventFlagAttachLv2EventQueue(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag);
//s32 cellSpursEventFlagSet(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, u16 bits);
//s32 cellSpursEventFlagWait(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode);
//s32 cellSpursEventFlagTryWait(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode);
//s32 cellSpursEventFlagAttachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag);
//s32 cellSpursEventFlagDetachLv2EventQueue(vm::ptr<CellSpursEventFlag> eventFlag);
//s32 cellSpursEventFlagGetDirection(vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u32> direction);
//s32 cellSpursEventFlagGetClearMode(vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u32> clear_mode);
@ -391,7 +369,7 @@ bool _spurs::is_libprof_loaded()
// SPURS core functions
//----------------------------------------------------------------------------
s32 _spurs::create_lv2_eq(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<u32> queueId, vm::ptr<u8> port, s32 size, const sys_event_queue_attribute_t& attr)
s32 _spurs::create_lv2_eq(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<u32> queueId, vm::ptr<u8> port, s32 size, const sys_event_queue_attribute_t& attr)
{
if (s32 rc = sys_event_queue_create(queueId, vm::make_var(attr), SYS_EVENT_QUEUE_LOCAL, size))
{
@ -406,7 +384,7 @@ s32 _spurs::create_lv2_eq(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<u32>
return CELL_OK;
}
s32 _spurs::attach_lv2_eq(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 queue, vm::ptr<u8> port, s32 isDynamic, bool spursCreated)
s32 _spurs::attach_lv2_eq(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 queue, vm::ptr<u8> port, s32 isDynamic, bool spursCreated)
{
if (!spurs || !port)
{
@ -503,7 +481,7 @@ s32 _spurs::detach_lv2_eq(vm::ptr<CellSpurs> spurs, u8 spuPort, bool spursCreate
return CELL_OK;
}
void _spurs::handler_wait_ready(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
void _spurs::handler_wait_ready(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
CHECK_SUCCESS(sys_lwmutex_lock(ppu, spurs.ptr(&CellSpurs::mutex), 0));
@ -580,7 +558,7 @@ void _spurs::handler_wait_ready(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
CHECK_SUCCESS(sys_lwmutex_unlock(ppu, spurs.ptr(&CellSpurs::mutex)));
}
void _spurs::handler_entry(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
void _spurs::handler_entry(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
if (spurs->flags & SAF_UNKNOWN_FLAG_30)
{
@ -619,12 +597,27 @@ void _spurs::handler_entry(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
s32 _spurs::create_handler(vm::ptr<CellSpurs> spurs, u32 ppuPriority)
{
spurs->ppu0 = ppu_thread_create(0, spurs.addr(), ppuPriority, 0x4000, std::string(spurs->prefix, spurs->prefixSize) + "SpursHdlr0", BIND_FUNC(_spurs::handler_entry));
struct handler_thread : ppu_thread
{
using ppu_thread::ppu_thread;
virtual void cpu_task() override
{
BIND_FUNC(_spurs::handler_entry)(*this);
}
};
auto&& eht = std::make_shared<handler_thread>(std::string(spurs->prefix, spurs->prefixSize) + "SpursHdlr0", ppuPriority, 0x4000);
spurs->ppu0 = idm::import_existing<ppu_thread>(eht);
eht->gpr[3] = spurs.addr();
eht->run();
return CELL_OK;
}
s32 _spurs::invoke_event_handlers(PPUThread& ppu, vm::ptr<CellSpurs::EventPortMux> eventPortMux)
s32 _spurs::invoke_event_handlers(ppu_thread& ppu, vm::ptr<CellSpurs::EventPortMux> eventPortMux)
{
if (eventPortMux->reqPending.exchange(0))
{
@ -637,7 +630,7 @@ s32 _spurs::invoke_event_handlers(PPUThread& ppu, vm::ptr<CellSpurs::EventPortMu
return CELL_OK;
}
s32 _spurs::wakeup_shutdown_completion_waiter(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 wid)
s32 _spurs::wakeup_shutdown_completion_waiter(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 wid)
{
if (!spurs)
{
@ -689,21 +682,19 @@ s32 _spurs::wakeup_shutdown_completion_waiter(PPUThread& ppu, vm::ptr<CellSpurs>
return rc;
}
void _spurs::event_helper_entry(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
void _spurs::event_helper_entry(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
bool terminate = false;
vm::var<sys_event_t[]> events(8);
vm::var<u32> count;
while (!terminate)
while (true)
{
CHECK_SUCCESS(sys_event_queue_receive(ppu, spurs->eventQueue, vm::null, 0));
const u64 event_src = ppu.GPR[4];
const u64 event_data1 = ppu.GPR[5];
const u64 event_data2 = ppu.GPR[6];
const u64 event_data3 = ppu.GPR[7];
const u64 event_src = ppu.gpr[4];
const u64 event_data1 = ppu.gpr[5];
const u64 event_data2 = ppu.gpr[6];
const u64 event_data3 = ppu.gpr[7];
if (event_src == SYS_SPU_THREAD_EVENT_EXCEPTION_KEY)
{
@ -737,7 +728,7 @@ void _spurs::event_helper_entry(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
if (data0 == 1)
{
terminate = true;
return;
}
else if (data0 < 1)
{
@ -772,7 +763,7 @@ void _spurs::event_helper_entry(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
}
}
s32 _spurs::create_event_helper(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 ppuPriority)
s32 _spurs::create_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 ppuPriority)
{
if (s32 rc = _spurs::create_lv2_eq(ppu, spurs, spurs.ptr(&CellSpurs::eventQueue), spurs.ptr(&CellSpurs::spuPort), 0x2A, sys_event_queue_attribute_t{ SYS_SYNC_PRIORITY, SYS_PPU_QUEUE, "_spuPrv" }))
{
@ -803,7 +794,19 @@ s32 _spurs::create_event_helper(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 pp
return CELL_SPURS_CORE_ERROR_STAT;
}
const u32 tid = ppu_thread_create(0, spurs.addr(), ppuPriority, 0x8000, std::string(spurs->prefix, spurs->prefixSize) + "SpursHdlr1", BIND_FUNC(_spurs::event_helper_entry));
struct event_helper_thread : ppu_thread
{
using ppu_thread::ppu_thread;
virtual void cpu_task() override
{
BIND_FUNC(_spurs::event_helper_entry)(*this);
}
};
auto&& eht = std::make_shared<event_helper_thread>(std::string(spurs->prefix, spurs->prefixSize) + "SpursHdlr1", ppuPriority, 0x8000);
const u32 tid = idm::import_existing<ppu_thread>(eht);
if (tid == 0)
{
@ -819,6 +822,9 @@ s32 _spurs::create_event_helper(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 pp
return CELL_SPURS_CORE_ERROR_STAT;
}
eht->gpr[3] = spurs.addr();
eht->run();
spurs->ppu1 = tid;
return CELL_OK;
}
@ -871,7 +877,7 @@ s32 _spurs::finalize_spu(vm::ptr<CellSpurs> spurs)
return CELL_OK;
}
s32 _spurs::stop_event_helper(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
s32 _spurs::stop_event_helper(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
if (spurs->ppu1 == 0xFFFFFFFF)
{
@ -898,7 +904,7 @@ s32 _spurs::stop_event_helper(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
return CELL_OK;
}
s32 _spurs::signal_to_handler_thread(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
s32 _spurs::signal_to_handler_thread(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
CHECK_SUCCESS(sys_lwmutex_lock(ppu, spurs.ptr(&CellSpurs::mutex), 0));
CHECK_SUCCESS(sys_lwcond_signal(ppu, spurs.ptr(&CellSpurs::cond)));
@ -907,7 +913,7 @@ s32 _spurs::signal_to_handler_thread(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
return CELL_OK;
}
s32 _spurs::join_handler_thread(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
s32 _spurs::join_handler_thread(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
if (spurs->ppu0 == 0xFFFFFFFF)
{
@ -920,7 +926,7 @@ s32 _spurs::join_handler_thread(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
return CELL_OK;
}
s32 _spurs::initialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 revision, u32 sdkVersion, s32 nSpus, s32 spuPriority, s32 ppuPriority, u32 flags, vm::cptr<char> prefix, u32 prefixSize, u32 container, vm::cptr<u8> swlPriority, u32 swlMaxSpu, u32 swlIsPreem)
s32 _spurs::initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 revision, u32 sdkVersion, s32 nSpus, s32 spuPriority, s32 ppuPriority, u32 flags, vm::cptr<char> prefix, u32 prefixSize, u32 container, vm::cptr<u8> swlPriority, u32 swlMaxSpu, u32 swlIsPreem)
{
vm::var<u32> sem;
vm::var<sys_semaphore_attribute_t> semAttr;
@ -1211,7 +1217,7 @@ s32 _spurs::initialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 revision, u
}
/// Initialize SPURS
s32 cellSpursInitialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, s32 nSpus, s32 spuPriority, s32 ppuPriority, b8 exitIfNoWork)
s32 cellSpursInitialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, s32 nSpus, s32 spuPriority, s32 ppuPriority, b8 exitIfNoWork)
{
cellSpurs.warning("cellSpursInitialize(spurs=*0x%x, nSpus=%d, spuPriority=%d, ppuPriority=%d, exitIfNoWork=%d)", spurs, nSpus, spuPriority, ppuPriority, exitIfNoWork);
@ -1219,7 +1225,7 @@ s32 cellSpursInitialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, s32 nSpus, s32
}
/// Initialise SPURS
s32 cellSpursInitializeWithAttribute(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::cptr<CellSpursAttribute> attr)
s32 cellSpursInitializeWithAttribute(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::cptr<CellSpursAttribute> attr)
{
cellSpurs.warning("cellSpursInitializeWithAttribute(spurs=*0x%x, attr=*0x%x)", spurs, attr);
@ -1256,7 +1262,7 @@ s32 cellSpursInitializeWithAttribute(PPUThread& ppu, vm::ptr<CellSpurs> spurs, v
}
/// Initialise SPURS
s32 cellSpursInitializeWithAttribute2(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::cptr<CellSpursAttribute> attr)
s32 cellSpursInitializeWithAttribute2(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::cptr<CellSpursAttribute> attr)
{
cellSpurs.warning("cellSpursInitializeWithAttribute2(spurs=*0x%x, attr=*0x%x)", spurs, attr);
@ -1697,7 +1703,7 @@ s32 cellSpursSetPreemptionVictimHints(vm::ptr<CellSpurs> spurs, vm::cptr<b8> isP
}
/// Attach an LV2 event queue to a SPURS instance
s32 cellSpursAttachLv2EventQueue(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 queue, vm::ptr<u8> port, s32 isDynamic)
s32 cellSpursAttachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 queue, vm::ptr<u8> port, s32 isDynamic)
{
cellSpurs.warning("cellSpursAttachLv2EventQueue(spurs=*0x%x, queue=0x%x, port=*0x%x, isDynamic=%d)", spurs, queue, port, isDynamic);
@ -1810,7 +1816,7 @@ s32 cellSpursGetSpuGuid()
// SPURS trace functions
//----------------------------------------------------------------------------
void _spurs::trace_status_update(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
void _spurs::trace_status_update(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
u8 init;
@ -1830,7 +1836,7 @@ void _spurs::trace_status_update(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
}
}
s32 _spurs::trace_initialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTraceInfo> buffer, u32 size, u32 mode, u32 updateStatus)
s32 _spurs::trace_initialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTraceInfo> buffer, u32 size, u32 mode, u32 updateStatus)
{
if (!spurs || !buffer)
{
@ -1881,7 +1887,7 @@ s32 _spurs::trace_initialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<C
}
/// Initialize SPURS trace
s32 cellSpursTraceInitialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTraceInfo> buffer, u32 size, u32 mode)
s32 cellSpursTraceInitialize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTraceInfo> buffer, u32 size, u32 mode)
{
cellSpurs.warning("cellSpursTraceInitialize(spurs=*0x%x, buffer=*0x%x, size=0x%x, mode=0x%x)", spurs, buffer, size, mode);
@ -1894,7 +1900,7 @@ s32 cellSpursTraceInitialize(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<C
}
/// Finalize SPURS trace
s32 cellSpursTraceFinalize(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
s32 cellSpursTraceFinalize(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
cellSpurs.warning("cellSpursTraceFinalize(spurs=*0x%x)", spurs);
@ -1922,7 +1928,7 @@ s32 cellSpursTraceFinalize(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
return CELL_OK;
}
s32 _spurs::trace_start(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 updateStatus)
s32 _spurs::trace_start(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 updateStatus)
{
if (!spurs)
{
@ -1949,7 +1955,7 @@ s32 _spurs::trace_start(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 updateStat
}
/// Start SPURS trace
s32 cellSpursTraceStart(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
s32 cellSpursTraceStart(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
cellSpurs.warning("cellSpursTraceStart(spurs=*0x%x)", spurs);
@ -1966,7 +1972,7 @@ s32 cellSpursTraceStart(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
return _spurs::trace_start(ppu, spurs, spurs->traceMode & CELL_SPURS_TRACE_MODE_FLAG_SYNCHRONOUS_START_STOP);
}
s32 _spurs::trace_stop(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 updateStatus)
s32 _spurs::trace_stop(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, u32 updateStatus)
{
if (!spurs)
{
@ -1993,7 +1999,7 @@ s32 _spurs::trace_stop(PPUThread& ppu, vm::ptr<CellSpurs> spurs, u32 updateStatu
}
/// Stop SPURS trace
s32 cellSpursTraceStop(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
s32 cellSpursTraceStop(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
cellSpurs.warning("cellSpursTraceStop(spurs=*0x%x)", spurs);
@ -2315,7 +2321,7 @@ s32 cellSpursRemoveWorkload()
return CELL_OK;
}
s32 cellSpursWakeUp(PPUThread& ppu, vm::ptr<CellSpurs> spurs)
s32 cellSpursWakeUp(ppu_thread& ppu, vm::ptr<CellSpurs> spurs)
{
cellSpurs.warning("cellSpursWakeUp(spurs=*0x%x)", spurs);
@ -2692,7 +2698,7 @@ s32 cellSpursEventFlagClear(vm::ptr<CellSpursEventFlag> eventFlag, u16 bits)
}
/// Set a SPURS event flag
s32 cellSpursEventFlagSet(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, u16 bits)
s32 cellSpursEventFlagSet(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, u16 bits)
{
cellSpurs.warning("cellSpursEventFlagSet(eventFlag=*0x%x, bits=0x%x)", eventFlag, bits);
@ -2820,7 +2826,7 @@ s32 cellSpursEventFlagSet(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag,
return CELL_OK;
}
s32 _spurs::event_flag_wait(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode, u32 block)
s32 _spurs::event_flag_wait(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode, u32 block)
{
if (!eventFlag || !mask)
{
@ -2975,7 +2981,7 @@ s32 _spurs::event_flag_wait(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFla
}
/// Wait for SPURS event flag
s32 cellSpursEventFlagWait(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode)
s32 cellSpursEventFlagWait(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode)
{
cellSpurs.warning("cellSpursEventFlagWait(eventFlag=*0x%x, mask=*0x%x, mode=%d)", eventFlag, mask, mode);
@ -2983,7 +2989,7 @@ s32 cellSpursEventFlagWait(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag
}
/// Check SPURS event flag
s32 cellSpursEventFlagTryWait(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode)
s32 cellSpursEventFlagTryWait(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag, vm::ptr<u16> mask, u32 mode)
{
cellSpurs.warning("cellSpursEventFlagTryWait(eventFlag=*0x%x, mask=*0x%x, mode=0x%x)", eventFlag, mask, mode);
@ -2991,7 +2997,7 @@ s32 cellSpursEventFlagTryWait(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventF
}
/// Attach an LV2 event queue to a SPURS event flag
s32 cellSpursEventFlagAttachLv2EventQueue(PPUThread& ppu, vm::ptr<CellSpursEventFlag> eventFlag)
s32 cellSpursEventFlagAttachLv2EventQueue(ppu_thread& ppu, vm::ptr<CellSpursEventFlag> eventFlag)
{
cellSpurs.warning("cellSpursEventFlagAttachLv2EventQueue(eventFlag=*0x%x)", eventFlag);
@ -3300,7 +3306,7 @@ s32 cellSpursQueueGetDirection()
return CELL_OK;
}
s32 _spurs::create_taskset(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, u64 args, vm::cptr<u8[8]> priority, u32 max_contention, vm::cptr<char> name, u32 size, s32 enable_clear_ls)
s32 _spurs::create_taskset(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, u64 args, vm::cptr<u8[8]> priority, u32 max_contention, vm::cptr<char> name, u32 size, s32 enable_clear_ls)
{
if (!spurs || !taskset)
{
@ -3342,7 +3348,7 @@ s32 _spurs::create_taskset(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<Cel
return CELL_OK;
}
s32 cellSpursCreateTasksetWithAttribute(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, vm::ptr<CellSpursTasksetAttribute> attr)
s32 cellSpursCreateTasksetWithAttribute(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, vm::ptr<CellSpursTasksetAttribute> attr)
{
cellSpurs.warning("cellSpursCreateTasksetWithAttribute(spurs=*0x%x, taskset=*0x%x, attr=*0x%x)", spurs, taskset, attr);
@ -3371,7 +3377,7 @@ s32 cellSpursCreateTasksetWithAttribute(PPUThread& ppu, vm::ptr<CellSpurs> spurs
return rc;
}
s32 cellSpursCreateTaskset(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, u64 args, vm::cptr<u8[8]> priority, u32 maxContention)
s32 cellSpursCreateTaskset(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, u64 args, vm::cptr<u8[8]> priority, u32 maxContention)
{
cellSpurs.warning("cellSpursCreateTaskset(spurs=*0x%x, taskset=*0x%x, args=0x%llx, priority=*0x%x, maxContention=%d)", spurs, taskset, args, priority, maxContention);
@ -3513,7 +3519,7 @@ s32 _spurs::create_task(vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> task_id,
return CELL_OK;
}
s32 _spurs::task_start(PPUThread& ppu, vm::ptr<CellSpursTaskset> taskset, u32 taskId)
s32 _spurs::task_start(ppu_thread& ppu, vm::ptr<CellSpursTaskset> taskset, u32 taskId)
{
auto pendingReady = taskset->pending_ready.value();
pendingReady._bit[taskId] = true;
@ -3536,7 +3542,7 @@ s32 _spurs::task_start(PPUThread& ppu, vm::ptr<CellSpursTaskset> taskset, u32 ta
return CELL_OK;
}
s32 cellSpursCreateTask(PPUThread& ppu, vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> taskId, vm::cptr<void> elf, vm::cptr<void> context, u32 size, vm::ptr<CellSpursTaskLsPattern> lsPattern, vm::ptr<CellSpursTaskArgument> argument)
s32 cellSpursCreateTask(ppu_thread& ppu, vm::ptr<CellSpursTaskset> taskset, vm::ptr<u32> taskId, vm::cptr<void> elf, vm::cptr<void> context, u32 size, vm::ptr<CellSpursTaskLsPattern> lsPattern, vm::ptr<CellSpursTaskArgument> argument)
{
cellSpurs.warning("cellSpursCreateTask(taskset=*0x%x, taskID=*0x%x, elf=*0x%x, context=*0x%x, size=0x%x, lsPattern=*0x%x, argument=*0x%x)", taskset, taskId, elf, context, size, lsPattern, argument);
@ -3565,7 +3571,7 @@ s32 cellSpursCreateTask(PPUThread& ppu, vm::ptr<CellSpursTaskset> taskset, vm::p
return CELL_OK;
}
s32 _cellSpursSendSignal(PPUThread& ppu, vm::ptr<CellSpursTaskset> taskset, u32 taskId)
s32 _cellSpursSendSignal(ppu_thread& ppu, vm::ptr<CellSpursTaskset> taskset, u32 taskId)
{
if (!taskset)
{
@ -3768,7 +3774,7 @@ s32 cellSpursTaskGetContextSaveAreaSize()
return CELL_OK;
}
s32 cellSpursCreateTaskset2(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, vm::ptr<CellSpursTasksetAttribute2> attr)
s32 cellSpursCreateTaskset2(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::ptr<CellSpursTaskset> taskset, vm::ptr<CellSpursTasksetAttribute2> attr)
{
cellSpurs.warning("cellSpursCreateTaskset2(spurs=*0x%x, taskset=*0x%x, attr=*0x%x)", spurs, taskset, attr);
@ -3877,7 +3883,7 @@ s32 cellSpursTasksetUnsetExceptionEventHandler(vm::ptr<CellSpursTaskset> taskset
return CELL_OK;
}
s32 cellSpursLookUpTasksetAddress(PPUThread& ppu, vm::ptr<CellSpurs> spurs, vm::pptr<CellSpursTaskset> taskset, u32 id)
s32 cellSpursLookUpTasksetAddress(ppu_thread& ppu, vm::ptr<CellSpurs> spurs, vm::pptr<CellSpursTaskset> taskset, u32 id)
{
cellSpurs.warning("cellSpursLookUpTasksetAddress(spurs=*0x%x, taskset=**0x%x, id=0x%x)", spurs, taskset, id);

View File

@ -17,8 +17,6 @@
extern logs::channel cellSpurs;
extern std::mutex& get_current_thread_mutex();
//----------------------------------------------------------------------------
// Function prototypes
//----------------------------------------------------------------------------
@ -774,7 +772,7 @@ void spursSysServiceIdleHandler(SPUThread& spu, SpursKernelContext* ctxt)
{
bool shouldExit;
std::unique_lock<std::mutex> lock(get_current_thread_mutex(), std::defer_lock);
std::unique_lock<named_thread> lock(spu, std::defer_lock);
while (true)
{
@ -864,8 +862,8 @@ void spursSysServiceIdleHandler(SPUThread& spu, SpursKernelContext* ctxt)
{
// The system service blocks by making a reservation and waiting on the lock line reservation lost event.
CHECK_EMU_STATUS;
if (!lock) lock.lock();
get_current_thread_cv().wait_for(lock, 1ms);
if (!lock) { lock.lock(); continue; }
thread_ctrl::wait_for(1000);
continue;
}

View File

@ -820,7 +820,7 @@ ppu_error_code cellSyncLFQueueInitialize(vm::ptr<CellSyncLFQueue> queue, vm::cpt
return CELL_OK;
}
ppu_error_code _cellSyncLFQueueGetPushPointer(PPUThread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 useEventQueue)
ppu_error_code _cellSyncLFQueueGetPushPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 useEventQueue)
{
cellSync.warning("_cellSyncLFQueueGetPushPointer(queue=*0x%x, pointer=*0x%x, isBlocking=%d, useEventQueue=%d)", queue, pointer, isBlocking, useEventQueue);
@ -913,7 +913,7 @@ ppu_error_code _cellSyncLFQueueGetPushPointer(PPUThread& ppu, vm::ptr<CellSyncLF
}
}
ppu_error_code _cellSyncLFQueueGetPushPointer2(PPUThread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 useEventQueue)
ppu_error_code _cellSyncLFQueueGetPushPointer2(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 useEventQueue)
{
// arguments copied from _cellSyncLFQueueGetPushPointer
cellSync.todo("_cellSyncLFQueueGetPushPointer2(queue=*0x%x, pointer=*0x%x, isBlocking=%d, useEventQueue=%d)", queue, pointer, isBlocking, useEventQueue);
@ -921,7 +921,7 @@ ppu_error_code _cellSyncLFQueueGetPushPointer2(PPUThread& ppu, vm::ptr<CellSyncL
throw EXCEPTION("");
}
ppu_error_code _cellSyncLFQueueCompletePushPointer(PPUThread& ppu, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal)
ppu_error_code _cellSyncLFQueueCompletePushPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal)
{
cellSync.warning("_cellSyncLFQueueCompletePushPointer(queue=*0x%x, pointer=%d, fpSendSignal=*0x%x)", queue, pointer, fpSendSignal);
@ -1053,7 +1053,7 @@ ppu_error_code _cellSyncLFQueueCompletePushPointer(PPUThread& ppu, vm::ptr<CellS
}
}
ppu_error_code _cellSyncLFQueueCompletePushPointer2(PPUThread& ppu, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal)
ppu_error_code _cellSyncLFQueueCompletePushPointer2(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal)
{
// arguments copied from _cellSyncLFQueueCompletePushPointer
cellSync.todo("_cellSyncLFQueueCompletePushPointer2(queue=*0x%x, pointer=%d, fpSendSignal=*0x%x)", queue, pointer, fpSendSignal);
@ -1061,7 +1061,7 @@ ppu_error_code _cellSyncLFQueueCompletePushPointer2(PPUThread& ppu, vm::ptr<Cell
throw EXCEPTION("");
}
ppu_error_code _cellSyncLFQueuePushBody(PPUThread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::cptr<void> buffer, u32 isBlocking)
ppu_error_code _cellSyncLFQueuePushBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::cptr<void> buffer, u32 isBlocking)
{
// cellSyncLFQueuePush has 1 in isBlocking param, cellSyncLFQueueTryPush has 0
cellSync.warning("_cellSyncLFQueuePushBody(queue=*0x%x, buffer=*0x%x, isBlocking=%d)", queue, buffer, isBlocking);
@ -1119,7 +1119,7 @@ ppu_error_code _cellSyncLFQueuePushBody(PPUThread& ppu, vm::ptr<CellSyncLFQueue>
}
}
ppu_error_code _cellSyncLFQueueGetPopPointer(PPUThread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 arg4, u32 useEventQueue)
ppu_error_code _cellSyncLFQueueGetPopPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 arg4, u32 useEventQueue)
{
cellSync.warning("_cellSyncLFQueueGetPopPointer(queue=*0x%x, pointer=*0x%x, isBlocking=%d, arg4=%d, useEventQueue=%d)", queue, pointer, isBlocking, arg4, useEventQueue);
@ -1212,7 +1212,7 @@ ppu_error_code _cellSyncLFQueueGetPopPointer(PPUThread& ppu, vm::ptr<CellSyncLFQ
}
}
ppu_error_code _cellSyncLFQueueGetPopPointer2(PPUThread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 useEventQueue)
ppu_error_code _cellSyncLFQueueGetPopPointer2(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<s32> pointer, u32 isBlocking, u32 useEventQueue)
{
// arguments copied from _cellSyncLFQueueGetPopPointer
cellSync.todo("_cellSyncLFQueueGetPopPointer2(queue=*0x%x, pointer=*0x%x, isBlocking=%d, useEventQueue=%d)", queue, pointer, isBlocking, useEventQueue);
@ -1220,7 +1220,7 @@ ppu_error_code _cellSyncLFQueueGetPopPointer2(PPUThread& ppu, vm::ptr<CellSyncLF
throw EXCEPTION("");
}
ppu_error_code _cellSyncLFQueueCompletePopPointer(PPUThread& ppu, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal, u32 noQueueFull)
ppu_error_code _cellSyncLFQueueCompletePopPointer(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal, u32 noQueueFull)
{
// arguments copied from _cellSyncLFQueueCompletePushPointer + unknown argument (noQueueFull taken from LFQueue2CompletePopPointer)
cellSync.warning("_cellSyncLFQueueCompletePopPointer(queue=*0x%x, pointer=%d, fpSendSignal=*0x%x, noQueueFull=%d)", queue, pointer, fpSendSignal, noQueueFull);
@ -1352,7 +1352,7 @@ ppu_error_code _cellSyncLFQueueCompletePopPointer(PPUThread& ppu, vm::ptr<CellSy
}
}
ppu_error_code _cellSyncLFQueueCompletePopPointer2(PPUThread& ppu, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal, u32 noQueueFull)
ppu_error_code _cellSyncLFQueueCompletePopPointer2(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, s32 pointer, vm::ptr<s32(u32 addr, u32 arg)> fpSendSignal, u32 noQueueFull)
{
// arguments copied from _cellSyncLFQueueCompletePopPointer
cellSync.todo("_cellSyncLFQueueCompletePopPointer2(queue=*0x%x, pointer=%d, fpSendSignal=*0x%x, noQueueFull=%d)", queue, pointer, fpSendSignal, noQueueFull);
@ -1360,7 +1360,7 @@ ppu_error_code _cellSyncLFQueueCompletePopPointer2(PPUThread& ppu, vm::ptr<CellS
throw EXCEPTION("");
}
ppu_error_code _cellSyncLFQueuePopBody(PPUThread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<void> buffer, u32 isBlocking)
ppu_error_code _cellSyncLFQueuePopBody(ppu_thread& ppu, vm::ptr<CellSyncLFQueue> queue, vm::ptr<void> buffer, u32 isBlocking)
{
// cellSyncLFQueuePop has 1 in isBlocking param, cellSyncLFQueueTryPop has 0
cellSync.warning("_cellSyncLFQueuePopBody(queue=*0x%x, buffer=*0x%x, isBlocking=%d)", queue, buffer, isBlocking);

View File

@ -8,20 +8,40 @@
#include "Utilities/StrUtil.h"
#include <mutex>
#include <queue>
logs::channel cellSysutil("cellSysutil", logs::level::notice);
// Temporarily
using sys_callbacks_t = std::array<std::pair<vm::ptr<CellSysutilCallback>, vm::ptr<void>>, 4>;
void sysutilSendSystemCommand(u64 status, u64 param)
struct sysutil_cb_manager
{
if (const auto g_sys_callback = fxm::get<sys_callbacks_t>())
std::mutex mutex;
std::array<std::pair<vm::ptr<CellSysutilCallback>, vm::ptr<void>>, 4> callbacks;
std::queue<std::function<s32(ppu_thread&)>> registered;
};
extern void sysutil_register_cb(std::function<s32(ppu_thread&)>&& cb)
{
const auto cbm = fxm::get_always<sysutil_cb_manager>();
std::lock_guard<std::mutex> lock(cbm->mutex);
cbm->registered.push(std::move(cb));
}
extern void sysutil_send_system_cmd(u64 status, u64 param)
{
if (const auto cbm = fxm::get<sysutil_cb_manager>())
{
for (auto& cb : *g_sys_callback)
for (auto& cb : cbm->callbacks)
{
if (cb.first)
{
Emu.GetCallbackManager().Register([=](PPUThread& ppu) -> s32
std::lock_guard<std::mutex> lock(cbm->mutex);
cbm->registered.push([=](ppu_thread& ppu) -> s32
{
// TODO: check it and find the source of the return value (void isn't equal to CELL_OK)
cb.first(ppu, status, param, cb.second);
@ -196,15 +216,26 @@ s32 cellSysutilGetSystemParamString(s32 id, vm::ptr<char> buf, u32 bufsize)
return CELL_OK;
}
s32 cellSysutilCheckCallback(PPUThread& CPU)
s32 cellSysutilCheckCallback(ppu_thread& ppu)
{
cellSysutil.trace("cellSysutilCheckCallback()");
while (auto func = Emu.GetCallbackManager().Check())
{
CHECK_EMU_STATUS;
const auto cbm = fxm::get_always<sysutil_cb_manager>();
if (s32 res = func(CPU))
while (true)
{
std::lock_guard<std::mutex> lock(cbm->mutex);
if (cbm->registered.empty())
{
break;
}
const auto func = std::move(cbm->registered.front());
cbm->registered.pop();
if (s32 res = func(ppu))
{
return res;
}
@ -217,12 +248,15 @@ s32 cellSysutilRegisterCallback(s32 slot, vm::ptr<CellSysutilCallback> func, vm:
{
cellSysutil.warning("cellSysutilRegisterCallback(slot=%d, func=*0x%x, userdata=*0x%x)", slot, func, userdata);
if (slot >= sys_callbacks_t{}.size())
if (slot >= 4)
{
return CELL_SYSUTIL_ERROR_VALUE;
}
fxm::get_always<sys_callbacks_t>()->at(slot) = std::make_pair(func, userdata);
const auto cbm = fxm::get_always<sysutil_cb_manager>();
cbm->callbacks[slot] = std::make_pair(func, userdata);
return CELL_OK;
}
@ -230,12 +264,15 @@ s32 cellSysutilUnregisterCallback(u32 slot)
{
cellSysutil.warning("cellSysutilUnregisterCallback(slot=%d)", slot);
if (slot >= sys_callbacks_t{}.size())
if (slot >= 4)
{
return CELL_SYSUTIL_ERROR_VALUE;
}
fxm::get_always<sys_callbacks_t>()->at(slot) = std::make_pair(vm::null, vm::null);
const auto cbm = fxm::get_always<sysutil_cb_manager>();
cbm->callbacks[slot] = std::make_pair(vm::null, vm::null);
return CELL_OK;
}

View File

@ -195,4 +195,5 @@ struct CellSysCacheParam
vm::ptr<void> reserved;
};
extern void sysutilSendSystemCommand(u64 status, u64 param);
extern void sysutil_register_cb(std::function<s32(ppu_thread&)>&&);
extern void sysutil_send_system_cmd(u64 status, u64 param);

View File

@ -3,8 +3,6 @@
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUModule.h"
std::mutex g_mutex_avcodec_open2;
extern "C"
{
#include "libavcodec/avcodec.h"
@ -15,200 +13,213 @@ extern "C"
#include "cellPamf.h"
#include "cellVdec.h"
#include <mutex>
#include <thread>
#include <queue>
std::mutex g_mutex_avcodec_open2;
logs::channel cellVdec("cellVdec", logs::level::notice);
vm::gvar<s32> _cell_vdec_prx_ver; // ???
VideoDecoder::VideoDecoder(s32 type, u32 profile, u32 addr, u32 size, vm::ptr<CellVdecCbMsg> func, u32 arg)
: type(type)
, profile(profile)
, memAddr(addr)
, memSize(size)
, memBias(0)
, cbFunc(func)
, cbArg(arg)
, is_finished(false)
, is_closed(false)
, frc_set(0)
, codec(nullptr)
, ctx(nullptr)
enum class vdec_cmd : u32
{
avcodec_register_all();
none = 0,
switch (type)
{
case CELL_VDEC_CODEC_TYPE_MPEG2:
{
codec = avcodec_find_decoder(AV_CODEC_ID_MPEG2VIDEO);
break;
}
case CELL_VDEC_CODEC_TYPE_AVC:
{
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
break;
}
case CELL_VDEC_CODEC_TYPE_DIVX:
{
codec = avcodec_find_decoder(AV_CODEC_ID_MPEG4);
break;
}
default:
{
throw fmt::exception("Unknown video decoder type (0x%x)" HERE, type);
}
}
start_seq,
end_seq,
decode,
set_frc,
close,
};
if (!codec)
{
throw fmt::exception("avcodec_find_decoder() failed (type=0x%x)" HERE, type);
}
ctx = avcodec_alloc_context3(codec);
if (!ctx)
{
throw fmt::exception("avcodec_alloc_context3() failed (type=0x%x)" HERE, type);
}
AVDictionary* opts{};
av_dict_set(&opts, "refcounted_frames", "1", 0);
std::lock_guard<std::mutex> lock(g_mutex_avcodec_open2);
int err = avcodec_open2(ctx, codec, &opts);
if (err || opts)
{
throw fmt::exception("avcodec_open2() failed (err=0x%x, opts=%d)" HERE, err, opts ? 1 : 0);
}
}
VideoDecoder::~VideoDecoder()
struct vdec_frame
{
VdecFrame vf;
while (frames.try_pop(vf))
struct frame_dtor
{
av_frame_unref(vf.data);
av_frame_free(&vf.data);
void operator()(AVFrame* data) const
{
av_frame_unref(data);
av_frame_free(&data);
}
};
std::unique_ptr<AVFrame, frame_dtor> avf;
u64 dts;
u64 pts;
u64 userdata;
u32 frc;
AVFrame* operator ->() const
{
return avf.get();
}
};
struct vdec_thread : ppu_thread
{
AVCodec* codec{};
AVCodecContext* ctx{};
const s32 type;
const u32 profile;
const u32 mem_addr;
const u32 mem_size;
const vm::ptr<CellVdecCbMsg> cb_func;
const u32 cb_arg;
u32 mem_bias{};
u32 frc_set{}; // Frame Rate Override
u64 last_pts{};
u64 last_dts{};
std::queue<vdec_frame> out;
std::queue<u64> user_data; // TODO
vdec_thread(s32 type, u32 profile, u32 addr, u32 size, vm::ptr<CellVdecCbMsg> func, u32 arg)
: ppu_thread("HLE Video Decoder")
, type(type)
, profile(profile)
, mem_addr(addr)
, mem_size(size)
, cb_func(func)
, cb_arg(arg)
{
avcodec_register_all();
switch (type)
{
case CELL_VDEC_CODEC_TYPE_MPEG2:
{
codec = avcodec_find_decoder(AV_CODEC_ID_MPEG2VIDEO);
break;
}
case CELL_VDEC_CODEC_TYPE_AVC:
{
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
break;
}
case CELL_VDEC_CODEC_TYPE_DIVX:
{
codec = avcodec_find_decoder(AV_CODEC_ID_MPEG4);
break;
}
default:
{
throw fmt::exception("Unknown video decoder type (0x%x)" HERE, type);
}
}
if (!codec)
{
throw fmt::exception("avcodec_find_decoder() failed (type=0x%x)" HERE, type);
}
ctx = avcodec_alloc_context3(codec);
if (!ctx)
{
throw fmt::exception("avcodec_alloc_context3() failed (type=0x%x)" HERE, type);
}
AVDictionary* opts{};
av_dict_set(&opts, "refcounted_frames", "1", 0);
std::lock_guard<std::mutex> lock(g_mutex_avcodec_open2);
int err = avcodec_open2(ctx, codec, &opts);
if (err || opts)
{
avcodec_free_context(&ctx);
throw fmt::exception("avcodec_open2() failed (err=0x%x, opts=%d)" HERE, err, opts ? 1 : 0);
}
}
if (ctx)
virtual ~vdec_thread() override
{
avcodec_close(ctx);
avcodec_free_context(&ctx);
}
}
u32 vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0 */, vm::ptr<CellVdecAttr> attr)
{
switch (type) // TODO: check profile levels
virtual std::string dump() const override
{
case CELL_VDEC_CODEC_TYPE_AVC: cellVdec.warning("cellVdecQueryAttr: AVC (profile=%d)", profile); break;
case CELL_VDEC_CODEC_TYPE_MPEG2: cellVdec.warning("cellVdecQueryAttr: MPEG2 (profile=%d)", profile); break;
case CELL_VDEC_CODEC_TYPE_DIVX: cellVdec.warning("cellVdecQueryAttr: DivX (profile=%d)", profile); break;
default: return CELL_VDEC_ERROR_ARG;
// TODO
return ppu_thread::dump();
}
// TODO: check values
attr->decoderVerLower = 0x280000; // from dmux
attr->decoderVerUpper = 0x260000;
attr->memSize = 4 * 1024 * 1024; // 4 MB
attr->cmdDepth = 16;
return CELL_OK;
}
void vdecOpen(u32 vdec_id) // TODO: call from the constructor
{
const auto sptr = idm::get<VideoDecoder>(vdec_id);
VideoDecoder& vdec = *sptr;
vdec.id = vdec_id;
vdec.vdecCb = idm::make_ptr<PPUThread>(fmt::format("VideoDecoder[0x%x] Thread", vdec_id));
vdec.vdecCb->prio = 1001;
vdec.vdecCb->stack_size = 0x10000;
vdec.vdecCb->custom_task = [sptr](PPUThread& ppu)
virtual void cpu_task() override
{
VideoDecoder& vdec = *sptr;
VdecTask& task = vdec.task;
while (true)
while (ppu_cmd cmd = cmd_wait())
{
if (Emu.IsStopped() || vdec.is_closed)
switch (vdec_cmd vcmd = cmd.arg1<vdec_cmd>())
{
case vdec_cmd::start_seq:
{
cmd_pop();
avcodec_flush_buffers(ctx);
frc_set = 0; // TODO: ???
last_pts = 0;
last_dts = 0;
cellVdec.trace("Start sequence...");
break;
}
if (!vdec.job.pop(task, &vdec.is_closed))
{
break;
}
switch (task.type)
{
case vdecStartSeq:
{
cellVdec.warning("vdecStartSeq:");
avcodec_flush_buffers(vdec.ctx);
vdec.frc_set = 0; // TODO: ???
vdec.last_pts = 0;
vdec.last_dts = 0;
break;
}
case vdecDecodeAu:
case vdecEndSeq:
case vdec_cmd::decode:
case vdec_cmd::end_seq:
{
AVPacket packet{};
packet.pos = -1;
if (task.type == vdecDecodeAu)
u32 au_type{};
u32 au_addr{};
u32 au_size{};
u64 au_pts{};
u64 au_dts{};
u64 au_usrd{};
u64 au_spec{};
if (vcmd == vdec_cmd::decode)
{
packet.pts = vdec.task.pts != -1 ? vdec.task.pts : AV_NOPTS_VALUE;
packet.dts = vdec.task.dts != -1 ? vdec.task.dts : AV_NOPTS_VALUE;
packet.data = vm::_ptr<u8>(vdec.task.addr);
packet.size = vdec.task.size;
cellVdec.trace("vdecDecodeAu: size = 0x%x, pts = 0x%llx, dts = 0x%llx", task.size, task.pts, task.dts);
const u32 pos = cmd_queue.peek();
au_type = cmd.arg2<u32>(); // TODO
au_addr = cmd_queue[pos + 1].load().arg1<u32>();
au_size = cmd_queue[pos + 1].load().arg2<u32>();
au_pts = cmd_queue[pos + 2].load().as<u64>();
au_dts = cmd_queue[pos + 3].load().as<u64>();
au_usrd = cmd_queue[pos + 4].load().as<u64>(); // TODO
au_spec = cmd_queue[pos + 5].load().as<u64>(); // TODO
cmd_pop(5);
packet.data = vm::_ptr<u8>(au_addr);
packet.size = au_size;
packet.pts = au_pts != -1 ? au_pts : AV_NOPTS_VALUE;
packet.dts = au_dts != -1 ? au_dts : AV_NOPTS_VALUE;
cellVdec.trace("AU decoding: size=0x%x, pts=0x%llx, dts=0x%llx, userdata=0x%llx", au_size, au_pts, au_dts, au_usrd);
}
else
{
cmd_pop();
packet.pts = AV_NOPTS_VALUE;
packet.dts = AV_NOPTS_VALUE;
packet.data = nullptr;
packet.size = 0;
cellVdec.warning("vdecEndSeq");
cellVdec.trace("End sequence...");
}
while (true)
{
struct VdecFrameHolder : VdecFrame
{
VdecFrameHolder()
{
data = av_frame_alloc();
}
vdec_frame frame;
frame.avf.reset(av_frame_alloc());
~VdecFrameHolder()
{
if (data)
{
av_frame_unref(data);
av_frame_free(&data);
}
}
} frame;
if (!frame.data)
if (!frame.avf)
{
throw fmt::exception("av_frame_alloc() failed" HERE);
}
int got_picture = 0;
int decode = avcodec_decode_video2(vdec.ctx, frame.data, &got_picture, &packet);
int decode = avcodec_decode_video2(ctx, frame.avf.get(), &got_picture, &packet);
if (decode < 0)
{
@ -227,21 +238,21 @@ void vdecOpen(u32 vdec_id) // TODO: call from the constructor
if (got_picture)
{
if (frame.data->interlaced_frame)
if (frame->interlaced_frame)
{
throw EXCEPTION("Interlaced frames not supported (0x%x)", frame.data->interlaced_frame);
throw fmt::exception("Interlaced frames not supported (0x%x)", frame->interlaced_frame);
}
if (frame.data->repeat_pict)
if (frame->repeat_pict)
{
throw EXCEPTION("Repeated frames not supported (0x%x)", frame.data->repeat_pict);
throw fmt::exception("Repeated frames not supported (0x%x)", frame->repeat_pict);
}
if (vdec.frc_set)
if (frc_set)
{
u64 amend = 0;
switch (vdec.frc_set)
switch (frc_set)
{
case CELL_VDEC_FRC_24000DIV1001: amend = 1001 * 90000 / 24000; break;
case CELL_VDEC_FRC_24: amend = 90000 / 24; break;
@ -253,23 +264,23 @@ void vdecOpen(u32 vdec_id) // TODO: call from the constructor
case CELL_VDEC_FRC_60: amend = 90000 / 60; break;
default:
{
throw EXCEPTION("Invalid frame rate code set (0x%x)", vdec.frc_set);
throw EXCEPTION("Invalid frame rate code set (0x%x)", frc_set);
}
}
vdec.last_pts += amend;
vdec.last_dts += amend;
frame.frc = vdec.frc_set;
last_pts += amend;
last_dts += amend;
frame.frc = frc_set;
}
else
{
const u64 amend = vdec.ctx->time_base.num * 90000 * vdec.ctx->ticks_per_frame / vdec.ctx->time_base.den;
vdec.last_pts += amend;
vdec.last_dts += amend;
const u64 amend = ctx->time_base.num * 90000 * ctx->ticks_per_frame / ctx->time_base.den;
last_pts += amend;
last_dts += amend;
if (vdec.ctx->time_base.num == 1)
if (ctx->time_base.num == 1)
{
switch ((u64)vdec.ctx->time_base.den + (u64)(vdec.ctx->ticks_per_frame - 1) * 0x100000000ull)
switch ((u64)ctx->time_base.den + (u64)(ctx->ticks_per_frame - 1) * 0x100000000ull)
{
case 24: case 0x100000000ull + 48: frame.frc = CELL_VDEC_FRC_24; break;
case 25: case 0x100000000ull + 50: frame.frc = CELL_VDEC_FRC_25; break;
@ -278,91 +289,95 @@ void vdecOpen(u32 vdec_id) // TODO: call from the constructor
case 60: case 0x100000000ull + 120: frame.frc = CELL_VDEC_FRC_60; break;
default:
{
throw EXCEPTION("Unsupported time_base.den (%d/1, tpf=%d)", vdec.ctx->time_base.den, vdec.ctx->ticks_per_frame);
throw EXCEPTION("Unsupported time_base.den (%d/1, tpf=%d)", ctx->time_base.den, ctx->ticks_per_frame);
}
}
}
else if (vdec.ctx->time_base.num == 1001)
else if (ctx->time_base.num == 1001)
{
if (vdec.ctx->time_base.den / vdec.ctx->ticks_per_frame == 24000)
if (ctx->time_base.den / ctx->ticks_per_frame == 24000)
{
frame.frc = CELL_VDEC_FRC_24000DIV1001;
}
else if (vdec.ctx->time_base.den / vdec.ctx->ticks_per_frame == 30000)
else if (ctx->time_base.den / ctx->ticks_per_frame == 30000)
{
frame.frc = CELL_VDEC_FRC_30000DIV1001;
}
else if (vdec.ctx->time_base.den / vdec.ctx->ticks_per_frame == 60000)
else if (ctx->time_base.den / ctx->ticks_per_frame == 60000)
{
frame.frc = CELL_VDEC_FRC_60000DIV1001;
}
else
{
throw EXCEPTION("Unsupported time_base.den (%d/1001, tpf=%d)", vdec.ctx->time_base.den, vdec.ctx->ticks_per_frame);
throw EXCEPTION("Unsupported time_base.den (%d/1001, tpf=%d)", ctx->time_base.den, ctx->ticks_per_frame);
}
}
else
{
throw EXCEPTION("Unsupported time_base.num (%d)", vdec.ctx->time_base.num);
throw EXCEPTION("Unsupported time_base.num (%d)", ctx->time_base.num);
}
}
frame.pts = vdec.last_pts = frame.data->pkt_pts != AV_NOPTS_VALUE ? frame.data->pkt_pts : vdec.last_pts;
frame.dts = vdec.last_dts = frame.data->pkt_dts != AV_NOPTS_VALUE ? frame.data->pkt_dts : vdec.last_dts;
frame.userdata = task.userData;
frame.pts = last_pts = frame->pkt_pts != AV_NOPTS_VALUE ? frame->pkt_pts : last_pts;
frame.dts = last_dts = frame->pkt_dts != AV_NOPTS_VALUE ? frame->pkt_dts : last_dts;
frame.userdata = au_usrd;
cellVdec.trace("got picture (pts=0x%llx, dts=0x%llx)", frame.pts, frame.dts);
if (vdec.frames.push(frame, &vdec.is_closed))
{
frame.data = nullptr; // to prevent destruction
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_PICOUT, CELL_OK, vdec.cbArg);
}
thread_lock{*this}, out.push(std::move(frame));
cb_func(*this, id, CELL_VDEC_MSG_TYPE_PICOUT, CELL_OK, cb_arg);
}
if (task.type == vdecDecodeAu)
if (vcmd == vdec_cmd::decode)
{
break;
}
}
if (task.type == vdecDecodeAu)
{
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_AUDONE, CELL_OK, vdec.cbArg);
}
else
{
vdec.cbFunc(*vdec.vdecCb, vdec.id, CELL_VDEC_MSG_TYPE_SEQDONE, CELL_OK, vdec.cbArg);
}
cb_func(*this, id, vcmd == vdec_cmd::decode ? CELL_VDEC_MSG_TYPE_AUDONE : CELL_VDEC_MSG_TYPE_SEQDONE, CELL_OK, cb_arg);
break;
}
case vdecSetFrameRate:
case vdec_cmd::set_frc:
{
cellVdec.warning("vdecSetFrameRate(0x%x)", task.frc);
vdec.frc_set = task.frc;
cmd_pop();
frc_set = cmd.arg2<u32>();
break;
}
case vdecClose:
case vdec_cmd::close:
{
break;
cmd_pop();
state += cpu_state::exit;
return;
}
default:
{
throw fmt::exception("Unknown task(%d)" HERE, task.type);
throw fmt::exception("Unknown command (0x%x)" HERE, vcmd);
}
}
}
}
};
vdec.is_finished = true;
};
u32 vdecQueryAttr(s32 type, u32 profile, u32 spec_addr /* may be 0 */, vm::ptr<CellVdecAttr> attr)
{
switch (type) // TODO: check profile levels
{
case CELL_VDEC_CODEC_TYPE_AVC: cellVdec.warning("cellVdecQueryAttr: AVC (profile=%d)", profile); break;
case CELL_VDEC_CODEC_TYPE_MPEG2: cellVdec.warning("cellVdecQueryAttr: MPEG2 (profile=%d)", profile); break;
case CELL_VDEC_CODEC_TYPE_DIVX: cellVdec.warning("cellVdecQueryAttr: DivX (profile=%d)", profile); break;
default: return CELL_VDEC_ERROR_ARG;
}
vdec.vdecCb->cpu_init();
vdec.vdecCb->state -= cpu_state::stop;
(*vdec.vdecCb)->lock_notify();
// TODO: check values
attr->decoderVerLower = 0x280000; // from dmux
attr->decoderVerUpper = 0x260000;
attr->memSize = 4 * 1024 * 1024; // 4 MB
attr->cmdDepth = 16;
return CELL_OK;
}
s32 cellVdecQueryAttr(vm::cptr<CellVdecType> type, vm::ptr<CellVdecAttr> attr)
@ -383,7 +398,13 @@ s32 cellVdecOpen(vm::cptr<CellVdecType> type, vm::cptr<CellVdecResource> res, vm
{
cellVdec.warning("cellVdecOpen(type=*0x%x, res=*0x%x, cb=*0x%x, handle=*0x%x)", type, res, cb, handle);
vdecOpen(*handle = idm::make<VideoDecoder>(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg));
// Create decoder thread
auto&& vdec = std::make_shared<vdec_thread>(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg);
// Hack: store thread id (normally it should be pointer)
*handle = idm::import_existing<ppu_thread>(vdec);
vdec->run();
return CELL_OK;
}
@ -392,7 +413,13 @@ s32 cellVdecOpenEx(vm::cptr<CellVdecTypeEx> type, vm::cptr<CellVdecResourceEx> r
{
cellVdec.warning("cellVdecOpenEx(type=*0x%x, res=*0x%x, cb=*0x%x, handle=*0x%x)", type, res, cb, handle);
vdecOpen(*handle = idm::make<VideoDecoder>(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg));
// Create decoder thread
auto&& vdec = std::make_shared<vdec_thread>(type->codecType, type->profileLevel, res->memAddr, res->memSize, cb->cbFunc, cb->cbArg);
// Hack: store thread id (normally it should be pointer)
*handle = idm::import_existing<ppu_thread>(vdec);
vdec->run();
return CELL_OK;
}
@ -401,25 +428,17 @@ s32 cellVdecClose(u32 handle)
{
cellVdec.warning("cellVdecClose(handle=0x%x)", handle);
const auto vdec = idm::get<VideoDecoder>(handle);
const auto vdec = std::dynamic_pointer_cast<vdec_thread>(idm::get<ppu_thread>(handle)); // TODO: avoid RTTI
if (!vdec)
{
return CELL_VDEC_ERROR_ARG;
}
vdec->is_closed = true;
vdec->job.try_push(VdecTask(vdecClose));
while (!vdec->is_finished)
{
CHECK_EMU_STATUS;
std::this_thread::sleep_for(1ms); // hack
}
idm::remove<PPUThread>(vdec->vdecCb->id);
idm::remove<VideoDecoder>(handle);
vdec->cmd_push({vdec_cmd::close, 0});
vdec->lock_notify();
vdec->join();
idm::remove<ppu_thread>(handle);
return CELL_OK;
}
@ -427,14 +446,15 @@ s32 cellVdecStartSeq(u32 handle)
{
cellVdec.trace("cellVdecStartSeq(handle=0x%x)", handle);
const auto vdec = idm::get<VideoDecoder>(handle);
const auto vdec = std::dynamic_pointer_cast<vdec_thread>(idm::get<ppu_thread>(handle)); // TODO: avoid RTTI
if (!vdec)
{
return CELL_VDEC_ERROR_ARG;
}
vdec->job.push(VdecTask(vdecStartSeq), &vdec->is_closed);
vdec->cmd_push({vdec_cmd::start_seq, 0});
vdec->lock_notify();
return CELL_OK;
}
@ -442,14 +462,15 @@ s32 cellVdecEndSeq(u32 handle)
{
cellVdec.warning("cellVdecEndSeq(handle=0x%x)", handle);
const auto vdec = idm::get<VideoDecoder>(handle);
const auto vdec = std::dynamic_pointer_cast<vdec_thread>(idm::get<ppu_thread>(handle)); // TODO: avoid RTTI
if (!vdec)
{
return CELL_VDEC_ERROR_ARG;
}
vdec->job.push(VdecTask(vdecEndSeq), &vdec->is_closed);
vdec->cmd_push({vdec_cmd::end_seq, 0});
vdec->lock_notify();
return CELL_OK;
}
@ -457,9 +478,9 @@ s32 cellVdecDecodeAu(u32 handle, CellVdecDecodeMode mode, vm::cptr<CellVdecAuInf
{
cellVdec.trace("cellVdecDecodeAu(handle=0x%x, mode=%d, auInfo=*0x%x)", handle, mode, auInfo);
const auto vdec = idm::get<VideoDecoder>(handle);
const auto vdec = std::dynamic_pointer_cast<vdec_thread>(idm::get<ppu_thread>(handle)); // TODO: avoid RTTI
if (!vdec || mode > CELL_VDEC_DEC_MODE_PB_SKIP)
if (mode > CELL_VDEC_DEC_MODE_PB_SKIP || !vdec)
{
return CELL_VDEC_ERROR_ARG;
}
@ -470,16 +491,17 @@ s32 cellVdecDecodeAu(u32 handle, CellVdecDecodeMode mode, vm::cptr<CellVdecAuInf
}
// TODO: check info
VdecTask task(vdecDecodeAu);
task.mode = mode;
task.addr = auInfo->startAddr;
task.size = auInfo->size;
task.dts = (u64)auInfo->dts.lower | ((u64)auInfo->dts.upper << 32);
task.pts = (u64)auInfo->pts.lower | ((u64)auInfo->pts.upper << 32);
task.userData = auInfo->userData;
task.specData = auInfo->codecSpecificData;
vdec->cmd_list
({
{ vdec_cmd::decode, mode },
{ auInfo->startAddr, auInfo->size },
u64{auInfo->pts.upper} << 32 | auInfo->pts.lower,
u64{auInfo->dts.upper} << 32 | auInfo->dts.lower,
auInfo->userData,
auInfo->codecSpecificData,
});
vdec->job.push(task, &vdec->is_closed);
vdec->lock_notify();
return CELL_OK;
}
@ -487,39 +509,35 @@ s32 cellVdecGetPicture(u32 handle, vm::cptr<CellVdecPicFormat> format, vm::ptr<u
{
cellVdec.trace("cellVdecGetPicture(handle=0x%x, format=*0x%x, outBuff=*0x%x)", handle, format, outBuff);
const auto vdec = idm::get<VideoDecoder>(handle);
const auto vdec = std::dynamic_pointer_cast<vdec_thread>(idm::get<ppu_thread>(handle)); // TODO: avoid RTTI
if (!vdec || !format)
if (!format || !vdec)
{
return CELL_VDEC_ERROR_ARG;
}
VdecFrame vf;
if (!vdec->frames.try_pop(vf))
vdec_frame frame;
{
//std::this_thread::sleep_for(1ms); // hack
return CELL_VDEC_ERROR_EMPTY;
thread_lock lock(*vdec);
if (vdec->out.empty())
{
return CELL_VDEC_ERROR_EMPTY;
}
frame = std::move(vdec->out.front());
vdec->out.pop();
}
if (!vf.data)
{
// hack
return CELL_OK;
}
std::unique_ptr<AVFrame, void(*)(AVFrame*)> frame(vf.data, [](AVFrame* frame)
{
av_frame_unref(frame);
av_frame_free(&frame);
});
vdec->notify();
if (outBuff)
{
const auto f = vdec->ctx->pix_fmt;
const auto w = vdec->ctx->width;
const auto h = vdec->ctx->height;
const int w = frame->width;
const int h = frame->height;
auto out_f = AV_PIX_FMT_YUV420P;
AVPixelFormat out_f = AV_PIX_FMT_YUV420P;
std::unique_ptr<u8[]> alpha_plane;
@ -532,29 +550,29 @@ s32 cellVdecGetPicture(u32 handle, vm::cptr<CellVdecPicFormat> format, vm::ptr<u
default:
{
throw EXCEPTION("Unknown formatType(%d)", type);
throw fmt::exception("Unknown formatType (%d)" HERE, type);
}
}
if (format->colorMatrixType != CELL_VDEC_COLOR_MATRIX_TYPE_BT709)
{
throw EXCEPTION("Unknown colorMatrixType(%d)", format->colorMatrixType);
throw fmt::exception("Unknown colorMatrixType (%d)" HERE, format->colorMatrixType);
}
if (alpha_plane)
{
memset(alpha_plane.get(), format->alpha, w * h);
std::memset(alpha_plane.get(), format->alpha, w * h);
}
auto in_f = AV_PIX_FMT_YUV420P;
AVPixelFormat in_f = AV_PIX_FMT_YUV420P;
switch (f)
switch (frame->format)
{
case AV_PIX_FMT_YUV420P: in_f = alpha_plane ? AV_PIX_FMT_YUVA420P : AV_PIX_FMT_YUV420P; break;
default:
{
throw EXCEPTION("Unknown pix_fmt(%d)", f);
throw fmt::exception("Unknown format (%d)" HERE, frame->format);
}
}
@ -611,43 +629,54 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
{
cellVdec.trace("cellVdecGetPicItem(handle=0x%x, picItem=**0x%x)", handle, picItem);
const auto vdec = idm::get<VideoDecoder>(handle);
const auto vdec = std::dynamic_pointer_cast<vdec_thread>(idm::get<ppu_thread>(handle)); // TODO: avoid RTTI
if (!vdec)
{
return CELL_VDEC_ERROR_ARG;
}
VdecFrame vf;
if (!vdec->frames.try_peek(vf))
AVFrame* frame{};
u64 pts;
u64 dts;
u64 usrd;
u32 frc;
{
//std::this_thread::sleep_for(1ms); // hack
return CELL_VDEC_ERROR_EMPTY;
thread_lock lock(*vdec);
if (vdec->out.empty())
{
return CELL_VDEC_ERROR_EMPTY;
}
frame = vdec->out.front().avf.get();
pts = vdec->out.front().pts;
dts = vdec->out.front().dts;
usrd = vdec->out.front().userdata;
frc = vdec->out.front().frc;
}
AVFrame& frame = *vf.data;
const vm::ptr<CellVdecPicItem> info = vm::cast(vdec->mem_addr + vdec->mem_bias);
const vm::ptr<CellVdecPicItem> info = vm::cast(vdec->memAddr + vdec->memBias);
vdec->memBias += 512;
if (vdec->memBias + 512 > vdec->memSize)
vdec->mem_bias += 512;
if (vdec->mem_bias + 512 > vdec->mem_size)
{
vdec->memBias = 0;
vdec->mem_bias = 0;
}
info->codecType = vdec->type;
info->startAddr = 0x00000123; // invalid value (no address for picture)
info->size = align(av_image_get_buffer_size(vdec->ctx->pix_fmt, vdec->ctx->width, vdec->ctx->height, 1), 128);
info->auNum = 1;
info->auPts[0].lower = (u32)(vf.pts);
info->auPts[0].upper = (u32)(vf.pts >> 32);
info->auPts[0].lower = (u32)(pts);
info->auPts[0].upper = (u32)(pts >> 32);
info->auPts[1].lower = (u32)CODEC_TS_INVALID;
info->auPts[1].upper = (u32)CODEC_TS_INVALID;
info->auDts[0].lower = (u32)(vf.dts);
info->auDts[0].upper = (u32)(vf.dts >> 32);
info->auDts[0].lower = (u32)(dts);
info->auDts[0].upper = (u32)(dts >> 32);
info->auDts[1].lower = (u32)CODEC_TS_INVALID;
info->auDts[1].upper = (u32)CODEC_TS_INVALID;
info->auUserData[0] = vf.userdata;
info->auUserData[0] = usrd;
info->auUserData[1] = 0;
info->status = CELL_OK;
info->attr = CELL_VDEC_PICITEM_ATTR_NORMAL;
@ -657,15 +686,15 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
{
const vm::ptr<CellVdecAvcInfo> avc = vm::cast(info.addr() + SIZE_32(CellVdecPicItem));
avc->horizontalSize = frame.width;
avc->verticalSize = frame.height;
avc->horizontalSize = frame->width;
avc->verticalSize = frame->height;
switch (frame.pict_type)
switch (frame->pict_type)
{
case AV_PICTURE_TYPE_I: avc->pictureType[0] = CELL_VDEC_AVC_PCT_I; break;
case AV_PICTURE_TYPE_P: avc->pictureType[0] = CELL_VDEC_AVC_PCT_P; break;
case AV_PICTURE_TYPE_B: avc->pictureType[0] = CELL_VDEC_AVC_PCT_B; break;
default: cellVdec.error("cellVdecGetPicItem(AVC): unknown pict_type value (0x%x)", frame.pict_type);
default: cellVdec.error("cellVdecGetPicItem(AVC): unknown pict_type value (0x%x)", frame->pict_type);
}
avc->pictureType[1] = CELL_VDEC_AVC_PCT_UNKNOWN; // ???
@ -687,7 +716,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
avc->matrix_coefficients = CELL_VDEC_AVC_MXC_ITU_R_BT_709_5; // important
avc->timing_info_present_flag = true;
switch (vf.frc)
switch (frc)
{
case CELL_VDEC_FRC_24000DIV1001: avc->frameRateCode = CELL_VDEC_AVC_FRC_24000DIV1001; break;
case CELL_VDEC_FRC_24: avc->frameRateCode = CELL_VDEC_AVC_FRC_24; break;
@ -697,7 +726,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
case CELL_VDEC_FRC_50: avc->frameRateCode = CELL_VDEC_AVC_FRC_50; break;
case CELL_VDEC_FRC_60000DIV1001: avc->frameRateCode = CELL_VDEC_AVC_FRC_60000DIV1001; break;
case CELL_VDEC_FRC_60: avc->frameRateCode = CELL_VDEC_AVC_FRC_60; break;
default: cellVdec.error("cellVdecGetPicItem(AVC): unknown frc value (0x%x)", vf.frc);
default: cellVdec.error("cellVdecGetPicItem(AVC): unknown frc value (0x%x)", frc);
}
avc->fixed_frame_rate_flag = true;
@ -713,16 +742,16 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
{
const vm::ptr<CellVdecDivxInfo> dvx = vm::cast(info.addr() + SIZE_32(CellVdecPicItem));
switch (frame.pict_type)
switch (frame->pict_type)
{
case AV_PICTURE_TYPE_I: dvx->pictureType = CELL_VDEC_DIVX_VCT_I; break;
case AV_PICTURE_TYPE_P: dvx->pictureType = CELL_VDEC_DIVX_VCT_P; break;
case AV_PICTURE_TYPE_B: dvx->pictureType = CELL_VDEC_DIVX_VCT_B; break;
default: cellVdec.error("cellVdecGetPicItem(DivX): unknown pict_type value (0x%x)", frame.pict_type);
default: cellVdec.error("cellVdecGetPicItem(DivX): unknown pict_type value (0x%x)", frame->pict_type);
}
dvx->horizontalSize = frame.width;
dvx->verticalSize = frame.height;
dvx->horizontalSize = frame->width;
dvx->verticalSize = frame->height;
dvx->pixelAspectRatio = CELL_VDEC_DIVX_ARI_PAR_1_1; // ???
dvx->parHeight = 0;
dvx->parWidth = 0;
@ -732,7 +761,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
dvx->matrixCoefficients = CELL_VDEC_DIVX_MXC_ITU_R_BT_709; // ???
dvx->pictureStruct = CELL_VDEC_DIVX_PSTR_FRAME; // ???
switch (vf.frc)
switch (frc)
{
case CELL_VDEC_FRC_24000DIV1001: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_24000DIV1001; break;
case CELL_VDEC_FRC_24: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_24; break;
@ -742,7 +771,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
case CELL_VDEC_FRC_50: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_50; break;
case CELL_VDEC_FRC_60000DIV1001: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_60000DIV1001; break;
case CELL_VDEC_FRC_60: dvx->frameRateCode = CELL_VDEC_DIVX_FRC_60; break;
default: cellVdec.error("cellVdecGetPicItem(DivX): unknown frc value (0x%x)", vf.frc);
default: cellVdec.error("cellVdecGetPicItem(DivX): unknown frc value (0x%x)", frc);
}
}
else if (vdec->type == CELL_VDEC_CODEC_TYPE_MPEG2)
@ -750,11 +779,11 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
const vm::ptr<CellVdecMpeg2Info> mp2 = vm::cast(info.addr() + SIZE_32(CellVdecPicItem));
std::memset(mp2.get_ptr(), 0, sizeof(CellVdecMpeg2Info));
mp2->horizontal_size = frame.width;
mp2->vertical_size = frame.height;
mp2->horizontal_size = frame->width;
mp2->vertical_size = frame->height;
mp2->aspect_ratio_information = CELL_VDEC_MPEG2_ARI_SAR_1_1; // ???
switch (vf.frc)
switch (frc)
{
case CELL_VDEC_FRC_24000DIV1001: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_24000DIV1001; break;
case CELL_VDEC_FRC_24: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_24; break;
@ -764,7 +793,7 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
case CELL_VDEC_FRC_50: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_50; break;
case CELL_VDEC_FRC_60000DIV1001: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_60000DIV1001; break;
case CELL_VDEC_FRC_60: mp2->frame_rate_code = CELL_VDEC_MPEG2_FRC_60; break;
default: cellVdec.error("cellVdecGetPicItem(MPEG2): unknown frc value (0x%x)", vf.frc);
default: cellVdec.error("cellVdecGetPicItem(MPEG2): unknown frc value (0x%x)", frc);
}
mp2->progressive_sequence = true; // ???
@ -772,12 +801,12 @@ s32 cellVdecGetPicItem(u32 handle, vm::pptr<CellVdecPicItem> picItem)
mp2->video_format = CELL_VDEC_MPEG2_VF_UNSPECIFIED; // ???
mp2->colour_description = false; // ???
switch (frame.pict_type)
switch (frame->pict_type)
{
case AV_PICTURE_TYPE_I: mp2->picture_coding_type[0] = CELL_VDEC_MPEG2_PCT_I; break;
case AV_PICTURE_TYPE_P: mp2->picture_coding_type[0] = CELL_VDEC_MPEG2_PCT_P; break;
case AV_PICTURE_TYPE_B: mp2->picture_coding_type[0] = CELL_VDEC_MPEG2_PCT_B; break;
default: cellVdec.error("cellVdecGetPicItem(MPEG2): unknown pict_type value (0x%x)", frame.pict_type);
default: cellVdec.error("cellVdecGetPicItem(MPEG2): unknown pict_type value (0x%x)", frame->pict_type);
}
mp2->picture_coding_type[1] = CELL_VDEC_MPEG2_PCT_FORBIDDEN; // ???
@ -795,18 +824,16 @@ s32 cellVdecSetFrameRate(u32 handle, CellVdecFrameRate frc)
{
cellVdec.trace("cellVdecSetFrameRate(handle=0x%x, frc=0x%x)", handle, frc);
const auto vdec = idm::get<VideoDecoder>(handle);
const auto vdec = std::dynamic_pointer_cast<vdec_thread>(idm::get<ppu_thread>(handle)); // TODO: avoid RTTI
if (!vdec)
{
return CELL_VDEC_ERROR_ARG;
}
// TODO: check frc value and set frame rate
VdecTask task(vdecSetFrameRate);
task.frc = frc;
vdec->job.push(task, &vdec->is_closed);
// TODO: check frc value
vdec->cmd_push({vdec_cmd::set_frc, frc});
vdec->lock_notify();
return CELL_OK;
}

View File

@ -61,7 +61,7 @@ enum CellVdecPicAttr : s32
};
// Universal Frame Rate Code
enum CellVdecFrameRate : u8
enum CellVdecFrameRate : s32
{
CELL_VDEC_FRC_24000DIV1001 = 0x80,
CELL_VDEC_FRC_24 = 0x81,
@ -644,81 +644,3 @@ struct CellVdecMpeg2Info
u8 ccData[2][128];
be_t<u64> reserved[2];
};
/* Video Decoder Thread Classes */
enum VdecJobType : u32
{
vdecStartSeq,
vdecEndSeq,
vdecDecodeAu,
vdecSetFrameRate,
vdecClose,
};
struct VdecTask
{
VdecJobType type;
union
{
u32 frc;
CellVdecDecodeMode mode;
};
u32 addr;
u32 size;
u64 pts;
u64 dts;
u64 userData;
u64 specData;
VdecTask(VdecJobType type)
: type(type)
{
}
VdecTask()
{
}
};
struct VdecFrame
{
AVFrame* data;
u64 dts;
u64 pts;
u64 userdata;
u32 frc;
};
class VideoDecoder
{
public:
squeue_t<VdecTask> job;
u32 id;
volatile bool is_closed;
volatile bool is_finished;
struct AVCodec* codec;
struct AVCodecContext* ctx;
squeue_t<VdecFrame, 64> frames;
const s32 type;
const u32 profile;
const u32 memAddr;
const u32 memSize;
const vm::ptr<CellVdecCbMsg> cbFunc;
const u32 cbArg;
u32 memBias;
VdecTask task; // current task variable
u32 frc_set; // frame rate overwriting
u64 last_pts;
u64 last_dts;
std::shared_ptr<PPUThread> vdecCb;
VideoDecoder(s32 type, u32 profile, u32 addr, u32 size, vm::ptr<CellVdecCbMsg> func, u32 arg);
~VideoDecoder();
};

View File

@ -8,9 +8,46 @@
#include <cmath>
#include <thread>
#include <mutex>
logs::channel libmixer("libmixer", logs::level::notice);
struct SurMixerConfig
{
std::mutex mutex;
u32 audio_port;
s32 priority;
u32 ch_strips_1;
u32 ch_strips_2;
u32 ch_strips_6;
u32 ch_strips_8;
vm::ptr<CellSurMixerNotifyCallbackFunction> cb;
vm::ptr<void> cb_arg;
f32 mixdata[8 * 256];
u64 mixcount;
};
struct SSPlayer
{
bool m_created; // SSPlayerCreate/Remove
bool m_connected; // AANConnect/Disconnect
bool m_active; // SSPlayerPlay/Stop
u32 m_channels; // 1 or 2
u32 m_addr;
u32 m_samples;
u32 m_loop_start;
u32 m_loop_mode;
u32 m_position;
float m_level;
float m_speed;
float m_x;
float m_y;
float m_z;
};
// TODO: use fxm
SurMixerConfig g_surmx;
@ -284,48 +321,14 @@ s32 cellSSPlayerGetState(u32 handle)
return CELL_SSPLAYER_STATE_OFF;
}
s32 cellSurMixerCreate(vm::cptr<CellSurMixerConfig> config)
struct surmixer_thread : ppu_thread
{
libmixer.warning("cellSurMixerCreate(config=*0x%x)", config);
using ppu_thread::ppu_thread;
const auto g_audio = fxm::get<audio_config>();
const auto port = g_audio->open_port();
if (!port)
virtual void cpu_task() override
{
return CELL_LIBMIXER_ERROR_FULL;
}
const auto g_audio = fxm::get<audio_config>();
g_surmx.audio_port = port->number;
g_surmx.priority = config->priority;
g_surmx.ch_strips_1 = config->chStrips1;
g_surmx.ch_strips_2 = config->chStrips2;
g_surmx.ch_strips_6 = config->chStrips6;
g_surmx.ch_strips_8 = config->chStrips8;
port->channel = 8;
port->block = 16;
port->attr = 0;
port->size = port->channel * port->block * AUDIO_SAMPLES * sizeof(float);
port->tag = 0;
port->level = 1.0f;
port->level_set.store({ 1.0f, 0.0f });
libmixer.warning("*** audio port opened (port=%d)", g_surmx.audio_port);
g_surmx.mixcount = 0;
g_surmx.cb = vm::null;
g_ssp.clear();
libmixer.warning("*** surMixer created (ch1=%d, ch2=%d, ch6=%d, ch8=%d)", config->chStrips1, config->chStrips2, config->chStrips6, config->chStrips8);
const auto ppu = idm::make_ptr<PPUThread>("Surmixer Thread");
ppu->prio = 1001;
ppu->stack_size = 0x10000;
ppu->custom_task = [g_audio](PPUThread& ppu)
{
audio_port& port = g_audio->ports[g_surmx.audio_port];
while (port.state != audio_port_state::closed)
@ -345,7 +348,7 @@ s32 cellSurMixerCreate(vm::cptr<CellSurMixerConfig> config)
memset(g_surmx.mixdata, 0, sizeof(g_surmx.mixdata));
if (g_surmx.cb)
{
g_surmx.cb(ppu, g_surmx.cb_arg, (u32)g_surmx.mixcount, 256);
g_surmx.cb(*this, g_surmx.cb_arg, (u32)g_surmx.mixcount, 256);
}
//u64 stamp1 = get_system_time();
@ -445,12 +448,52 @@ s32 cellSurMixerCreate(vm::cptr<CellSurMixerConfig> config)
g_surmx.mixcount++;
}
idm::remove<PPUThread>(ppu.id);
};
idm::remove<ppu_thread>(id);
}
};
ppu->cpu_init();
ppu->state -= cpu_state::stop;
(*ppu)->lock_notify();
s32 cellSurMixerCreate(vm::cptr<CellSurMixerConfig> config)
{
libmixer.warning("cellSurMixerCreate(config=*0x%x)", config);
const auto g_audio = fxm::get<audio_config>();
const auto port = g_audio->open_port();
if (!port)
{
return CELL_LIBMIXER_ERROR_FULL;
}
g_surmx.audio_port = port->number;
g_surmx.priority = config->priority;
g_surmx.ch_strips_1 = config->chStrips1;
g_surmx.ch_strips_2 = config->chStrips2;
g_surmx.ch_strips_6 = config->chStrips6;
g_surmx.ch_strips_8 = config->chStrips8;
port->channel = 8;
port->block = 16;
port->attr = 0;
port->size = port->channel * port->block * AUDIO_SAMPLES * sizeof(float);
port->tag = 0;
port->level = 1.0f;
port->level_set.store({ 1.0f, 0.0f });
libmixer.warning("*** audio port opened (port=%d)", g_surmx.audio_port);
g_surmx.mixcount = 0;
g_surmx.cb = vm::null;
g_ssp.clear();
libmixer.warning("*** surMixer created (ch1=%d, ch2=%d, ch6=%d, ch8=%d)", config->chStrips1, config->chStrips2, config->chStrips6, config->chStrips8);
auto&& thread = std::make_shared<surmixer_thread>("Surmixer Thread");
idm::import_existing<ppu_thread>(thread);
thread->run();
return CELL_OK;
}

View File

@ -166,39 +166,3 @@ struct CellSurMixerChStripParam
be_t<float> floatVal;
be_t<s32> intVal;
};
struct SurMixerConfig
{
std::mutex mutex;
u32 audio_port;
s32 priority;
u32 ch_strips_1;
u32 ch_strips_2;
u32 ch_strips_6;
u32 ch_strips_8;
vm::ptr<CellSurMixerNotifyCallbackFunction> cb;
vm::ptr<void> cb_arg;
f32 mixdata[8 * 256];
u64 mixcount;
};
struct SSPlayer
{
bool m_created; // SSPlayerCreate/Remove
bool m_connected; // AANConnect/Disconnect
bool m_active; // SSPlayerPlay/Stop
u32 m_channels; // 1 or 2
u32 m_addr;
u32 m_samples;
u32 m_loop_start;
u32 m_loop_mode;
u32 m_position;
float m_level;
float m_speed;
float m_x;
float m_y;
float m_z;
};

View File

@ -53,7 +53,7 @@ s32 sceNp2Term()
return CELL_OK;
}
s32 sceNpMatching2Term(PPUThread& ppu)
s32 sceNpMatching2Term(ppu_thread& ppu)
{
sceNp2.warning("sceNpMatching2Term()");

View File

@ -136,7 +136,7 @@ s32 sceNpTrophyDestroyContext(u32 context)
return CELL_OK;
}
s32 sceNpTrophyRegisterContext(PPUThread& CPU, u32 context, u32 handle, vm::ptr<SceNpTrophyStatusCallback> statusCb, vm::ptr<u32> arg, u64 options)
s32 sceNpTrophyRegisterContext(ppu_thread& CPU, u32 context, u32 handle, vm::ptr<SceNpTrophyStatusCallback> statusCb, vm::ptr<u32> arg, u64 options)
{
sceNpTrophy.error("sceNpTrophyRegisterContext(context=0x%x, handle=0x%x, statusCb=*0x%x, arg=*0x%x, options=0x%llx)", context, handle, statusCb, arg, options);

View File

@ -58,12 +58,12 @@ void ppu_free_tls(u32 addr)
}
}
void sys_initialize_tls(PPUThread& ppu, u64 main_thread_id, u32 tls_seg_addr, u32 tls_seg_size, u32 tls_mem_size)
void sys_initialize_tls(ppu_thread& ppu, u64 main_thread_id, u32 tls_seg_addr, u32 tls_seg_size, u32 tls_mem_size)
{
sysPrxForUser.notice("sys_initialize_tls(thread_id=0x%llx, addr=*0x%x, size=0x%x, mem_size=0x%x)", main_thread_id, tls_seg_addr, tls_seg_size, tls_mem_size);
// Uninitialized TLS expected.
if (ppu.GPR[13] != 0) return;
if (ppu.gpr[13] != 0) return;
// Initialize TLS memory
s_tls_addr = tls_seg_addr;
@ -75,7 +75,7 @@ void sys_initialize_tls(PPUThread& ppu, u64 main_thread_id, u32 tls_seg_addr, u3
s_tls_map = std::make_unique<atomic_t<bool>[]>(s_tls_max);
// Allocate TLS for main thread
ppu.GPR[13] = ppu_alloc_tls() + 0x7000 + 0x30;
ppu.gpr[13] = ppu_alloc_tls() + 0x7000 + 0x30;
sysPrxForUser.notice("TLS initialized (addr=0x%x, size=0x%x, max=0x%x)", s_tls_area - 0x30, s_tls_size, s_tls_max);
@ -105,7 +105,7 @@ s64 _sys_process_at_Exitspawn()
return CELL_OK;
}
s32 sys_interrupt_thread_disestablish(PPUThread& ppu, u32 ih)
s32 sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih)
{
sysPrxForUser.notice("sys_interrupt_thread_disestablish(ih=0x%x)", ih);

View File

@ -17,19 +17,19 @@ struct sys_lwmutex_t;
struct sys_lwmutex_attribute_t;
s32 sys_lwmutex_create(vm::ptr<sys_lwmutex_t> lwmutex, vm::ptr<sys_lwmutex_attribute_t> attr);
s32 sys_lwmutex_lock(PPUThread& CPU, vm::ptr<sys_lwmutex_t> lwmutex, u64 timeout);
s32 sys_lwmutex_trylock(PPUThread& CPU, vm::ptr<sys_lwmutex_t> lwmutex);
s32 sys_lwmutex_unlock(PPUThread& CPU, vm::ptr<sys_lwmutex_t> lwmutex);
s32 sys_lwmutex_destroy(PPUThread& CPU, vm::ptr<sys_lwmutex_t> lwmutex);
s32 sys_lwmutex_lock(ppu_thread& CPU, vm::ptr<sys_lwmutex_t> lwmutex, u64 timeout);
s32 sys_lwmutex_trylock(ppu_thread& CPU, vm::ptr<sys_lwmutex_t> lwmutex);
s32 sys_lwmutex_unlock(ppu_thread& CPU, vm::ptr<sys_lwmutex_t> lwmutex);
s32 sys_lwmutex_destroy(ppu_thread& CPU, vm::ptr<sys_lwmutex_t> lwmutex);
struct sys_lwcond_t;
struct sys_lwcond_attribute_t;
s32 sys_lwcond_create(vm::ptr<sys_lwcond_t> lwcond, vm::ptr<sys_lwmutex_t> lwmutex, vm::ptr<sys_lwcond_attribute_t> attr);
s32 sys_lwcond_destroy(vm::ptr<sys_lwcond_t> lwcond);
s32 sys_lwcond_signal(PPUThread& CPU, vm::ptr<sys_lwcond_t> lwcond);
s32 sys_lwcond_signal_all(PPUThread& CPU, vm::ptr<sys_lwcond_t> lwcond);
s32 sys_lwcond_signal_to(PPUThread& CPU, vm::ptr<sys_lwcond_t> lwcond, u32 ppu_thread_id);
s32 sys_lwcond_wait(PPUThread& CPU, vm::ptr<sys_lwcond_t> lwcond, u64 timeout);
s32 sys_lwcond_signal(ppu_thread& CPU, vm::ptr<sys_lwcond_t> lwcond);
s32 sys_lwcond_signal_all(ppu_thread& CPU, vm::ptr<sys_lwcond_t> lwcond);
s32 sys_lwcond_signal_to(ppu_thread& CPU, vm::ptr<sys_lwcond_t> lwcond, u32 ppu_thread_id);
s32 sys_lwcond_wait(ppu_thread& CPU, vm::ptr<sys_lwcond_t> lwcond, u64 timeout);
void sys_ppu_thread_exit(PPUThread& CPU, u64 val);
void sys_ppu_thread_exit(ppu_thread& CPU, u64 val);

View File

@ -6,7 +6,7 @@ extern logs::channel sysPrxForUser;
extern fs::file g_tty;
// TODO
static std::string ps3_fmt(PPUThread& context, vm::cptr<char> fmt, u32 g_count)
static std::string ps3_fmt(ppu_thread& context, vm::cptr<char> fmt, u32 g_count)
{
std::string result;
@ -312,7 +312,7 @@ s32 _sys_free(u32 addr)
return CELL_OK;
}
s32 _sys_snprintf(PPUThread& ppu, vm::ptr<char> dst, u32 count, vm::cptr<char> fmt, ppu_va_args_t va_args)
s32 _sys_snprintf(ppu_thread& ppu, vm::ptr<char> dst, u32 count, vm::cptr<char> fmt, ppu_va_args_t va_args)
{
sysPrxForUser.warning("_sys_snprintf(dst=*0x%x, count=%d, fmt=*0x%x, ...)", dst, count, fmt);
@ -334,7 +334,7 @@ s32 _sys_snprintf(PPUThread& ppu, vm::ptr<char> dst, u32 count, vm::cptr<char> f
}
}
s32 _sys_printf(PPUThread& ppu, vm::cptr<char> fmt, ppu_va_args_t va_args)
s32 _sys_printf(ppu_thread& ppu, vm::cptr<char> fmt, ppu_va_args_t va_args)
{
sysPrxForUser.warning("_sys_printf(fmt=*0x%x, ...)", fmt);

View File

@ -33,7 +33,7 @@ s32 sys_lwcond_destroy(vm::ptr<sys_lwcond_t> lwcond)
return res;
}
s32 sys_lwcond_signal(PPUThread& ppu, vm::ptr<sys_lwcond_t> lwcond)
s32 sys_lwcond_signal(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
{
sysPrxForUser.trace("sys_lwcond_signal(lwcond=*0x%x)", lwcond);
@ -91,7 +91,7 @@ s32 sys_lwcond_signal(PPUThread& ppu, vm::ptr<sys_lwcond_t> lwcond)
return CELL_OK;
}
s32 sys_lwcond_signal_all(PPUThread& ppu, vm::ptr<sys_lwcond_t> lwcond)
s32 sys_lwcond_signal_all(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond)
{
sysPrxForUser.trace("sys_lwcond_signal_all(lwcond=*0x%x)", lwcond);
@ -148,7 +148,7 @@ s32 sys_lwcond_signal_all(PPUThread& ppu, vm::ptr<sys_lwcond_t> lwcond)
return res;
}
s32 sys_lwcond_signal_to(PPUThread& ppu, vm::ptr<sys_lwcond_t> lwcond, u32 ppu_thread_id)
s32 sys_lwcond_signal_to(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u32 ppu_thread_id)
{
sysPrxForUser.trace("sys_lwcond_signal_to(lwcond=*0x%x, ppu_thread_id=0x%x)", lwcond, ppu_thread_id);
@ -206,7 +206,7 @@ s32 sys_lwcond_signal_to(PPUThread& ppu, vm::ptr<sys_lwcond_t> lwcond, u32 ppu_t
return CELL_OK;
}
s32 sys_lwcond_wait(PPUThread& ppu, vm::ptr<sys_lwcond_t> lwcond, u64 timeout)
s32 sys_lwcond_wait(ppu_thread& ppu, vm::ptr<sys_lwcond_t> lwcond, u64 timeout)
{
sysPrxForUser.trace("sys_lwcond_wait(lwcond=*0x%x, timeout=0x%llx)", lwcond, timeout);

View File

@ -39,7 +39,7 @@ s32 sys_lwmutex_create(vm::ptr<sys_lwmutex_t> lwmutex, vm::ptr<sys_lwmutex_attri
return CELL_OK;
}
s32 sys_lwmutex_destroy(PPUThread& ppu, vm::ptr<sys_lwmutex_t> lwmutex)
s32 sys_lwmutex_destroy(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex)
{
sysPrxForUser.trace("sys_lwmutex_destroy(lwmutex=*0x%x)", lwmutex);
@ -70,7 +70,7 @@ s32 sys_lwmutex_destroy(PPUThread& ppu, vm::ptr<sys_lwmutex_t> lwmutex)
return CELL_OK;
}
s32 sys_lwmutex_lock(PPUThread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64 timeout)
s32 sys_lwmutex_lock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64 timeout)
{
sysPrxForUser.trace("sys_lwmutex_lock(lwmutex=*0x%x, timeout=0x%llx)", lwmutex, timeout);
@ -164,7 +164,7 @@ s32 sys_lwmutex_lock(PPUThread& ppu, vm::ptr<sys_lwmutex_t> lwmutex, u64 timeout
return res;
}
s32 sys_lwmutex_trylock(PPUThread& ppu, vm::ptr<sys_lwmutex_t> lwmutex)
s32 sys_lwmutex_trylock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex)
{
sysPrxForUser.trace("sys_lwmutex_trylock(lwmutex=*0x%x)", lwmutex);
@ -231,7 +231,7 @@ s32 sys_lwmutex_trylock(PPUThread& ppu, vm::ptr<sys_lwmutex_t> lwmutex)
return CELL_EBUSY;
}
s32 sys_lwmutex_unlock(PPUThread& ppu, vm::ptr<sys_lwmutex_t> lwmutex)
s32 sys_lwmutex_unlock(ppu_thread& ppu, vm::ptr<sys_lwmutex_t> lwmutex)
{
sysPrxForUser.trace("sys_lwmutex_unlock(lwmutex=*0x%x)", lwmutex);

View File

@ -8,6 +8,7 @@
#include <winsock2.h>
#include <WS2tcpip.h>
#else
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

View File

@ -57,7 +57,7 @@ s32 sys_ppu_thread_create(vm::ptr<u64> thread_id, u32 entry, u64 arg, s32 prio,
return CELL_OK;
}
s32 sys_ppu_thread_get_id(PPUThread& ppu, vm::ptr<u64> thread_id)
s32 sys_ppu_thread_get_id(ppu_thread& ppu, vm::ptr<u64> thread_id)
{
sysPrxForUser.trace("sys_ppu_thread_get_id(thread_id=*0x%x)", thread_id);
@ -66,7 +66,7 @@ s32 sys_ppu_thread_get_id(PPUThread& ppu, vm::ptr<u64> thread_id)
return CELL_OK;
}
void sys_ppu_thread_exit(PPUThread& ppu, u64 val)
void sys_ppu_thread_exit(ppu_thread& ppu, u64 val)
{
sysPrxForUser.trace("sys_ppu_thread_exit(val=0x%llx)", val);
@ -74,12 +74,12 @@ void sys_ppu_thread_exit(PPUThread& ppu, u64 val)
// ...
// Deallocate TLS
ppu_free_tls(vm::cast(ppu.GPR[13], HERE) - 0x7030);
ppu_free_tls(vm::cast(ppu.gpr[13], HERE) - 0x7030);
if (ppu.GPR[3] == val && !ppu.custom_task)
if (ppu.gpr[3] == val)
{
// Change sys_ppu_thread_exit code to the syscall code (hack)
ppu.GPR[11] = 41;
ppu.gpr[11] = 41;
}
// Call the syscall
@ -88,7 +88,7 @@ void sys_ppu_thread_exit(PPUThread& ppu, u64 val)
std::mutex g_once_mutex;
void sys_ppu_thread_once(PPUThread& ppu, vm::ptr<atomic_be_t<u32>> once_ctrl, vm::ptr<void()> init)
void sys_ppu_thread_once(ppu_thread& ppu, vm::ptr<atomic_be_t<u32>> once_ctrl, vm::ptr<void()> init)
{
sysPrxForUser.warning("sys_ppu_thread_once(once_ctrl=*0x%x, init=*0x%x)", once_ctrl, init);

View File

@ -93,7 +93,7 @@ s32 sys_raw_spu_load(s32 id, vm::cptr<char> path, vm::ptr<u32> entry)
return CELL_OK;
}
s32 sys_raw_spu_image_load(PPUThread& ppu, s32 id, vm::ptr<sys_spu_image_t> img)
s32 sys_raw_spu_image_load(ppu_thread& ppu, s32 id, vm::ptr<sys_spu_image_t> img)
{
sysPrxForUser.warning("sys_raw_spu_image_load(id=%d, img=*0x%x)", id, img);
@ -140,7 +140,7 @@ s32 _sys_spu_printf_finalize()
return CELL_OK;
}
s32 _sys_spu_printf_attach_group(PPUThread& ppu, u32 group)
s32 _sys_spu_printf_attach_group(ppu_thread& ppu, u32 group)
{
sysPrxForUser.warning("_sys_spu_printf_attach_group(group=0x%x)", group);
@ -152,7 +152,7 @@ s32 _sys_spu_printf_attach_group(PPUThread& ppu, u32 group)
return g_spu_printf_agcb(ppu, group);
}
s32 _sys_spu_printf_detach_group(PPUThread& ppu, u32 group)
s32 _sys_spu_printf_detach_group(ppu_thread& ppu, u32 group)
{
sysPrxForUser.warning("_sys_spu_printf_detach_group(group=0x%x)", group);
@ -164,7 +164,7 @@ s32 _sys_spu_printf_detach_group(PPUThread& ppu, u32 group)
return g_spu_printf_dgcb(ppu, group);
}
s32 _sys_spu_printf_attach_thread(PPUThread& ppu, u32 thread)
s32 _sys_spu_printf_attach_thread(ppu_thread& ppu, u32 thread)
{
sysPrxForUser.warning("_sys_spu_printf_attach_thread(thread=0x%x)", thread);
@ -176,7 +176,7 @@ s32 _sys_spu_printf_attach_thread(PPUThread& ppu, u32 thread)
return g_spu_printf_atcb(ppu, thread);
}
s32 _sys_spu_printf_detach_thread(PPUThread& ppu, u32 thread)
s32 _sys_spu_printf_detach_thread(ppu_thread& ppu, u32 thread)
{
sysPrxForUser.warning("_sys_spu_printf_detach_thread(thread=0x%x)", thread);

View File

@ -403,6 +403,10 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
const u32 addr = ptr[0];
const u32 _toc = ptr[1];
// Rough Table of Contents borders
const u32 _toc_begin = _toc - 0x8000;
const u32 _toc_end = _toc + 0x8000;
// TODO: improve TOC constraints
if (_toc % 4 || _toc == 0 || _toc >= 0x40000000 || (_toc >= start && _toc < end))
{
@ -436,8 +440,8 @@ std::vector<ppu_function> ppu_analyse(const std::vector<std::pair<u32, u32>>& se
}
}
// Secondary attempt (TODO)
if (secs.empty() && lib_toc)
// Secondary attempt (TODO, needs better strategy)
if (/*secs.empty() &&*/ lib_toc)
{
add_toc(lib_toc);
}

View File

@ -1,103 +0,0 @@
#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "PPUThread.h"
#include "PPUCallback.h"
#include <condition_variable>
void CallbackManager::Register(check_cb_t func)
{
std::lock_guard<std::mutex> lock(m_mutex);
m_check_cb.emplace(std::move(func));
}
void CallbackManager::Async(async_cb_t func)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!m_cb_thread)
{
throw EXCEPTION("Callback thread not found");
}
m_async_cb.emplace(std::move(func));
(*m_cb_thread)->notify();
}
extern std::condition_variable& get_current_thread_cv();
CallbackManager::check_cb_t CallbackManager::Check()
{
std::lock_guard<std::mutex> lock(m_mutex);
if (m_check_cb.size())
{
check_cb_t func = std::move(m_check_cb.front());
m_check_cb.pop();
return func;
}
return nullptr;
}
void CallbackManager::Init()
{
std::lock_guard<std::mutex> lock(m_mutex);
auto task = [this](PPUThread& ppu)
{
std::unique_lock<std::mutex> lock(m_mutex);
while (true)
{
CHECK_EMU_STATUS;
if (!lock)
{
lock.lock();
continue;
}
if (m_async_cb.size())
{
async_cb_t func = std::move(m_async_cb.front());
m_async_cb.pop();
if (lock) lock.unlock();
func(ppu);
continue;
}
get_current_thread_cv().wait(lock);
}
};
auto thread = idm::make_ptr<PPUThread>("Callback Thread");
thread->prio = 1001;
thread->stack_size = 0x10000;
thread->custom_task = task;
thread->cpu_init();
m_cb_thread = thread;
}
void CallbackManager::Clear()
{
std::lock_guard<std::mutex> lock(m_mutex);
m_check_cb = decltype(m_check_cb){};
m_async_cb = decltype(m_async_cb){};
m_cb_thread.reset();
}

View File

@ -27,9 +27,9 @@ namespace ppu_cb_detail
static_assert(!std::is_reference<T>::value, "Invalid callback argument type (reference)");
static_assert(sizeof(T) <= 8, "Invalid callback argument type for ARG_GENERAL");
static inline void set_value(PPUThread& CPU, const T& arg)
static inline void set_value(ppu_thread& CPU, const T& arg)
{
CPU.GPR[g_count + 2] = ppu_gpr_cast(arg);
CPU.gpr[g_count + 2] = ppu_gpr_cast(arg);
}
};
@ -38,9 +38,9 @@ namespace ppu_cb_detail
{
static_assert(sizeof(T) <= 8, "Invalid callback argument type for ARG_FLOAT");
static inline void set_value(PPUThread& CPU, const T& arg)
static inline void set_value(ppu_thread& CPU, const T& arg)
{
CPU.FPR[f_count] = static_cast<T>(arg);
CPU.fpr[f_count] = static_cast<T>(arg);
}
};
@ -49,9 +49,9 @@ namespace ppu_cb_detail
{
static_assert(std::is_same<CV T, CV v128>::value, "Invalid callback argument type for ARG_VECTOR");
static inline void set_value(PPUThread& CPU, const T& arg)
static inline void set_value(ppu_thread& CPU, const T& arg)
{
CPU.VR[v_count + 1] = arg;
CPU.vr[v_count + 1] = arg;
}
};
@ -60,37 +60,37 @@ namespace ppu_cb_detail
{
static_assert(alignof(T) <= 16, "Unsupported callback argument type alignment for ARG_STACK");
static inline void set_value(PPUThread& CPU, const T& arg)
static inline void set_value(ppu_thread& CPU, const T& arg)
{
const s64 stack_pos = (g_count - 1) * 0x8 + 0x30 - FIXED_STACK_FRAME_SIZE;
static_assert(stack_pos < 0, "TODO: Increase FIXED_STACK_FRAME_SIZE (arg count limit broken)");
vm::ps3::write64(CPU.GPR[1] + stack_pos, ppu_gpr_cast(arg)); // TODO
vm::ps3::write64(CPU.gpr[1] + stack_pos, ppu_gpr_cast(arg)); // TODO
}
};
template<typename T, u32 g_count, u32 f_count, u32 v_count>
struct _func_arg<T, ARG_CONTEXT, g_count, f_count, v_count>
{
static_assert(std::is_same<T, PPUThread&>::value, "Invalid callback argument type for ARG_CONTEXT");
static_assert(std::is_same<T, ppu_thread&>::value, "Invalid callback argument type for ARG_CONTEXT");
force_inline static void set_value(PPUThread& CPU, const T& arg)
force_inline static void set_value(ppu_thread& CPU, const T& arg)
{
}
};
template<u32 g_count, u32 f_count, u32 v_count>
force_inline static bool _bind_func_args(PPUThread& CPU)
force_inline static bool _bind_func_args(ppu_thread& CPU)
{
// terminator
return false;
}
template<u32 g_count, u32 f_count, u32 v_count, typename T1, typename... T>
force_inline static bool _bind_func_args(PPUThread& CPU, T1 arg1, T... args)
force_inline static bool _bind_func_args(ppu_thread& CPU, T1 arg1, T... args)
{
const bool is_float = std::is_floating_point<T1>::value;
const bool is_vector = std::is_same<CV T1, CV v128>::value;
const bool is_context = std::is_same<T1, PPUThread&>::value;
const bool is_context = std::is_same<T1, ppu_thread&>::value;
const bool is_general = !is_float && !is_vector && !is_context;
const _func_arg_type t =
@ -116,9 +116,9 @@ namespace ppu_cb_detail
static_assert(type == ARG_GENERAL, "Unknown callback result type");
static_assert(sizeof(T) <= 8, "Invalid callback result type for ARG_GENERAL");
force_inline static T get_value(const PPUThread& CPU)
force_inline static T get_value(const ppu_thread& CPU)
{
return ppu_gpr_cast<T>(CPU.GPR[3]);
return ppu_gpr_cast<T>(CPU.gpr[3]);
}
};
@ -127,9 +127,9 @@ namespace ppu_cb_detail
{
static_assert(sizeof(T) <= 8, "Invalid callback result type for ARG_FLOAT");
force_inline static T get_value(const PPUThread& CPU)
force_inline static T get_value(const ppu_thread& CPU)
{
return static_cast<T>(CPU.FPR[1]);
return static_cast<T>(CPU.fpr[1]);
}
};
@ -138,16 +138,16 @@ namespace ppu_cb_detail
{
static_assert(std::is_same<CV T, CV v128>::value, "Invalid callback result type for ARG_VECTOR");
force_inline static T get_value(const PPUThread& CPU)
force_inline static T get_value(const ppu_thread& CPU)
{
return CPU.VR[2];
return CPU.vr[2];
}
};
template<typename RT, typename... T>
struct _func_caller
{
force_inline static RT call(PPUThread& CPU, u32 pc, u32 rtoc, T... args)
force_inline static RT call(ppu_thread& CPU, u32 pc, u32 rtoc, T... args)
{
_func_caller<void, T...>::call(CPU, pc, rtoc, args...);
@ -164,12 +164,12 @@ namespace ppu_cb_detail
template<typename... T>
struct _func_caller<void, T...>
{
force_inline static void call(PPUThread& CPU, u32 pc, u32 rtoc, T... args)
force_inline static void call(ppu_thread& CPU, u32 pc, u32 rtoc, T... args)
{
const bool stack = _bind_func_args<0, 0, 0, T...>(CPU, args...);
CPU.GPR[1] -= stack ? FIXED_STACK_FRAME_SIZE : 0x30; // create reserved area
CPU.gpr[1] -= stack ? FIXED_STACK_FRAME_SIZE : 0x30; // create reserved area
CPU.fast_call(pc, rtoc);
CPU.GPR[1] += stack ? FIXED_STACK_FRAME_SIZE : 0x30;
CPU.gpr[1] += stack ? FIXED_STACK_FRAME_SIZE : 0x30;
}
};
}
@ -177,7 +177,7 @@ namespace ppu_cb_detail
namespace vm
{
template<typename AT, typename RT, typename... T>
force_inline RT _ptr_base<RT(T...), AT>::operator()(PPUThread& CPU, T... args) const
force_inline RT _ptr_base<RT(T...), AT>::operator()(ppu_thread& CPU, T... args) const
{
const auto data = vm::ps3::_ptr<u32>(vm::cast(m_addr, HERE));
const u32 pc = data[0];
@ -187,37 +187,7 @@ namespace vm
}
}
template<typename RT, typename... T> inline RT cb_call(PPUThread& CPU, u32 pc, u32 rtoc, T... args)
template<typename RT, typename... T> inline RT cb_call(ppu_thread& CPU, u32 pc, u32 rtoc, T... args)
{
return ppu_cb_detail::_func_caller<RT, T...>::call(CPU, pc, rtoc, args...);
}
#include <queue>
#include <mutex>
class CallbackManager
{
using check_cb_t = std::function<s32(PPUThread&)>;
using async_cb_t = std::function<void(PPUThread&)>;
std::mutex m_mutex;
std::queue<check_cb_t> m_check_cb;
std::queue<async_cb_t> m_async_cb;
std::shared_ptr<PPUThread> m_cb_thread;
public:
// Register checked callback
void Register(check_cb_t func);
// Register async callback, called in callback thread
void Async(async_cb_t func);
// Get one registered callback
check_cb_t Check();
void Init();
void Clear();
};

View File

@ -2361,7 +2361,7 @@ s32 ppu_error_code::report(s32 error, const char* text)
{
if (thread->type == cpu_type::ppu)
{
if (auto func = static_cast<PPUThread*>(thread)->last_function)
if (auto func = static_cast<ppu_thread*>(thread)->last_function)
{
LOG_ERROR(PPU, "'%s' failed with 0x%08x : %s", func, error, text);
}
@ -2383,7 +2383,7 @@ std::vector<ppu_function_t>& ppu_function_manager::access()
static std::vector<ppu_function_t> list
{
nullptr,
[](PPUThread& ppu) { ppu.state += cpu_state::ret; },
[](ppu_thread& ppu) { ppu.state += cpu_state::ret; },
};
return list;

View File

@ -2,14 +2,14 @@
#include "PPUThread.h"
using ppu_function_t = void(*)(PPUThread&);
using ppu_function_t = void(*)(ppu_thread&);
// BIND_FUNC macro "converts" any appropriate HLE function to ppu_function_t, binding it to PPU thread context.
// If function already has type ppu_function_t, it's handled specially and classified as "low-level HLE function".
// 1) Low-level functions are bound directly so they don't save their name to ppu.last_function variable.
// 2) Low-level functions don't install thread_guar, so they are very limited, and may be dangerous.
// If you don't need "low-level function", be sure it's either `void()` or `void(PPUThread& ppu, PPUThread&)` for example.
#define BIND_FUNC(func) (std::is_same<decltype(func), ppu_function_t>::value ? reinterpret_cast<ppu_function_t>(func) : static_cast<ppu_function_t>([](PPUThread& ppu){\
// If you don't need "low-level function", be sure it's either `void()` or `void(ppu_thread& ppu, ppu_thread&)` for example.
#define BIND_FUNC(func) (std::is_same<decltype(func), ppu_function_t>::value ? reinterpret_cast<ppu_function_t>(func) : static_cast<ppu_function_t>([](ppu_thread& ppu){\
const thread_guard guard(ppu);\
const auto old_f = ppu.last_function;\
ppu.last_function = #func;\
@ -27,11 +27,11 @@ namespace ppu_func_detail
// argument type classification
enum arg_class : u32
{
ARG_GENERAL, // argument stored in GPR (from r3 to r10)
ARG_FLOAT, // argument stored in FPR (from f1 to f13)
ARG_VECTOR, // argument stored in VR (from v2 to v13)
ARG_GENERAL, // argument stored in gpr (from r3 to r10)
ARG_FLOAT, // argument stored in fpr (from f1 to f13)
ARG_VECTOR, // argument stored in vr (from v2 to v13)
ARG_STACK, // argument stored on the stack
ARG_CONTEXT, // PPUThread& passed, doesn't affect g/f/v_count
ARG_CONTEXT, // ppu_thread& passed, doesn't affect g/f/v_count
ARG_VARIADIC, // argument count at specific position, doesn't affect g/f/v_count
ARG_UNKNOWN,
};
@ -44,9 +44,9 @@ namespace ppu_func_detail
static_assert(!std::is_reference<T>::value, "Invalid function argument type (reference)");
static_assert(sizeof(T) <= 8, "Invalid function argument type for ARG_GENERAL");
static inline T get_arg(PPUThread& ppu)
static inline T get_arg(ppu_thread& ppu)
{
return ppu_gpr_cast<T>(ppu.GPR[g_count + 2]);
return ppu_gpr_cast<T>(ppu.gpr[g_count + 2]);
}
};
@ -55,9 +55,9 @@ namespace ppu_func_detail
{
static_assert(sizeof(T) <= 8, "Invalid function argument type for ARG_FLOAT");
static inline T get_arg(PPUThread& ppu)
static inline T get_arg(ppu_thread& ppu)
{
return static_cast<T>(ppu.FPR[f_count]);
return static_cast<T>(ppu.fpr[f_count]);
}
};
@ -66,9 +66,9 @@ namespace ppu_func_detail
{
static_assert(std::is_same<CV T, CV v128>::value, "Invalid function argument type for ARG_VECTOR");
static force_inline T get_arg(PPUThread& ppu)
static force_inline T get_arg(ppu_thread& ppu)
{
return ppu.VR[v_count + 1];
return ppu.vr[v_count + 1];
}
};
@ -77,7 +77,7 @@ namespace ppu_func_detail
{
static_assert(alignof(T) <= 16, "Unsupported type alignment for ARG_STACK");
static force_inline T get_arg(PPUThread& ppu)
static force_inline T get_arg(ppu_thread& ppu)
{
return ppu_gpr_cast<T, u64>(*ppu.get_stack_arg(g_count, alignof(T))); // TODO
}
@ -86,9 +86,9 @@ namespace ppu_func_detail
template<typename T, u32 g_count, u32 f_count, u32 v_count>
struct bind_arg<T, ARG_CONTEXT, g_count, f_count, v_count>
{
static_assert(std::is_same<T, PPUThread&>::value, "Invalid function argument type for ARG_CONTEXT");
static_assert(std::is_same<T, ppu_thread&>::value, "Invalid function argument type for ARG_CONTEXT");
static force_inline PPUThread& get_arg(PPUThread& ppu)
static force_inline ppu_thread& get_arg(ppu_thread& ppu)
{
return ppu;
}
@ -99,7 +99,7 @@ namespace ppu_func_detail
{
static_assert(std::is_same<T, ppu_va_args_t>::value, "Invalid function argument type for ARG_VARIADIC");
static force_inline ppu_va_args_t get_arg(PPUThread& ppu)
static force_inline ppu_va_args_t get_arg(ppu_thread& ppu)
{
return{ g_count };
}
@ -111,9 +111,9 @@ namespace ppu_func_detail
static_assert(type == ARG_GENERAL, "Unknown function result type");
static_assert(sizeof(T) <= 8, "Invalid function result type for ARG_GENERAL");
static force_inline void put_result(PPUThread& ppu, const T& result)
static force_inline void put_result(ppu_thread& ppu, const T& result)
{
ppu.GPR[3] = ppu_gpr_cast(result);
ppu.gpr[3] = ppu_gpr_cast(result);
}
};
@ -122,9 +122,9 @@ namespace ppu_func_detail
{
static_assert(sizeof(T) <= 8, "Invalid function result type for ARG_FLOAT");
static force_inline void put_result(PPUThread& ppu, const T& result)
static force_inline void put_result(ppu_thread& ppu, const T& result)
{
ppu.FPR[1] = static_cast<T>(result);
ppu.fpr[1] = static_cast<T>(result);
}
};
@ -133,9 +133,9 @@ namespace ppu_func_detail
{
static_assert(std::is_same<CV T, CV v128>::value, "Invalid function result type for ARG_VECTOR");
static force_inline void put_result(PPUThread& ppu, const T& result)
static force_inline void put_result(ppu_thread& ppu, const T& result)
{
ppu.VR[2] = result;
ppu.vr[2] = result;
}
};
@ -160,21 +160,21 @@ namespace ppu_func_detail
// argument type + g/f/v_count unpacker
template<typename T, u32 type_pack> struct bind_arg_packed
{
static force_inline T get_arg(PPUThread& ppu)
static force_inline T get_arg(ppu_thread& ppu)
{
return bind_arg<T, static_cast<arg_class>(type_pack & 0xff), (type_pack >> 8) & 0xff, (type_pack >> 16) & 0xff, (type_pack >> 24)>::get_arg(ppu);
}
};
template<u32... Info, typename RT, typename... Args>
force_inline RT call(PPUThread& ppu, RT(*func)(Args...), arg_info_pack_t<Info...>)
force_inline RT call(ppu_thread& ppu, RT(*func)(Args...), arg_info_pack_t<Info...>)
{
// do the actual function call when all arguments are prepared (simultaneous unpacking of Args... and Info...)
return func(bind_arg_packed<Args, Info>::get_arg(ppu)...);
}
template<typename T, typename... Types, u32... Info, typename RT, typename... Args>
force_inline RT call(PPUThread& ppu, RT(*func)(Args...), arg_info_pack_t<Info...> info)
force_inline RT call(ppu_thread& ppu, RT(*func)(Args...), arg_info_pack_t<Info...> info)
{
// unpack previous type counts (0/0/0 for the first time)
const u32 g_count = (info.last_value >> 8) & 0xff;
@ -184,7 +184,7 @@ namespace ppu_func_detail
// TODO: check calculations
const bool is_float = std::is_floating_point<T>::value;
const bool is_vector = std::is_same<CV T, CV v128>::value;
const bool is_context = std::is_same<T, PPUThread&>::value;
const bool is_context = std::is_same<T, ppu_thread&>::value;
const bool is_variadic = std::is_same<CV T, CV ppu_va_args_t>::value;
const bool is_general = !is_float && !is_vector && !is_context && !is_variadic;
@ -220,7 +220,7 @@ namespace ppu_func_detail
{
using func_t = void(*)(T...);
static force_inline void do_call(PPUThread& ppu, func_t func)
static force_inline void do_call(ppu_thread& ppu, func_t func)
{
call<T...>(ppu, func, arg_info_pack_t<>{});
}
@ -231,14 +231,14 @@ namespace ppu_func_detail
{
using func_t = RT(*)(T...);
static force_inline void do_call(PPUThread& ppu, func_t func)
static force_inline void do_call(ppu_thread& ppu, func_t func)
{
bind_result<RT, result_type<RT>::value>::put_result(ppu, call<T...>(ppu, func, arg_info_pack_t<>{}));
}
};
template<typename RT, typename... T>
force_inline void do_call(PPUThread& ppu, RT(*func)(T...))
force_inline void do_call(ppu_thread& ppu, RT(*func)(T...))
{
func_binder<RT, T...>::do_call(ppu, func);
}

File diff suppressed because it is too large Load Diff

View File

@ -2,392 +2,392 @@
#include "PPUOpcodes.h"
class PPUThread;
class ppu_thread;
struct ppu_interpreter
{
static bool MFVSCR(PPUThread&, ppu_opcode_t);
static bool MTVSCR(PPUThread&, ppu_opcode_t);
static bool VADDCUW(PPUThread&, ppu_opcode_t);
static bool VADDFP(PPUThread&, ppu_opcode_t);
static bool VADDSBS(PPUThread&, ppu_opcode_t);
static bool VADDSHS(PPUThread&, ppu_opcode_t);
static bool VADDSWS(PPUThread&, ppu_opcode_t);
static bool VADDUBM(PPUThread&, ppu_opcode_t);
static bool VADDUBS(PPUThread&, ppu_opcode_t);
static bool VADDUHM(PPUThread&, ppu_opcode_t);
static bool VADDUHS(PPUThread&, ppu_opcode_t);
static bool VADDUWM(PPUThread&, ppu_opcode_t);
static bool VADDUWS(PPUThread&, ppu_opcode_t);
static bool VAND(PPUThread&, ppu_opcode_t);
static bool VANDC(PPUThread&, ppu_opcode_t);
static bool VAVGSB(PPUThread&, ppu_opcode_t);
static bool VAVGSH(PPUThread&, ppu_opcode_t);
static bool VAVGSW(PPUThread&, ppu_opcode_t);
static bool VAVGUB(PPUThread&, ppu_opcode_t);
static bool VAVGUH(PPUThread&, ppu_opcode_t);
static bool VAVGUW(PPUThread&, ppu_opcode_t);
static bool VCFSX(PPUThread&, ppu_opcode_t);
static bool VCFUX(PPUThread&, ppu_opcode_t);
static bool VCMPBFP(PPUThread&, ppu_opcode_t);
static bool VCMPEQFP(PPUThread&, ppu_opcode_t);
static bool VCMPEQUB(PPUThread&, ppu_opcode_t);
static bool VCMPEQUH(PPUThread&, ppu_opcode_t);
static bool VCMPEQUW(PPUThread&, ppu_opcode_t);
static bool VCMPGEFP(PPUThread&, ppu_opcode_t);
static bool VCMPGTFP(PPUThread&, ppu_opcode_t);
static bool VCMPGTSB(PPUThread&, ppu_opcode_t);
static bool VCMPGTSH(PPUThread&, ppu_opcode_t);
static bool VCMPGTSW(PPUThread&, ppu_opcode_t);
static bool VCMPGTUB(PPUThread&, ppu_opcode_t);
static bool VCMPGTUH(PPUThread&, ppu_opcode_t);
static bool VCMPGTUW(PPUThread&, ppu_opcode_t);
static bool VCTSXS(PPUThread&, ppu_opcode_t);
static bool VCTUXS(PPUThread&, ppu_opcode_t);
static bool VEXPTEFP(PPUThread&, ppu_opcode_t);
static bool VLOGEFP(PPUThread&, ppu_opcode_t);
static bool VMADDFP(PPUThread&, ppu_opcode_t);
static bool VMAXFP(PPUThread&, ppu_opcode_t);
static bool VMAXSB(PPUThread&, ppu_opcode_t);
static bool VMAXSH(PPUThread&, ppu_opcode_t);
static bool VMAXSW(PPUThread&, ppu_opcode_t);
static bool VMAXUB(PPUThread&, ppu_opcode_t);
static bool VMAXUH(PPUThread&, ppu_opcode_t);
static bool VMAXUW(PPUThread&, ppu_opcode_t);
static bool VMHADDSHS(PPUThread&, ppu_opcode_t);
static bool VMHRADDSHS(PPUThread&, ppu_opcode_t);
static bool VMINFP(PPUThread&, ppu_opcode_t);
static bool VMINSB(PPUThread&, ppu_opcode_t);
static bool VMINSH(PPUThread&, ppu_opcode_t);
static bool VMINSW(PPUThread&, ppu_opcode_t);
static bool VMINUB(PPUThread&, ppu_opcode_t);
static bool VMINUH(PPUThread&, ppu_opcode_t);
static bool VMINUW(PPUThread&, ppu_opcode_t);
static bool VMLADDUHM(PPUThread&, ppu_opcode_t);
static bool VMRGHB(PPUThread&, ppu_opcode_t);
static bool VMRGHH(PPUThread&, ppu_opcode_t);
static bool VMRGHW(PPUThread&, ppu_opcode_t);
static bool VMRGLB(PPUThread&, ppu_opcode_t);
static bool VMRGLH(PPUThread&, ppu_opcode_t);
static bool VMRGLW(PPUThread&, ppu_opcode_t);
static bool VMSUMMBM(PPUThread&, ppu_opcode_t);
static bool VMSUMSHM(PPUThread&, ppu_opcode_t);
static bool VMSUMSHS(PPUThread&, ppu_opcode_t);
static bool VMSUMUBM(PPUThread&, ppu_opcode_t);
static bool VMSUMUHM(PPUThread&, ppu_opcode_t);
static bool VMSUMUHS(PPUThread&, ppu_opcode_t);
static bool VMULESB(PPUThread&, ppu_opcode_t);
static bool VMULESH(PPUThread&, ppu_opcode_t);
static bool VMULEUB(PPUThread&, ppu_opcode_t);
static bool VMULEUH(PPUThread&, ppu_opcode_t);
static bool VMULOSB(PPUThread&, ppu_opcode_t);
static bool VMULOSH(PPUThread&, ppu_opcode_t);
static bool VMULOUB(PPUThread&, ppu_opcode_t);
static bool VMULOUH(PPUThread&, ppu_opcode_t);
static bool VNMSUBFP(PPUThread&, ppu_opcode_t);
static bool VNOR(PPUThread&, ppu_opcode_t);
static bool VOR(PPUThread&, ppu_opcode_t);
static bool VPERM(PPUThread&, ppu_opcode_t);
static bool VPKPX(PPUThread&, ppu_opcode_t);
static bool VPKSHSS(PPUThread&, ppu_opcode_t);
static bool VPKSHUS(PPUThread&, ppu_opcode_t);
static bool VPKSWSS(PPUThread&, ppu_opcode_t);
static bool VPKSWUS(PPUThread&, ppu_opcode_t);
static bool VPKUHUM(PPUThread&, ppu_opcode_t);
static bool VPKUHUS(PPUThread&, ppu_opcode_t);
static bool VPKUWUM(PPUThread&, ppu_opcode_t);
static bool VPKUWUS(PPUThread&, ppu_opcode_t);
static bool VREFP(PPUThread&, ppu_opcode_t);
static bool VRFIM(PPUThread&, ppu_opcode_t);
static bool VRFIN(PPUThread&, ppu_opcode_t);
static bool VRFIP(PPUThread&, ppu_opcode_t);
static bool VRFIZ(PPUThread&, ppu_opcode_t);
static bool VRLB(PPUThread&, ppu_opcode_t);
static bool VRLH(PPUThread&, ppu_opcode_t);
static bool VRLW(PPUThread&, ppu_opcode_t);
static bool VRSQRTEFP(PPUThread&, ppu_opcode_t);
static bool VSEL(PPUThread&, ppu_opcode_t);
static bool VSL(PPUThread&, ppu_opcode_t);
static bool VSLB(PPUThread&, ppu_opcode_t);
static bool VSLDOI(PPUThread&, ppu_opcode_t);
static bool VSLH(PPUThread&, ppu_opcode_t);
static bool VSLO(PPUThread&, ppu_opcode_t);
static bool VSLW(PPUThread&, ppu_opcode_t);
static bool VSPLTB(PPUThread&, ppu_opcode_t);
static bool VSPLTH(PPUThread&, ppu_opcode_t);
static bool VSPLTISB(PPUThread&, ppu_opcode_t);
static bool VSPLTISH(PPUThread&, ppu_opcode_t);
static bool VSPLTISW(PPUThread&, ppu_opcode_t);
static bool VSPLTW(PPUThread&, ppu_opcode_t);
static bool VSR(PPUThread&, ppu_opcode_t);
static bool VSRAB(PPUThread&, ppu_opcode_t);
static bool VSRAH(PPUThread&, ppu_opcode_t);
static bool VSRAW(PPUThread&, ppu_opcode_t);
static bool VSRB(PPUThread&, ppu_opcode_t);
static bool VSRH(PPUThread&, ppu_opcode_t);
static bool VSRO(PPUThread&, ppu_opcode_t);
static bool VSRW(PPUThread&, ppu_opcode_t);
static bool VSUBCUW(PPUThread&, ppu_opcode_t);
static bool VSUBFP(PPUThread&, ppu_opcode_t);
static bool VSUBSBS(PPUThread&, ppu_opcode_t);
static bool VSUBSHS(PPUThread&, ppu_opcode_t);
static bool VSUBSWS(PPUThread&, ppu_opcode_t);
static bool VSUBUBM(PPUThread&, ppu_opcode_t);
static bool VSUBUBS(PPUThread&, ppu_opcode_t);
static bool VSUBUHM(PPUThread&, ppu_opcode_t);
static bool VSUBUHS(PPUThread&, ppu_opcode_t);
static bool VSUBUWM(PPUThread&, ppu_opcode_t);
static bool VSUBUWS(PPUThread&, ppu_opcode_t);
static bool VSUMSWS(PPUThread&, ppu_opcode_t);
static bool VSUM2SWS(PPUThread&, ppu_opcode_t);
static bool VSUM4SBS(PPUThread&, ppu_opcode_t);
static bool VSUM4SHS(PPUThread&, ppu_opcode_t);
static bool VSUM4UBS(PPUThread&, ppu_opcode_t);
static bool VUPKHPX(PPUThread&, ppu_opcode_t);
static bool VUPKHSB(PPUThread&, ppu_opcode_t);
static bool VUPKHSH(PPUThread&, ppu_opcode_t);
static bool VUPKLPX(PPUThread&, ppu_opcode_t);
static bool VUPKLSB(PPUThread&, ppu_opcode_t);
static bool VUPKLSH(PPUThread&, ppu_opcode_t);
static bool VXOR(PPUThread&, ppu_opcode_t);
static bool TDI(PPUThread&, ppu_opcode_t);
static bool TWI(PPUThread&, ppu_opcode_t);
static bool MULLI(PPUThread&, ppu_opcode_t);
static bool SUBFIC(PPUThread&, ppu_opcode_t);
static bool CMPLI(PPUThread&, ppu_opcode_t);
static bool CMPI(PPUThread&, ppu_opcode_t);
static bool ADDIC(PPUThread&, ppu_opcode_t);
static bool ADDI(PPUThread&, ppu_opcode_t);
static bool ADDIS(PPUThread&, ppu_opcode_t);
static bool BC(PPUThread&, ppu_opcode_t);
static bool HACK(PPUThread&, ppu_opcode_t);
static bool SC(PPUThread&, ppu_opcode_t);
static bool B(PPUThread&, ppu_opcode_t);
static bool MCRF(PPUThread&, ppu_opcode_t);
static bool BCLR(PPUThread&, ppu_opcode_t);
static bool CRNOR(PPUThread&, ppu_opcode_t);
static bool CRANDC(PPUThread&, ppu_opcode_t);
static bool ISYNC(PPUThread&, ppu_opcode_t);
static bool CRXOR(PPUThread&, ppu_opcode_t);
static bool CRNAND(PPUThread&, ppu_opcode_t);
static bool CRAND(PPUThread&, ppu_opcode_t);
static bool CREQV(PPUThread&, ppu_opcode_t);
static bool CRORC(PPUThread&, ppu_opcode_t);
static bool CROR(PPUThread&, ppu_opcode_t);
static bool BCCTR(PPUThread&, ppu_opcode_t);
static bool RLWIMI(PPUThread&, ppu_opcode_t);
static bool RLWINM(PPUThread&, ppu_opcode_t);
static bool RLWNM(PPUThread&, ppu_opcode_t);
static bool ORI(PPUThread&, ppu_opcode_t);
static bool ORIS(PPUThread&, ppu_opcode_t);
static bool XORI(PPUThread&, ppu_opcode_t);
static bool XORIS(PPUThread&, ppu_opcode_t);
static bool ANDI(PPUThread&, ppu_opcode_t);
static bool ANDIS(PPUThread&, ppu_opcode_t);
static bool RLDICL(PPUThread&, ppu_opcode_t);
static bool RLDICR(PPUThread&, ppu_opcode_t);
static bool RLDIC(PPUThread&, ppu_opcode_t);
static bool RLDIMI(PPUThread&, ppu_opcode_t);
static bool RLDCL(PPUThread&, ppu_opcode_t);
static bool RLDCR(PPUThread&, ppu_opcode_t);
static bool CMP(PPUThread&, ppu_opcode_t);
static bool TW(PPUThread&, ppu_opcode_t);
static bool LVSL(PPUThread&, ppu_opcode_t);
static bool LVEBX(PPUThread&, ppu_opcode_t);
static bool SUBFC(PPUThread&, ppu_opcode_t);
static bool MULHDU(PPUThread&, ppu_opcode_t);
static bool ADDC(PPUThread&, ppu_opcode_t);
static bool MULHWU(PPUThread&, ppu_opcode_t);
static bool MFOCRF(PPUThread&, ppu_opcode_t);
static bool LWARX(PPUThread&, ppu_opcode_t);
static bool LDX(PPUThread&, ppu_opcode_t);
static bool LWZX(PPUThread&, ppu_opcode_t);
static bool SLW(PPUThread&, ppu_opcode_t);
static bool CNTLZW(PPUThread&, ppu_opcode_t);
static bool SLD(PPUThread&, ppu_opcode_t);
static bool AND(PPUThread&, ppu_opcode_t);
static bool CMPL(PPUThread&, ppu_opcode_t);
static bool LVSR(PPUThread&, ppu_opcode_t);
static bool LVEHX(PPUThread&, ppu_opcode_t);
static bool SUBF(PPUThread&, ppu_opcode_t);
static bool LDUX(PPUThread&, ppu_opcode_t);
static bool DCBST(PPUThread&, ppu_opcode_t);
static bool LWZUX(PPUThread&, ppu_opcode_t);
static bool CNTLZD(PPUThread&, ppu_opcode_t);
static bool ANDC(PPUThread&, ppu_opcode_t);
static bool TD(PPUThread&, ppu_opcode_t);
static bool LVEWX(PPUThread&, ppu_opcode_t);
static bool MULHD(PPUThread&, ppu_opcode_t);
static bool MULHW(PPUThread&, ppu_opcode_t);
static bool LDARX(PPUThread&, ppu_opcode_t);
static bool DCBF(PPUThread&, ppu_opcode_t);
static bool LBZX(PPUThread&, ppu_opcode_t);
static bool LVX(PPUThread&, ppu_opcode_t);
static bool NEG(PPUThread&, ppu_opcode_t);
static bool LBZUX(PPUThread&, ppu_opcode_t);
static bool NOR(PPUThread&, ppu_opcode_t);
static bool STVEBX(PPUThread&, ppu_opcode_t);
static bool SUBFE(PPUThread&, ppu_opcode_t);
static bool ADDE(PPUThread&, ppu_opcode_t);
static bool MTOCRF(PPUThread&, ppu_opcode_t);
static bool STDX(PPUThread&, ppu_opcode_t);
static bool STWCX(PPUThread&, ppu_opcode_t);
static bool STWX(PPUThread&, ppu_opcode_t);
static bool STVEHX(PPUThread&, ppu_opcode_t);
static bool STDUX(PPUThread&, ppu_opcode_t);
static bool STWUX(PPUThread&, ppu_opcode_t);
static bool STVEWX(PPUThread&, ppu_opcode_t);
static bool SUBFZE(PPUThread&, ppu_opcode_t);
static bool ADDZE(PPUThread&, ppu_opcode_t);
static bool STDCX(PPUThread&, ppu_opcode_t);
static bool STBX(PPUThread&, ppu_opcode_t);
static bool STVX(PPUThread&, ppu_opcode_t);
static bool MULLD(PPUThread&, ppu_opcode_t);
static bool SUBFME(PPUThread&, ppu_opcode_t);
static bool ADDME(PPUThread&, ppu_opcode_t);
static bool MULLW(PPUThread&, ppu_opcode_t);
static bool DCBTST(PPUThread&, ppu_opcode_t);
static bool STBUX(PPUThread&, ppu_opcode_t);
static bool ADD(PPUThread&, ppu_opcode_t);
static bool DCBT(PPUThread&, ppu_opcode_t);
static bool LHZX(PPUThread&, ppu_opcode_t);
static bool EQV(PPUThread&, ppu_opcode_t);
static bool ECIWX(PPUThread&, ppu_opcode_t);
static bool LHZUX(PPUThread&, ppu_opcode_t);
static bool XOR(PPUThread&, ppu_opcode_t);
static bool MFSPR(PPUThread&, ppu_opcode_t);
static bool LWAX(PPUThread&, ppu_opcode_t);
static bool DST(PPUThread&, ppu_opcode_t);
static bool LHAX(PPUThread&, ppu_opcode_t);
static bool LVXL(PPUThread&, ppu_opcode_t);
static bool MFTB(PPUThread&, ppu_opcode_t);
static bool LWAUX(PPUThread&, ppu_opcode_t);
static bool DSTST(PPUThread&, ppu_opcode_t);
static bool LHAUX(PPUThread&, ppu_opcode_t);
static bool STHX(PPUThread&, ppu_opcode_t);
static bool ORC(PPUThread&, ppu_opcode_t);
static bool ECOWX(PPUThread&, ppu_opcode_t);
static bool STHUX(PPUThread&, ppu_opcode_t);
static bool OR(PPUThread&, ppu_opcode_t);
static bool DIVDU(PPUThread&, ppu_opcode_t);
static bool DIVWU(PPUThread&, ppu_opcode_t);
static bool MTSPR(PPUThread&, ppu_opcode_t);
static bool DCBI(PPUThread&, ppu_opcode_t);
static bool NAND(PPUThread&, ppu_opcode_t);
static bool STVXL(PPUThread&, ppu_opcode_t);
static bool DIVD(PPUThread&, ppu_opcode_t);
static bool DIVW(PPUThread&, ppu_opcode_t);
static bool LVLX(PPUThread&, ppu_opcode_t);
static bool LDBRX(PPUThread&, ppu_opcode_t);
static bool LSWX(PPUThread&, ppu_opcode_t);
static bool LWBRX(PPUThread&, ppu_opcode_t);
static bool LFSX(PPUThread&, ppu_opcode_t);
static bool SRW(PPUThread&, ppu_opcode_t);
static bool SRD(PPUThread&, ppu_opcode_t);
static bool LVRX(PPUThread&, ppu_opcode_t);
static bool LSWI(PPUThread&, ppu_opcode_t);
static bool LFSUX(PPUThread&, ppu_opcode_t);
static bool SYNC(PPUThread&, ppu_opcode_t);
static bool LFDX(PPUThread&, ppu_opcode_t);
static bool LFDUX(PPUThread&, ppu_opcode_t);
static bool STVLX(PPUThread&, ppu_opcode_t);
static bool STDBRX(PPUThread&, ppu_opcode_t);
static bool STSWX(PPUThread&, ppu_opcode_t);
static bool STWBRX(PPUThread&, ppu_opcode_t);
static bool STFSX(PPUThread&, ppu_opcode_t);
static bool STVRX(PPUThread&, ppu_opcode_t);
static bool STFSUX(PPUThread&, ppu_opcode_t);
static bool STSWI(PPUThread&, ppu_opcode_t);
static bool STFDX(PPUThread&, ppu_opcode_t);
static bool STFDUX(PPUThread&, ppu_opcode_t);
static bool LVLXL(PPUThread&, ppu_opcode_t);
static bool LHBRX(PPUThread&, ppu_opcode_t);
static bool SRAW(PPUThread&, ppu_opcode_t);
static bool SRAD(PPUThread&, ppu_opcode_t);
static bool LVRXL(PPUThread&, ppu_opcode_t);
static bool DSS(PPUThread&, ppu_opcode_t);
static bool SRAWI(PPUThread&, ppu_opcode_t);
static bool SRADI(PPUThread&, ppu_opcode_t);
static bool EIEIO(PPUThread&, ppu_opcode_t);
static bool STVLXL(PPUThread&, ppu_opcode_t);
static bool STHBRX(PPUThread&, ppu_opcode_t);
static bool EXTSH(PPUThread&, ppu_opcode_t);
static bool STVRXL(PPUThread&, ppu_opcode_t);
static bool EXTSB(PPUThread&, ppu_opcode_t);
static bool STFIWX(PPUThread&, ppu_opcode_t);
static bool EXTSW(PPUThread&, ppu_opcode_t);
static bool ICBI(PPUThread&, ppu_opcode_t);
static bool DCBZ(PPUThread&, ppu_opcode_t);
static bool LWZ(PPUThread&, ppu_opcode_t);
static bool LWZU(PPUThread&, ppu_opcode_t);
static bool LBZ(PPUThread&, ppu_opcode_t);
static bool LBZU(PPUThread&, ppu_opcode_t);
static bool STW(PPUThread&, ppu_opcode_t);
static bool STWU(PPUThread&, ppu_opcode_t);
static bool STB(PPUThread&, ppu_opcode_t);
static bool STBU(PPUThread&, ppu_opcode_t);
static bool LHZ(PPUThread&, ppu_opcode_t);
static bool LHZU(PPUThread&, ppu_opcode_t);
static bool LHA(PPUThread&, ppu_opcode_t);
static bool LHAU(PPUThread&, ppu_opcode_t);
static bool STH(PPUThread&, ppu_opcode_t);
static bool STHU(PPUThread&, ppu_opcode_t);
static bool LMW(PPUThread&, ppu_opcode_t);
static bool STMW(PPUThread&, ppu_opcode_t);
static bool LFS(PPUThread&, ppu_opcode_t);
static bool LFSU(PPUThread&, ppu_opcode_t);
static bool LFD(PPUThread&, ppu_opcode_t);
static bool LFDU(PPUThread&, ppu_opcode_t);
static bool STFS(PPUThread&, ppu_opcode_t);
static bool STFSU(PPUThread&, ppu_opcode_t);
static bool STFD(PPUThread&, ppu_opcode_t);
static bool STFDU(PPUThread&, ppu_opcode_t);
static bool LD(PPUThread&, ppu_opcode_t);
static bool LDU(PPUThread&, ppu_opcode_t);
static bool LWA(PPUThread&, ppu_opcode_t);
static bool STD(PPUThread&, ppu_opcode_t);
static bool STDU(PPUThread&, ppu_opcode_t);
static bool FDIVS(PPUThread&, ppu_opcode_t);
static bool FSUBS(PPUThread&, ppu_opcode_t);
static bool FADDS(PPUThread&, ppu_opcode_t);
static bool FSQRTS(PPUThread&, ppu_opcode_t);
static bool FRES(PPUThread&, ppu_opcode_t);
static bool FMULS(PPUThread&, ppu_opcode_t);
static bool FMADDS(PPUThread&, ppu_opcode_t);
static bool FMSUBS(PPUThread&, ppu_opcode_t);
static bool FNMSUBS(PPUThread&, ppu_opcode_t);
static bool FNMADDS(PPUThread&, ppu_opcode_t);
static bool MTFSB1(PPUThread&, ppu_opcode_t);
static bool MCRFS(PPUThread&, ppu_opcode_t);
static bool MTFSB0(PPUThread&, ppu_opcode_t);
static bool MTFSFI(PPUThread&, ppu_opcode_t);
static bool MFFS(PPUThread&, ppu_opcode_t);
static bool MTFSF(PPUThread&, ppu_opcode_t);
static bool FCMPU(PPUThread&, ppu_opcode_t);
static bool FRSP(PPUThread&, ppu_opcode_t);
static bool FCTIW(PPUThread&, ppu_opcode_t);
static bool FCTIWZ(PPUThread&, ppu_opcode_t);
static bool FDIV(PPUThread&, ppu_opcode_t);
static bool FSUB(PPUThread&, ppu_opcode_t);
static bool FADD(PPUThread&, ppu_opcode_t);
static bool FSQRT(PPUThread&, ppu_opcode_t);
static bool FSEL(PPUThread&, ppu_opcode_t);
static bool FMUL(PPUThread&, ppu_opcode_t);
static bool FRSQRTE(PPUThread&, ppu_opcode_t);
static bool FMSUB(PPUThread&, ppu_opcode_t);
static bool FMADD(PPUThread&, ppu_opcode_t);
static bool FNMSUB(PPUThread&, ppu_opcode_t);
static bool FNMADD(PPUThread&, ppu_opcode_t);
static bool FCMPO(PPUThread&, ppu_opcode_t);
static bool FNEG(PPUThread&, ppu_opcode_t);
static bool FMR(PPUThread&, ppu_opcode_t);
static bool FNABS(PPUThread&, ppu_opcode_t);
static bool FABS(PPUThread&, ppu_opcode_t);
static bool FCTID(PPUThread&, ppu_opcode_t);
static bool FCTIDZ(PPUThread&, ppu_opcode_t);
static bool FCFID(PPUThread&, ppu_opcode_t);
static bool MFVSCR(ppu_thread&, ppu_opcode_t);
static bool MTVSCR(ppu_thread&, ppu_opcode_t);
static bool VADDCUW(ppu_thread&, ppu_opcode_t);
static bool VADDFP(ppu_thread&, ppu_opcode_t);
static bool VADDSBS(ppu_thread&, ppu_opcode_t);
static bool VADDSHS(ppu_thread&, ppu_opcode_t);
static bool VADDSWS(ppu_thread&, ppu_opcode_t);
static bool VADDUBM(ppu_thread&, ppu_opcode_t);
static bool VADDUBS(ppu_thread&, ppu_opcode_t);
static bool VADDUHM(ppu_thread&, ppu_opcode_t);
static bool VADDUHS(ppu_thread&, ppu_opcode_t);
static bool VADDUWM(ppu_thread&, ppu_opcode_t);
static bool VADDUWS(ppu_thread&, ppu_opcode_t);
static bool VAND(ppu_thread&, ppu_opcode_t);
static bool VANDC(ppu_thread&, ppu_opcode_t);
static bool VAVGSB(ppu_thread&, ppu_opcode_t);
static bool VAVGSH(ppu_thread&, ppu_opcode_t);
static bool VAVGSW(ppu_thread&, ppu_opcode_t);
static bool VAVGUB(ppu_thread&, ppu_opcode_t);
static bool VAVGUH(ppu_thread&, ppu_opcode_t);
static bool VAVGUW(ppu_thread&, ppu_opcode_t);
static bool VCFSX(ppu_thread&, ppu_opcode_t);
static bool VCFUX(ppu_thread&, ppu_opcode_t);
static bool VCMPBFP(ppu_thread&, ppu_opcode_t);
static bool VCMPEQFP(ppu_thread&, ppu_opcode_t);
static bool VCMPEQUB(ppu_thread&, ppu_opcode_t);
static bool VCMPEQUH(ppu_thread&, ppu_opcode_t);
static bool VCMPEQUW(ppu_thread&, ppu_opcode_t);
static bool VCMPGEFP(ppu_thread&, ppu_opcode_t);
static bool VCMPGTFP(ppu_thread&, ppu_opcode_t);
static bool VCMPGTSB(ppu_thread&, ppu_opcode_t);
static bool VCMPGTSH(ppu_thread&, ppu_opcode_t);
static bool VCMPGTSW(ppu_thread&, ppu_opcode_t);
static bool VCMPGTUB(ppu_thread&, ppu_opcode_t);
static bool VCMPGTUH(ppu_thread&, ppu_opcode_t);
static bool VCMPGTUW(ppu_thread&, ppu_opcode_t);
static bool VCTSXS(ppu_thread&, ppu_opcode_t);
static bool VCTUXS(ppu_thread&, ppu_opcode_t);
static bool VEXPTEFP(ppu_thread&, ppu_opcode_t);
static bool VLOGEFP(ppu_thread&, ppu_opcode_t);
static bool VMADDFP(ppu_thread&, ppu_opcode_t);
static bool VMAXFP(ppu_thread&, ppu_opcode_t);
static bool VMAXSB(ppu_thread&, ppu_opcode_t);
static bool VMAXSH(ppu_thread&, ppu_opcode_t);
static bool VMAXSW(ppu_thread&, ppu_opcode_t);
static bool VMAXUB(ppu_thread&, ppu_opcode_t);
static bool VMAXUH(ppu_thread&, ppu_opcode_t);
static bool VMAXUW(ppu_thread&, ppu_opcode_t);
static bool VMHADDSHS(ppu_thread&, ppu_opcode_t);
static bool VMHRADDSHS(ppu_thread&, ppu_opcode_t);
static bool VMINFP(ppu_thread&, ppu_opcode_t);
static bool VMINSB(ppu_thread&, ppu_opcode_t);
static bool VMINSH(ppu_thread&, ppu_opcode_t);
static bool VMINSW(ppu_thread&, ppu_opcode_t);
static bool VMINUB(ppu_thread&, ppu_opcode_t);
static bool VMINUH(ppu_thread&, ppu_opcode_t);
static bool VMINUW(ppu_thread&, ppu_opcode_t);
static bool VMLADDUHM(ppu_thread&, ppu_opcode_t);
static bool VMRGHB(ppu_thread&, ppu_opcode_t);
static bool VMRGHH(ppu_thread&, ppu_opcode_t);
static bool VMRGHW(ppu_thread&, ppu_opcode_t);
static bool VMRGLB(ppu_thread&, ppu_opcode_t);
static bool VMRGLH(ppu_thread&, ppu_opcode_t);
static bool VMRGLW(ppu_thread&, ppu_opcode_t);
static bool VMSUMMBM(ppu_thread&, ppu_opcode_t);
static bool VMSUMSHM(ppu_thread&, ppu_opcode_t);
static bool VMSUMSHS(ppu_thread&, ppu_opcode_t);
static bool VMSUMUBM(ppu_thread&, ppu_opcode_t);
static bool VMSUMUHM(ppu_thread&, ppu_opcode_t);
static bool VMSUMUHS(ppu_thread&, ppu_opcode_t);
static bool VMULESB(ppu_thread&, ppu_opcode_t);
static bool VMULESH(ppu_thread&, ppu_opcode_t);
static bool VMULEUB(ppu_thread&, ppu_opcode_t);
static bool VMULEUH(ppu_thread&, ppu_opcode_t);
static bool VMULOSB(ppu_thread&, ppu_opcode_t);
static bool VMULOSH(ppu_thread&, ppu_opcode_t);
static bool VMULOUB(ppu_thread&, ppu_opcode_t);
static bool VMULOUH(ppu_thread&, ppu_opcode_t);
static bool VNMSUBFP(ppu_thread&, ppu_opcode_t);
static bool VNOR(ppu_thread&, ppu_opcode_t);
static bool VOR(ppu_thread&, ppu_opcode_t);
static bool VPERM(ppu_thread&, ppu_opcode_t);
static bool VPKPX(ppu_thread&, ppu_opcode_t);
static bool VPKSHSS(ppu_thread&, ppu_opcode_t);
static bool VPKSHUS(ppu_thread&, ppu_opcode_t);
static bool VPKSWSS(ppu_thread&, ppu_opcode_t);
static bool VPKSWUS(ppu_thread&, ppu_opcode_t);
static bool VPKUHUM(ppu_thread&, ppu_opcode_t);
static bool VPKUHUS(ppu_thread&, ppu_opcode_t);
static bool VPKUWUM(ppu_thread&, ppu_opcode_t);
static bool VPKUWUS(ppu_thread&, ppu_opcode_t);
static bool VREFP(ppu_thread&, ppu_opcode_t);
static bool VRFIM(ppu_thread&, ppu_opcode_t);
static bool VRFIN(ppu_thread&, ppu_opcode_t);
static bool VRFIP(ppu_thread&, ppu_opcode_t);
static bool VRFIZ(ppu_thread&, ppu_opcode_t);
static bool VRLB(ppu_thread&, ppu_opcode_t);
static bool VRLH(ppu_thread&, ppu_opcode_t);
static bool VRLW(ppu_thread&, ppu_opcode_t);
static bool VRSQRTEFP(ppu_thread&, ppu_opcode_t);
static bool VSEL(ppu_thread&, ppu_opcode_t);
static bool VSL(ppu_thread&, ppu_opcode_t);
static bool VSLB(ppu_thread&, ppu_opcode_t);
static bool VSLDOI(ppu_thread&, ppu_opcode_t);
static bool VSLH(ppu_thread&, ppu_opcode_t);
static bool VSLO(ppu_thread&, ppu_opcode_t);
static bool VSLW(ppu_thread&, ppu_opcode_t);
static bool VSPLTB(ppu_thread&, ppu_opcode_t);
static bool VSPLTH(ppu_thread&, ppu_opcode_t);
static bool VSPLTISB(ppu_thread&, ppu_opcode_t);
static bool VSPLTISH(ppu_thread&, ppu_opcode_t);
static bool VSPLTISW(ppu_thread&, ppu_opcode_t);
static bool VSPLTW(ppu_thread&, ppu_opcode_t);
static bool VSR(ppu_thread&, ppu_opcode_t);
static bool VSRAB(ppu_thread&, ppu_opcode_t);
static bool VSRAH(ppu_thread&, ppu_opcode_t);
static bool VSRAW(ppu_thread&, ppu_opcode_t);
static bool VSRB(ppu_thread&, ppu_opcode_t);
static bool VSRH(ppu_thread&, ppu_opcode_t);
static bool VSRO(ppu_thread&, ppu_opcode_t);
static bool VSRW(ppu_thread&, ppu_opcode_t);
static bool VSUBCUW(ppu_thread&, ppu_opcode_t);
static bool VSUBFP(ppu_thread&, ppu_opcode_t);
static bool VSUBSBS(ppu_thread&, ppu_opcode_t);
static bool VSUBSHS(ppu_thread&, ppu_opcode_t);
static bool VSUBSWS(ppu_thread&, ppu_opcode_t);
static bool VSUBUBM(ppu_thread&, ppu_opcode_t);
static bool VSUBUBS(ppu_thread&, ppu_opcode_t);
static bool VSUBUHM(ppu_thread&, ppu_opcode_t);
static bool VSUBUHS(ppu_thread&, ppu_opcode_t);
static bool VSUBUWM(ppu_thread&, ppu_opcode_t);
static bool VSUBUWS(ppu_thread&, ppu_opcode_t);
static bool VSUMSWS(ppu_thread&, ppu_opcode_t);
static bool VSUM2SWS(ppu_thread&, ppu_opcode_t);
static bool VSUM4SBS(ppu_thread&, ppu_opcode_t);
static bool VSUM4SHS(ppu_thread&, ppu_opcode_t);
static bool VSUM4UBS(ppu_thread&, ppu_opcode_t);
static bool VUPKHPX(ppu_thread&, ppu_opcode_t);
static bool VUPKHSB(ppu_thread&, ppu_opcode_t);
static bool VUPKHSH(ppu_thread&, ppu_opcode_t);
static bool VUPKLPX(ppu_thread&, ppu_opcode_t);
static bool VUPKLSB(ppu_thread&, ppu_opcode_t);
static bool VUPKLSH(ppu_thread&, ppu_opcode_t);
static bool VXOR(ppu_thread&, ppu_opcode_t);
static bool TDI(ppu_thread&, ppu_opcode_t);
static bool TWI(ppu_thread&, ppu_opcode_t);
static bool MULLI(ppu_thread&, ppu_opcode_t);
static bool SUBFIC(ppu_thread&, ppu_opcode_t);
static bool CMPLI(ppu_thread&, ppu_opcode_t);
static bool CMPI(ppu_thread&, ppu_opcode_t);
static bool ADDIC(ppu_thread&, ppu_opcode_t);
static bool ADDI(ppu_thread&, ppu_opcode_t);
static bool ADDIS(ppu_thread&, ppu_opcode_t);
static bool BC(ppu_thread&, ppu_opcode_t);
static bool HACK(ppu_thread&, ppu_opcode_t);
static bool SC(ppu_thread&, ppu_opcode_t);
static bool B(ppu_thread&, ppu_opcode_t);
static bool MCRF(ppu_thread&, ppu_opcode_t);
static bool BCLR(ppu_thread&, ppu_opcode_t);
static bool CRNOR(ppu_thread&, ppu_opcode_t);
static bool CRANDC(ppu_thread&, ppu_opcode_t);
static bool ISYNC(ppu_thread&, ppu_opcode_t);
static bool CRXOR(ppu_thread&, ppu_opcode_t);
static bool CRNAND(ppu_thread&, ppu_opcode_t);
static bool CRAND(ppu_thread&, ppu_opcode_t);
static bool CREQV(ppu_thread&, ppu_opcode_t);
static bool CRORC(ppu_thread&, ppu_opcode_t);
static bool CROR(ppu_thread&, ppu_opcode_t);
static bool BCCTR(ppu_thread&, ppu_opcode_t);
static bool RLWIMI(ppu_thread&, ppu_opcode_t);
static bool RLWINM(ppu_thread&, ppu_opcode_t);
static bool RLWNM(ppu_thread&, ppu_opcode_t);
static bool ORI(ppu_thread&, ppu_opcode_t);
static bool ORIS(ppu_thread&, ppu_opcode_t);
static bool XORI(ppu_thread&, ppu_opcode_t);
static bool XORIS(ppu_thread&, ppu_opcode_t);
static bool ANDI(ppu_thread&, ppu_opcode_t);
static bool ANDIS(ppu_thread&, ppu_opcode_t);
static bool RLDICL(ppu_thread&, ppu_opcode_t);
static bool RLDICR(ppu_thread&, ppu_opcode_t);
static bool RLDIC(ppu_thread&, ppu_opcode_t);
static bool RLDIMI(ppu_thread&, ppu_opcode_t);
static bool RLDCL(ppu_thread&, ppu_opcode_t);
static bool RLDCR(ppu_thread&, ppu_opcode_t);
static bool CMP(ppu_thread&, ppu_opcode_t);
static bool TW(ppu_thread&, ppu_opcode_t);
static bool LVSL(ppu_thread&, ppu_opcode_t);
static bool LVEBX(ppu_thread&, ppu_opcode_t);
static bool SUBFC(ppu_thread&, ppu_opcode_t);
static bool MULHDU(ppu_thread&, ppu_opcode_t);
static bool ADDC(ppu_thread&, ppu_opcode_t);
static bool MULHWU(ppu_thread&, ppu_opcode_t);
static bool MFOCRF(ppu_thread&, ppu_opcode_t);
static bool LWARX(ppu_thread&, ppu_opcode_t);
static bool LDX(ppu_thread&, ppu_opcode_t);
static bool LWZX(ppu_thread&, ppu_opcode_t);
static bool SLW(ppu_thread&, ppu_opcode_t);
static bool CNTLZW(ppu_thread&, ppu_opcode_t);
static bool SLD(ppu_thread&, ppu_opcode_t);
static bool AND(ppu_thread&, ppu_opcode_t);
static bool CMPL(ppu_thread&, ppu_opcode_t);
static bool LVSR(ppu_thread&, ppu_opcode_t);
static bool LVEHX(ppu_thread&, ppu_opcode_t);
static bool SUBF(ppu_thread&, ppu_opcode_t);
static bool LDUX(ppu_thread&, ppu_opcode_t);
static bool DCBST(ppu_thread&, ppu_opcode_t);
static bool LWZUX(ppu_thread&, ppu_opcode_t);
static bool CNTLZD(ppu_thread&, ppu_opcode_t);
static bool ANDC(ppu_thread&, ppu_opcode_t);
static bool TD(ppu_thread&, ppu_opcode_t);
static bool LVEWX(ppu_thread&, ppu_opcode_t);
static bool MULHD(ppu_thread&, ppu_opcode_t);
static bool MULHW(ppu_thread&, ppu_opcode_t);
static bool LDARX(ppu_thread&, ppu_opcode_t);
static bool DCBF(ppu_thread&, ppu_opcode_t);
static bool LBZX(ppu_thread&, ppu_opcode_t);
static bool LVX(ppu_thread&, ppu_opcode_t);
static bool NEG(ppu_thread&, ppu_opcode_t);
static bool LBZUX(ppu_thread&, ppu_opcode_t);
static bool NOR(ppu_thread&, ppu_opcode_t);
static bool STVEBX(ppu_thread&, ppu_opcode_t);
static bool SUBFE(ppu_thread&, ppu_opcode_t);
static bool ADDE(ppu_thread&, ppu_opcode_t);
static bool MTOCRF(ppu_thread&, ppu_opcode_t);
static bool STDX(ppu_thread&, ppu_opcode_t);
static bool STWCX(ppu_thread&, ppu_opcode_t);
static bool STWX(ppu_thread&, ppu_opcode_t);
static bool STVEHX(ppu_thread&, ppu_opcode_t);
static bool STDUX(ppu_thread&, ppu_opcode_t);
static bool STWUX(ppu_thread&, ppu_opcode_t);
static bool STVEWX(ppu_thread&, ppu_opcode_t);
static bool SUBFZE(ppu_thread&, ppu_opcode_t);
static bool ADDZE(ppu_thread&, ppu_opcode_t);
static bool STDCX(ppu_thread&, ppu_opcode_t);
static bool STBX(ppu_thread&, ppu_opcode_t);
static bool STVX(ppu_thread&, ppu_opcode_t);
static bool MULLD(ppu_thread&, ppu_opcode_t);
static bool SUBFME(ppu_thread&, ppu_opcode_t);
static bool ADDME(ppu_thread&, ppu_opcode_t);
static bool MULLW(ppu_thread&, ppu_opcode_t);
static bool DCBTST(ppu_thread&, ppu_opcode_t);
static bool STBUX(ppu_thread&, ppu_opcode_t);
static bool ADD(ppu_thread&, ppu_opcode_t);
static bool DCBT(ppu_thread&, ppu_opcode_t);
static bool LHZX(ppu_thread&, ppu_opcode_t);
static bool EQV(ppu_thread&, ppu_opcode_t);
static bool ECIWX(ppu_thread&, ppu_opcode_t);
static bool LHZUX(ppu_thread&, ppu_opcode_t);
static bool XOR(ppu_thread&, ppu_opcode_t);
static bool MFSPR(ppu_thread&, ppu_opcode_t);
static bool LWAX(ppu_thread&, ppu_opcode_t);
static bool DST(ppu_thread&, ppu_opcode_t);
static bool LHAX(ppu_thread&, ppu_opcode_t);
static bool LVXL(ppu_thread&, ppu_opcode_t);
static bool MFTB(ppu_thread&, ppu_opcode_t);
static bool LWAUX(ppu_thread&, ppu_opcode_t);
static bool DSTST(ppu_thread&, ppu_opcode_t);
static bool LHAUX(ppu_thread&, ppu_opcode_t);
static bool STHX(ppu_thread&, ppu_opcode_t);
static bool ORC(ppu_thread&, ppu_opcode_t);
static bool ECOWX(ppu_thread&, ppu_opcode_t);
static bool STHUX(ppu_thread&, ppu_opcode_t);
static bool OR(ppu_thread&, ppu_opcode_t);
static bool DIVDU(ppu_thread&, ppu_opcode_t);
static bool DIVWU(ppu_thread&, ppu_opcode_t);
static bool MTSPR(ppu_thread&, ppu_opcode_t);
static bool DCBI(ppu_thread&, ppu_opcode_t);
static bool NAND(ppu_thread&, ppu_opcode_t);
static bool STVXL(ppu_thread&, ppu_opcode_t);
static bool DIVD(ppu_thread&, ppu_opcode_t);
static bool DIVW(ppu_thread&, ppu_opcode_t);
static bool LVLX(ppu_thread&, ppu_opcode_t);
static bool LDBRX(ppu_thread&, ppu_opcode_t);
static bool LSWX(ppu_thread&, ppu_opcode_t);
static bool LWBRX(ppu_thread&, ppu_opcode_t);
static bool LFSX(ppu_thread&, ppu_opcode_t);
static bool SRW(ppu_thread&, ppu_opcode_t);
static bool SRD(ppu_thread&, ppu_opcode_t);
static bool LVRX(ppu_thread&, ppu_opcode_t);
static bool LSWI(ppu_thread&, ppu_opcode_t);
static bool LFSUX(ppu_thread&, ppu_opcode_t);
static bool SYNC(ppu_thread&, ppu_opcode_t);
static bool LFDX(ppu_thread&, ppu_opcode_t);
static bool LFDUX(ppu_thread&, ppu_opcode_t);
static bool STVLX(ppu_thread&, ppu_opcode_t);
static bool STDBRX(ppu_thread&, ppu_opcode_t);
static bool STSWX(ppu_thread&, ppu_opcode_t);
static bool STWBRX(ppu_thread&, ppu_opcode_t);
static bool STFSX(ppu_thread&, ppu_opcode_t);
static bool STVRX(ppu_thread&, ppu_opcode_t);
static bool STFSUX(ppu_thread&, ppu_opcode_t);
static bool STSWI(ppu_thread&, ppu_opcode_t);
static bool STFDX(ppu_thread&, ppu_opcode_t);
static bool STFDUX(ppu_thread&, ppu_opcode_t);
static bool LVLXL(ppu_thread&, ppu_opcode_t);
static bool LHBRX(ppu_thread&, ppu_opcode_t);
static bool SRAW(ppu_thread&, ppu_opcode_t);
static bool SRAD(ppu_thread&, ppu_opcode_t);
static bool LVRXL(ppu_thread&, ppu_opcode_t);
static bool DSS(ppu_thread&, ppu_opcode_t);
static bool SRAWI(ppu_thread&, ppu_opcode_t);
static bool SRADI(ppu_thread&, ppu_opcode_t);
static bool EIEIO(ppu_thread&, ppu_opcode_t);
static bool STVLXL(ppu_thread&, ppu_opcode_t);
static bool STHBRX(ppu_thread&, ppu_opcode_t);
static bool EXTSH(ppu_thread&, ppu_opcode_t);
static bool STVRXL(ppu_thread&, ppu_opcode_t);
static bool EXTSB(ppu_thread&, ppu_opcode_t);
static bool STFIWX(ppu_thread&, ppu_opcode_t);
static bool EXTSW(ppu_thread&, ppu_opcode_t);
static bool ICBI(ppu_thread&, ppu_opcode_t);
static bool DCBZ(ppu_thread&, ppu_opcode_t);
static bool LWZ(ppu_thread&, ppu_opcode_t);
static bool LWZU(ppu_thread&, ppu_opcode_t);
static bool LBZ(ppu_thread&, ppu_opcode_t);
static bool LBZU(ppu_thread&, ppu_opcode_t);
static bool STW(ppu_thread&, ppu_opcode_t);
static bool STWU(ppu_thread&, ppu_opcode_t);
static bool STB(ppu_thread&, ppu_opcode_t);
static bool STBU(ppu_thread&, ppu_opcode_t);
static bool LHZ(ppu_thread&, ppu_opcode_t);
static bool LHZU(ppu_thread&, ppu_opcode_t);
static bool LHA(ppu_thread&, ppu_opcode_t);
static bool LHAU(ppu_thread&, ppu_opcode_t);
static bool STH(ppu_thread&, ppu_opcode_t);
static bool STHU(ppu_thread&, ppu_opcode_t);
static bool LMW(ppu_thread&, ppu_opcode_t);
static bool STMW(ppu_thread&, ppu_opcode_t);
static bool LFS(ppu_thread&, ppu_opcode_t);
static bool LFSU(ppu_thread&, ppu_opcode_t);
static bool LFD(ppu_thread&, ppu_opcode_t);
static bool LFDU(ppu_thread&, ppu_opcode_t);
static bool STFS(ppu_thread&, ppu_opcode_t);
static bool STFSU(ppu_thread&, ppu_opcode_t);
static bool STFD(ppu_thread&, ppu_opcode_t);
static bool STFDU(ppu_thread&, ppu_opcode_t);
static bool LD(ppu_thread&, ppu_opcode_t);
static bool LDU(ppu_thread&, ppu_opcode_t);
static bool LWA(ppu_thread&, ppu_opcode_t);
static bool STD(ppu_thread&, ppu_opcode_t);
static bool STDU(ppu_thread&, ppu_opcode_t);
static bool FDIVS(ppu_thread&, ppu_opcode_t);
static bool FSUBS(ppu_thread&, ppu_opcode_t);
static bool FADDS(ppu_thread&, ppu_opcode_t);
static bool FSQRTS(ppu_thread&, ppu_opcode_t);
static bool FRES(ppu_thread&, ppu_opcode_t);
static bool FMULS(ppu_thread&, ppu_opcode_t);
static bool FMADDS(ppu_thread&, ppu_opcode_t);
static bool FMSUBS(ppu_thread&, ppu_opcode_t);
static bool FNMSUBS(ppu_thread&, ppu_opcode_t);
static bool FNMADDS(ppu_thread&, ppu_opcode_t);
static bool MTFSB1(ppu_thread&, ppu_opcode_t);
static bool MCRFS(ppu_thread&, ppu_opcode_t);
static bool MTFSB0(ppu_thread&, ppu_opcode_t);
static bool MTFSFI(ppu_thread&, ppu_opcode_t);
static bool MFFS(ppu_thread&, ppu_opcode_t);
static bool MTFSF(ppu_thread&, ppu_opcode_t);
static bool FCMPU(ppu_thread&, ppu_opcode_t);
static bool FRSP(ppu_thread&, ppu_opcode_t);
static bool FCTIW(ppu_thread&, ppu_opcode_t);
static bool FCTIWZ(ppu_thread&, ppu_opcode_t);
static bool FDIV(ppu_thread&, ppu_opcode_t);
static bool FSUB(ppu_thread&, ppu_opcode_t);
static bool FADD(ppu_thread&, ppu_opcode_t);
static bool FSQRT(ppu_thread&, ppu_opcode_t);
static bool FSEL(ppu_thread&, ppu_opcode_t);
static bool FMUL(ppu_thread&, ppu_opcode_t);
static bool FRSQRTE(ppu_thread&, ppu_opcode_t);
static bool FMSUB(ppu_thread&, ppu_opcode_t);
static bool FMADD(ppu_thread&, ppu_opcode_t);
static bool FNMSUB(ppu_thread&, ppu_opcode_t);
static bool FNMADD(ppu_thread&, ppu_opcode_t);
static bool FCMPO(ppu_thread&, ppu_opcode_t);
static bool FNEG(ppu_thread&, ppu_opcode_t);
static bool FMR(ppu_thread&, ppu_opcode_t);
static bool FNABS(ppu_thread&, ppu_opcode_t);
static bool FABS(ppu_thread&, ppu_opcode_t);
static bool FCTID(ppu_thread&, ppu_opcode_t);
static bool FCTIDZ(ppu_thread&, ppu_opcode_t);
static bool FCFID(ppu_thread&, ppu_opcode_t);
static bool UNK(PPUThread&, ppu_opcode_t);
static bool UNK(ppu_thread&, ppu_opcode_t);
};
struct ppu_interpreter_precise final : ppu_interpreter

View File

@ -114,9 +114,9 @@ cfg::set_entry g_cfg_load_libs(cfg::root.core, "Load libraries");
extern std::string ppu_get_function_name(const std::string& module, u32 fnid);
extern std::string ppu_get_variable_name(const std::string& module, u32 vnid);
extern void sys_initialize_tls(PPUThread&, u64, u32, u32, u32);
extern void sys_initialize_tls(ppu_thread&, u64, u32, u32, u32);
extern void ppu_initialize(const std::string& name, const std::vector<ppu_function>& set, u32 entry);
extern void ppu_initialize(const std::string& name, const std::vector<ppu_function>& set);
extern u32 g_ps3_sdk_version;
@ -139,17 +139,17 @@ extern std::string ppu_get_module_function_name(u32 index)
return fmt::format(".%u", index);
}
extern void ppu_execute_function(PPUThread& ppu, u32 index)
extern void ppu_execute_function(ppu_thread& ppu, u32 index)
{
if (index < g_ppu_function_cache.size())
{
// If autopause occures, check_status() will hold the thread until unpaused.
if (debug::autopause::pause_function(g_ppu_fnid_cache[index]) && ppu.check_status()) throw cpu_state::ret;
if (debug::autopause::pause_function(g_ppu_fnid_cache[index]) && ppu.check_state()) throw cpu_state::ret;
if (const auto func = g_ppu_function_cache[index])
{
func(ppu);
LOG_TRACE(HLE, "'%s' finished, r3=0x%llx", ppu_get_module_function_name(index), ppu.GPR[3]);
LOG_TRACE(HLE, "'%s' finished, r3=0x%llx", ppu_get_module_function_name(index), ppu.gpr[3]);
return;
}
}
@ -783,7 +783,7 @@ std::shared_ptr<lv2_prx_t> ppu_load_prx(const ppu_prx_object& elf)
const u32 addr = vm::cast(s.sh_addr);
const u32 size = vm::cast(s.sh_size);
if (s.sh_type == 1 && addr && size)
if (s.sh_type == 1 && addr && size) // TODO: some sections with addr=0 are valid
{
for (auto i = 0; i < segments.size(); i++)
{
@ -1138,7 +1138,15 @@ void ppu_load_exec(const ppu_exec_object& elf)
// Add functions
exec_set.insert(exec_set.end(), prx->funcs.begin(), prx->funcs.end());
ppu_validate(lle_dir + '/' + name, prx->funcs, prx->funcs[0].addr);
if (prx->funcs.empty())
{
LOG_FATAL(LOADER, "Module %s has no functions!", name);
}
else
{
// TODO: fix arguments
ppu_validate(lle_dir + '/' + name, prx->funcs, prx->funcs[0].addr);
}
}
else
{
@ -1279,100 +1287,19 @@ void ppu_load_exec(const ppu_exec_object& elf)
exec_set.emplace_back(pair);
}
// TODO: adjust for liblv2 loading option
using namespace ppu_instructions;
static const int branch_size = 10 * 4;
auto make_branch = [](vm::ptr<u32>& ptr, u32 addr, bool last)
{
const u32 stub = vm::read32(addr);
const u32 rtoc = vm::read32(addr + 4);
*ptr++ = LI(r0, 0);
*ptr++ = ORI(r0, r0, stub & 0xffff);
*ptr++ = ORIS(r0, r0, stub >> 16);
*ptr++ = LI(r2, 0);
*ptr++ = ORI(r2, r2, rtoc & 0xffff);
*ptr++ = ORIS(r2, r2, rtoc >> 16);
*ptr++ = MTCTR(r0);
*ptr++ = last ? BCTR() : BCTRL();
};
auto entry = vm::ptr<u32>::make(vm::alloc(48 + branch_size * (::size32(start_funcs) + 1), vm::main));
// Save initialization args
*entry++ = MR(r14, r3);
*entry++ = MR(r15, r4);
*entry++ = MR(r16, r5);
*entry++ = MR(r17, r6);
*entry++ = MR(r18, r11);
*entry++ = MR(r19, r12);
if (!g_cfg_load_liblv2)
{
// Call sys_initialize_tls explicitly
*entry++ = MR(r3, r7);
*entry++ = MR(r4, r8);
*entry++ = MR(r5, r9);
*entry++ = MR(r6, r10);
*entry++ = HACK(FIND_FUNC(sys_initialize_tls));
}
for (auto& f : start_funcs)
{
// Reset arguments (TODO)
*entry++ = LI(r3, 0);
*entry++ = LI(r4, 0);
make_branch(entry, f, false);
}
// Restore initialization args
*entry++ = MR(r3, r14);
*entry++ = MR(r4, r15);
*entry++ = MR(r5, r16);
*entry++ = MR(r6, r17);
*entry++ = MR(r11, r18);
*entry++ = MR(r12, r19);
// Branch to initialization
make_branch(entry, static_cast<u32>(elf.header.e_entry), false);
// Register entry function (addr, size)
ppu_function entry_func;
entry_func.addr = entry.addr() & -0x1000;
entry_func.size = entry.addr() & 0xfff;
entry_func.attr += ppu_attr::entry_point;
exec_set.emplace_back(entry_func);
// Initialize recompiler
ppu_initialize("", exec_set, static_cast<u32>(elf.header.e_entry));
// Initialize interpreter/recompiler
ppu_initialize("", exec_set);
// Set SDK version
g_ps3_sdk_version = sdk_version;
auto ppu = idm::make_ptr<PPUThread>("main_thread");
ppu->pc = entry.addr() & -0x1000;
ppu->stack_size = std::max<u32>(primary_stacksize, 0x4000);
ppu->prio = primary_prio;
ppu->cpu_init();
ppu->GPR[2] = 0xdeadbeef; // rtoc
ppu->GPR[11] = 0xabadcafe; // OPD ???
ppu->GPR[12] = malloc_pagesize;
// Initialize process arguments
std::initializer_list<std::string> args = { Emu.GetPath()/*, "-emu"s*/ };
auto argv = vm::ptr<u64>::make(vm::alloc(SIZE_32(u64) * ::size32(args), vm::main));
auto envp = vm::ptr<u64>::make(vm::alloc(::align(SIZE_32(u64), 0x10), vm::main));
*envp = 0;
ppu->GPR[3] = args.size(); // argc
ppu->GPR[4] = argv.addr();
ppu->GPR[5] = envp.addr();
ppu->GPR[6] = 0; // ???
for (const auto& arg : args)
{
const u32 arg_size = ::align(::size32(arg) + 1, 0x10);
@ -1383,15 +1310,43 @@ void ppu_load_exec(const ppu_exec_object& elf)
*argv++ = arg_addr;
}
// Arguments for sys_initialize_tls()
ppu->GPR[7] = ppu->id;
ppu->GPR[8] = tls_vaddr;
ppu->GPR[9] = tls_fsize;
ppu->GPR[10] = tls_vsize;
argv -= args.size();
//ppu->state += cpu_state::interrupt;
// Initialize main thread
auto ppu = idm::make_ptr<ppu_thread>("main_thread", primary_prio, primary_stacksize);
// Set memory protection
// TODO: adjust for liblv2 loading option
if (!g_cfg_load_liblv2)
{
// Set TLS args, call sys_initialize_tls
ppu->cmd_list
({
{ ppu_cmd::set_args, 4 }, u64{ppu->id}, u64{tls_vaddr}, u64{tls_fsize}, u64{tls_vsize},
{ ppu_cmd::hle_call, FIND_FUNC(sys_initialize_tls) },
});
}
// Run start functions
for (u32 func : start_funcs)
{
// Reset arguments, run module entry point function
ppu->cmd_list
({
{ ppu_cmd::set_args, 2 }, u64{0}, u64{0},
{ ppu_cmd::lle_call, func },
});
}
// Set command line arguments, run entry function
ppu->cmd_list
({
{ ppu_cmd::set_args, 8 }, u64{args.size()}, u64{argv.addr()}, u64{envp.addr()}, u64{0}, u64{ppu->id}, u64{tls_vaddr}, u64{tls_fsize}, u64{tls_vsize},
{ ppu_cmd::set_gpr, 11 }, u64{0xabadcafe},
{ ppu_cmd::set_gpr, 12 }, u64{malloc_pagesize},
{ ppu_cmd::lle_call, static_cast<u32>(elf.header.e_entry) },
});
// Set actual memory protection (experimental)
for (const auto& prog : elf.progs)
{
const u32 addr = static_cast<u32>(prog.p_vaddr);
@ -1399,7 +1354,7 @@ void ppu_load_exec(const ppu_exec_object& elf)
if (prog.p_type == 0x1 /* LOAD */ && prog.p_memsz && (prog.p_flags & 0x2) == 0 /* W */)
{
// Set memory protection to read-only where necessary
// Set memory protection to read-only when necessary
VERIFY(vm::page_protect(addr, ::align(size, 0x1000), 0, 0, vm::page_writable));
}
}

View File

@ -197,7 +197,7 @@ public:
// Call specified function directly if LLE is not available, call LLE equivalent in callback style otherwise
template<typename T, T Func, typename... Args, typename RT = std::result_of_t<T(Args...)>>
inline RT ppu_execute_function_or_callback(const char* name, PPUThread& ppu, Args&&... args)
inline RT ppu_execute_function_or_callback(const char* name, ppu_thread& ppu, Args&&... args)
{
return Func(std::forward<Args>(args)...);
}

View File

@ -57,6 +57,9 @@ cfg::map_entry<ppu_decoder_type> g_cfg_ppu_decoder(cfg::root.core, "PPU Decoder"
const ppu_decoder<ppu_interpreter_precise> s_ppu_interpreter_precise;
const ppu_decoder<ppu_interpreter_fast> s_ppu_interpreter_fast;
extern void ppu_execute_syscall(ppu_thread& ppu, u64 code);
extern void ppu_execute_function(ppu_thread& ppu, u32 index);
const auto s_ppu_compiled = static_cast<u32*>(memory_helper::reserve_memory(0x100000000));
extern void ppu_register_function_at(u32 addr, ppu_function_t ptr)
@ -68,82 +71,117 @@ extern void ppu_register_function_at(u32 addr, ppu_function_t ptr)
}
}
std::string PPUThread::get_name() const
std::string ppu_thread::get_name() const
{
return fmt::format("PPU[0x%x] Thread (%s)", id, m_name);
}
std::string PPUThread::dump() const
std::string ppu_thread::dump() const
{
std::string ret = "Registers:\n=========\n";
std::string ret;
for (uint i = 0; i<32; ++i) ret += fmt::format("GPR[%d] = 0x%llx\n", i, GPR[i]);
for (uint i = 0; i<32; ++i) ret += fmt::format("FPR[%d] = %.6G\n", i, FPR[i]);
for (uint i = 0; i<32; ++i) ret += fmt::format("VR[%d] = 0x%s [%s]\n", i, VR[i].to_hex().c_str(), VR[i].to_xyzw().c_str());
ret += fmt::format("CR = 0x%08x\n", GetCR());
ret += fmt::format("LR = 0x%llx\n", LR);
ret += fmt::format("CTR = 0x%llx\n", CTR);
ret += fmt::format("XER = [CA=%u | OV=%u | SO=%u | CNT=%u]\n", u32{ CA }, u32{ OV }, u32{ SO }, u32{ XCNT });
//ret += fmt::format("FPSCR = 0x%x "
// "[RN=%d | NI=%d | XE=%d | ZE=%d | UE=%d | OE=%d | VE=%d | "
// "VXCVI=%d | VXSQRT=%d | VXSOFT=%d | FPRF=%d | "
// "FI=%d | FR=%d | VXVC=%d | VXIMZ=%d | "
// "VXZDZ=%d | VXIDI=%d | VXISI=%d | VXSNAN=%d | "
// "XX=%d | ZX=%d | UX=%d | OX=%d | VX=%d | FEX=%d | FX=%d]\n",
// FPSCR.FPSCR,
// u32{ FPSCR.RN },
// u32{ FPSCR.NI }, u32{ FPSCR.XE }, u32{ FPSCR.ZE }, u32{ FPSCR.UE }, u32{ FPSCR.OE }, u32{ FPSCR.VE },
// u32{ FPSCR.VXCVI }, u32{ FPSCR.VXSQRT }, u32{ FPSCR.VXSOFT }, u32{ FPSCR.FPRF },
// u32{ FPSCR.FI }, u32{ FPSCR.FR }, u32{ FPSCR.VXVC }, u32{ FPSCR.VXIMZ },
// u32{ FPSCR.VXZDZ }, u32{ FPSCR.VXIDI }, u32{ FPSCR.VXISI }, u32{ FPSCR.VXSNAN },
// u32{ FPSCR.XX }, u32{ FPSCR.ZX }, u32{ FPSCR.UX }, u32{ FPSCR.OX }, u32{ FPSCR.VX }, u32{ FPSCR.FEX }, u32{ FPSCR.FX });
ret += fmt::format("State: 0x%08x\n", state.load());
ret += fmt::format("Priority: %d\n", prio);
ret += "\nRegisters:\n=========\n";
for (uint i = 0; i < 32; ++i) ret += fmt::format("GPR[%d] = 0x%llx\n", i, gpr[i]);
for (uint i = 0; i < 32; ++i) ret += fmt::format("FPR[%d] = %.6G\n", i, fpr[i]);
for (uint i = 0; i < 32; ++i) ret += fmt::format("VR[%d] = 0x%s [%s]\n", i, vr[i].to_hex().c_str(), vr[i].to_xyzw().c_str());
if (g_cfg_ppu_decoder.get() != ppu_decoder_type::llvm)
{
ret += fmt::format("CR = 0x%08x\n", cr_pack());
ret += fmt::format("LR = 0x%llx\n", lr);
ret += fmt::format("CTR = 0x%llx\n", ctr);
ret += fmt::format("VRSAVE = 0x%08x\n", vrsave);
ret += fmt::format("XER = [CA=%u | OV=%u | SO=%u | CNT=%u]\n", xer.ca, xer.ov, xer.so, xer.cnt);
ret += fmt::format("VSCR = [SAT=%u | NJ=%u]\n", sat, nj);
ret += fmt::format("FPSCR = [FL=%u | FG=%u | FE=%u | FU=%u]\n", fpscr.fl, fpscr.fg, fpscr.fe, fpscr.fu);
ret += "\nCall stack:\n=========\n";
ret += fmt::format("0x%08x (0x0) called\n", cia);
const u32 stack_max = ::align(stack_addr + stack_size, 0x200) - 0x200;
for (u64 sp = vm::read64(static_cast<u32>(gpr[1])); sp >= stack_addr && sp < stack_max; sp = vm::read64(static_cast<u32>(sp)))
{
// TODO: print also function addresses
ret += fmt::format("> from 0x%08llx (0x0)\n", vm::read64(static_cast<u32>(sp + 16)));
}
}
return ret;
}
void PPUThread::cpu_init()
{
if (!stack_addr)
{
if (!stack_size)
{
throw EXCEPTION("Invalid stack size");
}
stack_addr = vm::alloc(stack_size, vm::stack);
if (!stack_addr)
{
throw EXCEPTION("Out of stack memory");
}
}
GPR[1] = align(stack_addr + stack_size, 0x200) - 0x200;
}
extern thread_local std::string(*g_tls_log_prefix)();
void PPUThread::cpu_task()
void ppu_thread::cpu_task()
{
//SetHostRoundingMode(FPSCR_RN_NEAR);
return custom_task ? custom_task(*this) : fast_call(pc, static_cast<u32>(GPR[2]));
// Execute cmd_queue
while (ppu_cmd cmd = cmd_wait())
{
const u32 pos = cmd_queue.peek() + 1; // Additional arguments start from [pos]
const u32 arg = cmd.arg2<u32>(); // 32-bit arg extracted
switch (u32 type = cmd.arg1<u32>())
{
case ppu_cmd::opcode:
{
cmd_pop(), s_ppu_interpreter_fast.decode(arg)(*this, {arg});
break;
}
case ppu_cmd::set_gpr:
{
if (arg >= 32)
{
throw fmt::exception("Invalid ppu_cmd::set_gpr arg (0x%x)" HERE, arg);
}
gpr[arg % 32] = cmd_queue[pos].load().as<u64>();
cmd_pop(1);
break;
}
case ppu_cmd::set_args:
{
if (arg > 8)
{
throw fmt::exception("Unsupported ppu_cmd::set_args size (0x%x)" HERE, arg);
}
for (u32 i = 0; i < arg; i++)
{
gpr[i + 3] = cmd_queue[pos + i].load().as<u64>();
}
cmd_pop(arg);
break;
}
case ppu_cmd::lle_call:
{
const vm::ptr<u32> opd(arg < 32 ? vm::cast(gpr[arg]) : vm::cast(arg));
cmd_pop(), fast_call(opd[0], opd[1]);
break;
}
case ppu_cmd::hle_call:
{
cmd_pop(), ppu_execute_function(*this, arg);
break;
}
default:
{
throw fmt::exception("Unknown ppu_cmd(0x%x)" HERE, type);
}
}
}
}
void PPUThread::cpu_task_main()
void ppu_thread::exec_task()
{
if (g_cfg_ppu_decoder.get() == ppu_decoder_type::llvm)
{
return reinterpret_cast<ppu_function_t>((std::uintptr_t)s_ppu_compiled[pc / 4])(*this);
return reinterpret_cast<ppu_function_t>((std::uintptr_t)s_ppu_compiled[cia / 4])(*this);
}
g_tls_log_prefix = []
{
const auto cpu = static_cast<PPUThread*>(get_current_cpu_thread());
return fmt::format("%s [0x%08x]", cpu->get_name(), cpu->pc);
};
const auto base = vm::_ptr<const u8>(0);
// Select opcode table
@ -159,12 +197,12 @@ void PPUThread::cpu_task_main()
{
if (UNLIKELY(state.load()))
{
if (check_status()) return;
if (check_state()) return;
}
// Reinitialize
{
const auto _ops = _mm_shuffle_epi8(_mm_lddqu_si128(reinterpret_cast<const __m128i*>(base + pc)), _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3));
const auto _ops = _mm_shuffle_epi8(_mm_lddqu_si128(reinterpret_cast<const __m128i*>(base + cia)), _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3));
_op.vi = _ops;
const v128 _i = v128::fromV(_mm_and_si128(_mm_or_si128(_mm_slli_epi32(_op.vi, 6), _mm_srli_epi32(_op.vi, 26)), _mm_set1_epi32(0x1ffff)));
func0 = table[_i._u32[0]];
@ -175,14 +213,14 @@ void PPUThread::cpu_task_main()
while (LIKELY(func0(*this, { _op._u32[0] })))
{
if (pc += 4, LIKELY(func1(*this, { _op._u32[1] })))
if (cia += 4, LIKELY(func1(*this, { _op._u32[1] })))
{
if (pc += 4, LIKELY(func2(*this, { _op._u32[2] })))
if (cia += 4, LIKELY(func2(*this, { _op._u32[2] })))
{
pc += 4;
cia += 4;
func0 = func3;
const auto _ops = _mm_shuffle_epi8(_mm_lddqu_si128(reinterpret_cast<const __m128i*>(base + pc + 4)), _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3));
const auto _ops = _mm_shuffle_epi8(_mm_lddqu_si128(reinterpret_cast<const __m128i*>(base + cia + 4)), _mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3));
_op.vi = _mm_alignr_epi8(_ops, _op.vi, 12);
const v128 _i = v128::fromV(_mm_and_si128(_mm_or_si128(_mm_slli_epi32(_op.vi, 6), _mm_srli_epi32(_op.vi, 26)), _mm_set1_epi32(0x1ffff)));
func1 = table[_i._u32[1]];
@ -204,87 +242,7 @@ void PPUThread::cpu_task_main()
constexpr auto stop_state = make_bitset(cpu_state::stop, cpu_state::exit, cpu_state::suspend);
atomic_t<u32> g_ppu_core[2]{};
bool PPUThread::handle_interrupt()
{
// Reschedule and wake up a new thread, possibly this one as well.
return false;
// Check virtual core allocation
if (g_ppu_core[0] != id && g_ppu_core[1] != id)
{
auto cpu0 = idm::get<PPUThread>(g_ppu_core[0]);
auto cpu1 = idm::get<PPUThread>(g_ppu_core[1]);
if (cpu0 && cpu1)
{
if (cpu1->prio > cpu0->prio)
{
cpu0 = std::move(cpu1);
}
// Preempt thread with the lowest priority
if (prio < cpu0->prio)
{
cpu0->state += cpu_state::interrupt;
}
}
else
{
// Try to obtain a virtual core in optimistic way
if (g_ppu_core[0].compare_and_swap_test(0, id) || g_ppu_core[1].compare_and_swap_test(0, id))
{
state -= cpu_state::interrupt;
return true;
}
}
return false;
}
// Select appropriate thread
u32 top_prio = -1;
u32 selected = -1;
idm::select<PPUThread>([&](u32 id, PPUThread& ppu)
{
// Exclude suspended and low-priority threads
if (!ppu.state.test(stop_state) && ppu.prio < top_prio /*&& (!ppu.is_sleep() || ppu.state & cpu_state::signal)*/)
{
top_prio = ppu.prio;
selected = id;
}
});
// If current thread selected
if (selected == id)
{
state -= cpu_state::interrupt;
VERIFY(g_ppu_core[0] == id || g_ppu_core[1] == id);
return true;
}
// If another thread selected
const auto thread = idm::get<PPUThread>(selected);
// Lend virtual core to another thread
if (thread && thread->state.test_and_reset(cpu_state::interrupt))
{
g_ppu_core[0].compare_and_swap(id, thread->id);
g_ppu_core[1].compare_and_swap(id, thread->id);
(*thread)->lock_notify();
}
else
{
g_ppu_core[0].compare_and_swap(id, 0);
g_ppu_core[1].compare_and_swap(id, 0);
}
return false;
}
PPUThread::~PPUThread()
ppu_thread::~ppu_thread()
{
if (stack_addr)
{
@ -292,40 +250,126 @@ PPUThread::~PPUThread()
}
}
PPUThread::PPUThread(const std::string& name)
ppu_thread::ppu_thread(const std::string& name, u32 prio, u32 stack)
: cpu_thread(cpu_type::ppu)
, prio(prio)
, stack_size(std::max<u32>(stack, 0x4000))
, stack_addr(vm::alloc(stack_size, vm::stack))
, m_name(name)
{
if (!stack_addr)
{
throw fmt::exception("Out of stack memory (size=0x%x)" HERE, stack_size);
}
gpr[1] = ::align(stack_addr + stack_size, 0x200) - 0x200;
}
be_t<u64>* PPUThread::get_stack_arg(s32 i, u64 align)
void ppu_thread::cmd_push(ppu_cmd cmd)
{
// Reserve queue space
const u32 pos = cmd_queue.push_begin();
// Write single command
cmd_queue[pos] = cmd;
}
void ppu_thread::cmd_list(std::initializer_list<ppu_cmd> list)
{
// Reserve queue space
const u32 pos = cmd_queue.push_begin(static_cast<u32>(list.size()));
// Write command tail in relaxed manner
for (u32 i = 1; i < list.size(); i++)
{
cmd_queue[pos + i].raw() = list.begin()[i];
}
// Write command head after all
cmd_queue[pos] = *list.begin();
}
void ppu_thread::cmd_pop(u32 count)
{
// Get current position
const u32 pos = cmd_queue.peek();
// Clean command buffer for command tail
for (u32 i = 1; i <= count; i++)
{
cmd_queue[pos + i].raw() = ppu_cmd{};
}
// Free
cmd_queue.pop_end(count + 1);
}
ppu_cmd ppu_thread::cmd_wait()
{
std::unique_lock<named_thread> lock(*this, std::defer_lock);
while (true)
{
if (UNLIKELY(state.load()))
{
if (lock) lock.unlock();
if (check_state()) // check_status() requires unlocked mutex
{
return ppu_cmd{};
}
}
// Lightweight queue doesn't care about mutex state
if (ppu_cmd result = cmd_queue[cmd_queue.peek()].exchange(ppu_cmd{}))
{
return result;
}
if (!lock)
{
lock.lock();
continue;
}
thread_ctrl::wait(); // Waiting requires locked mutex
}
}
be_t<u64>* ppu_thread::get_stack_arg(s32 i, u64 align)
{
if (align != 1 && align != 2 && align != 4 && align != 8 && align != 16) throw fmt::exception("Unsupported alignment: 0x%llx" HERE, align);
return vm::_ptr<u64>(vm::cast((GPR[1] + 0x30 + 0x8 * (i - 1)) & (0 - align), HERE));
return vm::_ptr<u64>(vm::cast((gpr[1] + 0x30 + 0x8 * (i - 1)) & (0 - align), HERE));
}
void PPUThread::fast_call(u32 addr, u32 rtoc)
void ppu_thread::fast_call(u32 addr, u32 rtoc)
{
const auto old_PC = pc;
const auto old_stack = GPR[1];
const auto old_rtoc = GPR[2];
const auto old_LR = LR;
const auto old_task = std::move(custom_task);
const auto old_pc = cia;
const auto old_stack = gpr[1];
const auto old_rtoc = gpr[2];
const auto old_lr = lr;
const auto old_func = last_function;
const auto old_fmt = g_tls_log_prefix;
pc = addr;
GPR[2] = rtoc;
LR = Emu.GetCPUThreadStop();
custom_task = nullptr;
cia = addr;
gpr[2] = rtoc;
lr = Emu.GetCPUThreadStop();
last_function = nullptr;
g_tls_log_prefix = []
{
const auto ppu = static_cast<ppu_thread*>(get_current_cpu_thread());
return fmt::format("%s [0x%08x]", ppu->get_name(), ppu->cia);
};
try
{
cpu_task_main();
exec_task();
if (GPR[1] != old_stack && !state.test(cpu_state::ret) && !state.test(cpu_state::exit)) // GPR[1] shouldn't change
if (gpr[1] != old_stack && !state.test(cpu_state::ret) && !state.test(cpu_state::exit)) // gpr[1] shouldn't change
{
throw fmt::exception("Stack inconsistency (addr=0x%x, rtoc=0x%x, SP=0x%llx, old=0x%llx)", addr, rtoc, GPR[1], old_stack);
throw fmt::exception("Stack inconsistency (addr=0x%x, rtoc=0x%x, SP=0x%llx, old=0x%llx)", addr, rtoc, gpr[1], old_stack);
}
}
catch (cpu_state _s)
@ -348,25 +392,17 @@ void PPUThread::fast_call(u32 addr, u32 rtoc)
state -= cpu_state::ret;
pc = old_PC;
GPR[1] = old_stack;
GPR[2] = old_rtoc;
LR = old_LR;
custom_task = std::move(old_task);
cia = old_pc;
gpr[1] = old_stack;
gpr[2] = old_rtoc;
lr = old_lr;
last_function = old_func;
//if (custom_task)
//{
// state += cpu_state::interrupt;
// handle_interrupt();
//}
g_tls_log_prefix = old_fmt;
}
const ppu_decoder<ppu_itype> s_ppu_itype;
extern u64 get_timebased_time();
extern void ppu_execute_syscall(PPUThread& ppu, u64 code);
extern void ppu_execute_function(PPUThread& ppu, u32 index);
extern ppu_function_t ppu_get_syscall(u64 code);
extern std::string ppu_get_syscall_name(u64 code);
extern ppu_function_t ppu_get_function(u32 index);
@ -434,7 +470,7 @@ static bool adde_carry(u64 a, u64 b, bool c)
#endif
}
extern void ppu_initialize(const std::string& name, const std::vector<ppu_function>& funcs, u32 entry)
extern void ppu_initialize(const std::string& name, const std::vector<ppu_function>& funcs)
{
if (g_cfg_ppu_decoder.get() != ppu_decoder_type::llvm || funcs.empty())
{
@ -485,7 +521,7 @@ extern void ppu_initialize(const std::string& name, const std::vector<ppu_functi
module->setTargetTriple(Triple::normalize(sys::getProcessTriple()));
// Initialize translator
std::unique_ptr<PPUTranslator> translator = std::make_unique<PPUTranslator>(g_llvm_ctx, module.get(), 0, entry);
std::unique_ptr<PPUTranslator> translator = std::make_unique<PPUTranslator>(g_llvm_ctx, module.get(), 0);
// Define some types
const auto _void = Type::getVoidTy(g_llvm_ctx);

View File

@ -3,28 +3,123 @@
#include "Common.h"
#include "../CPU/CPUThread.h"
#include "../Memory/vm.h"
#include "Utilities/lockless.h"
class PPUThread final : public cpu_thread
// Lightweight PPU command queue element
struct ppu_cmd : any64
{
enum : u32
{
none = 0,
opcode, // Execute PPU instruction from arg
set_gpr, // Set gpr[arg] (+1 cmd)
set_args, // Set general-purpose args (+arg cmd)
lle_call, // Load addr and rtoc at *arg or *gpr[arg] and execute
hle_call, // Execute function by index (arg)
};
struct pair_t
{
any32 arg1;
any32 arg2;
};
ppu_cmd() = default;
template<typename T>
ppu_cmd(const T& value)
: any64(value)
{
}
template<typename T1, typename T2>
ppu_cmd(const T1& arg1, const T2& arg2)
: any64(pair_t{arg1, arg2})
{
}
explicit operator bool() const
{
return as<u64>() != 0;
}
template<typename T>
decltype(auto) arg1()
{
return as<pair_t>().arg1.as<T>();
}
template<typename T>
decltype(auto) arg1() const
{
return as<const pair_t>().arg1.as<const T>();
}
template<typename T>
decltype(auto) arg2()
{
return as<pair_t>().arg2.as<T>();
}
template<typename T>
decltype(auto) arg2() const
{
return as<const pair_t>().arg2.as<const T>();
}
};
static_assert(sizeof(ppu_cmd) == 8 && std::is_pod<ppu_cmd>::value, "Incorrect ppu_cmd struct");
class ppu_thread : public cpu_thread
{
public:
virtual std::string get_name() const override;
virtual std::string dump() const override;
virtual void cpu_init() override;
virtual void cpu_init() override final {}
virtual void cpu_task() override;
virtual void cpu_task_main();
virtual bool handle_interrupt() override;
virtual ~PPUThread() override;
virtual ~ppu_thread() override;
PPUThread(const std::string& name);
ppu_thread(const std::string& name, u32 prio = 0, u32 stack = 0x10000);
u64 GPR[32]{}; // General-Purpose Registers
f64 FPR[32]{}; // Floating Point Registers
v128 VR[32]{}; // Vector Registers
alignas(16) bool CR[32]{}; // Condition Registers
bool SO{}; // XER: Summary overflow
bool OV{}; // XER: Overflow
bool CA{}; // XER: Carry
u8 XCNT{}; // XER: 0..6
u64 gpr[32] = {}; // General-Purpose Registers
f64 fpr[32] = {}; // Floating Point Registers
v128 vr[32] = {}; // Vector Registers
alignas(16) bool cr[32] = {}; // Condition Registers (abstract representation)
// Pack CR bits
u32 cr_pack() const
{
u32 result{};
for (u32 bit : cr)
{
result = (result << 1) | bit;
}
return result;
}
// Unpack CR bits
void cr_unpack(u32 value)
{
for (bool& b : cr)
{
b = (value & 0x1) != 0;
value >>= 1;
}
}
// Fixed-Point Exception Register (abstract representation)
struct
{
bool so{}; // Summary Overflow
bool ov{}; // Overflow
bool ca{}; // Carry
u8 cnt{}; // 0..6
}
xer;
/*
Saturation. A sticky status bit indicating that some field in a saturating instruction saturated since the last
@ -45,7 +140,7 @@ public:
Vector Convert to Fixed-Point with Saturation (vctuxs, vctsxs)
0 Indicates no saturation occurred; mtvscr can explicitly clear this bit.
*/
bool SAT{};
bool sat{};
/*
Non-Java. A mode control bit that determines whether vector floating-point operations will be performed
@ -54,88 +149,47 @@ public:
by Java, IEEE, and C9X standard.
1 The non-Java/non-IEEE-compliant mode is selected. If an element in a source vector register
contains a denormalized value, the value '0' is used instead. If an instruction causes an underflow
exception, the corresponding element in the target VR is cleared to '0'. In both cases, the '0'
exception, the corresponding element in the target vr is cleared to '0'. In both cases, the '0'
has the same sign as the denormalized or underflowing value.
*/
bool NJ{ true };
bool nj = true;
bool FL{}; // FPSCR.FPCC.FL
bool FG{}; // FPSCR.FPCC.FG
bool FE{}; // FPSCR.FPCC.FE
bool FU{}; // FPSCR.FPCC.FU
struct // Floating-Point Status and Control Register (abstract representation)
{
bool fl{}; // FPCC.FL
bool fg{}; // FPCC.FG
bool fe{}; // FPCC.FE
bool fu{}; // FPCC.FU
}
fpscr;
u64 LR{}; // Link Register
u64 CTR{}; // Counter Register
u32 VRSAVE{};
u32 cia{}; // Current Instruction Address
u64 lr{}; // Link Register
u64 ctr{}; // Counter Register
u32 vrsave{0xffffffff}; // VR Save Register (almost unused)
u32 pc = 0;
u32 prio = -1; // Thread priority
u32 stack_addr = 0; // Stack address
u32 stack_size = 0; // Stack size
u32 prio = 0; // Thread priority (0..3071)
const u32 stack_size; // Stack size
const u32 stack_addr; // Stack address
bool is_joinable = true;
bool is_joining = false;
lf_fifo<atomic_t<ppu_cmd>, 255> cmd_queue; // Command queue for asynchronous operations.
void cmd_push(ppu_cmd);
void cmd_list(std::initializer_list<ppu_cmd>);
void cmd_pop(u32 = 0);
ppu_cmd cmd_wait(); // Empty command means caller must return, like true from cpu_thread::check_status().
const char* last_function{}; // Last function name for diagnosis, optimized for speed.
const std::string m_name; // Thread name
std::function<void(PPUThread&)> custom_task;
// Function name can be stored here. Used to print the last called function.
const char* last_function = nullptr;
// When a thread has met an exception, this variable is used to retro propagate it through stack call.
std::exception_ptr pending_exception;
// Pack CR bits
u32 GetCR() const
{
u32 result{};
for (u32 bit : CR)
{
result = (result << 1) | bit;
}
return result;
}
// Unpack CR bits
void SetCR(u32 value)
{
for (bool& b : CR)
{
b = (value & 0x1) != 0;
value >>= 1;
}
}
// Set CR field
void SetCR(u32 field, bool le, bool gt, bool eq, bool so)
{
CR[field * 4 + 0] = le;
CR[field * 4 + 1] = gt;
CR[field * 4 + 2] = eq;
CR[field * 4 + 3] = so;
}
// Set CR field for comparison
template<typename T>
void SetCR(u32 field, const T& a, const T& b)
{
SetCR(field, a < b, a > b, a == b, SO);
}
// Set overflow bit
void SetOV(const bool set)
{
OV = set;
SO |= set;
}
u64 get_next_arg(u32& g_count)
{
if (g_count < 8)
{
return GPR[g_count++ + 3];
return gpr[g_count++ + 3];
}
else
{
@ -144,6 +198,7 @@ public:
}
be_t<u64>* get_stack_arg(s32 i, u64 align = alignof(u64));
void exec_task();
void fast_call(u32 addr, u32 rtoc);
};

View File

@ -63,7 +63,7 @@ const ppu_decoder<PPUTranslator> s_ppu_decoder;
GetGpr(op.ra),\
GetGpr(op.rb)))
PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, u64 base, u64 entry)
PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, u64 base)
: m_context(context)
, m_module(module)
, m_base_addr(base)
@ -74,12 +74,12 @@ PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, u64 base, u64
m_base = new GlobalVariable(*module, ArrayType::get(GetType<char>(), 0x100000000)->getPointerTo(), true, GlobalValue::ExternalLinkage, 0, "__mptr");
// Thread context struct (TODO: safer member access)
std::vector<Type*> thread_struct{ArrayType::get(GetType<char>(), OFFSET_32(PPUThread, GPR))};
std::vector<Type*> thread_struct{ArrayType::get(GetType<char>(), OFFSET_32(ppu_thread, gpr))};
thread_struct.insert(thread_struct.end(), 32, GetType<u64>()); // GPR[0..31]
thread_struct.insert(thread_struct.end(), 32, GetType<f64>()); // FPR[0..31]
thread_struct.insert(thread_struct.end(), 32, GetType<u32[4]>()); // VR[0..31]
thread_struct.insert(thread_struct.end(), 32, GetType<bool>()); // CR[0..31]
thread_struct.insert(thread_struct.end(), 32, GetType<u64>()); // gpr[0..31]
thread_struct.insert(thread_struct.end(), 32, GetType<f64>()); // fpr[0..31]
thread_struct.insert(thread_struct.end(), 32, GetType<u32[4]>()); // vr[0..31]
thread_struct.insert(thread_struct.end(), 32, GetType<bool>()); // cr[0..31]
m_thread_type = StructType::create(m_context, thread_struct, "context_t");

View File

@ -238,16 +238,16 @@ public:
// Get the basic block for the specified address
llvm::BasicBlock* GetBasicBlock(u64 addr);
// Load GPR
// Load gpr
llvm::Value* GetGpr(u32 r, u32 num_bits = 64);
// Set GPR
// Set gpr
void SetGpr(u32 r, llvm::Value* value);
// Get FPR
// Get fpr
llvm::Value* GetFpr(u32 r, u32 bits = 64, bool as_int = false);
// Set FPR
// Set fpr
void SetFpr(u32 r, llvm::Value* val);
// Vector register type
@ -260,7 +260,7 @@ public:
i128, // Solid 128-bit integer
};
// Load VR
// Load vr
llvm::Value* GetVr(u32 vr, VrType);
// Load VRs
@ -271,7 +271,7 @@ public:
return{ GetVr(regs, type)... };
}
// Set VR to the specified value
// Set vr to the specified value
void SetVr(u32 vr, llvm::Value*);
// Bitcast to scalar integer value
@ -431,7 +431,7 @@ public:
// Handle compilation errors
void CompilationError(const std::string& error);
PPUTranslator(llvm::LLVMContext& context, llvm::Module* module, u64 base, u64 entry);
PPUTranslator(llvm::LLVMContext& context, llvm::Module* module, u64 base);
~PPUTranslator();
// Get thread context struct type

View File

@ -87,21 +87,9 @@ bool RawSPUThread::write_reg(const u32 addr, const u32 value)
{
auto try_start = [this]()
{
if (status.atomic_op([](u32& status) -> bool
if (!status.test_and_set(SPU_STATUS_RUNNING))
{
if (status & SPU_STATUS_RUNNING)
{
return false;
}
else
{
status = SPU_STATUS_RUNNING;
return true;
}
}))
{
state -= cpu_state::stop;
(*this)->lock_notify();
run();
}
};

View File

@ -269,7 +269,7 @@ void spu_recompiler::InterpreterCall(spu_opcode_t op)
const u32 old_pc = _spu->pc;
if (_spu->state.load() && _spu->check_status())
if (_spu->state.load() && _spu->check_state())
{
return 0x2000000 | _spu->pc;
}
@ -340,7 +340,7 @@ void spu_recompiler::FunctionCall()
LOG_ERROR(SPU, "Branch-to-self");
}
while (!_spu->state.load() || !_spu->check_status())
while (!_spu->state.load() || !_spu->check_state())
{
// Proceed recursively
spu_recompiler_base::enter(*_spu);

View File

@ -24,8 +24,6 @@
extern u64 get_timebased_time();
extern std::mutex& get_current_thread_mutex();
enum class spu_decoder_type
{
precise,
@ -57,8 +55,7 @@ void spu_int_ctrl_t::set(u64 ints)
if (tag && tag->handler)
{
tag->handler->signal++;
(*tag->handler->thread)->notify();
tag->handler->exec();
}
}
}
@ -126,7 +123,7 @@ spu_imm_table_t::spu_imm_table_t()
std::string SPUThread::get_name() const
{
return fmt::format("%sSPU[0x%x] Thread (%s)", offset > RAW_SPU_BASE_ADDR ? "Raw" : "", id, m_name);
return fmt::format("%sSPU[0x%x] Thread (%s)", offset >= RAW_SPU_BASE_ADDR ? "Raw" : "", id, m_name);
}
std::string SPUThread::dump() const
@ -187,7 +184,7 @@ void SPUThread::cpu_task()
if (custom_task)
{
if (check_status()) return;
if (check_state()) return;
return custom_task(*this);
}
@ -229,7 +226,7 @@ void SPUThread::cpu_task()
continue;
}
if (check_status()) return;
if (check_state()) return;
}
}
@ -281,7 +278,7 @@ void SPUThread::do_dma_transfer(u32 cmd, spu_mfc_arg_t args)
u32 eal = vm::cast(args.ea, HERE);
if (eal >= SYS_SPU_THREAD_BASE_LOW && offset >= RAW_SPU_BASE_ADDR) // SPU Thread Group MMIO (LS and SNR)
if (eal >= SYS_SPU_THREAD_BASE_LOW && offset < RAW_SPU_BASE_ADDR) // SPU Thread Group MMIO (LS and SNR)
{
const u32 index = (eal - SYS_SPU_THREAD_BASE_LOW) / SYS_SPU_THREAD_OFFSET; // thread number in group
const u32 offset = (eal - SYS_SPU_THREAD_BASE_LOW) % SYS_SPU_THREAD_OFFSET; // LS offset or MMIO register
@ -548,7 +545,7 @@ void SPUThread::set_events(u32 mask)
// Notify if some events were set
if (~old_stat & mask && old_stat & SPU_EVENT_WAITING && ch_event_stat & SPU_EVENT_WAITING)
{
(*this)->lock_notify();
lock_notify();
}
}
@ -602,7 +599,7 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
{
if (!channel.try_pop(out))
{
cpu_thread_lock{*this}, thread_ctrl::wait(WRAP_EXPR(state & cpu_state::stop || channel.try_pop(out)));
thread_lock{*this}, thread_ctrl::wait(WRAP_EXPR(state & cpu_state::stop || channel.try_pop(out)));
return !state.test(cpu_state::stop);
}
@ -617,7 +614,7 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
// break;
case SPU_RdInMbox:
{
std::unique_lock<std::mutex> lock(get_current_thread_mutex(), std::defer_lock);
std::unique_lock<named_thread> lock(*this, std::defer_lock);
while (true)
{
@ -644,7 +641,7 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
continue;
}
get_current_thread_cv().wait(lock);
thread_ctrl::wait();
}
}
@ -693,7 +690,7 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
case SPU_RdEventStat:
{
std::unique_lock<std::mutex> lock(get_current_thread_mutex(), std::defer_lock);
std::unique_lock<named_thread> lock(*this, std::defer_lock);
// start waiting or return immediately
if (u32 res = get_events(true))
@ -716,7 +713,7 @@ bool SPUThread::get_ch_value(u32 ch, u32& out)
{
CHECK_EMU_STATUS;
get_current_thread_cv().wait(lock);
thread_ctrl::wait();
}
}
@ -756,7 +753,7 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
{
if (offset >= RAW_SPU_BASE_ADDR)
{
std::unique_lock<std::mutex> lock(get_current_thread_mutex(), std::defer_lock);
std::unique_lock<named_thread> lock(*this, std::defer_lock);
while (!ch_out_intr_mbox.try_push(value))
{
@ -773,7 +770,7 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
continue;
}
get_current_thread_cv().wait(lock);
thread_ctrl::wait();
}
int_ctrl[2].set(SPU_INT2_STAT_MAILBOX_INT);
@ -963,7 +960,7 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
case SPU_WrOutMbox:
{
std::unique_lock<std::mutex> lock(get_current_thread_mutex(), std::defer_lock);
std::unique_lock<named_thread> lock(*this, std::defer_lock);
while (!ch_out_mbox.try_push(value))
{
@ -980,7 +977,7 @@ bool SPUThread::set_ch_value(u32 ch, u32 value)
continue;
}
get_current_thread_cv().wait(lock);
thread_ctrl::wait();
}
return true;
@ -1305,7 +1302,7 @@ bool SPUThread::stop_and_signal(u32 code)
if (thread && thread.get() != this)
{
thread->state -= cpu_state::suspend;
(*thread)->lock_notify();
thread->lock_notify();
}
}
@ -1344,7 +1341,7 @@ bool SPUThread::stop_and_signal(u32 code)
if (thread && thread.get() != this)
{
thread->state += cpu_state::stop;
(*thread)->lock_notify();
thread->lock_notify();
}
}

View File

@ -180,7 +180,7 @@ public:
data.value |= value;
});
if (old.wait) spu->lock_notify();
if (old.wait) spu.lock_notify();
}
// push unconditionally (overwriting previous value), may require notification
@ -193,7 +193,7 @@ public:
data.value = value;
});
if (old.wait) spu->lock_notify();
if (old.wait) spu.lock_notify();
}
// returns true on success
@ -228,7 +228,7 @@ public:
// value is not cleared and may be read again
});
if (old.wait) spu->lock_notify();
if (old.wait) spu.lock_notify();
return old.value;
}
@ -253,12 +253,8 @@ struct spu_channel_4_t
{
struct alignas(16) sync_var_t
{
struct
{
u32 waiting : 1;
u32 count : 3;
};
u8 waiting;
u8 count;
u32 value0;
u32 value1;
u32 value2;
@ -299,7 +295,7 @@ public:
return false;
}))
{
spu->lock_notify();
spu.lock_notify();
}
}
@ -337,7 +333,7 @@ public:
void set_values(u32 count, u32 value0, u32 value1 = 0, u32 value2 = 0, u32 value3 = 0)
{
this->values.raw() = { 0, count, value0, value1, value2 };
this->values.raw() = { 0, static_cast<u8>(count), value0, value1, value2 };
this->value3 = value3;
}
};

View File

@ -908,22 +908,22 @@ std::array<ppu_function_t, 1024> g_ppu_syscall_table
null_func, null_func, null_func, null_func, //1023 UNS
};
extern void ppu_execute_syscall(PPUThread& ppu, u64 code)
extern void ppu_execute_syscall(ppu_thread& ppu, u64 code)
{
if (code < g_ppu_syscall_table.size())
{
// If autopause occures, check_status() will hold the thread till unpaused.
if (debug::autopause::pause_syscall(code) && ppu.check_status()) throw cpu_state::ret;
if (debug::autopause::pause_syscall(code) && ppu.check_state()) throw cpu_state::ret;
if (auto func = g_ppu_syscall_table[code])
{
func(ppu);
LOG_TRACE(PPU, "Syscall '%s' (%llu) finished, r3=0x%llx", ppu_get_syscall_name(code), code, ppu.GPR[3]);
LOG_TRACE(PPU, "Syscall '%s' (%llu) finished, r3=0x%llx", ppu_get_syscall_name(code), code, ppu.gpr[3]);
}
else
{
LOG_TODO(HLE, "Unimplemented syscall %s -> CELL_OK", ppu_get_syscall_name(code));
ppu.GPR[3] = 0;
ppu.gpr[3] = 0;
}
return;

View File

@ -23,10 +23,10 @@ void lv2_cond_t::notify(lv2_lock_t, cpu_thread* thread)
}
else
{
mutex->owner = idm::get<PPUThread>(thread->id);
mutex->owner = idm::get<ppu_thread>(thread->id);
VERIFY(!thread->state.test_and_set(cpu_state::signal));
(*thread)->notify();
thread->notify();
}
}
@ -165,7 +165,7 @@ s32 sys_cond_signal_to(u32 cond_id, u32 thread_id)
return CELL_OK;
}
s32 sys_cond_wait(PPUThread& ppu, u32 cond_id, u64 timeout)
s32 sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout)
{
sys_cond.trace("sys_cond_wait(cond_id=0x%x, timeout=%lld)", cond_id, timeout);
@ -212,7 +212,7 @@ s32 sys_cond_wait(PPUThread& ppu, u32 cond_id, u64 timeout)
// try to reown mutex and exit if timed out
if (!cond->mutex->owner)
{
cond->mutex->owner = idm::get<PPUThread>(ppu.id);
cond->mutex->owner = idm::get<ppu_thread>(ppu.id);
break;
}

View File

@ -33,12 +33,12 @@ struct lv2_cond_t
void notify(lv2_lock_t, cpu_thread* thread);
};
class PPUThread;
class ppu_thread;
// SysCalls
s32 sys_cond_create(vm::ptr<u32> cond_id, u32 mutex_id, vm::ptr<sys_cond_attribute_t> attr);
s32 sys_cond_destroy(u32 cond_id);
s32 sys_cond_wait(PPUThread& ppu, u32 cond_id, u64 timeout);
s32 sys_cond_wait(ppu_thread& ppu, u32 cond_id, u64 timeout);
s32 sys_cond_signal(u32 cond_id);
s32 sys_cond_signal_all(u32 cond_id);
s32 sys_cond_signal_to(u32 cond_id, u32 thread_id);

View File

@ -64,12 +64,12 @@ void lv2_event_queue_t::push(lv2_lock_t, u64 source, u64 data1, u64 data2, u64 d
if (type == SYS_PPU_QUEUE && thread->type == cpu_type::ppu)
{
// store event data in registers
auto& ppu = static_cast<PPUThread&>(*thread);
auto& ppu = static_cast<ppu_thread&>(*thread);
ppu.GPR[4] = source;
ppu.GPR[5] = data1;
ppu.GPR[6] = data2;
ppu.GPR[7] = data3;
ppu.gpr[4] = source;
ppu.gpr[5] = data1;
ppu.gpr[6] = data2;
ppu.gpr[7] = data3;
}
else if (type == SYS_SPU_QUEUE && thread->type == cpu_type::spu)
{
@ -84,7 +84,7 @@ void lv2_event_queue_t::push(lv2_lock_t, u64 source, u64 data1, u64 data2, u64 d
}
VERIFY(!thread->state.test_and_set(cpu_state::signal));
(*thread)->notify();
thread->notify();
return m_sq.pop_front();
}
@ -165,7 +165,7 @@ s32 sys_event_queue_destroy(u32 equeue_id, s32 mode)
{
if (queue->type == SYS_PPU_QUEUE && thread->type == cpu_type::ppu)
{
static_cast<PPUThread&>(*thread).GPR[3] = 1;
static_cast<ppu_thread&>(*thread).gpr[3] = 1;
}
else if (queue->type == SYS_SPU_QUEUE && thread->type == cpu_type::spu)
{
@ -177,7 +177,7 @@ s32 sys_event_queue_destroy(u32 equeue_id, s32 mode)
}
thread->state += cpu_state::signal;
(*thread)->notify();
thread->notify();
}
return CELL_OK;
@ -220,7 +220,7 @@ s32 sys_event_queue_tryreceive(u32 equeue_id, vm::ptr<sys_event_t> event_array,
return CELL_OK;
}
s32 sys_event_queue_receive(PPUThread& ppu, u32 equeue_id, vm::ptr<sys_event_t> dummy_event, u64 timeout)
s32 sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sys_event_t> dummy_event, u64 timeout)
{
sys_event.trace("sys_event_queue_receive(equeue_id=0x%x, *0x%x, timeout=0x%llx)", equeue_id, dummy_event, timeout);
@ -243,12 +243,12 @@ s32 sys_event_queue_receive(PPUThread& ppu, u32 equeue_id, vm::ptr<sys_event_t>
if (queue->events())
{
// event data is returned in registers (dummy_event is not used)
std::tie(ppu.GPR[4], ppu.GPR[5], ppu.GPR[6], ppu.GPR[7]) = queue->pop(lv2_lock);
std::tie(ppu.gpr[4], ppu.gpr[5], ppu.gpr[6], ppu.gpr[7]) = queue->pop(lv2_lock);
return CELL_OK;
}
// cause (if cancelled) will be returned in r3
ppu.GPR[3] = 0;
ppu.gpr[3] = 0;
// add waiter; protocol is ignored in current implementation
sleep_entry<cpu_thread> waiter(queue->thread_queue(lv2_lock), ppu);
@ -274,7 +274,7 @@ s32 sys_event_queue_receive(PPUThread& ppu, u32 equeue_id, vm::ptr<sys_event_t>
}
}
if (ppu.GPR[3])
if (ppu.gpr[3])
{
ENSURES(!idm::check<lv2_event_queue_t>(equeue_id));
return CELL_ECANCELED;

View File

@ -132,12 +132,12 @@ struct lv2_event_port_t
}
};
class PPUThread;
class ppu_thread;
// SysCalls
s32 sys_event_queue_create(vm::ptr<u32> equeue_id, vm::ptr<sys_event_queue_attribute_t> attr, u64 event_queue_key, s32 size);
s32 sys_event_queue_destroy(u32 equeue_id, s32 mode);
s32 sys_event_queue_receive(PPUThread& ppu, u32 equeue_id, vm::ptr<sys_event_t> dummy_event, u64 timeout);
s32 sys_event_queue_receive(ppu_thread& ppu, u32 equeue_id, vm::ptr<sys_event_t> dummy_event, u64 timeout);
s32 sys_event_queue_tryreceive(u32 equeue_id, vm::ptr<sys_event_t> event_array, s32 size, vm::ptr<u32> number);
s32 sys_event_queue_drain(u32 event_queue_id);

View File

@ -17,20 +17,20 @@ void lv2_event_flag_t::notify_all(lv2_lock_t)
{
auto pred = [this](cpu_thread* thread) -> bool
{
auto& ppu = static_cast<PPUThread&>(*thread);
auto& ppu = static_cast<ppu_thread&>(*thread);
// load pattern and mode from registers
const u64 bitptn = ppu.GPR[4];
const u32 mode = static_cast<u32>(ppu.GPR[5]);
const u64 bitptn = ppu.gpr[4];
const u32 mode = static_cast<u32>(ppu.gpr[5]);
// check specific pattern
if (check_pattern(bitptn, mode))
{
// save pattern
ppu.GPR[4] = clear_pattern(bitptn, mode);
ppu.gpr[4] = clear_pattern(bitptn, mode);
VERIFY(!thread->state.test_and_set(cpu_state::signal));
(*thread)->notify();
thread->notify();
return true;
}
@ -101,7 +101,7 @@ s32 sys_event_flag_destroy(u32 id)
return CELL_OK;
}
s32 sys_event_flag_wait(PPUThread& ppu, u32 id, u64 bitptn, u32 mode, vm::ptr<u64> result, u64 timeout)
s32 sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm::ptr<u64> result, u64 timeout)
{
sys_event_flag.trace("sys_event_flag_wait(id=0x%x, bitptn=0x%llx, mode=0x%x, result=*0x%x, timeout=0x%llx)", id, bitptn, mode, result, timeout);
@ -109,8 +109,8 @@ s32 sys_event_flag_wait(PPUThread& ppu, u32 id, u64 bitptn, u32 mode, vm::ptr<u6
// If this syscall is called through the SC instruction, these registers must already contain corresponding values.
// But let's fixup them (in the case of explicit function call or something) because these values are used externally.
ppu.GPR[4] = bitptn;
ppu.GPR[5] = mode;
ppu.gpr[4] = bitptn;
ppu.gpr[5] = mode;
LV2_LOCK;
@ -172,11 +172,11 @@ s32 sys_event_flag_wait(PPUThread& ppu, u32 id, u64 bitptn, u32 mode, vm::ptr<u6
// load pattern saved upon signaling
if (result)
{
*result = ppu.GPR[4];
*result = ppu.gpr[4];
}
// check cause
if (ppu.GPR[5] == 0)
if (ppu.gpr[5] == 0)
{
return CELL_ECANCELED;
}
@ -284,16 +284,16 @@ s32 sys_event_flag_cancel(u32 id, vm::ptr<u32> num)
// signal all threads to return CELL_ECANCELED
for (auto& thread : eflag->sq)
{
auto& ppu = static_cast<PPUThread&>(*thread);
auto& ppu = static_cast<ppu_thread&>(*thread);
// save existing pattern
ppu.GPR[4] = pattern;
ppu.gpr[4] = pattern;
// clear "mode" as a sign of cancellation
ppu.GPR[5] = 0;
ppu.gpr[5] = 0;
VERIFY(!thread->state.test_and_set(cpu_state::signal));
(*thread)->notify();
thread->notify();
}
eflag->sq.clear();

View File

@ -107,12 +107,12 @@ struct lv2_event_flag_t
};
// Aux
class PPUThread;
class ppu_thread;
// SysCalls
s32 sys_event_flag_create(vm::ptr<u32> id, vm::ptr<sys_event_flag_attribute_t> attr, u64 init);
s32 sys_event_flag_destroy(u32 id);
s32 sys_event_flag_wait(PPUThread& ppu, u32 id, u64 bitptn, u32 mode, vm::ptr<u64> result, u64 timeout);
s32 sys_event_flag_wait(ppu_thread& ppu, u32 id, u64 bitptn, u32 mode, vm::ptr<u64> result, u64 timeout);
s32 sys_event_flag_trywait(u32 id, u64 bitptn, u32 mode, vm::ptr<u64> result);
s32 sys_event_flag_set(u32 id, u64 bitptn);
s32 sys_event_flag_clear(u32 id, u64 bitptn);

View File

@ -5,17 +5,35 @@
#include "Emu/Cell/ErrorCodes.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/PPUOpcodes.h"
#include "sys_interrupt.h"
logs::channel sys_interrupt("sys_interrupt", logs::level::notice);
void lv2_int_serv_t::join(PPUThread& ppu, lv2_lock_t lv2_lock)
void lv2_int_serv_t::exec()
{
// Use is_joining to stop interrupt thread and signal
thread->is_joining = true;
(*thread)->notify();
thread->cmd_list
({
{ ppu_cmd::set_args, 2 }, arg1, arg2,
{ ppu_cmd::lle_call, 2 },
});
// Start joining
thread->lock_notify();
}
void lv2_int_serv_t::join(ppu_thread& ppu, lv2_lock_t lv2_lock)
{
// Enqueue _sys_ppu_thread_exit call
thread->cmd_list
({
{ ppu_cmd::set_args, 1 }, u64{0},
{ ppu_cmd::set_gpr, 11 }, u64{41},
{ ppu_cmd::opcode, ppu_instructions::SC(0) },
});
thread->lock_notify();
// Join thread (TODO)
while (!(thread->state & cpu_state::exit))
{
CHECK_EMU_STATUS;
@ -25,7 +43,6 @@ void lv2_int_serv_t::join(PPUThread& ppu, lv2_lock_t lv2_lock)
// Cleanup
idm::remove<lv2_int_serv_t>(id);
idm::remove<PPUThread>(thread->id);
}
s32 sys_interrupt_tag_destroy(u32 intrtag)
@ -66,7 +83,7 @@ s32 _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread
}
// Get interrupt thread
const auto it = idm::get<PPUThread>(intrthread);
const auto it = idm::get<ppu_thread>(intrthread);
if (!it)
{
@ -86,55 +103,16 @@ s32 _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread
return CELL_ESTAT;
}
const auto handler = idm::make_ptr<lv2_int_serv_t>(it);
tag->handler = idm::make_ptr<lv2_int_serv_t>(it, arg1, arg2);
tag->handler = handler;
it->run();
it->custom_task = [handler, arg1, arg2](PPUThread& ppu)
{
const u32 pc = ppu.pc;
const u32 rtoc = ppu.GPR[2];
LV2_LOCK;
while (!ppu.is_joining)
{
CHECK_EMU_STATUS;
// call interrupt handler until int status is clear
if (handler->signal)
{
if (lv2_lock) lv2_lock.unlock();
ppu.GPR[3] = arg1;
ppu.GPR[4] = arg2;
ppu.fast_call(pc, rtoc);
handler->signal--;
continue;
}
if (!lv2_lock)
{
lv2_lock.lock();
continue;
}
get_current_thread_cv().wait(lv2_lock);
}
ppu.state += cpu_state::exit;
};
it->state -= cpu_state::stop;
(*it)->lock_notify();
*ih = handler->id;
*ih = tag->handler->id;
return CELL_OK;
}
s32 _sys_interrupt_thread_disestablish(PPUThread& ppu, u32 ih, vm::ptr<u64> r13)
s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr<u64> r13)
{
sys_interrupt.warning("_sys_interrupt_thread_disestablish(ih=0x%x, r13=*0x%x)", ih, r13);
@ -151,12 +129,12 @@ s32 _sys_interrupt_thread_disestablish(PPUThread& ppu, u32 ih, vm::ptr<u64> r13)
handler->join(ppu, lv2_lock);
// Save TLS base
*r13 = handler->thread->GPR[13];
*r13 = handler->thread->gpr[13];
return CELL_OK;
}
void sys_interrupt_thread_eoi(PPUThread& ppu) // Low-level PPU function example
void sys_interrupt_thread_eoi(ppu_thread& ppu) // Low-level PPU function example
{
// Low-level function body must guard all C++-ish calls and all objects with non-trivial destructors
thread_guard{ppu}, sys_interrupt.trace("sys_interrupt_thread_eoi()");
@ -164,7 +142,7 @@ void sys_interrupt_thread_eoi(PPUThread& ppu) // Low-level PPU function example
ppu.state += cpu_state::ret;
// Throw if this syscall was not called directly by the SC instruction (hack)
if (ppu.LR == 0 || ppu.GPR[11] != 88 || ppu.custom_task)
if (ppu.lr == 0 || ppu.gpr[11] != 88)
{
// Low-level function must disable interrupts before throwing (not related to sys_interrupt_*, it's rather coincidence)
ppu->interrupt_disable();

View File

@ -2,7 +2,7 @@
#include "sys_sync.h"
class PPUThread;
class ppu_thread;
struct lv2_int_tag_t
{
@ -13,22 +13,26 @@ struct lv2_int_tag_t
struct lv2_int_serv_t
{
const std::shared_ptr<PPUThread> thread;
const std::shared_ptr<ppu_thread> thread;
const id_value<> id{};
atomic_t<u32> signal{ 0 }; // signal count
const u64 arg1;
const u64 arg2;
lv2_int_serv_t(const std::shared_ptr<PPUThread>& thread)
lv2_int_serv_t(const std::shared_ptr<ppu_thread>& thread, u64 arg1, u64 arg2)
: thread(thread)
, arg1(arg1)
, arg2(arg2)
{
}
void join(PPUThread& ppu, lv2_lock_t);
void exec();
void join(ppu_thread& ppu, lv2_lock_t);
};
// SysCalls
s32 sys_interrupt_tag_destroy(u32 intrtag);
s32 _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2);
s32 _sys_interrupt_thread_disestablish(PPUThread& ppu, u32 ih, vm::ptr<u64> r13);
void sys_interrupt_thread_eoi(PPUThread& ppu);
s32 _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr<u64> r13);
void sys_interrupt_thread_eoi(ppu_thread& ppu);

View File

@ -16,9 +16,9 @@ extern u64 get_system_time();
void lv2_lwcond_t::notify(lv2_lock_t, cpu_thread* thread, const std::shared_ptr<lv2_lwmutex_t>& mutex, bool mode2)
{
auto& ppu = static_cast<PPUThread&>(*thread);
auto& ppu = static_cast<ppu_thread&>(*thread);
ppu.GPR[3] = mode2; // set to return CELL_EBUSY
ppu.gpr[3] = mode2; // set to return CELL_EBUSY
if (!mode2)
{
@ -31,7 +31,7 @@ void lv2_lwcond_t::notify(lv2_lock_t, cpu_thread* thread, const std::shared_ptr<
}
VERIFY(!thread->state.test_and_set(cpu_state::signal));
(*thread)->notify();
thread->notify();
}
s32 _sys_lwcond_create(vm::ptr<u32> lwcond_id, u32 lwmutex_id, vm::ptr<sys_lwcond_t> control, u64 name, u32 arg5)
@ -155,7 +155,7 @@ s32 _sys_lwcond_signal_all(u32 lwcond_id, u32 lwmutex_id, u32 mode)
return result;
}
s32 _sys_lwcond_queue_wait(PPUThread& ppu, u32 lwcond_id, u32 lwmutex_id, u64 timeout)
s32 _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u64 timeout)
{
sys_lwcond.trace("_sys_lwcond_queue_wait(lwcond_id=0x%x, lwmutex_id=0x%x, timeout=0x%llx)", lwcond_id, lwmutex_id, timeout);
@ -212,5 +212,5 @@ s32 _sys_lwcond_queue_wait(PPUThread& ppu, u32 lwcond_id, u32 lwmutex_id, u64 ti
}
// return cause
return ppu.GPR[3] ? CELL_EBUSY : CELL_OK;
return ppu.gpr[3] ? CELL_EBUSY : CELL_OK;
}

View File

@ -32,11 +32,11 @@ struct lv2_lwcond_t
};
// Aux
class PPUThread;
class ppu_thread;
// SysCalls
s32 _sys_lwcond_create(vm::ptr<u32> lwcond_id, u32 lwmutex_id, vm::ptr<sys_lwcond_t> control, u64 name, u32 arg5);
s32 _sys_lwcond_destroy(u32 lwcond_id);
s32 _sys_lwcond_signal(u32 lwcond_id, u32 lwmutex_id, u32 ppu_thread_id, u32 mode);
s32 _sys_lwcond_signal_all(u32 lwcond_id, u32 lwmutex_id, u32 mode);
s32 _sys_lwcond_queue_wait(PPUThread& ppu, u32 lwcond_id, u32 lwmutex_id, u64 timeout);
s32 _sys_lwcond_queue_wait(ppu_thread& ppu, u32 lwcond_id, u32 lwmutex_id, u64 timeout);

View File

@ -22,7 +22,7 @@ void lv2_lwmutex_t::unlock(lv2_lock_t)
{
auto& thread = sq.front();
VERIFY(!thread->state.test_and_set(cpu_state::signal));
(*thread)->notify();
thread->notify();
sq.pop_front();
}
@ -75,7 +75,7 @@ s32 _sys_lwmutex_destroy(u32 lwmutex_id)
return CELL_OK;
}
s32 _sys_lwmutex_lock(PPUThread& ppu, u32 lwmutex_id, u64 timeout)
s32 _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout)
{
sys_lwmutex.trace("_sys_lwmutex_lock(lwmutex_id=0x%x, timeout=0x%llx)", lwmutex_id, timeout);

View File

@ -64,11 +64,11 @@ struct lv2_lwmutex_t
};
// Aux
class PPUThread;
class ppu_thread;
// SysCalls
s32 _sys_lwmutex_create(vm::ptr<u32> lwmutex_id, u32 protocol, vm::ptr<sys_lwmutex_t> control, u32 arg4, u64 name, u32 arg6);
s32 _sys_lwmutex_destroy(u32 lwmutex_id);
s32 _sys_lwmutex_lock(PPUThread& ppu, u32 lwmutex_id, u64 timeout);
s32 _sys_lwmutex_lock(ppu_thread& ppu, u32 lwmutex_id, u64 timeout);
s32 _sys_lwmutex_trylock(u32 lwmutex_id);
s32 _sys_lwmutex_unlock(u32 lwmutex_id);

View File

@ -18,10 +18,10 @@ void lv2_mutex_t::unlock(lv2_lock_t)
if (sq.size())
{
// pick new owner; protocol is ignored in current implementation
owner = idm::get<PPUThread>(sq.front()->id);
owner = idm::get<ppu_thread>(sq.front()->id);
VERIFY(!owner->state.test_and_set(cpu_state::signal));
(*owner)->notify();
owner->notify();
}
}
@ -91,7 +91,7 @@ s32 sys_mutex_destroy(u32 mutex_id)
return CELL_OK;
}
s32 sys_mutex_lock(PPUThread& ppu, u32 mutex_id, u64 timeout)
s32 sys_mutex_lock(ppu_thread& ppu, u32 mutex_id, u64 timeout)
{
sys_mutex.trace("sys_mutex_lock(mutex_id=0x%x, timeout=0x%llx)", mutex_id, timeout);
@ -127,7 +127,7 @@ s32 sys_mutex_lock(PPUThread& ppu, u32 mutex_id, u64 timeout)
// lock immediately if not locked
if (!mutex->owner)
{
mutex->owner = idm::get<PPUThread>(ppu.id);
mutex->owner = idm::get<ppu_thread>(ppu.id);
return CELL_OK;
}
@ -165,7 +165,7 @@ s32 sys_mutex_lock(PPUThread& ppu, u32 mutex_id, u64 timeout)
return CELL_OK;
}
s32 sys_mutex_trylock(PPUThread& ppu, u32 mutex_id)
s32 sys_mutex_trylock(ppu_thread& ppu, u32 mutex_id)
{
sys_mutex.trace("sys_mutex_trylock(mutex_id=0x%x)", mutex_id);
@ -202,12 +202,12 @@ s32 sys_mutex_trylock(PPUThread& ppu, u32 mutex_id)
}
// own the mutex if free
mutex->owner = idm::get<PPUThread>(ppu.id);
mutex->owner = idm::get<ppu_thread>(ppu.id);
return CELL_OK;
}
s32 sys_mutex_unlock(PPUThread& ppu, u32 mutex_id)
s32 sys_mutex_unlock(ppu_thread& ppu, u32 mutex_id)
{
sys_mutex.trace("sys_mutex_unlock(mutex_id=0x%x)", mutex_id);

View File

@ -41,11 +41,11 @@ struct lv2_mutex_t
void unlock(lv2_lock_t);
};
class PPUThread;
class ppu_thread;
// SysCalls
s32 sys_mutex_create(vm::ptr<u32> mutex_id, vm::ptr<sys_mutex_attribute_t> attr);
s32 sys_mutex_destroy(u32 mutex_id);
s32 sys_mutex_lock(PPUThread& ppu, u32 mutex_id, u64 timeout);
s32 sys_mutex_trylock(PPUThread& ppu, u32 mutex_id);
s32 sys_mutex_unlock(PPUThread& ppu, u32 mutex_id);
s32 sys_mutex_lock(ppu_thread& ppu, u32 mutex_id, u64 timeout);
s32 sys_mutex_trylock(ppu_thread& ppu, u32 mutex_id);
s32 sys_mutex_unlock(ppu_thread& ppu, u32 mutex_id);

View File

@ -11,37 +11,24 @@
logs::channel sys_ppu_thread("sys_ppu_thread", logs::level::notice);
void _sys_ppu_thread_exit(PPUThread& ppu, u64 errorcode)
void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode)
{
sys_ppu_thread.warning("_sys_ppu_thread_exit(errorcode=0x%llx)", errorcode);
// TODO: Should we really unlock mutexes?
// TODO: shall sys_mutex objects be unlocked?
//// get all sys_mutex objects
//for (auto& mutex : idm::get_all<lv2_mutex_t>())
//{
// // unlock mutex if locked by this thread
// if (mutex->owner.get() == &ppu)
// {
// mutex->unlock(lv2_lock);
// }
//}
LV2_LOCK;
ppu.state += cpu_state::exit;
// Delete detached thread
if (!ppu.is_joinable)
{
LV2_LOCK;
ppu.state += cpu_state::exit;
//ppu.handle_interrupt();
// Delete detached thread
if (!ppu.is_joinable)
{
idm::remove<PPUThread>(ppu.id);
}
idm::remove<ppu_thread>(ppu.id);
}
// Throw if this syscall was not called directly by the SC instruction (hack)
if (ppu.LR == 0 || ppu.GPR[11] != 41 || ppu.custom_task)
if (ppu.lr == 0 || ppu.gpr[11] != 41)
{
throw cpu_state::exit;
}
@ -54,13 +41,13 @@ void sys_ppu_thread_yield()
std::this_thread::yield();
}
s32 sys_ppu_thread_join(PPUThread& ppu, u32 thread_id, vm::ptr<u64> vptr)
s32 sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr<u64> vptr)
{
sys_ppu_thread.warning("sys_ppu_thread_join(thread_id=0x%x, vptr=*0x%x)", thread_id, vptr);
LV2_LOCK;
const auto thread = idm::get<PPUThread>(thread_id);
const auto thread = idm::get<ppu_thread>(thread_id);
if (!thread)
{
@ -77,8 +64,6 @@ s32 sys_ppu_thread_join(PPUThread& ppu, u32 thread_id, vm::ptr<u64> vptr)
return CELL_EDEADLK;
}
ppu.sleep();
// mark joining
thread->is_joining = true;
@ -90,13 +75,11 @@ s32 sys_ppu_thread_join(PPUThread& ppu, u32 thread_id, vm::ptr<u64> vptr)
get_current_thread_cv().wait_for(lv2_lock, 1ms);
}
ppu.awake();
// get exit status from the register
if (vptr) *vptr = thread->GPR[3];
if (vptr) *vptr = thread->gpr[3];
// cleanup
idm::remove<PPUThread>(thread->id);
idm::remove<ppu_thread>(thread->id);
return CELL_OK;
}
@ -107,7 +90,7 @@ s32 sys_ppu_thread_detach(u32 thread_id)
LV2_LOCK;
const auto thread = idm::get<PPUThread>(thread_id);
const auto thread = idm::get<ppu_thread>(thread_id);
if (!thread)
{
@ -130,7 +113,7 @@ s32 sys_ppu_thread_detach(u32 thread_id)
return CELL_OK;
}
void sys_ppu_thread_get_join_state(PPUThread& ppu, vm::ptr<s32> isjoinable)
void sys_ppu_thread_get_join_state(ppu_thread& ppu, vm::ptr<s32> isjoinable)
{
sys_ppu_thread.warning("sys_ppu_thread_get_join_state(isjoinable=*0x%x)", isjoinable);
@ -145,7 +128,7 @@ s32 sys_ppu_thread_set_priority(u32 thread_id, s32 prio)
LV2_LOCK;
const auto thread = idm::get<PPUThread>(thread_id);
const auto thread = idm::get<ppu_thread>(thread_id);
if (!thread)
{
@ -168,7 +151,7 @@ s32 sys_ppu_thread_get_priority(u32 thread_id, vm::ptr<s32> priop)
LV2_LOCK;
const auto thread = idm::get<PPUThread>(thread_id);
const auto thread = idm::get<ppu_thread>(thread_id);
if (!thread)
{
@ -180,7 +163,7 @@ s32 sys_ppu_thread_get_priority(u32 thread_id, vm::ptr<s32> priop)
return CELL_OK;
}
s32 sys_ppu_thread_get_stack_information(PPUThread& ppu, vm::ptr<sys_ppu_thread_stack_t> sp)
s32 sys_ppu_thread_get_stack_information(ppu_thread& ppu, vm::ptr<sys_ppu_thread_stack_t> sp)
{
sys_ppu_thread.trace("sys_ppu_thread_get_stack_information(sp=*0x%x)", sp);
@ -196,7 +179,7 @@ s32 sys_ppu_thread_stop(u32 thread_id)
LV2_LOCK;
const auto thread = idm::get<PPUThread>(thread_id);
const auto thread = idm::get<ppu_thread>(thread_id);
if (!thread)
{
@ -214,7 +197,7 @@ s32 sys_ppu_thread_restart(u32 thread_id)
LV2_LOCK;
const auto thread = idm::get<PPUThread>(thread_id);
const auto thread = idm::get<ppu_thread>(thread_id);
if (!thread)
{
@ -239,28 +222,30 @@ s32 _sys_ppu_thread_create(vm::ptr<u64> thread_id, vm::ptr<ppu_thread_param_t> p
return CELL_EINVAL;
}
const bool is_joinable = (flags & SYS_PPU_THREAD_CREATE_JOINABLE) != 0;
const bool is_interrupt = (flags & SYS_PPU_THREAD_CREATE_INTERRUPT) != 0;
if (is_joinable && is_interrupt)
if ((flags & 3) == 3) // Check two flags: joinable + interrupt not allowed
{
return CELL_EPERM;
}
const auto ppu = idm::make_ptr<PPUThread>(threadname ? threadname.get_ptr() : "");
const auto ppu = idm::make_ptr<ppu_thread>(threadname ? threadname.get_ptr() : "", prio, stacksize);
ppu->prio = prio;
ppu->stack_size = std::max<u32>(stacksize, 0x4000);
ppu->cpu_init();
ppu->is_joinable = (flags & SYS_PPU_THREAD_CREATE_JOINABLE) != 0;
ppu->gpr[13] = param->tls.value();
ppu->pc = vm::read32(param->entry);
ppu->GPR[2] = vm::read32(param->entry + 4); // rtoc
ppu->GPR[3] = arg;
ppu->GPR[4] = unk; // actually unknown
ppu->GPR[13] = param->tls;
ppu->is_joinable = is_joinable;
//ppu->state += cpu_state::interrupt;
if ((flags & SYS_PPU_THREAD_CREATE_INTERRUPT) == 0)
{
// Initialize thread entry point
ppu->cmd_list
({
{ ppu_cmd::set_args, 2 }, arg, unk, // Actually unknown
{ ppu_cmd::lle_call, param->entry.value() },
});
}
else
{
// Save entry for further use
ppu->gpr[2] = param->entry.value();
}
*thread_id = ppu->id;
@ -273,15 +258,14 @@ s32 sys_ppu_thread_start(u32 thread_id)
LV2_LOCK;
const auto thread = idm::get<PPUThread>(thread_id);
const auto thread = idm::get<ppu_thread>(thread_id);
if (!thread)
{
return CELL_ESRCH;
}
thread->state -= cpu_state::stop;
(*thread)->lock_notify();
thread->run();
return CELL_OK;
}
@ -292,7 +276,7 @@ s32 sys_ppu_thread_rename(u32 thread_id, vm::cptr<char> name)
LV2_LOCK;
const auto thread = idm::get<PPUThread>(thread_id);
const auto thread = idm::get<ppu_thread>(thread_id);
if (!thread)
{

View File

@ -2,7 +2,7 @@
#include "sys_sync.h"
class PPUThread;
class ppu_thread;
enum : u32
{
@ -42,14 +42,14 @@ enum : u32
};
// SysCalls
void _sys_ppu_thread_exit(PPUThread& ppu, u64 errorcode);
void _sys_ppu_thread_exit(ppu_thread& ppu, u64 errorcode);
void sys_ppu_thread_yield();
s32 sys_ppu_thread_join(PPUThread& ppu, u32 thread_id, vm::ptr<u64> vptr);
s32 sys_ppu_thread_join(ppu_thread& ppu, u32 thread_id, vm::ptr<u64> vptr);
s32 sys_ppu_thread_detach(u32 thread_id);
void sys_ppu_thread_get_join_state(PPUThread& ppu, vm::ptr<s32> isjoinable);
void sys_ppu_thread_get_join_state(ppu_thread& ppu, vm::ptr<s32> isjoinable);
s32 sys_ppu_thread_set_priority(u32 thread_id, s32 prio);
s32 sys_ppu_thread_get_priority(u32 thread_id, vm::ptr<s32> priop);
s32 sys_ppu_thread_get_stack_information(PPUThread& ppu, vm::ptr<sys_ppu_thread_stack_t> sp);
s32 sys_ppu_thread_get_stack_information(ppu_thread& ppu, vm::ptr<sys_ppu_thread_stack_t> sp);
s32 sys_ppu_thread_stop(u32 thread_id);
s32 sys_ppu_thread_restart(u32 thread_id);
s32 _sys_ppu_thread_create(vm::ptr<u64> thread_id, vm::ptr<ppu_thread_param_t> param, u64 arg, u64 arg4, s32 prio, u32 stacksize, u64 flags, vm::cptr<char> threadname);

View File

@ -241,7 +241,7 @@ s32 sys_process_wait_for_child2(u64 unk1, u64 unk2, u64 unk3, u64 unk4, u64 unk5
s32 sys_process_get_status(u64 unk)
{
sys_process.todo("sys_process_get_status(unk=0x%llx)", unk);
//vm::write32(CPU.GPR[4], GetPPUThreadStatus(CPU));
//vm::write32(CPU.gpr[4], GetPPUThreadStatus(CPU));
return CELL_OK;
}

View File

@ -16,10 +16,10 @@ void lv2_rwlock_t::notify_all(lv2_lock_t)
// pick a new writer if possible; protocol is ignored in current implementation
if (!readers && !writer && wsq.size())
{
writer = idm::get<PPUThread>(wsq.front()->id);
writer = idm::get<ppu_thread>(wsq.front()->id);
VERIFY(!writer->state.test_and_set(cpu_state::signal));
(*writer)->notify();
writer->notify();
return wsq.pop_front();
}
@ -32,7 +32,7 @@ void lv2_rwlock_t::notify_all(lv2_lock_t)
for (auto& thread : rsq)
{
VERIFY(!thread->state.test_and_set(cpu_state::signal));
(*thread)->notify();
thread->notify();
}
return rsq.clear();
@ -90,7 +90,7 @@ s32 sys_rwlock_destroy(u32 rw_lock_id)
return CELL_OK;
}
s32 sys_rwlock_rlock(PPUThread& ppu, u32 rw_lock_id, u64 timeout)
s32 sys_rwlock_rlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
{
sys_rwlock.trace("sys_rwlock_rlock(rw_lock_id=0x%x, timeout=0x%llx)", rw_lock_id, timeout);
@ -199,7 +199,7 @@ s32 sys_rwlock_runlock(u32 rw_lock_id)
return CELL_OK;
}
s32 sys_rwlock_wlock(PPUThread& ppu, u32 rw_lock_id, u64 timeout)
s32 sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout)
{
sys_rwlock.trace("sys_rwlock_wlock(rw_lock_id=0x%x, timeout=0x%llx)", rw_lock_id, timeout);
@ -221,7 +221,7 @@ s32 sys_rwlock_wlock(PPUThread& ppu, u32 rw_lock_id, u64 timeout)
if (!rwlock->readers && !rwlock->writer)
{
rwlock->writer = idm::get<PPUThread>(ppu.id);
rwlock->writer = idm::get<ppu_thread>(ppu.id);
return CELL_OK;
}
@ -270,7 +270,7 @@ s32 sys_rwlock_wlock(PPUThread& ppu, u32 rw_lock_id, u64 timeout)
return CELL_OK;
}
s32 sys_rwlock_trywlock(PPUThread& ppu, u32 rw_lock_id)
s32 sys_rwlock_trywlock(ppu_thread& ppu, u32 rw_lock_id)
{
sys_rwlock.trace("sys_rwlock_trywlock(rw_lock_id=0x%x)", rw_lock_id);
@ -293,12 +293,12 @@ s32 sys_rwlock_trywlock(PPUThread& ppu, u32 rw_lock_id)
return CELL_EBUSY;
}
rwlock->writer = idm::get<PPUThread>(ppu.id);
rwlock->writer = idm::get<ppu_thread>(ppu.id);
return CELL_OK;
}
s32 sys_rwlock_wunlock(PPUThread& ppu, u32 rw_lock_id)
s32 sys_rwlock_wunlock(ppu_thread& ppu, u32 rw_lock_id)
{
sys_rwlock.trace("sys_rwlock_wunlock(rw_lock_id=0x%x)", rw_lock_id);

View File

@ -38,14 +38,14 @@ struct lv2_rwlock_t
};
// Aux
class PPUThread;
class ppu_thread;
// SysCalls
s32 sys_rwlock_create(vm::ptr<u32> rw_lock_id, vm::ptr<sys_rwlock_attribute_t> attr);
s32 sys_rwlock_destroy(u32 rw_lock_id);
s32 sys_rwlock_rlock(PPUThread& ppu, u32 rw_lock_id, u64 timeout);
s32 sys_rwlock_rlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout);
s32 sys_rwlock_tryrlock(u32 rw_lock_id);
s32 sys_rwlock_runlock(u32 rw_lock_id);
s32 sys_rwlock_wlock(PPUThread& ppu, u32 rw_lock_id, u64 timeout);
s32 sys_rwlock_trywlock(PPUThread& ppu, u32 rw_lock_id);
s32 sys_rwlock_wunlock(PPUThread& ppu, u32 rw_lock_id);
s32 sys_rwlock_wlock(ppu_thread& ppu, u32 rw_lock_id, u64 timeout);
s32 sys_rwlock_trywlock(ppu_thread& ppu, u32 rw_lock_id);
s32 sys_rwlock_wunlock(ppu_thread& ppu, u32 rw_lock_id);

View File

@ -68,7 +68,7 @@ s32 sys_semaphore_destroy(u32 sem_id)
return CELL_OK;
}
s32 sys_semaphore_wait(PPUThread& ppu, u32 sem_id, u64 timeout)
s32 sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout)
{
sys_semaphore.trace("sys_semaphore_wait(sem_id=0x%x, timeout=0x%llx)", sem_id, timeout);
@ -174,7 +174,7 @@ s32 sys_semaphore_post(u32 sem_id, s32 count)
auto& thread = sem->sq.front();
VERIFY(!thread->state.test_and_set(cpu_state::signal));
(*thread)->notify();
thread->notify();
sem->sq.pop_front();
}

View File

@ -37,12 +37,12 @@ struct lv2_sema_t
};
// Aux
class PPUThread;
class ppu_thread;
// SysCalls
s32 sys_semaphore_create(vm::ptr<u32> sem_id, vm::ptr<sys_semaphore_attribute_t> attr, s32 initial_val, s32 max_val);
s32 sys_semaphore_destroy(u32 sem_id);
s32 sys_semaphore_wait(PPUThread& ppu, u32 sem_id, u64 timeout);
s32 sys_semaphore_wait(ppu_thread& ppu, u32 sem_id, u64 timeout);
s32 sys_semaphore_trywait(u32 sem_id);
s32 sys_semaphore_post(u32 sem_id, s32 count);
s32 sys_semaphore_get_value(u32 sem_id, vm::ptr<s32> count);

View File

@ -321,8 +321,7 @@ s32 sys_spu_thread_group_start(u32 id)
{
if (thread)
{
thread->state -= cpu_state::stop;
(*thread)->lock_notify();
thread->run();
}
}
@ -420,7 +419,7 @@ s32 sys_spu_thread_group_resume(u32 id)
if (thread)
{
thread->state -= cpu_state::suspend;
(*thread)->lock_notify();
thread->lock_notify();
}
}
@ -503,7 +502,7 @@ s32 sys_spu_thread_group_terminate(u32 id, s32 value)
if (thread)
{
thread->state += cpu_state::stop;
(*thread)->lock_notify();
thread->lock_notify();
}
}
@ -1154,7 +1153,7 @@ s32 sys_raw_spu_create(vm::ptr<u32> id, vm::ptr<void> attr)
return CELL_OK;
}
s32 sys_raw_spu_destroy(PPUThread& ppu, u32 id)
s32 sys_raw_spu_destroy(ppu_thread& ppu, u32 id)
{
sys_spu.warning("sys_raw_spu_destroy(id=%d)", id);

View File

@ -196,7 +196,7 @@ struct lv2_spu_group_t
}
};
class PPUThread;
class ppu_thread;
// Aux
void LoadSpuImage(const fs::file& stream, u32& spu_ep, u32 addr);
@ -233,7 +233,7 @@ s32 sys_spu_thread_unbind_queue(u32 id, u32 spuq_num);
s32 sys_spu_thread_get_exit_status(u32 id, vm::ptr<u32> status);
s32 sys_raw_spu_create(vm::ptr<u32> id, vm::ptr<void> attr);
s32 sys_raw_spu_destroy(PPUThread& ppu, u32 id);
s32 sys_raw_spu_destroy(ppu_thread& ppu, u32 id);
s32 sys_raw_spu_create_interrupt_tag(u32 id, u32 class_id, u32 hwthread, vm::ptr<u32> intrtag);
s32 sys_raw_spu_set_int_mask(u32 id, u32 class_id, u64 mask);
s32 sys_raw_spu_get_int_mask(u32 id, u32 class_id, vm::ptr<u64> mask);

View File

@ -8,17 +8,13 @@
#include "sys_process.h"
#include "sys_timer.h"
#include <thread>
logs::channel sys_timer("sys_timer", logs::level::notice);
extern u64 get_system_time();
extern std::mutex& get_current_thread_mutex();
void lv2_timer_t::on_task()
{
std::unique_lock<std::mutex> lock(get_current_thread_mutex());
thread_lock lock(*this);
while (state <= SYS_TIMER_STATE_RUN)
{
@ -54,7 +50,7 @@ void lv2_timer_t::on_task()
continue;
}
get_current_thread_cv().wait_for(lock, 1ms);
thread_ctrl::wait_for(100);
}
}
@ -67,7 +63,7 @@ void lv2_timer_t::on_stop()
{
// Signal thread using invalid state and join
state = -1;
(*this)->lock_notify();
this->lock_notify();
named_thread::on_stop();
}
@ -172,8 +168,7 @@ s32 _sys_timer_start(u32 timer_id, u64 base_time, u64 period)
timer->expire = base_time ? base_time : start_time + period;
timer->period = period;
timer->state = SYS_TIMER_STATE_RUN;
(*timer)->lock_notify();
timer->lock_notify();
return CELL_OK;
}

View File

@ -823,19 +823,19 @@ namespace vm
{
case cpu_type::ppu:
{
PPUThread& context = static_cast<PPUThread&>(*cpu);
ppu_thread& context = static_cast<ppu_thread&>(*cpu);
const u32 old_pos = vm::cast(context.GPR[1], HERE);
context.GPR[1] -= align(size + 4, 8); // room minimal possible size
context.GPR[1] &= ~(align_v - 1); // fix stack alignment
const u32 old_pos = vm::cast(context.gpr[1], HERE);
context.gpr[1] -= align(size + 4, 8); // room minimal possible size
context.gpr[1] &= ~(align_v - 1); // fix stack alignment
if (context.GPR[1] < context.stack_addr)
if (context.gpr[1] < context.stack_addr)
{
throw EXCEPTION("Stack overflow (size=0x%x, align=0x%x, SP=0x%llx, stack=*0x%x)", size, align_v, old_pos, context.stack_addr);
}
else
{
const u32 addr = static_cast<u32>(context.GPR[1]);
const u32 addr = static_cast<u32>(context.gpr[1]);
vm::ps3::_ref<nse_t<u32>>(addr + size) = old_pos;
std::memset(vm::base(addr), 0, size);
return addr;
@ -896,15 +896,15 @@ namespace vm
{
case cpu_type::ppu:
{
PPUThread& context = static_cast<PPUThread&>(*cpu);
ppu_thread& context = static_cast<ppu_thread&>(*cpu);
if (context.GPR[1] != addr)
if (context.gpr[1] != addr)
{
LOG_ERROR(MEMORY, "Stack inconsistency (addr=0x%x, SP=0x%llx, size=0x%x)", addr, context.GPR[1], size);
LOG_ERROR(MEMORY, "Stack inconsistency (addr=0x%x, SP=0x%llx, size=0x%x)", addr, context.gpr[1], size);
return;
}
context.GPR[1] = vm::ps3::_ref<nse_t<u32>>(context.GPR[1] + size);
context.gpr[1] = vm::ps3::_ref<nse_t<u32>>(context.gpr[1] + size);
return;
}

View File

@ -2,7 +2,7 @@
#include "vm_ref.h"
class PPUThread;
class ppu_thread;
class ARMv7Thread;
namespace vm
@ -264,7 +264,7 @@ namespace vm
}
// Callback; defined in PPUCallback.h, passing context is mandatory
RT operator()(PPUThread& ppu, T... args) const;
RT operator()(ppu_thread& ppu, T... args) const;
// Callback; defined in ARMv7Callback.h, passing context is mandatory
RT operator()(ARMv7Thread& cpu, T... args) const;

View File

@ -77,7 +77,7 @@ void ARMv7Thread::cpu_task_main()
return fmt::format("%s [0x%08x]", cpu->get_name(), cpu->PC);
};
while (!state.load() || !check_status())
while (!state.load() || !check_state())
{
if (ISET == Thumb)
{

View File

@ -129,9 +129,7 @@ arm_error_code sceKernelStartThread(s32 threadId, u32 argSize, vm::cptr<void> pA
// set SceKernelThreadEntry function arguments
thread->GPR[0] = argSize;
thread->GPR[1] = pos;
thread->state -= cpu_state::stop;
(*thread)->lock_notify();
thread->run();
return SCE_OK;
}
@ -308,7 +306,7 @@ arm_error_code sceKernelWaitThreadEnd(s32 threadId, vm::ptr<s32> pExitStatus, vm
{
}
(*thread)->join();
thread->join();
if (pExitStatus)
{
@ -480,23 +478,20 @@ struct psp2_event_flag final
// Returns true if the command has been completed immediately. Its status is unknown otherwise.
bool exec(task type, u32 arg)
{
// Acquire position in the queue
// Allocate position in the queue
const u32 push_pos = m_workload.push_begin();
// Make the command
cmd_t cmd{type, arg};
// Get queue head
u32 pos = m_workload.peek();
// Check optimistic case
// Check non-optimistic case
if (UNLIKELY(pos != push_pos))
{
// Write the command
m_workload[push_pos] = cmd;
pos = m_workload.peek(); // ???
// Try to acquire a command
cmd = m_workload[pos].exchange({task::null});
// Try to acquire first command in the queue, *then* write current command
m_workload[push_pos] = std::exchange(cmd, m_workload[pos].exchange({task::null}));
}
while (true)
@ -523,7 +518,7 @@ struct psp2_event_flag final
idm::get<ARMv7Thread>(cmd.arg, [&](u32, ARMv7Thread& cpu)
{
cpu.state += cpu_state::signal;
cpu->lock_notify();
cpu.lock_notify();
});
break;
@ -869,9 +864,9 @@ arm_error_code sceKernelWaitEventFlag(ARMv7Thread& cpu, s32 evfId, u32 bitPatter
return SCE_OK;
}
cpu_thread_lock entry(cpu);
thread_lock entry(cpu);
if (!thread_ctrl::wait(timeout, WRAP_EXPR(cpu.state.test_and_reset(cpu_state::signal))))
if (!thread_ctrl::wait_for(timeout, WRAP_EXPR(cpu.state.test_and_reset(cpu_state::signal))))
{
// Timeout cleanup
cpu.owner = nullptr;

View File

@ -375,7 +375,7 @@ namespace rsx
last_flip_time = get_system_time() - 1000000;
m_vblank_thread = thread_ctrl::spawn("VBlank Thread", [this]()
thread_ctrl::spawn(m_vblank_thread, "VBlank Thread", [this]()
{
const u64 start_time = get_system_time();
@ -390,10 +390,13 @@ namespace rsx
if (vblank_handler)
{
Emu.GetCallbackManager().Async([func = vblank_handler](PPUThread& ppu)
{
func(ppu, 1);
intr_thread->cmd_list
({
{ ppu_cmd::set_args, 1 }, u64{1},
{ ppu_cmd::lle_call, vblank_handler },
});
intr_thread->lock_notify();
}
continue;

View File

@ -196,6 +196,8 @@ namespace rsx
void capture_frame(const std::string &name);
public:
std::shared_ptr<class ppu_thread> intr_thread;
u32 ioAddress, ioSize;
int flip_status;
int flip_mode;

View File

@ -746,10 +746,13 @@ namespace rsx
if (rsx->flip_handler)
{
Emu.GetCallbackManager().Async([func = rsx->flip_handler](PPUThread& ppu)
{
func(ppu, 1);
rsx->intr_thread->cmd_list
({
{ ppu_cmd::set_args, 1 }, u64{1},
{ ppu_cmd::lle_call, rsx->flip_handler },
});
rsx->intr_thread->lock_notify();
}
}
@ -757,10 +760,13 @@ namespace rsx
{
if (rsx->user_handler)
{
Emu.GetCallbackManager().Async([func = rsx->user_handler, arg](PPUThread& ppu)
{
func(ppu, arg);
rsx->intr_thread->cmd_list
({
{ ppu_cmd::set_args, 1 }, u64{arg},
{ ppu_cmd::lle_call, rsx->user_handler },
});
rsx->intr_thread->lock_notify();
}
}

View File

@ -42,8 +42,6 @@ std::string g_cfg_defaults;
extern atomic_t<u32> g_thread_count;
extern atomic_t<u32> g_ppu_core[2];
extern u64 get_system_time();
extern void ppu_load_exec(const ppu_exec_object&);
@ -64,7 +62,6 @@ namespace rpcs3
Emulator::Emulator()
: m_status(Stopped)
, m_cpu_thr_stop(0)
, m_callback_manager(new CallbackManager())
{
}
@ -78,9 +75,6 @@ void Emulator::Init()
idm::init();
fxm::init();
g_ppu_core[0] = 0;
g_ppu_core[1] = 0;
// Reset defaults, cache them
cfg::root.from_default();
g_cfg_defaults = cfg::root.to_string();
@ -277,7 +271,6 @@ void Emulator::Load()
ppu_load_exec(ppu_exec);
Emu.GetCallbackManager().Init();
fxm::import<GSRender>(PURE_EXPR(Emu.GetCallbacks().get_gs_render())); // TODO: must be created in appropriate sys_rsx syscall
}
else if (ppu_prx.open(elf_file) == elf_error::ok)
@ -286,7 +279,6 @@ void Emulator::Load()
m_status = Ready;
vm::ps3::init();
ppu_load_prx(ppu_prx);
GetCallbackManager().Init();
}
else if (spu_exec.open(elf_file) == elf_error::ok)
{
@ -348,10 +340,9 @@ void Emulator::Run()
m_pause_amend_time = 0;
m_status = Running;
idm::select<PPUThread, SPUThread, RawSPUThread, ARMv7Thread>([](u32, cpu_thread& cpu)
idm::select<ppu_thread, SPUThread, RawSPUThread, ARMv7Thread>([](u32, cpu_thread& cpu)
{
cpu.state -= cpu_state::stop;
cpu->lock_notify();
cpu.run();
});
SendDbgCommand(DID_STARTED_EMU);
@ -377,7 +368,7 @@ bool Emulator::Pause()
SendDbgCommand(DID_PAUSE_EMU);
idm::select<PPUThread, SPUThread, RawSPUThread, ARMv7Thread>([](u32, cpu_thread& cpu)
idm::select<ppu_thread, SPUThread, RawSPUThread, ARMv7Thread>([](u32, cpu_thread& cpu)
{
cpu.state += cpu_state::dbg_global_pause;
});
@ -411,10 +402,10 @@ void Emulator::Resume()
SendDbgCommand(DID_RESUME_EMU);
idm::select<PPUThread, SPUThread, RawSPUThread, ARMv7Thread>([](u32, cpu_thread& cpu)
idm::select<ppu_thread, SPUThread, RawSPUThread, ARMv7Thread>([](u32, cpu_thread& cpu)
{
cpu.state -= cpu_state::dbg_global_pause;
cpu->lock_notify();
cpu.lock_notify();
});
rpcs3::on_resume()();
@ -437,7 +428,7 @@ void Emulator::Stop()
{
LV2_LOCK;
idm::select<PPUThread, SPUThread, RawSPUThread, ARMv7Thread>([](u32, cpu_thread& cpu)
idm::select<ppu_thread, SPUThread, RawSPUThread, ARMv7Thread>([](u32, cpu_thread& cpu)
{
cpu.state += cpu_state::dbg_global_stop;
cpu->lock();
@ -463,8 +454,6 @@ void Emulator::Stop()
LOG_NOTICE(GENERAL, "Objects cleared...");
GetCallbackManager().Clear();
RSXIOMem.Clear();
vm::close();

View File

@ -32,8 +32,6 @@ enum Status : u32
// Emulation Stopped exception event
class EmulationStopped {};
class CallbackManager;
class Emulator final
{
atomic_t<u32> m_status;
@ -45,8 +43,6 @@ class Emulator final
u32 m_cpu_thr_stop;
std::unique_ptr<CallbackManager> m_callback_manager;
std::string m_path;
std::string m_elf_path;
std::string m_title_id;
@ -107,11 +103,6 @@ public:
return m_pause_amend_time;
}
CallbackManager& GetCallbackManager()
{
return *m_callback_manager;
}
void SetCPUThreadStop(u32 addr)
{
m_cpu_thr_stop = addr;

View File

@ -25,7 +25,7 @@ u32 InterpreterDisAsmFrame::GetPc() const
{
switch (cpu->type)
{
case cpu_type::ppu: return static_cast<PPUThread*>(cpu)->pc;
case cpu_type::ppu: return static_cast<ppu_thread*>(cpu)->cia;
case cpu_type::spu: return static_cast<SPUThread*>(cpu)->pc;
case cpu_type::arm: return static_cast<ARMv7Thread*>(cpu)->PC;
}
@ -123,7 +123,7 @@ void InterpreterDisAsmFrame::UpdateUnitList()
m_choice_units->Freeze();
m_choice_units->Clear();
idm::select<PPUThread, SPUThread, RawSPUThread, ARMv7Thread>([&](u32, cpu_thread& cpu)
idm::select<ppu_thread, SPUThread, RawSPUThread, ARMv7Thread>([&](u32, cpu_thread& cpu)
{
m_choice_units->Append(cpu.get_name(), &cpu);
});

View File

@ -220,11 +220,11 @@ void KernelExplorer::Update()
}
// PPU Threads
if (const u32 count = idm::get_count<PPUThread>())
if (const u32 count = idm::get_count<ppu_thread>())
{
const auto& node = m_tree->AppendItem(root, fmt::format("PPU Threads (%zu)", count));
idm::select<PPUThread>([&](u32 id, PPUThread& ppu)
idm::select<ppu_thread>([&](u32 id, ppu_thread& ppu)
{
m_tree->AppendItem(node, fmt::format("PPU Thread: ID = 0x%08x '%s'", id, ppu.get_name()));
});

View File

@ -414,16 +414,16 @@ void MainFrame::Stop(wxCommandEvent& WXUNUSED(event))
}
// This is ugly, but PS3 headers shall not be included there.
extern void sysutilSendSystemCommand(u64 status, u64 param);
extern void sysutil_send_system_cmd(u64 status, u64 param);
void MainFrame::SendExit(wxCommandEvent& event)
{
sysutilSendSystemCommand(0x0101 /* CELL_SYSUTIL_REQUEST_EXITGAME */, 0);
sysutil_send_system_cmd(0x0101 /* CELL_SYSUTIL_REQUEST_EXITGAME */, 0);
}
void MainFrame::SendOpenCloseSysMenu(wxCommandEvent& event)
{
sysutilSendSystemCommand(m_sys_menu_opened ? 0x0132 /* CELL_SYSUTIL_SYSTEM_MENU_CLOSE */ : 0x0131 /* CELL_SYSUTIL_SYSTEM_MENU_OPEN */, 0);
sysutil_send_system_cmd(m_sys_menu_opened ? 0x0132 /* CELL_SYSUTIL_SYSTEM_MENU_CLOSE */ : 0x0131 /* CELL_SYSUTIL_SYSTEM_MENU_OPEN */, 0);
m_sys_menu_opened = !m_sys_menu_opened;
wxCommandEvent ce;
UpdateUI(ce);

View File

@ -86,7 +86,7 @@ RegisterEditorDialog::RegisterEditorDialog(wxPanel *parent, u32 _pc, cpu_thread*
{
case cpu_type::ppu:
{
auto& ppu = *static_cast<PPUThread*>(cpu);
auto& ppu = *static_cast<ppu_thread*>(cpu);
while (value.length() < 32) value = "0" + value;
std::string::size_type first_brk = reg.find('[');
@ -99,8 +99,8 @@ RegisterEditorDialog::RegisterEditorDialog(wxPanel *parent, u32 _pc, cpu_thread*
{
unsigned long long reg_value;
reg_value = std::stoull(value.substr(16, 31), 0, 16);
if (reg.find("GPR") == 0) ppu.GPR[reg_index] = (u64)reg_value;
if (reg.find("FPR") == 0) (u64&)ppu.FPR[reg_index] = (u64)reg_value;
if (reg.find("GPR") == 0) ppu.gpr[reg_index] = (u64)reg_value;
if (reg.find("FPR") == 0) (u64&)ppu.fpr[reg_index] = (u64)reg_value;
return;
}
if (reg.find("VR") == 0)
@ -109,8 +109,8 @@ RegisterEditorDialog::RegisterEditorDialog(wxPanel *parent, u32 _pc, cpu_thread*
unsigned long long reg_value1;
reg_value0 = std::stoull(value.substr(16, 31), 0, 16);
reg_value1 = std::stoull(value.substr(0, 15), 0, 16);
ppu.VR[reg_index]._u64[0] = (u64)reg_value0;
ppu.VR[reg_index]._u64[1] = (u64)reg_value1;
ppu.vr[reg_index]._u64[0] = (u64)reg_value0;
ppu.vr[reg_index]._u64[1] = (u64)reg_value1;
return;
}
}
@ -118,15 +118,15 @@ RegisterEditorDialog::RegisterEditorDialog(wxPanel *parent, u32 _pc, cpu_thread*
{
unsigned long long reg_value;
reg_value = std::stoull(value.substr(16, 31), 0, 16);
if (reg == "LR") ppu.LR = (u64)reg_value;
if (reg == "CTR") ppu.CTR = (u64)reg_value;
if (reg == "LR") ppu.lr = (u64)reg_value;
if (reg == "CTR") ppu.ctr = (u64)reg_value;
return;
}
if (reg == "CR")
{
unsigned long long reg_value;
reg_value = std::stoull(value.substr(24, 31), 0, 16);
if (reg == "CR") ppu.SetCR((u32)reg_value);
if (reg == "CR") ppu.cr_unpack((u32)reg_value);
return;
}
}
@ -183,19 +183,19 @@ void RegisterEditorDialog::updateRegister(wxCommandEvent& event)
{
case cpu_type::ppu:
{
auto& ppu = *static_cast<PPUThread*>(cpu);
auto& ppu = *static_cast<ppu_thread*>(cpu);
std::size_t first_brk = reg.find('[');
if (first_brk != -1)
{
long reg_index = std::atol(reg.substr(first_brk + 1, reg.length() - first_brk - 2).c_str());
if (reg.find("GPR") == 0) str = fmt::format("%016llx", ppu.GPR[reg_index]);
if (reg.find("FPR") == 0) str = fmt::format("%016llx", ppu.FPR[reg_index]);
if (reg.find("VR") == 0) str = fmt::format("%016llx%016llx", ppu.VR[reg_index]._u64[1], ppu.VR[reg_index]._u64[0]);
if (reg.find("GPR") == 0) str = fmt::format("%016llx", ppu.gpr[reg_index]);
if (reg.find("FPR") == 0) str = fmt::format("%016llx", ppu.fpr[reg_index]);
if (reg.find("VR") == 0) str = fmt::format("%016llx%016llx", ppu.vr[reg_index]._u64[1], ppu.vr[reg_index]._u64[0]);
}
if (reg == "CR") str = fmt::format("%08x", ppu.GetCR());
if (reg == "LR") str = fmt::format("%016llx", ppu.LR);
if (reg == "CTR") str = fmt::format("%016llx", ppu.CTR);
if (reg == "CR") str = fmt::format("%08x", ppu.cr_pack());
if (reg == "LR") str = fmt::format("%016llx", ppu.lr);
if (reg == "CTR") str = fmt::format("%016llx", ppu.ctr);
break;
}
case cpu_type::spu:

View File

@ -240,7 +240,6 @@
<ClCompile Include="Emu\Cell\Modules\sys_prx_.cpp" />
<ClCompile Include="Emu\Cell\Modules\sys_spinlock.cpp" />
<ClCompile Include="Emu\Cell\Modules\sys_spu_.cpp" />
<ClCompile Include="Emu\Cell\PPUCallback.cpp" />
<ClCompile Include="Emu\Cell\PPUDisAsm.cpp" />
<ClCompile Include="Emu\Cell\PPUFunction.cpp" />
<ClCompile Include="Emu\Cell\PPUInterpreter.cpp" />
@ -390,6 +389,7 @@
<ClInclude Include="..\Utilities\GSL.h" />
<ClInclude Include="..\Utilities\JIT.h" />
<ClInclude Include="..\Utilities\lockless.h" />
<ClInclude Include="..\Utilities\SleepQueue.h" />
<ClInclude Include="..\Utilities\sync.h" />
<ClInclude Include="..\Utilities\Platform.h" />
<ClInclude Include="..\Utilities\Log.h" />

View File

@ -632,9 +632,6 @@
<ClCompile Include="Emu\Cell\Modules\sysPrxForUser.cpp">
<Filter>Emu\Cell\Modules</Filter>
</ClCompile>
<ClCompile Include="Emu\Cell\PPUCallback.cpp">
<Filter>Emu\Cell</Filter>
</ClCompile>
<ClCompile Include="Emu\Cell\PPUFunction.cpp">
<Filter>Emu\Cell</Filter>
</ClCompile>
@ -1696,5 +1693,8 @@
<ClInclude Include="Emu\RSX\rsx_trace.h">
<Filter>Emu\GPU\RSX</Filter>
</ClInclude>
<ClInclude Include="..\Utilities\SleepQueue.h">
<Filter>Utilities</Filter>
</ClInclude>
</ItemGroup>
</Project>