2014-06-23 03:03:16 +02:00
|
|
|
#include "stdafx.h"
|
2016-04-14 00:23:53 +02:00
|
|
|
#include "Utilities/Config.h"
|
2014-06-23 03:03:16 +02:00
|
|
|
#include "Emu/Memory/Memory.h"
|
|
|
|
|
#include "Emu/System.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"
|
2014-06-25 00:38:34 +02:00
|
|
|
#include "sys_interrupt.h"
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2016-04-14 00:23:53 +02:00
|
|
|
LOG_CHANNEL(sys_interrupt);
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2016-04-14 00:23:53 +02:00
|
|
|
void lv2_int_serv_t::join(PPUThread& ppu, lv2_lock_t lv2_lock)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2015-07-12 23:02:02 +02:00
|
|
|
// Use is_joining to stop interrupt thread and signal
|
|
|
|
|
thread->is_joining = true;
|
2016-04-25 12:49:12 +02:00
|
|
|
thread->notify();
|
2015-07-12 23:02:02 +02:00
|
|
|
|
|
|
|
|
// Start joining
|
2016-04-14 00:23:53 +02:00
|
|
|
while (!(thread->state & cpu_state::exit))
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2015-07-12 23:02:02 +02:00
|
|
|
CHECK_EMU_STATUS;
|
|
|
|
|
|
2016-04-25 12:49:12 +02:00
|
|
|
get_current_thread_cv().wait_for(lv2_lock, std::chrono::milliseconds(1));
|
2014-06-23 03:03:16 +02:00
|
|
|
}
|
|
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
// Cleanup
|
2015-08-05 17:30:32 +02:00
|
|
|
idm::remove<lv2_int_serv_t>(id);
|
2016-04-14 00:23:53 +02:00
|
|
|
idm::remove<PPUThread>(thread->id);
|
2015-07-12 23:02:02 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s32 sys_interrupt_tag_destroy(u32 intrtag)
|
|
|
|
|
{
|
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
|
|
|
|
|
|
|
|
LV2_LOCK;
|
2015-03-02 22:09:20 +01:00
|
|
|
|
2015-08-05 17:30:32 +02:00
|
|
|
const auto tag = idm::get<lv2_int_tag_t>(intrtag);
|
2015-07-12 23:02:02 +02:00
|
|
|
|
|
|
|
|
if (!tag)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
|
|
|
|
return CELL_ESRCH;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
if (tag->handler)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2015-07-12 23:02:02 +02:00
|
|
|
return CELL_EBUSY;
|
2014-06-23 03:03:16 +02:00
|
|
|
}
|
|
|
|
|
|
2015-08-05 17:30:32 +02:00
|
|
|
idm::remove<lv2_int_tag_t>(intrtag);
|
2015-07-12 23:02:02 +02:00
|
|
|
|
2014-06-23 03:03:16 +02:00
|
|
|
return CELL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
s32 _sys_interrupt_thread_establish(vm::ptr<u32> ih, u32 intrtag, u32 intrthread, u64 arg1, u64 arg2)
|
2014-06-23 03:03:16 +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
|
|
|
|
|
|
|
|
LV2_LOCK;
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
// Get interrupt tag
|
2015-08-05 17:30:32 +02:00
|
|
|
const auto tag = idm::get<lv2_int_tag_t>(intrtag);
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
if (!tag)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
|
|
|
|
return CELL_ESRCH;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
// Get interrupt thread
|
2015-08-05 17:30:32 +02:00
|
|
|
const auto it = idm::get<PPUThread>(intrthread);
|
2015-03-02 22:09:20 +01:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
if (!it)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
|
|
|
|
return CELL_ESRCH;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
// If interrupt thread is running, it's already established on another interrupt tag
|
2016-04-14 00:23:53 +02:00
|
|
|
if (!(it->state & cpu_state::stop))
|
2015-07-12 23:02:02 +02:00
|
|
|
{
|
|
|
|
|
return CELL_EAGAIN;
|
|
|
|
|
}
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
// It's unclear if multiple handlers can be established on single interrupt tag
|
|
|
|
|
if (tag->handler)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2016-01-12 22:57:16 +01:00
|
|
|
sys_interrupt.error("_sys_interrupt_thread_establish(): handler service already exists (intrtag=0x%x) -> CELL_ESTAT", intrtag);
|
2015-07-12 23:02:02 +02:00
|
|
|
return CELL_ESTAT;
|
2014-06-23 03:03:16 +02:00
|
|
|
}
|
|
|
|
|
|
2015-08-05 17:30:32 +02:00
|
|
|
const auto handler = idm::make_ptr<lv2_int_serv_t>(it);
|
2015-07-12 23:02:02 +02:00
|
|
|
|
|
|
|
|
tag->handler = handler;
|
|
|
|
|
|
|
|
|
|
it->custom_task = [handler, arg1, arg2](PPUThread& ppu)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2015-07-12 23:02:02 +02:00
|
|
|
const u32 pc = ppu.PC;
|
|
|
|
|
const u32 rtoc = ppu.GPR[2];
|
|
|
|
|
|
2015-03-04 05:42:04 +01:00
|
|
|
LV2_LOCK;
|
2015-03-02 22:09:20 +01:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
while (!ppu.is_joining)
|
2015-03-02 22:09:20 +01:00
|
|
|
{
|
2015-07-12 23:02:02 +02:00
|
|
|
CHECK_EMU_STATUS;
|
2015-03-02 22:09:20 +01:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
// call interrupt handler until int status is clear
|
|
|
|
|
if (handler->signal)
|
2015-03-02 22:09:20 +01:00
|
|
|
{
|
2015-07-12 23:02:02 +02:00
|
|
|
if (lv2_lock) lv2_lock.unlock();
|
2015-03-02 22:09:20 +01:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
ppu.GPR[3] = arg1;
|
|
|
|
|
ppu.GPR[4] = arg2;
|
2015-07-19 13:36:32 +02:00
|
|
|
ppu.fast_call(pc, rtoc);
|
2015-03-02 22:09:20 +01:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
handler->signal--;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2015-03-02 22:09:20 +01:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
if (!lv2_lock)
|
2015-03-02 22:09:20 +01:00
|
|
|
{
|
2015-07-12 23:02:02 +02:00
|
|
|
lv2_lock.lock();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2015-07-04 01:22:24 +02:00
|
|
|
|
2016-04-25 12:49:12 +02:00
|
|
|
get_current_thread_cv().wait(lv2_lock);
|
2015-07-12 23:02:02 +02:00
|
|
|
}
|
2015-03-02 22:09:20 +01:00
|
|
|
|
2016-04-14 00:23:53 +02:00
|
|
|
ppu.state += cpu_state::exit;
|
2015-07-12 23:02:02 +02:00
|
|
|
};
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2016-04-14 00:23:53 +02:00
|
|
|
it->state -= cpu_state::stop;
|
2016-04-25 12:49:12 +02:00
|
|
|
it->lock_notify();
|
2015-03-02 22:09:20 +01:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
*ih = handler->id;
|
|
|
|
|
|
2014-06-23 03:03:16 +02:00
|
|
|
return CELL_OK;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
s32 _sys_interrupt_thread_disestablish(PPUThread& ppu, u32 ih, vm::ptr<u64> r13)
|
2014-06-23 03:03:16 +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
|
|
|
|
|
|
|
|
LV2_LOCK;
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2015-08-05 17:30:32 +02:00
|
|
|
const auto handler = idm::get<lv2_int_serv_t>(ih);
|
2015-04-12 03:36:25 +02:00
|
|
|
|
|
|
|
|
if (!handler)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
|
|
|
|
return CELL_ESRCH;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
// Wait for sys_interrupt_thread_eoi() and destroy interrupt thread
|
|
|
|
|
handler->join(ppu, lv2_lock);
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
// Save TLS base
|
2015-07-03 18:07:36 +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;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
void sys_interrupt_thread_eoi(PPUThread& ppu)
|
2014-06-23 03:03:16 +02:00
|
|
|
{
|
2016-01-12 22:57:16 +01:00
|
|
|
sys_interrupt.trace("sys_interrupt_thread_eoi()");
|
2014-06-23 03:03:16 +02:00
|
|
|
|
2015-07-03 18:07:36 +02:00
|
|
|
// TODO: maybe it should actually unwind the stack of PPU thread?
|
|
|
|
|
|
2015-07-12 23:02:02 +02:00
|
|
|
ppu.GPR[1] = align(ppu.stack_addr + ppu.stack_size, 0x200) - 0x200; // supercrutch to bypass stack check
|
2015-04-20 01:49:13 +02:00
|
|
|
|
2016-04-14 00:23:53 +02:00
|
|
|
ppu.state += cpu_state::ret;
|
2014-07-12 09:46:14 +02:00
|
|
|
}
|