1
// Copyright (c) 2012- PPSSPP Project.
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
18
#include "Common/ChunkFile.h"
19
#include "Core/CoreTiming.h"
20
#include "Core/MemMapHelpers.h"
21
#include "Core/Reporting.h"
22
#include "Core/HLE/HLE.h"
23
#include "Core/HLE/sceDmac.h"
24
#include "Core/HLE/sceKernel.h"
25
#include "Core/HLE/FunctionWrappers.h"
26
#include "Core/Debugger/Breakpoints.h"
27
#include "GPU/GPUInterface.h"
28
#include "GPU/GPUState.h"
30
u64 dmacMemcpyDeadline;
33
dmacMemcpyDeadline = 0;
36
void __DmacDoState(PointerWrap &p) {
37
auto s = p.Section("sceDmac", 0, 1);
39
dmacMemcpyDeadline = 0;
43
p.Do(dmacMemcpyDeadline);
46
static int __DmacMemcpy(u32 dst, u32 src, u32 size) {
48
if (Memory::IsVRAMAddress(src) || Memory::IsVRAMAddress(dst)) {
49
skip = gpu->PerformMemoryCopy(dst, src, size);
52
Memory::Memcpy(dst, Memory::GetPointer(src), size);
53
currentMIPS->InvalidateICache(dst, size);
56
// This number seems strangely reproducible.
58
// Approx. 225 MiB/s or 235929600 B/s, so let's go with 236 B/us.
59
int delayUs = size / 236;
60
dmacMemcpyDeadline = CoreTiming::GetTicks() + usToCycles(delayUs);
61
return hleDelayResult(0, "dmac copy", delayUs);
66
static u32 sceDmacMemcpy(u32 dst, u32 src, u32 size) {
68
// Some games seem to do this frequently.
69
DEBUG_LOG(HLE, "sceDmacMemcpy(dest=%08x, src=%08x, size=%i): invalid size", dst, src, size);
70
return SCE_KERNEL_ERROR_INVALID_SIZE;
72
if (!Memory::IsValidAddress(dst) || !Memory::IsValidAddress(src)) {
73
ERROR_LOG(HLE, "sceDmacMemcpy(dest=%08x, src=%08x, size=%i): invalid address", dst, src, size);
74
return SCE_KERNEL_ERROR_INVALID_POINTER;
76
if (dst + size >= 0x80000000 || src + size >= 0x80000000 || size >= 0x80000000) {
77
ERROR_LOG(HLE, "sceDmacMemcpy(dest=%08x, src=%08x, size=%i): illegal size", dst, src, size);
78
return SCE_KERNEL_ERROR_PRIV_REQUIRED;
81
if (dmacMemcpyDeadline > CoreTiming::GetTicks()) {
82
WARN_LOG_REPORT(HLE, "sceDmacMemcpy(dest=%08x, src=%08x, size=%i): overlapping read", dst, src, size);
83
// TODO: Should block, seems like copy doesn't start until previous finishes.
84
// Might matter for overlapping copies.
86
DEBUG_LOG(HLE, "sceDmacMemcpy(dest=%08x, src=%08x, size=%i)", dst, src, size);
89
return __DmacMemcpy(dst, src, size);
92
static u32 sceDmacTryMemcpy(u32 dst, u32 src, u32 size) {
94
ERROR_LOG(HLE, "sceDmacTryMemcpy(dest=%08x, src=%08x, size=%i): invalid size", dst, src, size);
95
return SCE_KERNEL_ERROR_INVALID_SIZE;
97
if (!Memory::IsValidAddress(dst) || !Memory::IsValidAddress(src)) {
98
ERROR_LOG(HLE, "sceDmacTryMemcpy(dest=%08x, src=%08x, size=%i): invalid address", dst, src, size);
99
return SCE_KERNEL_ERROR_INVALID_POINTER;
101
if (dst + size >= 0x80000000 || src + size >= 0x80000000 || size >= 0x80000000) {
102
ERROR_LOG(HLE, "sceDmacTryMemcpy(dest=%08x, src=%08x, size=%i): illegal size", dst, src, size);
103
return SCE_KERNEL_ERROR_PRIV_REQUIRED;
106
if (dmacMemcpyDeadline > CoreTiming::GetTicks()) {
107
DEBUG_LOG(HLE, "sceDmacTryMemcpy(dest=%08x, src=%08x, size=%i): busy", dst, src, size);
108
return SCE_KERNEL_ERROR_BUSY;
110
DEBUG_LOG(HLE, "sceDmacTryMemcpy(dest=%08x, src=%08x, size=%i)", dst, src, size);
113
return __DmacMemcpy(dst, src, size);
116
const HLEFunction sceDmac[] = {
117
{0X617F3FE6, &WrapU_UUU<sceDmacMemcpy>, "sceDmacMemcpy", 'x', "xxx"},
118
{0XD97F94D8, &WrapU_UUU<sceDmacTryMemcpy>, "sceDmacTryMemcpy", 'x', "xxx"},
121
void Register_sceDmac() {
122
RegisterModule("sceDmac", ARRAY_SIZE(sceDmac), sceDmac);