Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: build

on: [push, pull_request]
on: [push, pull_request, workflow_dispatch]

jobs:
linux-gcc-clang:
Expand Down Expand Up @@ -30,13 +30,15 @@ jobs:
export CC=clang
export CXX=clang++
fi
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS=-msse4.1 -DCMAKE_CXX_FLAGS=-msse4.1
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS=-msse4.1 -DCMAKE_CXX_FLAGS=-msse4.1 -DPS2X_IOP_BUILD_TESTS=ON

- name: Build
run: cmake --build build --config Release

- name: Tests
run: ./build/ps2xTest/ps2x_tests
run: |
./build/ps2xTest/ps2x_tests
./build/ps2xIOP/ps2_iop_import_tests

windows-msvc:
strategy:
Expand All @@ -63,13 +65,14 @@ jobs:
- name: Build
shell: cmd
run: |
cmake -B build
cmake -B build -DPS2X_IOP_BUILD_TESTS=ON
cmake --build build --config Release

- name: Tests
shell: cmd
run: |
.\build\ps2xTest\Release\ps2x_tests.exe
.\build\ps2xIOP\Release\ps2_iop_import_tests.exe
- name: Upload artifact
uses: actions/upload-artifact@v6
with:
Expand Down
3 changes: 3 additions & 0 deletions ps2xIOP/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,6 @@ ctest --test-dir out/build/iop-tests --output-on-failure

The suites cover IRX execution, RPC, imports, version resolution and generic
HLE compatibility. `ps2x_tests` also covers runtime SIF RPC/DMA integration.


The IOMAN compatibility layer currently supports read-only `open`, `close`, `read`, and `lseek` operations through the host VFS adapter. Write and directory operations are not implemented by this layer yet.
160 changes: 158 additions & 2 deletions ps2xIOP/src/emulator/imports/iop_ioman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@

