rpcsx/rpcs3/Emu/Cell/lv2/sys_interrupt.cpp

172 lines
3.3 KiB
C++
Raw Normal View History

#include "stdafx.h"
#include "sys_interrupt.h"
2015-03-06 23:58:42 +01:00
#include "Emu/IdManager.h"
2014-08-23 16:51:51 +02:00
2016-04-14 00:23:53 +02:00
#include "Emu/Cell/ErrorCodes.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/PPUOpcodes.h"
LOG_CHANNEL(sys_interrupt);
void lv2_int_serv::exec()
{
thread->cmd_list
({
{ ppu_cmd::reset_stack, 0 },
{ ppu_cmd::set_args, 2 }, arg1, arg2,
{ ppu_cmd::opd_call, 0 }, thread->entry_func,
2017-02-06 19:36:46 +01:00
{ ppu_cmd::sleep, 0 }
});
2015-07-12 23:02:02 +02:00
thread_ctrl::notify(*thread);
}
bool interrupt_thread_exit(ppu_thread& ppu)
{
ppu.state += cpu_flag::exit;
return false;
}
2017-02-04 17:30:21 +01:00
void lv2_int_serv::join()
{
thread->cmd_list
({
{ ppu_cmd::ptr_call, 0 },
std::bit_cast<u64>(&interrupt_thread_exit)
});
thread_ctrl::notify(*thread);
(*thread)();
idm::remove_verify<named_thread<ppu_thread>>(thread->id, static_cast<std::weak_ptr<named_thread<ppu_thread>>>(thread));
2015-07-12 23:02:02 +02:00
}
error_code sys_interrupt_tag_destroy(ppu_thread& ppu, u32 intrtag)
2015-07-12 23:02:02 +02:00
{
ppu.state += cpu_flag::wait;
sys_interrupt.warning("sys_interrupt_tag_destroy(intrtag=0x%x)", intrtag);
2015-07-12 23:02:02 +02:00
2017-02-04 17:30:21 +01:00
const auto tag = idm::withdraw<lv2_obj, lv2_int_tag>(intrtag, [](lv2_int_tag& tag) -> CellError
{
if (!tag.handler.expired())
{
return CELL_EBUSY;
}
2015-03-02 22:09:20 +01:00
2017-02-04 17:30:21 +01:00
return {};
});
2015-07-12 23:02:02 +02:00
if (!tag)
{
return CELL_ESRCH;
}
2017-02-04 17:30:21 +01:00
if (tag.ret)
{
2017-02-04 17:30:21 +01:00
return tag.ret;
}
return CELL_OK;
}
error_code _sys_interrupt_thread_establish(ppu_thread& ppu, vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2)
{
ppu.state += cpu_flag::wait;
sys_interrupt.warning("_sys_interrupt_thread_establish(ih=*0x%x, intrtag=0x%x, intrthread=0x%x, arg1=0x%llx, arg2=0x%llx)", ih, intrtag, intrthread, arg1, arg2);
2015-07-12 23:02:02 +02:00
2017-02-04 17:30:21 +01:00
CellError error = CELL_EAGAIN;
2017-02-04 17:30:21 +01:00
const u32 id = idm::import<lv2_obj, lv2_int_serv>([&]()
2015-07-12 23:02:02 +02:00
{
2017-02-04 17:30:21 +01:00
std::shared_ptr<lv2_int_serv> result;
// Get interrupt tag
const auto tag = idm::check_unlocked<lv2_obj, lv2_int_tag>(intrtag);
if (!tag)
{
error = CELL_ESRCH;
return result;
}
// Get interrupt thread
const auto it = idm::get_unlocked<named_thread<ppu_thread>>(intrthread);
2017-02-04 17:30:21 +01:00
if (!it)
{
error = CELL_ESRCH;
return result;
}
// If interrupt thread is running, it's already established on another interrupt tag
if (!(it->state & cpu_flag::stop))
2017-02-04 17:30:21 +01:00
{
error = CELL_EAGAIN;
return result;
}
// It's unclear if multiple handlers can be established on single interrupt tag
if (!tag->handler.expired())
{
error = CELL_ESTAT;
return result;
}
result = std::make_shared<lv2_int_serv>(it, arg1, arg2);
tag->handler = result;
it->state -= cpu_flag::stop;
thread_ctrl::notify(*it);
2017-02-04 17:30:21 +01:00
return result;
});
2017-02-04 17:30:21 +01:00
if (id)
{
2017-02-04 17:30:21 +01:00
*ih = id;
return CELL_OK;
}
2017-02-04 17:30:21 +01:00
return error;
}
2017-02-04 17:30:21 +01:00
error_code _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr<u64> r13)
{
ppu.state += cpu_flag::wait;
sys_interrupt.warning("_sys_interrupt_thread_disestablish(ih=0x%x, r13=*0x%x)", ih, r13);
2015-07-12 23:02:02 +02:00
2017-02-04 17:30:21 +01:00
const auto handler = idm::withdraw<lv2_obj, lv2_int_serv>(ih);
2015-04-12 03:36:25 +02:00
if (!handler)
{
if (const auto thread = idm::withdraw<named_thread<ppu_thread>>(ih))
2017-10-01 03:40:51 +02:00
{
*r13 = thread->gpr[13];
return CELL_OK;
}
return CELL_ESRCH;
}
lv2_obj::sleep(ppu);
2015-07-12 23:02:02 +02:00
// Wait for sys_interrupt_thread_eoi() and destroy interrupt thread
2017-02-04 17:30:21 +01:00
handler->join();
2015-07-12 23:02:02 +02:00
// Save TLS base
*r13 = handler->thread->gpr[13];
2015-03-02 22:09:20 +01:00
return CELL_OK;
}
2017-02-04 17:30:21 +01:00
void sys_interrupt_thread_eoi(ppu_thread& ppu)
{
ppu.state += cpu_flag::wait;
2017-03-11 00:14:48 +01:00
2017-02-04 17:30:21 +01:00
sys_interrupt.trace("sys_interrupt_thread_eoi()");
2015-04-20 01:49:13 +02:00
ppu.state += cpu_flag::ret;
2014-07-12 09:46:14 +02:00
}