unit ps4_map_mm;

{$mode objfpc}{$H+}

interface

uses
  Windows,
  RWLock,
  sys_types,
  sys_crt,
  mmap,
  mm_adr_direct,
  mm_adr_virtual,
  mm_adr_name,
  Classes,
  SysUtils;

{
Flexible memory: elf sections, stack, calloc, cpu only

Direct memory: phisical mapped, cpu/gpu mem

Pooled memory: section of direct memory, 64 KiB blocks,

The application program can use a total of 5184 MiB (5824 MiB in NEO mode) physical memory.

If there is no specification, 448 MiB will be assigned as flexible memory.

Physical Address Space and Direct Memory Areas is guaranteed to be aligned to a 2 MiB boundary.


Unmapped area       : 0x0000 0000 0000 - 0x0000 0040 0000 Size: 0x0000 0040 0000 (4MB)

System managed area : 0x0000 0040 0000 - 0x0007 FFFF C000 Size: 0x0007 FFBF C000 (31GB)

System reserved area: 0x0007 FFFF C000 - 0x0010 0000 0000 Size: 0x0008 0000 4000 (32GB)

User area           : 0x0010 0000 0000 - 0x00FC 0000 0000 Size: 0x00EC 0000 0000 (944GB)

System reserved area: 0x00FC 0000 0000 - 0x00FF FFFF FFFF Size: 0x0003 FFFF FFFF (15GB)
}

//FFFF FF|FF FFFF FFFF
//0000 00|FF FFFF FFFF
//mirror |addr

var
 MMLock:System.TRTLCriticalSection;

 DirectManager :TDirectManager;
 VirtualManager:TVirtualManager;
 NamedManager  :TNamedManager;

Const
 SCE_KERNEL_MAIN_DMEM_SIZE=$180000000; //6GB

type
 pSceKernelDirectMemoryQueryInfo=^SceKernelDirectMemoryQueryInfo;
 SceKernelDirectMemoryQueryInfo=packed record
  start:QWORD;
  __end:QWORD;
  mType:Integer;
  align:Integer;
 end;

const
 SCE_KERNEL_VIRTUAL_RANGE_NAME_SIZE=32;
 SCE_KERNEL_DMQ_FIND_NEXT=1;
 SCE_KERNEL_VQ_FIND_NEXT =1;

type
 pSceKernelVirtualQueryInfo=^SceKernelVirtualQueryInfo;
 SceKernelVirtualQueryInfo=packed record
  pstart:Pointer;
  pend  :Pointer;
  offset:QWORD;
  protection:Integer;
  memoryType:Integer;
  bits:bitpacked record
   isFlexibleMemory:0..1;
   isDirectMemory  :0..1;
   isStack         :0..1;
   isPooledMemory  :0..1;
   isCommitted     :0..1;
  end;
  name:array[0..SCE_KERNEL_VIRTUAL_RANGE_NAME_SIZE-1] of AnsiChar;
  align:array[0..6] of Byte;
 end;

const
 //SceKernelMapEntryOperation
 SCE_KERNEL_MAP_OP_MAP_DIRECT  =0;
 SCE_KERNEL_MAP_OP_UNMAP       =1;
 SCE_KERNEL_MAP_OP_PROTECT     =2;
 SCE_KERNEL_MAP_OP_MAP_FLEXIBLE=3;
 SCE_KERNEL_MAP_OP_TYPE_PROTECT=4;

type
 pSceKernelBatchMapEntry=^SceKernelBatchMapEntry;
 SceKernelBatchMapEntry=packed record
  start:Pointer;
  offset:QWORD;
  length:QWORD;
  protection:Byte;
  mtype:Byte;
  pad1:Word;
  operation:Integer;
 end;

function ps4_sceKernelGetDirectMemorySize:Int64; SysV_ABI_CDecl;
function ps4_getpagesize:Integer; SysV_ABI_CDecl;
function ps4_sceKernelAvailableFlexibleMemorySize(sizeOut:PQWORD):Integer; SysV_ABI_CDecl;
function ps4_sceKernelConfiguredFlexibleMemorySize(sizeOut:PQWORD):Integer; SysV_ABI_CDecl;

//direct

function ps4_sceKernelAllocateDirectMemory(
           searchStart:QWORD;
           searchEnd:QWORD;
           length:QWORD;
           alignment:QWORD;
           memoryType:Integer;
           physicalAddrDest:PQWORD):Integer; SysV_ABI_CDecl;

function ps4_sceKernelAllocateMainDirectMemory(
           length:QWORD;
           alignment:QWORD;
           memoryType:Integer;
           physicalAddrDest:PQWORD):Integer; SysV_ABI_CDecl;

function ps4_sceKernelAvailableDirectMemorySize(
           searchStart:QWORD;
           searchEnd:QWORD;
           alignment:QWORD;
           physAddrOut:PQWORD;
           sizeOut:PQWORD):Integer; SysV_ABI_CDecl;

function ps4_sceKernelDirectMemoryQuery(
            offset:QWORD;
            flags:Integer;
            info:pSceKernelDirectMemoryQueryInfo;
            infoSize:QWORD):Integer; SysV_ABI_CDecl;

function ps4_sceKernelGetDirectMemoryType(
            start:QWORD;
            memoryTypeOut:PInteger;
            regionStartOut:PQWORD;
            regionEndOut:PQWORD):Integer; SysV_ABI_CDecl;

function ps4_sceKernelCheckedReleaseDirectMemory(start,len:QWORD):Integer; SysV_ABI_CDecl;
function ps4_sceKernelReleaseDirectMemory(start,len:QWORD):Integer; SysV_ABI_CDecl;

//mapping

function ps4_mmap(addr:Pointer;
                  len:size_t;
                  prot,flags,fd:Integer;
                  offset:size_t):Pointer; SysV_ABI_CDecl;

function ps4_sceKernelMmap(addr:Pointer;
                           len:size_t;
                           prot,flags,fd:Integer;
                           offset:size_t;
                           res:PPointer):Integer; SysV_ABI_CDecl;

function ps4_munmap(addr:Pointer;len:size_t):Integer; SysV_ABI_CDecl;

function ps4_sceKernelMunmap(addr:Pointer;len:size_t):Integer; SysV_ABI_CDecl;

function ps4_sceKernelReleaseFlexibleMemory(addr:Pointer;len:size_t):Integer; SysV_ABI_CDecl;

function ps4_mprotect(addr:Pointer;len:size_t;prot:Integer):Integer; SysV_ABI_CDecl;

function ps4_sceKernelMprotect(addr:Pointer;len:QWORD;prot:Integer):Integer; SysV_ABI_CDecl;

function ps4_sceKernelMtypeprotect(addr:Pointer;len:size_t;mtype,prot:Integer):Integer; SysV_ABI_CDecl;

function ps4_sceKernelQueryMemoryProtection(addr:Pointer;
                                            pStart,pEnd:PPointer;
                                            pProt:PInteger):Integer; SysV_ABI_CDecl;

