2020-12-05 13:08:24 +01:00
|
|
|
#include "stdafx.h"
|
2018-09-29 00:12:00 +02:00
|
|
|
#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"
|
2014-06-23 03:03:16 +02:00
|
|
|
#include "Emu/Cell/PPUThread.h"
|
2022-05-24 10:20:30 +02:00
|
|
|
#include "Emu/Cell/SPUThread.h"
|
2016-07-27 23:43:22 +02:00
|
|
|
#include "Emu/Cell/PPUOpcodes.h"
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2018-08-25 14:39:00 +02:00
|
|
|
LOG_CHANNEL(sys_interrupt);
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2021-05-07 20:55:06 +02:00
|
|
|
lv2_int_tag::lv2_int_tag() noexcept
|
|
|
|
|
: id(idm::last_id())
|
|
|
|
|
{
|
2021-05-14 16:55:07 +02:00
|
|
|
exists.release(1);
|
2021-05-07 20:55:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lv2_int_serv::lv2_int_serv(const std::shared_ptr<named_thread<ppu_thread>>& thread, u64 arg1, u64 arg2) noexcept
|
|
|
|
|
: id(idm::last_id())
|
|
|
|
|
, thread(thread)
|
|
|
|
|
, arg1(arg1)
|
|
|
|
|
, arg2(arg2)
|
|
|
|
|
{
|
2021-05-14 16:55:07 +02:00
|
|
|
exists.release(1);
|
2021-05-07 20:55:06 +02:00
|
|
|
}
|
|
|
|
|
|
2022-05-24 10:20:30 +02:00
|
|
|
void ppu_interrupt_thread_entry(ppu_thread&, ppu_opcode_t, be_t<u32>*, struct ppu_intrp_func*);
|
|
|
|
|
|
2021-04-09 21:12:47 +02:00
|
|
|
void lv2_int_serv::exec() const
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2016-07-27 23:43:22 +02:00
|
|
|
thread->cmd_list
|
|
|
|
|
({
|
2018-03-06 03:36:33 +01:00
|
|
|
{ ppu_cmd::reset_stack, 0 },
|
2016-07-27 23:43:22 +02:00
|
|
|
{ ppu_cmd::set_args, 2 }, arg1, arg2,
|
2020-04-29 07:03:07 +02:00
|
|
|
{ ppu_cmd::opd_call, 0 }, thread->entry_func,
|
2022-05-24 10:20:30 +02:00
|
|
|
{ ppu_cmd::sleep, 0 },
|
|
|
|
|
{ ppu_cmd::ptr_call, 0 },
|
|
|
|
|
std::bit_cast<u64>(&ppu_interrupt_thread_entry)
|
2016-07-27 23:43:22 +02:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-24 10:20:30 +02:00
|
|
|
void ppu_thread_exit(ppu_thread&, ppu_opcode_t, be_t<u32>*, struct ppu_intrp_func*);
|
2019-08-12 06:31:05 +02:00
|
|
|
|
2021-04-09 21:12:47 +02:00
|
|
|
void lv2_int_serv::join() const
|
2016-07-27 23:43:22 +02:00
|
|
|
{
|
|
|
|
|
thread->cmd_list
|
|
|
|
|
({
|
2019-08-12 06:31:05 +02:00
|
|
|
{ ppu_cmd::ptr_call, 0 },
|
2021-01-19 19:15:57 +01:00
|
|
|
std::bit_cast<u64>(&ppu_thread_exit)
|
2016-07-27 23:43:22 +02:00
|
|
|
});
|
|
|
|
|
|
2021-02-13 15:50:07 +01:00
|
|
|
thread->cmd_notify++;
|
|
|
|
|
thread->cmd_notify.notify_one();
|
2018-10-11 00:17:19 +02:00
|
|
|
(*thread)();
|
2019-08-12 06:31:05 +02:00
|
|
|
|
2020-03-14 18:24:45 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2019-07-14 17:08:18 +02:00
|
|
|
error_code sys_interrupt_tag_destroy(ppu_thread& ppu, u32 intrtag)
|
2015-07-12 23:02:02 +02:00
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
ppu.state += cpu_flag::wait;
|
2019-07-14 17:08:18 +02:00
|
|
|
|
2016-01-12 22:57:16 +01:00
|
|
|
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
|
|
|
|
|
{
|
2021-05-14 19:10:47 +02:00
|
|
|
if (lv2_obj::check(tag.handler))
|
2017-02-04 17:30:21 +01:00
|
|
|
{
|
|
|
|
|
return CELL_EBUSY;
|
|
|
|
|
}
|
2015-03-02 22:09:20 +01:00
|
|
|
|
2021-05-14 16:55:07 +02:00
|
|
|
tag.exists.release(0);
|
2017-02-04 17:30:21 +01:00
|
|
|
return {};
|
|
|
|
|
});
|
2015-07-12 23:02:02 +02:00
|
|
|
|
|
|
|
|
if (!tag)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
|
|
|
|
return CELL_ESRCH;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-04 17:30:21 +01:00
|
|
|
if (tag.ret)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2017-02-04 17:30:21 +01:00
|
|
|
return tag.ret;
|
2014-06-23 03:03:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return CELL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-14 17:08:18 +02:00
|
|
|
error_code _sys_interrupt_thread_establish(ppu_thread& ppu, vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
ppu.state += cpu_flag::wait;
|
2019-07-14 17:08:18 +02:00
|
|
|
|
2016-01-12 22:57:16 +01:00
|
|
|
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;
|
2014-06-23 03:03:16 +02:00
|
|
|
|
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
|
2018-10-11 00:17:19 +02:00
|
|
|
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
|
2021-04-17 14:39:13 +02:00
|
|
|
if (cpu_flag::stop - it->state)
|
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
|
2021-05-14 19:10:47 +02:00
|
|
|
if (lv2_obj::check(tag->handler))
|
2017-02-04 17:30:21 +01:00
|
|
|
{
|
|
|
|
|
error = CELL_ESTAT;
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = std::make_shared<lv2_int_serv>(it, arg1, arg2);
|
|
|
|
|
tag->handler = result;
|
2022-05-25 13:32:52 +02:00
|
|
|
|
|
|
|
|
it->cmd_list
|
|
|
|
|
({
|
|
|
|
|
{ ppu_cmd::ptr_call, 0 },
|
|
|
|
|
std::bit_cast<u64>(&ppu_interrupt_thread_entry)
|
|
|
|
|
});
|
|
|
|
|
|
2018-10-11 00:17:19 +02:00
|
|
|
it->state -= cpu_flag::stop;
|
2021-02-13 15:50:07 +01:00
|
|
|
it->state.notify_one(cpu_flag::stop);
|
2022-05-25 13:32:52 +02:00
|
|
|
|
2017-02-04 17:30:21 +01:00
|
|
|
return result;
|
|
|
|
|
});
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2017-02-04 17:30:21 +01:00
|
|
|
if (id)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2017-02-04 17:30:21 +01:00
|
|
|
*ih = id;
|
|
|
|
|
return CELL_OK;
|
2014-06-23 03:03:16 +02:00
|
|
|
}
|
|
|
|
|
|
2017-02-04 17:30:21 +01:00
|
|
|
return error;
|
2014-06-23 03:03:16 +02:00
|
|
|
}
|
|
|
|
|
|
2017-02-04 17:30:21 +01:00
|
|
|
error_code _sys_interrupt_thread_disestablish(ppu_thread& ppu, u32 ih, vm::ptr<u64> r13)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
ppu.state += cpu_flag::wait;
|
2019-07-14 17:08:18 +02:00
|
|
|
|
2016-01-12 22:57:16 +01:00
|
|
|
sys_interrupt.warning("_sys_interrupt_thread_disestablish(ih=0x%x, r13=*0x%x)", ih, r13);
|
2015-07-12 23:02:02 +02:00
|
|
|
|
2021-05-14 16:55:07 +02:00
|
|
|
const auto handler = idm::withdraw<lv2_obj, lv2_int_serv>(ih, [](lv2_obj& obj)
|
|
|
|
|
{
|
|
|
|
|
obj.exists.release(0);
|
|
|
|
|
});
|
2015-04-12 03:36:25 +02:00
|
|
|
|
|
|
|
|
if (!handler)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2018-10-11 00:17:19 +02:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-23 03:03:16 +02:00
|
|
|
return CELL_ESRCH;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-15 11:49:37 +01:00
|
|
|
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();
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
// Save TLS base
|
2016-07-27 23:43:22 +02:00
|
|
|
*r13 = handler->thread->gpr[13];
|
2015-03-02 22:09:20 +01:00
|
|
|
|
2014-06-23 03:03:16 +02:00
|
|
|
return CELL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-04 17:30:21 +01:00
|
|
|
void sys_interrupt_thread_eoi(ppu_thread& ppu)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2020-06-05 11:36:28 +02:00
|
|
|
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
|
|
|
|
2016-08-09 16:14:41 +02:00
|
|
|
ppu.state += cpu_flag::ret;
|
2022-05-24 10:20:30 +02:00
|
|
|
|
|
|
|
|
lv2_obj::sleep(ppu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ppu_interrupt_thread_entry(ppu_thread& ppu, ppu_opcode_t, be_t<u32>*, struct ppu_intrp_func*)
|
|
|
|
|
{
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
std::shared_ptr<lv2_int_serv> serv = nullptr;
|
|
|
|
|
|
|
|
|
|
// Loop endlessly trying to invoke an interrupt if required
|
|
|
|
|
idm::select<named_thread<spu_thread>>([&](u32, spu_thread& spu)
|
|
|
|
|
{
|
|
|
|
|
if (spu.get_type() != spu_type::threaded)
|
|
|
|
|
{
|
|
|
|
|
auto& ctrl = spu.int_ctrl[2];
|
|
|
|
|
|
|
|
|
|
if (lv2_obj::check(ctrl.tag))
|
|
|
|
|
{
|
|
|
|
|
auto& handler = ctrl.tag->handler;
|
|
|
|
|
|
|
|
|
|
if (lv2_obj::check(handler))
|
|
|
|
|
{
|
|
|
|
|
if (handler->thread.get() == &ppu)
|
|
|
|
|
{
|
|
|
|
|
if (spu.ch_out_intr_mbox.get_count() && ctrl.mask & SPU_INT2_STAT_MAILBOX_INT)
|
|
|
|
|
{
|
|
|
|
|
ctrl.stat |= SPU_INT2_STAT_MAILBOX_INT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ctrl.mask & ctrl.stat)
|
|
|
|
|
{
|
|
|
|
|
ensure(!serv);
|
|
|
|
|
serv = handler;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (serv)
|
|
|
|
|
{
|
|
|
|
|
// Queue interrupt, after the interrupt has finished the PPU returns to this loop
|
|
|
|
|
serv->exec();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const auto state = +ppu.state;
|
|
|
|
|
|
2022-05-25 13:32:52 +02:00
|
|
|
if (::is_stopped(state) || ppu.cmd_notify.exchange(0))
|
2022-05-24 10:20:30 +02:00
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-25 13:32:52 +02:00
|
|
|
thread_ctrl::wait_on(ppu.cmd_notify, 0);
|
2022-05-24 10:20:30 +02:00
|
|
|
}
|
2014-07-12 09:46:14 +02:00
|
|
|
}
|