martes, 28 de febrero de 2012

New version! Soywiz's Psp Emulator 2012-02-28 (updated)

Today I release a new version of my emulator. This time it is a completely rewritten emulator from scratch. Now it's written in C# instead of D, and features a dynamic recompilation instead of the old interpreter. Though at this moment the cpu is monothreaded. I will make it optionally multithreaded in the future for games that works fine with multithreading.


Also I have moved to github: https://github.com/soywiz/cspspemu

You can grab the sources with TortoiseGit and compile with Visual C# Express 2010.

And the windows binaries for this version here: Download page

(It will probably work on Linux and Mac in the next versions using Mono because I'm using OpenTK. But at this point mono has some issues with the DynamicMethod generation that keeps it from working.)


Requirements:


This emulator features a "fast memory" option that will improve the speed a lot, but it has some issues with some games (though not many), so it's disabled by default. You can check the improvement of using this memory with the minifire (with vsync off) or compilerperf demos.


This is the first public release of the emulator and I have just tested it on my computer, if you have any issue with it, please comment here and I will try to help you.














Information about the release (from the github's README.markdown):

Soywiz's CSharp Psp Emulator

A PSP Emulator for >= .NET 4.0 made in C# by soywiz - Carlos Ballesteros Velasco
Work In Progress emulator

EMULATOR STATUS:

  • Cpu and Fpu is almost fully implemented
  • VFpu still has lots of functions without implementation
  • Audio is implemented
  • Gpu still requires lots of works
  • Hle emulation has implemented a basic set of APIs (still lot of work left)
  • There are lot of homebrew games running
  • There are a small number of commercial games running

KEY FEATURES:

Fully Managed and Disengaged

Being fully managed (thought it will need unsafe access) will allow to port to lots of platforms in the future: AKA: Linux, Mac, future devices supporting mono/C# unsafe code...

Very Fast Dynamic Recompilation

The quality of the generated code is fantastic.
Lots of MIPs instructions encodes in a single X86 instruction.
The registers are inlined in the CpuThreadState class, so access
to a register is as fast as accessing a non-virtual field.

Fast As Hell Memory Access (Currently on Windows/Linux only)

Using VirtualAlloc/mmap, allows all the PSP Memory Segments to be in a virtual space that fits the PSP Memory.
And since there is no DMA access on a HLE emulation, we can get a pointer just making an ADD operation between the base and the PSP address.
This is as fast as possible.

MainPtr = VirtualAlloc(Base + MainOffset, MainSize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

Very Fast Function Cache

Detect and access to a function cache features O(1),
adding a function cache O(log(n)),
and flushing a range or the whole Function Cache is as fast as possible too.
This is achieved with a Array that contains all the possible memory addresses and a SortedSet
that allows to know which functions have been generated and should be purged when invalidating instruction cache.

Very Fast Thread Switching

Each PSP Thread is a native Green Thread / Fiber. Since the PSP has a single core, and some games requres
thread scheduling, we simulate thread scheduling with fibers. Each PspThread have its own registers in an object,
so instead of restoring the registers, it just resumes the thread execution. Skiping completely the register restoring.
This model also allows to pause the execution of the code at any time very easy, even inside an HleFunction or even
in the future, if we optimize the function generation to detect JAL instructions and call delegates directly, inside
nested function calls.

Very clean HLE Emulation using CustomAttributes, LINQ, method mapping and XML Documentation

LINQ + functional-programming helps to make the code very clean, simple and easy to understand.

/// <summary>
/// Get the size of the largest free memory block.
/// </summary>
/// <returns>The size of the largest free memory block, in bytes.</returns>
[HlePspFunction(NID = 0xA291F107, FirmwareVersion = 150)]
public int sceKernelMaxFreeMemSize()
{
    return HleState.MemoryManager.GetPartition(HleMemoryManager.Partitions.User).ChildPartitions
        .Where(Partition => !Partition.Allocated)
        .OrderByDescending(Partition => Partition.Size)
        .First()
        .Size
    ;
}

C# support of structs and pointers, allow the HLE functions to use pointers directly and seamlessly.

/// <summary>
/// Reads an entry from an opened file descriptor.
/// </summary>
/// <param name="FileHandle">Already opened file descriptor (using sceIoDopen)</param>
/// <param name="IoDirent">Pointer to an io_dirent_t structure to hold the file information</param>
/// <returns>
///     Read status
///     Equal to   0 - No more directory entries left
///     Great than 0 - More directory entired to go
///     Less  than 0 - Error
/// </returns>
[HlePspFunction(NID = 0xE3EB004C, FirmwareVersion = 150)]
public int sceIoDread(int FileHandle, HleIoDirent* IoDirent)
{
    var HleIoDrvFileArg = GetFileArgFromHandle(FileHandle);
    return HleIoDrvFileArg.HleIoDriver.IoDread(HleIoDrvFileArg, IoDirent);
}

Faster than the original PSP, than DPspEmu and than JPCSP

Most CPU/FPU/VFPU operations are something like 8x times faster than the original PSP on a modern computer.
Still the GPU implementation is still limited because of the Cpu <-> Gpu memory BUS. But probably will be enough in most cases.

KISS and DRY

With the "Keep It Simple Stupid" and "Don't Repeat Yourself" philosophies always in mind,
the quality of the core is a priority always over new features.

CPU/Gpu Instruction decoding, assembler and disassembler share a single table with the instruction descriptions that will allow generating the required code on the fly.

    // Arithmetic operations.
    ID("add",    VM("000000:rs:rt:rd:00000:100000"), "%d, %s, %t", ADDR_TYPE_NONE, 0),
    ID("addu",   VM("000000:rs:rt:rd:00000:100001"), "%d, %s, %t", ADDR_TYPE_NONE, 0),
    ID("addi",   VM("001000:rs:rt:imm16"          ), "%t, %s, %i", ADDR_TYPE_NONE, 0),

UnitTesting and Integration Tests

The idea is to create a full set of tests, testing the emulator interface, code generation, and HLE APIs.
UnitTests are integrated in the code, and HLE APIs are external integration tests in form of PSP executables than can be executed on any emulator to test APIs.

Fully Open Source

Project is on GitHub, so anyone can branch the project and perform Pull Requests helping to improve the code.

Created after 4 tries

I'm the creator of the D PSP Emulator. That emulator had 4 versions, every one was created almost from the scratch with different approaches and trying
to improve the quality of the code in each iteration.
The lack of a good IDE, the complicated structure of the D language, the horrible compilation times, caused that it taked too much time for everything,
and made it impossible to refactoring the code without days or weeks of work.
In this time I have learned lot of things, and since the .NET platform is fast enough, have "mono", and solves all my problems with D, it will be my final choice.

Final Words

The first emulator made by Noxa was a C# emulator too. It was for .NET 2.0. .NET and C# have evolved a lot since that 2.0 version. And now it is probably the best
platform/language out there. It has the right balance between ease, power and speed. Of course it lacks some stuff, but it is the language where less programming features I have missed.
I didn't used the pspplayer code as base because I wanted to make things with all the stuff I have learned this time and with a different approach.
I started the first version of my emulator using Noxa's pspplayer as base. I didn't managed to compile his code that time because of a mixed DLL x86/x64, and I wanted to learn to make an emulator.
I want to acknowledge all the great work of Noxa, because

The old D version of the emulator can be found here: old version.

Download page for the new C# version

2 comentarios:

  1. You can use this emulator for homebrew development. Just call the executable with the PBP or ELF file to execute as argument. Also, you will be able to see STDOUT/STDERR printf stuff on the console.

    Also, you can autorun a PSP game with this emulator, just rename the cspspemu.exe to the name of the ISO/CSO or PBP and it will run automatically without menus.
    For example, in the case you want to auto execute:

    "MyGreatPspGame.cso", just rename the cspspemu.exe to "MyGreatPspGame.exe"

    ResponderEliminar
  2. Also has some manually CWCHEAT support.

    If you have an iso called "MyGame.iso" you can create a "MyGame.iso.cwcheat", and put there cwcheat codes.
    For example, for Tales of Eternia you would put in the .cwcheat file to enable DEBUG MODE:
    _L 0x70D69F04 0x00000007

    ResponderEliminar