function ps4_sceKernelVirtualQuery(addr:Pointer;
                                   flags:Integer;
                                   info:pSceKernelVirtualQueryInfo;
                                   infoSize:QWORD):Integer; SysV_ABI_CDecl;

Function ps4_sceKernelSetVirtualRangeName(addr:Pointer;len:QWORD;pname:PChar):Integer; SysV_ABI_CDecl;

function ps4_sceKernelMapFlexibleMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer):Integer; SysV_ABI_CDecl;

function ps4_sceKernelMapNamedFlexibleMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer;
           name:PChar):Integer; SysV_ABI_CDecl;

function ps4_sceKernelMapNamedSystemFlexibleMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer;
           name:PChar):Integer; SysV_ABI_CDecl;

function ps4_sceKernelReserveVirtualRange(
           virtualAddrDest:PPointer;
           length:QWORD;
           flags:Integer;
           alignment:QWORD):Integer; SysV_ABI_CDecl;

function ps4_sceKernelMapDirectMemory2(
           virtualAddrDest:PPointer;
           length:QWORD;
           mtype,prots,flags:Integer;
           physicalAddr:QWORD;
           alignment:QWORD):Integer; SysV_ABI_CDecl;

function ps4_sceKernelMapDirectMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer;
           physicalAddr:QWORD;
           alignment:QWORD):Integer; SysV_ABI_CDecl;

function ps4_sceKernelMapNamedDirectMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer;
           physicalAddr:QWORD;
           alignment:QWORD;
           name:Pchar):Integer; SysV_ABI_CDecl;

//

function ps4_sceKernelBatchMap(
           entries:pSceKernelBatchMapEntry;
           numberOfEntries:Integer;
           numberOfEntriesOut:PInteger
           ):Integer; SysV_ABI_CDecl;

function ps4_sceKernelBatchMap2(
           entries:pSceKernelBatchMapEntry;
           numberOfEntries:Integer;
           numberOfEntriesOut:PInteger;
           flags:Integer):Integer; SysV_ABI_CDecl;

//

function ps4_mlock(addr:Pointer;len:qword):Integer; SysV_ABI_CDecl;
function ps4_munlock(addr:Pointer;len:qword):Integer; SysV_ABI_CDecl;
function ps4_mlockall(flags:Integer):Integer; SysV_ABI_CDecl;
function ps4_munlockall:Integer; SysV_ABI_CDecl;

function ps4_sceKernelMlock(addr:Pointer;len:qword):Integer; SysV_ABI_CDecl;
function ps4_sceKernelMunlock(addr:Pointer;len:qword):Integer; SysV_ABI_CDecl;
function ps4_sceKernelMlockall(flags:Integer):Integer; SysV_ABI_CDecl;
function ps4_sceKernelMunlockall:Integer; SysV_ABI_CDecl;

//

function ps4_msync(addr:Pointer;len:size_t;flags:Integer):Integer; SysV_ABI_CDecl;
function ps4_sceKernelMsync(addr:Pointer;len:size_t;flags:Integer):Integer; SysV_ABI_CDecl;

type
 TGpuMemAlloc=function(addr:Pointer;len:size_t):Pointer;
 TGpuMemFree =procedure(h:Pointer);

 TGpuMemBlock=record
  pAddr:Pointer;
  nSize:Int64;
  Handle:Pointer;
 end;

 TGpuMemCb=record
  Alloc:TGpuMemAlloc;
  Free :TGpuMemFree;
 end;

var
 GpuMemCb:TGpuMemCb;

Function  TryGetGpuMemBlockByAddr(addr:Pointer;var block:TGpuMemBlock):Boolean;
Procedure RegistredStack;
Procedure UnRegistredStack;

var
 SceKernelFlexibleMemorySize:QWORD=0;

Procedure _mem_init;
Procedure _mem_print;

implementation

uses
 ps4_program,
 sys_kernel,
 sys_signal;

Procedure INIT_MLOCK; inline;
begin
 System.InitCriticalSection(MMLock);
end;

Procedure MLOCK; inline;
begin
 System.EnterCriticalSection(MMLock);
end;

Procedure MUNLOCK; inline;
begin
 System.LeaveCriticalSection(MMLock);
end;

function IsPowerOfTwo(x:QWORD):Boolean; inline;
begin
 Result:=(x and (x - 1))=0;
end;

function fastIntLog2(i:QWORD):QWORD; inline;
begin
 Result:=BsfQWORD(i);
end;

function str_mem_type(memoryType:Integer):RawByteString;
begin
 Result:='';
 Case memoryType of
  SCE_KERNEL_WB_ONION :Result:='WB_ONION';
  SCE_KERNEL_WC_GARLIC:Result:='WC_GARLIC';
  SCE_KERNEL_WB_GARLIC:Result:='WB_GARLIC';
  else
                       Result:=IntToStr(memoryType);
 end;
end;

function test_KP_flags(flags:Integer):RawByteString;
begin
 Result:='';
 if (flags and SCE_KERNEL_PROT_CPU_READ) <>0 then Result:=Result+' CPU_READ';
 if (flags and SCE_KERNEL_PROT_CPU_WRITE)<>0 then Result:=Result+' CPU_WRIT';
 if (flags and SCE_KERNEL_PROT_CPU_EXEC) <>0 then Result:=Result+' CPU_EXEC';
 if (flags and SCE_KERNEL_PROT_GPU_READ) <>0 then Result:=Result+' GPU_READ';
 if (flags and SCE_KERNEL_PROT_GPU_WRITE)<>0 then Result:=Result+' GPU_WRIT';
end;

Function TryGetGpuMemBlockByAddr(addr:Pointer;var block:TGpuMemBlock):Boolean;
var
 pb:PVirtualAdrBlock;
label
 __exit;
begin
 Result:=False;

 pb:=nil;

 MLOCK;

 if VirtualManager.TryGetMapBlockByAddr(addr,pb) then
 begin
  if (not pb^.isgpu) {(pb^.F.btype<>BT_GPUM)} then goto __exit;

  if (pb^.Handle=nil) then
  begin
   if (GpuMemCb.Alloc=nil) then goto __exit;
   pb^.Handle:=GpuMemCb.Alloc(pb^.Offset,pb^.Size);
   if (pb^.Handle=nil) then goto __exit;
  end;

  block.pAddr :=pb^.Offset;
  block.nSize :=pb^.Size;
  block.Handle:=pb^.Handle;
  Result:=true;

 end;

 __exit:
 MUNLOCK;
end;

function __free_block(block:PVirtualAdrBlock):Integer;
begin
 Result:=0;
 if (block=nil) then Exit;
 //if (block^.F.btype<>BT_GPUM) then Exit;
 if (block^.Handle<>nil) then Exit;

 if (GpuMemCb.Free<>nil) then Exit;

 GpuMemCb.Free(block^.Handle);

 block^.Handle:=nil;
end;