namespace ps2x::iop::detail
{
IopIoman::IopIoman(IopMemory &memory) noexcept
: m_memory(memory)
IopIoman::IopIoman(IopHost &host, IopMemory &memory) noexcept
: m_host(host), m_memory(memory)
{
}

void IopIoman::reset()
{
for (const auto &[fd, file] : m_openFiles)
{
(void)fd;
m_host.closeHostFile(file.hostHandle);
}
m_openFiles.clear();
m_devices.clear();
}

Expand All @@ -29,6 +35,156 @@ namespace ps2x::iop::detail

switch (ordinal)
{
case 4: // open
{
constexpr uint32_t kAccessModeMask = 0x0003u;
constexpr uint32_t kReadOnly = 0x0001u;
constexpr uint32_t kCreate = 0x0200u;
constexpr uint32_t kTruncate = 0x0400u;
constexpr uint32_t kExclusive = 0x0800u;

const std::string guestPath = m_memory.readString(a0, 1024u);
const uint32_t mode = cpu.gpr[5];
if (guestPath.empty() ||
(mode & kAccessModeMask) != kReadOnly ||
(mode & (kCreate | kTruncate | kExclusive)) != 0u)
{
setV0(0xFFFFFFFFu);
return true;
}

const std::string hostPath = m_host.translateGuestPath(guestPath);
if (hostPath.empty())
{
setV0(0xFFFFFFFFu);
return true;
}

const uint64_t handle = m_host.openHostFile(hostPath);
if (handle == 0u)
{
setV0(0xFFFFFFFFu);
return true;
}

uint64_t size = 0u;
if (!m_host.hostFileSize(handle, size))
{
m_host.closeHostFile(handle);
setV0(0xFFFFFFFFu);
return true;
}

int32_t fd = -1;
for (int32_t candidate = 3; candidate < 256; ++candidate)
{
if (m_openFiles.find(candidate) == m_openFiles.end())
{
fd = candidate;
break;
}
}
if (fd < 0)
{
m_host.closeHostFile(handle);
setV0(0xFFFFFFFFu);
return true;
}

m_openFiles.emplace(fd, OpenFile{handle, 0u, size});
setV0(static_cast<uint32_t>(fd));
return true;
}
case 5: // close
{
const int32_t fd = static_cast<int32_t>(a0);
const auto file = m_openFiles.find(fd);
if (file == m_openFiles.end())
{
setV0(0xFFFFFFFFu);
return true;
}

m_host.closeHostFile(file->second.hostHandle);
m_openFiles.erase(file);
setV0(0u);
return true;
}
case 6: // read
{
const int32_t fd = static_cast<int32_t>(a0);
const auto file = m_openFiles.find(fd);
const uint32_t destination = cpu.gpr[5];
const uint32_t requested = cpu.gpr[6];
if (file == m_openFiles.end() || requested > IopMemory::RamSize)
{
setV0(0xFFFFFFFFu);
return true;
}
if (requested == 0u)
{
setV0(0u);
return true;
}

std::vector<uint8_t> buffer(requested);
size_t bytesRead = 0u;
if (!m_host.readHostFile(file->second.hostHandle,
file->second.offset,
buffer.data(),
buffer.size(),
bytesRead) ||
bytesRead > buffer.size() ||
!m_memory.writeRam(destination, buffer.data(), bytesRead))
{
setV0(0xFFFFFFFFu);
return true;
}

file->second.offset += bytesRead;
setV0(static_cast<uint32_t>(bytesRead));
return true;
}
case 8: // lseek
{
const int32_t fd = static_cast<int32_t>(a0);
const auto file = m_openFiles.find(fd);
if (file == m_openFiles.end())
{
setV0(0xFFFFFFFFu);
return true;
}

const int32_t offset = static_cast<int32_t>(cpu.gpr[5]);
const uint32_t whence = cpu.gpr[6];
int64_t base = 0;
switch (whence)
{
case 0u: // SEEK_SET
base = 0;
break;
case 1u: // SEEK_CUR
base = static_cast<int64_t>(file->second.offset);
break;
case 2u: // SEEK_END
base = static_cast<int64_t>(file->second.size);
break;
default:
setV0(0xFFFFFFFFu);
return true;
}

const int64_t newOffset = base + static_cast<int64_t>(offset);
if (newOffset < 0 || newOffset > 0x7FFFFFFFLL)
{
setV0(0xFFFFFFFFu);
return true;
}

file->second.offset = static_cast<uint64_t>(newOffset);
setV0(static_cast<uint32_t>(newOffset));
return true;
}
case 20: // AddDrv
{
if (a0 == 0u || m_devices.size() >= kMaxDevices)
Expand Down
14 changes: 13 additions & 1 deletion ps2xIOP/src/emulator/imports/iop_ioman.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#pragma once

#include "ps2x/iop/iop_host.h"

#include <cstdint>
#include <map>
#include <string>
#include <vector>

Expand All @@ -13,7 +16,7 @@ namespace ps2x::iop::detail
class IopIoman
{
public:
explicit IopIoman(IopMemory &memory) noexcept;
IopIoman(IopHost &host, IopMemory &memory) noexcept;

void reset();
[[nodiscard]] bool dispatchImport(uint16_t ordinal, IopCpuState &cpu, IopGuestExecutor &executor);
Expand All @@ -26,7 +29,16 @@ namespace ps2x::iop::detail
std::string name;
};

struct OpenFile
{
uint64_t hostHandle = 0u;
uint64_t offset = 0u;
uint64_t size = 0u;
};

IopHost &m_host;
IopMemory &m_memory;
std::vector<Device> m_devices;
std::map<int32_t, OpenFile> m_openFiles;
};
}
2 changes: 1 addition & 1 deletion ps2xIOP/src/emulator/iop_emulator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ namespace ps2x::iop::detail
heaplib(memory),
intrman(memory),
timrman(),
ioman(memory),
ioman(host, memory),
cpuCore(memory),
imports(memory),
loadcore(memory, imports)
Expand Down
Loading