diff --git a/rpcs3/Emu/Cell/SPUThread.cpp b/rpcs3/Emu/Cell/SPUThread.cpp index 88af92d8ca..0939e3e0c8 100644 --- a/rpcs3/Emu/Cell/SPUThread.cpp +++ b/rpcs3/Emu/Cell/SPUThread.cpp @@ -1500,16 +1500,77 @@ bool spu_thread::do_dma_check(const spu_mfc_cmd& args) bool spu_thread::do_list_transfer(spu_mfc_cmd& args) { - struct list_element + // Amount of elements to fetch in one go + constexpr u32 fetch_size = 6; + + struct alignas(8) list_element { be_t sb; // Stall-and-Notify bit (0x8000) be_t ts; // List Transfer Size be_t ea; // External Address Low - } item{}; + }; - while (args.size) + union { - if (UNLIKELY(item.sb & 0x8000)) + list_element items[fetch_size]; + alignas(v128) char bufitems[sizeof(items)]; + }; + + spu_mfc_cmd transfer; + transfer.eah = 0; + transfer.tag = args.tag; + transfer.cmd = MFC(args.cmd & ~MFC_LIST_MASK); + + args.lsa &= 0x3fff0; + + u32 index = fetch_size; + + // Assume called with size greater than 0 + while (true) + { + // Check if fetching is needed + if (index == fetch_size) + { + // Reset to elements array head + index = 0; + + const auto src = _ptr(args.eal & 0x3fff8); + const v128 data0 = v128::fromV(_mm_loadu_si128(src + 0)); + const v128 data1 = v128::fromV(_mm_loadu_si128(src + 1)); + const v128 data2 = v128::fromV(_mm_loadu_si128(src + 2)); + + ((v128*)+bufitems)[0] = data0; + ((v128*)+bufitems)[1] = data1; + ((v128*)+bufitems)[2] = data2; + } + + const u32 size = items[index].ts & 0x7fff; + const u32 addr = items[index].ea; + + LOG_TRACE(SPU, "LIST: addr=0x%x, size=0x%x, lsa=0x%05x, sb=0x%x", addr, size, args.lsa | (addr & 0xf), items[index].sb); + + if (size) + { + transfer.eal = addr; + transfer.lsa = args.lsa | (addr & 0xf); + transfer.size = size; + + do_dma_transfer(transfer); + const u32 add_size = std::max(size, 16); + args.lsa += add_size; + } + + args.size -= 8; + + if (!args.size) + { + // No more elements + break; + } + + args.eal += 8; + + if (UNLIKELY(items[index].sb & 0x8000)) { ch_stall_mask |= utils::rol32(1, args.tag); @@ -1524,31 +1585,7 @@ bool spu_thread::do_list_transfer(spu_mfc_cmd& args) return false; } - args.lsa &= 0x3fff0; - item = _ref(args.eal & 0x3fff8); - - const u32 size = item.ts & 0x7fff; - const u32 addr = item.ea; - - LOG_TRACE(SPU, "LIST: addr=0x%x, size=0x%x, lsa=0x%05x, sb=0x%x", addr, size, args.lsa | (addr & 0xf), item.sb); - - if (size) - { - spu_mfc_cmd transfer; - transfer.eal = addr; - transfer.eah = 0; - transfer.lsa = args.lsa | (addr & 0xf); - transfer.tag = args.tag; - transfer.cmd = MFC(args.cmd & ~MFC_LIST_MASK); - transfer.size = size; - - do_dma_transfer(transfer); - const u32 add_size = std::max(size, 16); - args.lsa += add_size; - } - - args.eal += 8; - args.size -= 8; + index++; } return true; @@ -2040,7 +2077,7 @@ bool spu_thread::process_mfc_cmd() if (LIKELY(do_dma_check(cmd))) { - if (LIKELY(do_list_transfer(cmd))) + if (LIKELY(!cmd.size || do_list_transfer(cmd))) { return true; }