Procedure RegistredStack;
//var
// block:PBlock;
begin
 //MLOCK;
 //block:=AllocMem(SizeOf(TBlock));
 //if (block=nil) then Exit;
 //block^.pAddr:=StackBottom;
 //block^.nSize:=StackLength;
 //block^.bType:=BT_STACK;
 //PageMM.FMapBlockSet.Insert(block);
 //MUNLOCK;
end;

Procedure UnRegistredStack;
begin
 //MLOCK;
 //PageMM._DeleteBlockByAddr(StackBottom);
 //MUNLOCK;
end;

function ps4_sceKernelGetDirectMemorySize:Int64; SysV_ABI_CDecl;
begin
 Result:=SCE_KERNEL_MAIN_DMEM_SIZE;
 _set_errno(0);
end;

function ps4_getpagesize:Integer; SysV_ABI_CDecl;
begin
 Result:=PHYSICAL_PAGE_SIZE;
 _set_errno(0);
end;

function ps4_sceKernelAvailableFlexibleMemorySize(sizeOut:PQWORD):Integer; SysV_ABI_CDecl;
var
 flex:QWORD;
begin
 Result:=0;
 if (sizeOut=nil) then
 begin
  _set_errno(EINVAL);
  Exit(SCE_KERNEL_ERROR_EINVAL);
 end;

 _sig_lock;
 MLOCK;

 flex:=VirtualManager.stat.flex;

 MUNLOCK;
 _sig_unlock;

 if (flex<SceKernelFlexibleMemorySize) then
 begin
  flex:=SceKernelFlexibleMemorySize-flex;
 end else
 begin
  flex:=0;
 end;

 sizeOut^:=flex;
end;

function ps4_sceKernelConfiguredFlexibleMemorySize(sizeOut:PQWORD):Integer; SysV_ABI_CDecl;
begin
 Result:=0;
 if (sizeOut=nil) then
 begin
  _set_errno(EINVAL);
  Exit(SCE_KERNEL_ERROR_EINVAL);
 end;
 sizeOut^:=SceKernelFlexibleMemorySize;
end;

function _test_mtype(mtype:Integer):Boolean; inline;
begin
 Case mtype of
  SCE_KERNEL_WB_ONION..SCE_KERNEL_WB_GARLIC:Result:=True;
  else
   Result:=False;
 end;
end;

//direct

function _sceKernelAllocateDirectMemory(
           searchStart:QWORD;
           searchEnd:QWORD;
           length:QWORD;
           alignment:QWORD;
           memoryType:Integer;
           physicalAddrDest:PQWORD):Integer;
begin
 Result:=EINVAL;

 Writeln('srchd:',HexStr(searchStart,10),'..',HexStr(searchEnd,10),' len:',HexStr(length,10));
 Writeln('align:',HexStr(alignment,10),' ','mType:',str_mem_type(memoryType));

 if (physicalAddrDest=nil) or (length=0) or (searchEnd<=searchStart) then Exit;

 if (searchEnd>SCE_KERNEL_MAIN_DMEM_SIZE) then Exit;

 if (alignment=0) then alignment:=LOGICAL_PAGE_SIZE;

 if not IsAlign(length   ,LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(alignment,LOGICAL_PAGE_SIZE) then Exit;
 if not IsPowerOfTwo(alignment)              then Exit;
 if (fastIntLog2(alignment)>31)              then Exit;

 if not _test_mtype(memoryType) then Exit;

 searchStart:=AlignUp(searchStart,LOGICAL_PAGE_SIZE);

 _sig_lock;
 MLOCK;

 Result:=DirectManager.Alloc(searchStart,searchEnd,length,alignment,Byte(memoryType),physicalAddrDest^);

 MUNLOCK;
 _sig_unlock;
end;

function _sceKernelAllocateMainDirectMemory(
           length:QWORD;
           alignment:QWORD;
           memoryType:Integer;
           physicalAddrDest:PQWORD):Integer;
begin
 Result:=EINVAL;

 Writeln('srchm: len:',HexStr(length,10));
 Writeln('align:',HexStr(alignment,10),' ','mType:',str_mem_type(memoryType));

 if (physicalAddrDest=nil) or (length=0) then Exit;

 if (alignment=0) then alignment:=LOGICAL_PAGE_SIZE;

 if not IsAlign(length   ,LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(alignment,LOGICAL_PAGE_SIZE) then Exit;
 if not IsPowerOfTwo(alignment)              then Exit;
 if (fastIntLog2(alignment)>31)              then Exit;

 if not _test_mtype(memoryType) then Exit;

 _sig_lock;
 MLOCK;

 Result:=DirectManager.Alloc(length,alignment,Byte(memoryType),physicalAddrDest^);

 MUNLOCK;
 _sig_unlock;
end;

function _sceKernelAvailableDirectMemorySize(
            searchStart:QWORD;
            searchEnd:QWORD;
            alignment:QWORD;
            physAddrOut:PQWORD;
            sizeOut:PQWORD):Integer;
var
 FAdrOut,FSizeOut:QWORD;
begin
 Result:=EINVAL;

 if (searchEnd<=searchStart) then Exit;

 if (searchEnd>SCE_KERNEL_MAIN_DMEM_SIZE) then Exit;

 if (alignment=0) then alignment:=LOGICAL_PAGE_SIZE;

 if not IsAlign(searchStart,LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(searchEnd  ,LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(alignment  ,LOGICAL_PAGE_SIZE) then Exit;
 if not IsPowerOfTwo(alignment)                then Exit;
 if (fastIntLog2(alignment)>31)                then Exit;

 searchStart:=AlignUp(searchStart,LOGICAL_PAGE_SIZE);

 FAdrOut :=0;
 FSizeOut:=0;

 _sig_lock;
 MLOCK;

 Result:=DirectManager.QueryAvailable(searchStart,searchEnd,alignment,FAdrOut,FSizeOut);

 MUNLOCK;
 _sig_unlock;

 if (Result=0) then
 begin
  if (physAddrOut<>nil) then
  begin
   physAddrOut^:=FAdrOut;
  end;
  if (sizeOut<>nil) then
  begin
   sizeOut^:=FSizeOut;
  end;
 end;
end;

function _sceKernelDirectMemoryQuery(
            offset:QWORD;
            flags:Integer;
            info:pSceKernelDirectMemoryQueryInfo;
            infoSize:QWORD):Integer;
var
 ROut:TDirectAdrNode;
begin
 Result:=EINVAL;

 if (info=nil) or (infoSize<>SizeOf(SceKernelDirectMemoryQueryInfo)) then Exit;

 if not IsAlign(offset,LOGICAL_PAGE_SIZE) then Exit;

 ROut:=Default(TDirectAdrNode);

 _sig_lock;
 MLOCK;

 Result:=DirectManager.Query(offset,(flags=SCE_KERNEL_DMQ_FIND_NEXT),ROut);

 MUNLOCK;
 _sig_unlock;

 info^:=Default(SceKernelDirectMemoryQueryInfo);

 if (Result=0) then
 begin
  info^.start:=ROut.Offset;
  info^.__end:=ROut.Offset+ROut.Size;
  info^.mType:=ROut.F.mtype;
 end;
end;

function _sceKernelGetDirectMemoryType(
            start:QWORD;
            memoryTypeOut:PInteger;
            regionStartOut:PQWORD;
            regionEndOut:PQWORD):Integer;
var
 ROut:TDirectAdrNode;
begin
 Result:=EINVAL;

 if (memoryTypeOut=nil) then Exit;
 if (regionStartOut=nil) then Exit;
 if (regionEndOut=nil) then Exit;

 start:=AlignDw(start,PHYSICAL_PAGE_SIZE);

 ROut:=Default(TDirectAdrNode);

 _sig_lock;
 MLOCK;

 Result:=DirectManager.QueryMType(start,ROut);

 MUNLOCK;
 _sig_unlock;

 if (Result=0) then
 begin
  memoryTypeOut ^:=ROut.F.mtype;
  regionStartOut^:=ROut.Offset;
  regionEndOut  ^:=ROut.Offset+ROut.Size;
 end;
end;

function _sceKernelCheckedReleaseDirectMemory(start,len:QWORD):Integer;
begin
 Result:=EINVAL;

 if not IsAlign(start,LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(len  ,LOGICAL_PAGE_SIZE) then Exit;

 _sig_lock;
 MLOCK;

 Result:=DirectManager.CheckRelease(start,len);
 if (Result=0) then
 begin
  Result:=DirectManager.Release(start,len);
 end;

 MUNLOCK;
 _sig_unlock;
end;

function _sceKernelReleaseDirectMemory(start,len:QWORD):Integer;
begin
 Result:=EINVAL;

 if not IsAlign(start,LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(len  ,LOGICAL_PAGE_SIZE) then Exit;

 _sig_lock;
 MLOCK;

 Result:=DirectManager.Release(start,len);

 MUNLOCK;
 _sig_unlock;
end;

//

function ps4_sceKernelAllocateDirectMemory(
           searchStart:QWORD;
           searchEnd:QWORD;
           length:QWORD;
           alignment:QWORD;
           memoryType:Integer;
           physicalAddrDest:PQWORD):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelAllocateDirectMemory(
            searchStart,
            searchEnd,
            length,
            alignment,
            memoryType,
            physicalAddrDest);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelAllocateDirectMemory:',Result);
 end;
 _set_errno(Result);

 Result:=px2sce(Result);
end;

function ps4_sceKernelAllocateMainDirectMemory(
           length:QWORD;
           alignment:QWORD;
           memoryType:Integer;
           physicalAddrDest:PQWORD):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelAllocateMainDirectMemory(
            length,
            alignment,
            memoryType,
            physicalAddrDest);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelAllocateMainDirectMemory:',Result);
 end;
 _set_errno(Result);

 Result:=px2sce(Result);
end;

function ps4_sceKernelAvailableDirectMemorySize(
            searchStart:QWORD;
            searchEnd:QWORD;
            alignment:QWORD;
            physAddrOut:PQWORD;
            sizeOut:PQWORD):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelAvailableDirectMemorySize(
            searchStart,
            searchEnd,
            alignment,
            physAddrOut,
            sizeOut);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelAvailableDirectMemorySize:',Result);
 end;
 _set_errno(Result);

 Result:=px2sce(Result);
end;

function ps4_sceKernelDirectMemoryQuery(
            offset:QWORD;
            flags:Integer;
            info:pSceKernelDirectMemoryQueryInfo;
            infoSize:QWORD):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelDirectMemoryQuery(
             offset,
             flags,
             info,
             infoSize);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelDirectMemoryQuery:',Result);
 end;
 _set_errno(Result);

 Result:=px2sce(Result);
end;

function ps4_sceKernelGetDirectMemoryType(
            start:QWORD;
            memoryTypeOut:PInteger;
            regionStartOut:PQWORD;
            regionEndOut:PQWORD):Integer; SysV_ABI_CDecl;

begin
 Result:=_sceKernelGetDirectMemoryType(
             start,
             memoryTypeOut,
             regionStartOut,
             regionEndOut);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelGetDirectMemoryType:',Result);
 end;
 _set_errno(Result);

 Result:=px2sce(Result);
end;

function ps4_sceKernelCheckedReleaseDirectMemory(start,len:QWORD):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelCheckedReleaseDirectMemory(start,len);

 if (Result<>0) then
 begin
  Result:=_sceKernelCheckedReleaseDirectMemory(start,len);
  Writeln(StdWrn,'[WARN]:sceKernelCheckedReleaseDirectMemory:',Result);
 end;
 _set_errno(Result);

 Result:=px2sce(Result);
end;

function ps4_sceKernelReleaseDirectMemory(start,len:QWORD):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelReleaseDirectMemory(start,len);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelReleaseDirectMemory:',Result);
 end;
 _set_errno(Result);

 Result:=px2sce(Result);
end;

//mapping

//flag:MAP_VOID   fd=-1                               //reserve
//flag:MAP_ANON   fd=-1                               //flex
//flag:MAP_SHARED fd=/dev/dmem%d offset=physicalAddr  //direct

function __mmap(addr:Pointer;len,align:size_t;prot,flags,fd:Integer;offset:size_t;var res:Pointer):Integer;
begin
 Result:=EINVAL;

 if not IsAlign(addr  ,PHYSICAL_PAGE_SIZE) then Exit;
 //if not IsAlign(len   ,PHYSICAL_PAGE_SIZE) then Exit;
 if not IsAlign(offset,PHYSICAL_PAGE_SIZE) then Exit;

 if (align<PHYSICAL_PAGE_SIZE) then align:=PHYSICAL_PAGE_SIZE;

 _sig_lock;
 MLOCK;

 if (flags and MAP_VOID)<>0 then //reserved
 begin
  Result:=VirtualManager.mmap(addr,len,align,prot,flags,-1,0,res);
 end else
 if (flags and MAP_ANON)<>0 then //flex
 begin
  Result:=VirtualManager.mmap(addr,len,align,prot,flags,-1,0,res);
 end else
 if (fd>=0) then
 begin
  if (fd=0) then //direct (psevdo dmem fd=0)
  begin
   Result:=DirectManager.CheckMMap(offset,len);

   if (Result=0) then
   begin

    Result:=VirtualManager.mmap(addr,len,align,prot,flags,fd,offset,res);

    if (Result=0) then
    begin
     Result:=DirectManager.mmap_addr(offset,len,res);
    end;

   end;
  end else
  begin //map file
   Result:=VirtualManager.mmap(addr,len,align,prot,flags,fd,offset,res);
  end;
 end;

 MUNLOCK;
 _sig_unlock;
end;

function _mmap(addr:Pointer;
               len:size_t;
               prots,flags,fd:Integer;
               offset:size_t;
               var res:Pointer):Integer;
var
 align:size_t;
begin
 Result:=EINVAL;

 if ((prots and $ffffffc8)<>0) then Exit;

 if not IsAlign(addr,PHYSICAL_PAGE_SIZE) then Exit;

 align:=(flags and MAP_ALIGNMENT_MASK) shr MAP_ALIGNMENT_BIT;
 align:=1 shl align;

 flags:=flags and (not MAP_ALIGNMENT_MASK);

 Result:=__mmap(addr,len,align,prots,flags,fd,offset,res);
end;

function __sys_mmap_dmem(
           addr:Pointer;
           length:QWORD;
           alignment:QWORD;
           mtype,prots,flags:Integer;
           physicalAddr:QWORD;
           var res:Pointer):Integer;
begin
 Result:=0;

 _sig_lock;
 MLOCK;

 Result:=DirectManager.CheckMMap(physicalAddr,length);

 if (Result=0) then
 begin
  Result:=VirtualManager.mmap(addr,length,alignment,prots,flags,0,physicalAddr,res);

  if (Result=0) then
  begin
   Result:=DirectManager.mmap_addr(physicalAddr,length,res,mtype);
  end;
 end;

 MUNLOCK;
 _sig_unlock;
end;

function __munmap(addr:Pointer;len:size_t):Integer;
begin
 Result:=VirtualManager.Release(addr,len,True);

 if (Result=0) then
 begin
  NamedManager.Mname(addr,len,nil);
 end;
end;

function __unmap_direct(Offset,Size:QWORD):Integer;
begin
 Result:=DirectManager.unmap_addr(Offset,Size);
end;

function __mtype_direct(Offset,Size:QWORD;mtype:Integer):Integer;
begin
 Result:=DirectManager.mmap_type(Offset,Size,mtype);
end;

function _munmap(addr:Pointer;len:size_t):Integer;
begin
 Result:=EINVAL;

 _sig_lock;
 MLOCK;

 Result:=VirtualManager.Release(addr,len,False);

 if (Result=0) then
 begin
  NamedManager.Mname(addr,len,nil);
 end;

 MUNLOCK;
 _sig_unlock;
end;

function _mprotect(addr:Pointer;len:size_t;prot:Integer):Integer;
begin
 Result:=EINVAL;

 _sig_lock;
 MLOCK;

 Result:=VirtualManager.Protect(addr,len,prot);

 MUNLOCK;
 _sig_unlock;
end;

function _sys_mtypeprotect(addr:Pointer;len:size_t;mtype,prot:Integer):Integer;
begin
 Result:=EINVAL;

 _sig_lock;
 MLOCK;

 Result:=VirtualManager.Mtypeprotect(addr,len,mtype,prot);

 MUNLOCK;
 _sig_unlock;
end;

function _sys_query_memory_protection(addr:Pointer;
                                      pStart,pEnd:PPointer;
                                      pProt:PInteger):Integer;
var
 ROut:TVirtualAdrNode;
begin
 Result:=0;

 ROut:=Default(TVirtualAdrNode);

 _sig_lock;
 MLOCK;

 Result:=VirtualManager.QueryProt(addr,ROut);

 MUNLOCK;
 _sig_unlock;

 if (Result=0) then
 begin
  if (pStart<>nil) then
  begin
   pStart^:=ROut.Offset;
  end;

  if (pEnd<>nil) then
  begin
   pEnd  ^:=ROut.Offset+ROut.Size;
  end;

  if (pProt<>nil) then
  begin
   pProt ^:=ROut.F.prot;
  end;
 end;
end;

function _sys_virtual_query(addr:Pointer;
                            flags:Integer;
                            info:pSceKernelVirtualQueryInfo;
                            infoSize:QWORD):Integer;
var
 VOut:TVirtualAdrNode;
 DOut:TDirectAdrNode;
 Name:TName;
 Committed:Boolean;
begin
 Result:=EFAULT;

 if (info=nil) then Exit;
 if (infoSize<>SizeOf(SceKernelVirtualQueryInfo)) then Exit;

 VOut:=Default(TVirtualAdrNode);
 DOut:=Default(TDirectAdrNode);
 Name:=Default(TName);

 _sig_lock;
 MLOCK;

 Result:=VirtualManager.Query(addr,(flags=SCE_KERNEL_VQ_FIND_NEXT),VOut);

 if (Result=0) and (VOut.F.direct=1) then
 begin
  Result:=DirectManager.QueryMType(VOut.addr,DOut);
 end;

 if (Result=0) then
 begin
  NamedManager.Query(addr,@name);
 end;

 MUNLOCK;
 _sig_unlock;

 info^:=Default(SceKernelVirtualQueryInfo);

 if (Result=0) then
 begin
  Committed:=(VOut.F.Free=0) and (VOut.F.reserv=0);
  info^.pstart               :=VOut.Offset;
  info^.pend                 :=VOut.Offset+VOut.Size;
  info^.offset               :=VOut.addr;
  info^.protection           :=VOut.F.prot;
  info^.memoryType           :=DOut.F.mtype;
  info^.bits.isFlexibleMemory:=Byte((VOut.F.direct=0) and Committed);
  info^.bits.isDirectMemory  :=VOut.F.direct;
  info^.bits.isStack         :=VOut.F.stack;
  info^.bits.isPooledMemory  :=VOut.F.polled;
  info^.bits.isCommitted     :=Byte(Committed);
  info^.name                 :=Name;
 end;
end;

Function _sys_mname(addr:Pointer;len:QWORD;pname:PChar):Integer;
begin
 Result:=EFAULT;

 if (pname=nil) then Exit;
 if (StrLen(pname)>32) then Exit;

 _sig_lock;
 MLOCK;

 Result:=NamedManager.Mname(addr,len,pname);

 MUNLOCK;
 _sig_unlock;
end;

function _sceKernelMapFlexibleMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer):Integer;
var
 addr:Pointer;
begin
 Result:=SCE_KERNEL_ERROR_EINVAL;

 if ((flags and $ffbfff6f)<>0) then Exit;
 if ((prots and $ffffffc8)<>0) then Exit;

 if (length<LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(length,LOGICAL_PAGE_SIZE) then Exit;

 addr:=virtualAddrDest^;
 if not IsAlign(addr,LOGICAL_PAGE_SIZE) then Exit;

 if (((flags and MAP_FIXED) <> 0) and (addr=nil)) then
 begin
  if (SDK_VERSION > $16fffff) then
  begin
   Exit(SCE_KERNEL_ERROR_EINVAL);
  end;
  flags:=flags and $ffffffef;
  Writeln(StdWrn,'[WARNING] map(addr=0, flags=MAP_FIXED)');
 end;

 if (((flags and MAP_FIXED)=0) and (addr=nil)) then
 begin
  addr:=Pointer($880000000);
 end;

 Result:=__mmap(addr,length,0,prots,flags or MAP_ANON,-1,0,addr);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Result:=px2sce(Result);
 end else
 begin
  virtualAddrDest^:=addr;
  Result:=0;
 end;
end;

function _sceKernelMapNamedSystemFlexibleMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer;
           name:PChar):Integer;
var
 addr:Pointer;
begin
 Result:=SCE_KERNEL_ERROR_EINVAL;

 if ((flags and $ffbfff6f)<>0) then Exit;
 if ((prots and $ffffffc8)<>0) then Exit;

 if (length<LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(length,LOGICAL_PAGE_SIZE) then Exit;

 addr:=virtualAddrDest^;
 if not IsAlign(addr,LOGICAL_PAGE_SIZE) then Exit;

 if (((flags and MAP_FIXED) <> 0) and (addr=nil)) then
 begin
  if (SDK_VERSION > $16fffff) then
  begin
   Exit(SCE_KERNEL_ERROR_EINVAL);
  end;
  flags:=flags and $ffffffef;
  Writeln('[WARNING] map(addr=0, flags=MAP_FIXED)');
 end;

 if (((flags and MAP_FIXED)=0) and (addr=nil)) then
 begin
  addr:=Pointer($880000000);
 end;

 Result:=__mmap(addr,length,0,prots,flags or MAP_ANON or MAP_SYSTEM,-1,0,addr);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Result:=px2sce(Result);
 end else
 begin
  _sys_mname(addr,length,name);
  virtualAddrDest^:=addr;
  Result:=0;
 end;
end;

function _sceKernelReserveVirtualRange(
           virtualAddrDest:PPointer;
           length:QWORD;
           flags:Integer;
           alignment:QWORD):Integer;
var
 addr:Pointer;
begin
 Result:=SCE_KERNEL_ERROR_EINVAL;

 if ((flags and $ffbfff6f)<>0) then Exit;

 if (length<LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(length   ,LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(alignment,LOGICAL_PAGE_SIZE) then Exit;
 if not IsPowerOfTwo(alignment) then Exit;

 if (alignment<LOGICAL_PAGE_SIZE) then alignment:=LOGICAL_PAGE_SIZE;
 if (fastIntLog2(alignment)>31) then Exit;

 addr:=virtualAddrDest^;
 if not IsAlign(addr,LOGICAL_PAGE_SIZE) then Exit;

 if (((flags and MAP_FIXED)<>0) and (addr=nil)) then
 begin
  if (SDK_VERSION > $16fffff) then
  begin
   Exit(SCE_KERNEL_ERROR_EINVAL);
  end;
  flags:=flags and $ffffffef;
  Writeln('[WARNING] map(addr=0, flags=MAP_FIXED)');
 end;

 if (((flags and MAP_FIXED)=0) and (addr=nil)) then
 begin
  addr:=Pointer($880000000);
 end;

 Result:=__mmap(addr,length,alignment,0,flags or MAP_VOID or MAP_SHARED,-1,0,addr);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Result:=px2sce(Result);
 end else
 begin
  virtualAddrDest^:=addr;
  Result:=0;
 end;
end;

function _sceKernelMapDirectMemory2(
           virtualAddrDest:PPointer;
           length:QWORD;
           mtype,prots,flags:Integer;
           physicalAddr:QWORD;
           alignment:QWORD):Integer;
var
 addr:Pointer;
begin
 Result:=SCE_KERNEL_ERROR_EINVAL;

 if ((flags and $1f000000)<>0) then Exit;
 if ((prots and $ffffffc8)<>0) then Exit;

 if (length<LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(length      ,LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(physicalAddr,LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(alignment   ,LOGICAL_PAGE_SIZE) then Exit;
 if not IsPowerOfTwo(alignment) then Exit;

 if (alignment<LOGICAL_PAGE_SIZE) then alignment:=LOGICAL_PAGE_SIZE;
 if (fastIntLog2(alignment)>31) then Exit;

 addr:=virtualAddrDest^;
 if not IsAlign(addr,LOGICAL_PAGE_SIZE) then Exit;

 if (((flags and MAP_FIXED)=0) and (addr=nil)) then
 begin
  addr:=Pointer($880000000);
 end;

 Result:=__sys_mmap_dmem(addr,length,alignment,mtype,prots,flags,physicalAddr,addr);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Result:=px2sce(Result);
 end else
 begin
  virtualAddrDest^:=addr;
  Result:=0;
 end;
end;

function _sceKernelMapDirectMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer;
           physicalAddr:QWORD;
           alignment:QWORD):Integer;
var
 addr:Pointer;
 _flags:Integer;
begin
 Result:=SCE_KERNEL_ERROR_EINVAL;

 if ((physicalAddr < $3000000000) or (physicalAddr > $301fffffff)) and
    ((flags and SCE_KERNEL_MAP_DMEM_COMPAT)=0) and (SDK_VERSION > $24fffff) then
 begin
  Result:=_sceKernelMapDirectMemory2(virtualAddrDest,length,-1,prots,flags,physicalAddr,alignment);
  Exit;
 end;

 if ((flags and $1f000000)<>0) then Exit;
 if ((prots and $ffffffc8)<>0) then Exit;

 if (length<LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(length      ,LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(physicalAddr,LOGICAL_PAGE_SIZE) then Exit;
 if not IsAlign(alignment   ,LOGICAL_PAGE_SIZE) then Exit;
 if not IsPowerOfTwo(alignment) then Exit;

 if (alignment<LOGICAL_PAGE_SIZE) then alignment:=LOGICAL_PAGE_SIZE;
 if (fastIntLog2(alignment)>31) then Exit;

 addr:=virtualAddrDest^;
 if not IsAlign(addr,LOGICAL_PAGE_SIZE) then Exit;

 _flags:=flags and $fffffbff;

 if (((flags and MAP_FIXED) <> 0) and (addr=nil)) then
 begin
  if (SDK_VERSION > $16fffff) then
  begin
   Exit(SCE_KERNEL_ERROR_EINVAL);
  end;
  _flags:=flags and $fffffbef;
  Writeln('[WARNING] map(addr=0, flags=MAP_FIXED)');
 end;

 if (((flags and MAP_FIXED)=0) and (addr=nil)) then
 begin
  addr:=Pointer($880000000);
 end;

 Result:=__mmap(addr,length,alignment,prots,_flags or MAP_SHARED,0,physicalAddr,addr);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Result:=px2sce(Result);
 end else
 begin
  virtualAddrDest^:=addr;
  Result:=0;
 end;
end;

////
////

function ps4_mmap(addr:Pointer;
                  len:size_t;
                  prot,flags,fd:Integer;
                  offset:size_t):Pointer; SysV_ABI_CDecl;
var
 err:Integer;
begin

 if (((flags and MAP_FIXED)=0) and (addr=nil)) then
 begin
  addr:=Pointer($880000000);
 end;

 err:=_mmap(addr,len,prot,flags,fd,offset,addr);
 _set_errno(err);

 if (err=0) then
 begin
  Result:=addr;
 end else
 begin
  Writeln(StdWrn,'[WARN]:mmap:',err);
  Result:=MAP_FAILED;
 end;
end;

function ps4_sceKernelMmap(addr:Pointer;
                           len:size_t;
                           prot,flags,fd:Integer;
                           offset:size_t;
                           res:PPointer):Integer; SysV_ABI_CDecl;
begin

 if (((flags and MAP_FIXED)=0) and (addr=nil)) then
 begin
  addr:=Pointer($880000000);
 end;

 Result:=_mmap(addr,len,prot,flags,fd,offset,addr);
 _set_errno(Result);

 if (Result=0) then
 begin
  res^:=addr;
 end else
 begin
  Writeln(StdWrn,'[WARN]:sceKernelMmap:',Result);
 end;

 Result:=px2sce(Result);
end;

function ps4_munmap(addr:Pointer;len:size_t):Integer; SysV_ABI_CDecl;
begin
 Result:=_munmap(addr,len);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:munmap:',Result);
 end;

 Result:=_set_errno(Result);
end;

function ps4_sceKernelMunmap(addr:Pointer;len:size_t):Integer; SysV_ABI_CDecl;
begin
 Result:=_munmap(addr,len);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelMunmap:',Result);
 end;

 Result:=px2sce(Result);
end;

function ps4_sceKernelReleaseFlexibleMemory(addr:Pointer;len:size_t):Integer; SysV_ABI_CDecl;
begin
 Result:=_munmap(addr,len);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelReleaseFlexibleMemory:',Result);
 end;

 Result:=px2sce(Result);
end;

function ps4_mprotect(addr:Pointer;
                      len:size_t;
                      prot:Integer):Integer; SysV_ABI_CDecl;
begin
 Result:=_mprotect(addr,len,prot);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:mprotect:',Result);
 end;

 Result:=_set_errno(Result);
end;

function ps4_sceKernelMprotect(addr:Pointer;
                               len:size_t;
                               prot:Integer):Integer; SysV_ABI_CDecl;
begin
 Result:=_mprotect(addr,len,prot);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelMprotect:',Result);
 end;

 Result:=px2sce(Result);
end;

function ps4_sceKernelMtypeprotect(addr:Pointer;
                                   len:size_t;
                                   mtype,prot:Integer):Integer; SysV_ABI_CDecl;
begin
 Result:=_sys_mtypeprotect(addr,len,mtype,prot);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelMtypeprotect:',Result);
 end;

 Result:=px2sce(Result);
end;

function ps4_sceKernelQueryMemoryProtection(addr:Pointer;
                                            pStart,pEnd:PPointer;
                                            pProt:PInteger):Integer; SysV_ABI_CDecl;
begin
 Result:=_sys_query_memory_protection(addr,pStart,pEnd,pProt);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelQueryMemoryProtection:',Result);
 end;

 Result:=px2sce(Result);
end;

function ps4_sceKernelVirtualQuery(addr:Pointer;
                                   flags:Integer;
                                   info:pSceKernelVirtualQueryInfo;
                                   infoSize:QWORD):Integer; SysV_ABI_CDecl;
begin
 Result:=_sys_virtual_query(addr,flags,info,infoSize);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelVirtualQuery:',Result);
 end;

 Result:=px2sce(Result);
end;

Function ps4_sceKernelSetVirtualRangeName(addr:Pointer;len:QWORD;pname:PChar):Integer; SysV_ABI_CDecl;
begin
 Result:=_sys_mname(addr,len,pname);
 _set_errno(Result);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelSetVirtualRangeName:',Result);
 end;

 Result:=px2sce(Result);
end;

function ps4_sceKernelMapFlexibleMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelMapFlexibleMemory(virtualAddrDest,length,prots,flags);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelMapFlexibleMemory:',Result);
 end;
end;

function ps4_sceKernelMapNamedFlexibleMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer;
           name:PChar):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelMapFlexibleMemory(virtualAddrDest,length,prots,flags);

 if (Result=0) then
 begin
  _sys_mname(virtualAddrDest^,length,name);
 end else
 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelMapNamedFlexibleMemory:',Result);
 end;
end;

function ps4_sceKernelMapNamedSystemFlexibleMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer;
           name:PChar):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelMapNamedSystemFlexibleMemory(virtualAddrDest,length,prots,flags,name);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelMapNamedSystemFlexibleMemory:',Result);
 end;
end;

function ps4_sceKernelReserveVirtualRange(
           virtualAddrDest:PPointer;
           length:QWORD;
           flags:Integer;
           alignment:QWORD):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelReserveVirtualRange(virtualAddrDest,length,flags,alignment);

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelReserveVirtualRange:',Result);
 end;
end;

function ps4_sceKernelMapDirectMemory2(
           virtualAddrDest:PPointer;
           length:QWORD;
           mtype,prots,flags:Integer;
           physicalAddr:QWORD;
           alignment:QWORD):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelMapDirectMemory2(virtualAddrDest,length,mtype,prots,flags,physicalAddr,alignment);

 if _isgpu(prots) then
 begin
  Writeln('GPU:',HexStr(virtualAddrDest^),'..',HexStr(virtualAddrDest^+length));
 end;

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelMapDirectMemory2:',Result);
 end;
end;

function ps4_sceKernelMapDirectMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer;
           physicalAddr:QWORD;
           alignment:QWORD):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelMapDirectMemory(virtualAddrDest,length,prots,flags,physicalAddr,alignment);

 if _isgpu(prots) then
 begin
  Writeln('GPU:',HexStr(virtualAddrDest^),'..',HexStr(virtualAddrDest^+length));
 end;

 if (Result<>0) then
 begin
  Writeln(StdWrn,'[WARN]:sceKernelMapDirectMemory:',Result);
 end;
end;

function ps4_sceKernelMapNamedDirectMemory(
           virtualAddrDest:PPointer;
           length:QWORD;
           prots,flags:Integer;
           physicalAddr:QWORD;
           alignment:QWORD;
           name:Pchar):Integer; SysV_ABI_CDecl;
begin
 Result:=_sceKernelMapDirectMemory(virtualAddrDest,length,prots,flags,physicalAddr,alignment);

 if _isgpu(prots) then
 begin
  Writeln('GPU:',HexStr(virtualAddrDest^),'..',HexStr(virtualAddrDest^+length));
 end;

 if (Result=0) then
 begin
  _sys_mname(virtualAddrDest^,length,name);
 end else
 begin
  Writeln(StdWrn,'[WARN]:sceKernelMapNamedDirectMemory:',Result);
 end;
end;

//

function ps4_sceKernelBatchMap(
           entries:pSceKernelBatchMapEntry;
           numberOfEntries:Integer;
           numberOfEntriesOut:PInteger
           ):Integer; SysV_ABI_CDecl;
begin
 Result:=ps4_sceKernelBatchMap2(
            entries,
            numberOfEntries,
            numberOfEntriesOut,
            MAP_FIXED);
//There is a function with the same name exported from the libkernel_pre250mmap
// library, it differs only in that the flag is 0x410 (MAP_FIXED or MAP_STACK)
end;

function ps4_sceKernelBatchMap2(
           entries:pSceKernelBatchMapEntry;
           numberOfEntries:Integer;
           numberOfEntriesOut:PInteger;
           flags:Integer):Integer; SysV_ABI_CDecl;
label
 _exit,_exit_lock;
var
 i:Integer;
begin
 if (entries=nil) then
 begin
  numberOfEntries:=0;
  _set_errno(EFAULT);
  Result:=SCE_KERNEL_ERROR_EFAULT;
  goto _exit;
 end;
 if (numberOfEntries=0) then
 begin
  numberOfEntries:=0;
  _set_errno(EINVAL);
  Result:=SCE_KERNEL_ERROR_EINVAL;
  goto _exit;
 end;

 _sig_lock;
 MLOCK;

 For i:=0 to numberOfEntries-1 do
 begin
  Case entries[i].operation of
   SCE_KERNEL_MAP_OP_MAP_DIRECT:
    begin
     Result:=ps4_sceKernelMapDirectMemory(
               @entries[i].start,
               entries[i].length,
               entries[i].protection,
               flags,
               entries[i].offset,
               0);
    end;
   SCE_KERNEL_MAP_OP_UNMAP:
    begin
     Result:=ps4_sceKernelMunmap(entries[i].start,
                                 entries[i].length);
    end;
   SCE_KERNEL_MAP_OP_PROTECT:
    begin
     Result:=ps4_sceKernelMprotect(entries[i].start,
                                   entries[i].length,
                                   entries[i].protection);
    end;
   SCE_KERNEL_MAP_OP_MAP_FLEXIBLE:
    begin
     Result:=ps4_sceKernelMapFlexibleMemory(@entries[i].start,
                                            entries[i].length,
                                            entries[i].protection,
                                            flags);
    end;
   SCE_KERNEL_MAP_OP_TYPE_PROTECT:
    begin
     Result:=ps4_sceKernelMtypeprotect(entries[i].start,
                                       entries[i].length,
                                       entries[i].mtype,
                                       entries[i].protection);
    end;
   else
    begin
     numberOfEntries:=i;
     _set_errno(EINVAL);
     Result:=SCE_KERNEL_ERROR_EINVAL;
     goto _exit_lock;
    end;
  end;

  if (Result<>0) then
  begin
   numberOfEntries:=i;
   goto _exit_lock;
  end;

 end;

 _exit_lock:
  MUNLOCK;
  _sig_unlock;
 _exit:
  if (numberOfEntriesOut<>nil) then numberOfEntriesOut^:=numberOfEntries;
end;

//

function _sys_check_mmaped(addr:Pointer;len:qword):Integer;
begin
 _sig_lock;
 MLOCK;

 Result:=VirtualManager.check_mmaped(addr,len);

 MUNLOCK;
 _sig_unlock;
end;

function _sys_mlockall(flags:Integer):Integer;
const
 MNALL=not Integer(MCL_CURRENT or MCL_FUTURE);
begin
 if (flags=0) or ((flags and MNALL)<>0) then Exit(EINVAL);
 Result:=0;
end;

function ps4_mlock(addr:Pointer;len:qword):Integer; SysV_ABI_CDecl;
begin
 Result:=_set_errno(_sys_check_mmaped(addr,len));
end;

function ps4_munlock(addr:Pointer;len:qword):Integer; SysV_ABI_CDecl;
begin
 Result:=_set_errno(_sys_check_mmaped(addr,len));
end;

function ps4_mlockall(flags:Integer):Integer; SysV_ABI_CDecl;
begin
 Result:=_set_errno(_sys_mlockall(flags));
end;

function ps4_munlockall:Integer; SysV_ABI_CDecl;
begin
 Result:=_set_errno(0);
end;

function ps4_sceKernelMlock(addr:Pointer;len:qword):Integer; SysV_ABI_CDecl;
begin
 Result:=_sys_check_mmaped(addr,len);
 _set_errno(Result);
 Result:=px2sce(Result);
end;

function ps4_sceKernelMunlock(addr:Pointer;len:qword):Integer; SysV_ABI_CDecl;
begin
 Result:=_sys_check_mmaped(addr,len);
 _set_errno(Result);
 Result:=px2sce(Result);
end;

function ps4_sceKernelMlockall(flags:Integer):Integer; SysV_ABI_CDecl;
begin
 Result:=_sys_mlockall(flags);
 _set_errno(Result);
 Result:=px2sce(Result);
end;

function ps4_sceKernelMunlockall:Integer; SysV_ABI_CDecl;
begin
 _set_errno(0);
 Result:=0;
end;

////

function _sys_msync(addr:Pointer;len:size_t;flags:Integer):Integer;
begin
 if not IsAlign(addr,PHYSICAL_PAGE_SIZE) then Exit(EINVAL);

 if ((flags and MS_ASYNC)<>0) and ((flags and MS_INVALIDATE)<>0) then Exit(EINVAL);

 _sig_lock;
 MLOCK;

 Result:=VirtualManager.check_mmaped(addr,len);

 MUNLOCK;
 _sig_unlock;

 if (Result=0) then
 begin
  FlushViewOfFile(addr,len);
 end;
end;

function ps4_msync(addr:Pointer;len:size_t;flags:Integer):Integer; SysV_ABI_CDecl;
begin
 Result:=_set_errno(_sys_msync(addr,len,flags));
end;

function ps4_sceKernelMsync(addr:Pointer;len:size_t;flags:Integer):Integer; SysV_ABI_CDecl;
begin
 Result:=_sys_msync(addr,len,flags);
 _set_errno(Result);
 Result:=px2sce(Result);
end;

Procedure _mem_init;
var
 p:PQWORD;
begin
 SceKernelFlexibleMemorySize:=(448*1024*1024);
 p:=GetSceKernelFlexibleMemorySize;
 if (p<>nil) then
 begin
  SceKernelFlexibleMemorySize:=p^;
 end;
end;

Procedure _mem_print;
begin
 Writeln('---[Virtual]---');
 VirtualManager.Print;
 Writeln('---[Named]---');
 NamedManager.Print;
 Writeln('---[Direct]---');
 DirectManager.Print;
end;

initialization
 INIT_MLOCK;

 DirectManager :=TDirectManager .Create;
 DirectManager .OnMemoryUnmapCb:=@__munmap;

 VirtualManager:=TVirtualManager.Create($400000,$FFFFFFFFFF);
 VirtualManager.OnDirectUnmapCb:=@__unmap_direct;
 VirtualManager.OnDirectMtypeCb:=@__mtype_direct;
 VirtualManager.OnFreeBlockCb  :=@__free_block;
 VirtualManager._mmap_sys(Pointer($400000),$7FFBFC000);

 NamedManager  :=TNamedManager.Create($400000,$FFFFFFFFFF);

end.

