PS2 Julian's various PS2 projects (Worklog)

Let's talk a bit about filesystems and some surrounding information about porting them...

I wrote pfsfuse, which basically allows you to mount PS2 formatted hard drives in operating systems that support the FUSE (Filesystem in USErspace) interface, and access the files like any other file system.
Compared to using in-kernel filesystems, writing a FUSE interface is an easy way to write a filesystem interface without needing to go low level or deal with the changing kernel API/ABI. However, some performance is left on the table.

How pfsfuse is basically written is it integrates iomanX, pfs, and apa drivers from ps2sdk (just like pfsshell) and the rest of the functions like the ATAD functions, threading functions, etc. are translated to the host system by a shim library.
I'd like to eventually also do the same for the memory card filesystem and the FAT filesystems in ps2sdk, so that it is possible to debug those filesystems more easily and also use code quality tools like clang-analyzer, american fuzzy lop, Valgrind, etc. to find issues like null pointer access and buffer overflows.
This same interface may also be useful for the DVRP, where its filesystem is exposed in the interface. So you can record TV and use the filesystem at the same time (compared to just raw block access, where it's only safe for one driver to access the filesystem at the same time...)

A comparison between how POSIX treats file systems and how PS2 / iomanX treats file systems...

* Where file systems are mounted
POSIX: File systems can be mounted in directories and sub-directories
iomanX: File systems can only be mounted in "XXXY:" where XXX is a variable length string and Y is an integer
* How file systems are mounted
POSIX: Filesystem can be mounted based on a block device, either automatically (e.g. fstab) or manually (mount helper program)
iomanX: Filesystem driver dependent. Some are mounted using the "mount" function, while others auto-mount when a device is plugged in.
* How block devices are exposed
POSIX: Block devices are usually exposed under /dev
iomanX: Some devices have their own block-size access devctls e.g. "hdd0:".
BDM: A callback/event based system, not exposed as a filesystem
* Where filesystem drivers are resident
POSIX: on the application processor (e.g. EE), in either kernel space or user space
iomanX: on the I/O processor (e.g. IOP), always in kernel space
* API surface area
POSIX/FUSE: 42 functions (see https://libfuse.github.io/doxygen/structfuse__operations.html )
iomanX: 27 functions (see https://github.com/ps2dev/ps2sdk/bl...3cbbd37dd21cd/iop/kernel/include/iomanX.h#L58 )
* Notable exclusions
POSIX/FUSE: "mount" function (the mount helper and filesystem driver programs are the same and should be used), "devctl" function
iomanX: copy_file_range, mmap, openat (and its series of functions)

As you can see, iomanX runs on the slower processor with less RAM, has inconsistencies on how devices/filesystems are exposed, and has a limited API surface.
Meanwhile, POSIX runs on the faster processor with more RAM, is more consistent on how devices/filesystems are exposed, and has a more complete API surface.

So in that case, after looking at these facts and the PS2 Linux kernel being worked on, I don't think it would be worth my time to not only write a FUSE-to-iomanX layer (basically what pfsfuse is doing but the roles are reversed), but debug it and iron out the issues, which would probably have only myself looking at it, but in the Linux kernel, a lot of companies are looking at the code and have even wrote automated tests for it.

Now, if I did decide to write a FUSE-to-iomanX layer, it could be useful for read-only filesystems.
GRUB supports a lot of filesystems: https://www.gnu.org/software/grub/manual/grub/grub.html#Features
It also has a FUSE interface for using those filesystems drivers in the FUSE interface: https://www.gnu.org/software/grub/manual/grub/grub.html#Invoking-grub_002dmount

I don't believe a FUSE shim layer has been writen for using Linux, FreeBSD, OpenBSD, or Windows NT kernel file system drivers.
 
I asked about this years ago on ASSEMblergames, and no-one really had an answer.

I'd love a driver for the PCMCIA Adapter for PC for use of the external drive. So basically the same as connecting an IDE drive with adapter to the PC, but you don't have to dismantle your external drive to do so.
 
I asked about this years ago on ASSEMblergames, and no-one really had an answer.

I'd love a driver for the PCMCIA Adapter for PC for use of the external drive. So basically the same as connecting an IDE drive with adapter to the PC, but you don't have to dismantle your external drive to do so.
Here's the thing… the """PCMCIA""" HDD card is not electrically compatible.

Let's compare it to a different interface, like USB-C alternate mode.

USB-C has reserved pins to carry different signals like DisplayPort and Thunderbolt. These signals are not electrically compatible with USB and require a chipset specific to those signals. When connecting a USB cable, the USB controllers will negotiate with each other to switch to e.g. DisplayPort or Thunderbolt.

Basically, the """PCMCIA""" HDD card is doing the same sort of thing here. When you plug the card into the PS2, it will start up in PCMCIA mode. However, it then negotiates to switch to a specific "SPEED" mode, which is electrically incompatible with PCMCIA and electrically incompatible with CardBus.

However, if the end goal here is to connect the external HDD to a PC without shucking it, you don't actually need the HDD card. The cable going from the HDD to the HDD card is actually just IDE electrically. In that case, you can use continuity mode on a multimeter to figure out the pinout of the cable (I'm not aware of any published pinouts of that cable), and then just make your own adapter to IDE which then can be plugged into a e.g. IDE to USB adapter.

I believe a way to make the HDD connector could be possibly make either a thin (0.? mm) PCB, or make a flex PCB with exposed contacts similar to how USB or mini-USB or USB-C connectors directly on a PCB are done. But I haven't actually investigated this in detail.
 
So, what's the deal with IOP module reimplementations?
* It can lead to smaller code size
* It can possibly run faster due to new compiler optimizations
* It can be recompiled to run natively on the PPC-IOP
* It can be used independently of the hardware/ROM (so DTL-T and COH can also be used)

Here's the current status:
CDVDFSV.irx <- already implemented, needs debugging
CDVDMAN.irx <- already implemented, needs debugging
DMACMAN.irx <- should be safe to reimplement latest version, not included in 3.1.0 IOPRP
EXCEPMAN.irx
FILEIO.irx <- large API changes
HEAPLIB.irx
IGREETING.irx <- can be stubbed
INTRMANI.irx
INTRMANP.irx <- don't need to worry about this, PS1 mode not used
IOMAN.irx <- large API additions, should be safe to leave additions
LOADCORE.irx
LOADFILE.irx <- Loadcore RPC, need to return the correct version in 0xff RPC?
MODLOAD.irx
REBOOT.irx
ROMDRV.irx <- see IOMAN
SIFCMD.irx
SIFINIT.irx
SIFMAN.irx
SSBUSC.irx
STDIO.irx
SYSCLIB.irx <- already implemented, but currently size is bigger than original 3.1.0 IRX. Need to investigate
SYSMEM.irx
THREADMAN.irx
TIMEMANI.irx
TIMEMANP.irx <- don't need to worry about this, PS1 mode not used
VBLANK.irx

---
Once UDPBD is finished, how would you use it?

There are many ways to do this. One of:
1. Attach a FAT32 formatted disk and put your games on there, then unmount the disk and run the UDPBD server with it
2. Make a FAT32 formatted raw disk image and put your games on there, then unmount the disk image and run the UDPBD server with it
3. Make a FAT32 formatted disk image (any type supported by QEMU, including VHD and VMDK) and put your games on there, then unmount the disk image and export it with qemu-nbd, mount the NBD disk, then run the UDPBD server with the mounted NBD disk
4. Put your games inside a directory (limit 4GB, split if bigger), then export it with qemu-nbd with the VVFAT disk backend, mount the NBD disk, then run the UDPBD server with the mounted NBD disk
This is complicated right now, I'd like to simplify it later

You will still need to split your ISOs and defragment them.

---

Why not exFAT?
exFAT has more chance of corruption, especially since PS2 has no power off handler without a network adapter attached.
So that means if you hit the power button at the wrong time, you have a higher chance of losing data compared to FAT32.
The Nintendo Switch scene has an issue with exFAT data corruption.
 
Last edited:
Potential method for loading IRX modules from memory without code patching LOADFILE

The current method for loading IRX files involves embedding the IRX file into your application, then using SifExecModuleBuffer on it.
However, the LOADFILE IRX module in the ROM of retail PS2s do not have the functionality, so sbv_patch_enable_lmb is used to patch code into the LOADFILE module to add that functionality.

There may be a way to do this without patching code, and just adjusting data in IOP memory. This involves the ROMDRV driver.

For protokernel systems, there is a limit of 2 mounted IOPRP ("RESET" header) images. When the ROMDRV driver is loaded, it mounts images: First one is BOOT ROM ("rom0:") while second one is DVD player ROM ("rom1:"). It does not have an API to mount or unmount additional images.
For other systems, there is a limit of 4 mounted IOPRP images. When the ROMDRV driver is loaded, it mounts only a single image: First one is BOOT ROM ("rom0:"). It does have an API to mount or unmount additional images, and that is indeed what ADDDRV, ADDROM2, and ACDEV modules use to mount additional images.

So, using this information:
1. Use SifAllocIopHeap to allocate just enough memory for RESET / ROMDIR + padding for DMAC + your IRX
2. Prepare the RESET / ROMDIR structure
3. Set SifDmaTransfer_t structures with the RESET / ROMDIR structure, and your IRX
4. Use SifWriteBackDCache on both elements to invalidate the data cache, to ensure the DMAC is fed correct data
5. Use SifSetDma to transfer the data specified by the contents of the SifDmaTransfer_t structures
6. Busy loop on SifDmaStat
7. Use SifIopSetVal to set addresses inside ROMDRV (preferably the one accessed using "rom1:") to the address allocated by SifAllocIopHeap
8. Use SifLoadStartModule to run the module mapped in "rom1:"
9. Use SifFreeIopHeap once you are done
 
I took a look at porting mbedtls and libcurl.

For mbedtls, it seems it's looking for some includes:

I replaced

Code:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

with

Code:
#include <tcpip.h>
#include <ps2ip.h>

Also, for the areas that use IPv6 (struct sockaddr_in6), I disabled those areas of code.

There are also some arbritary platform checks, which I also disabled.

mbedtls also requires a source of entropy. For now, I disabled it by uncommenting MBEDTLS_NO_PLATFORM_ENTROPY

Also, getaddrinfo / freeaddrinfo is used, but doesn't seem to be defined in ps2ip.h, so I'll need to add those definitions. Also, definitions for these are missing from the RPC server.

Extra CMake defines:
-DENABLE_PROGRAMS=OFF
-DENABLE_TESTING=OFF

For libcurl, it also has the include issue.

I replaced

Code:
#include <sys/socket.h>

with

Code:
#include <tcpip.h>
#include <ps2ip.h>

Another thing is that the compile-time checks done check for specific symbol. The check fails incorrectly, possible due to the aforementioned include issue.
Thus, checking for socket(), select() or poll() is incorrect.

Also, it seems that the check for in_addr_t fails, so it gets redefined, which is incorrect.

The last remaining definitions that are missing are basename and gethostbyname.

Extra CMake defines:
-DENABLE_THREADED_RESOLVER=OFF
-DCURL_USE_OPENSSL=OFF
-DCURL_USE_MBEDTLS=ON
-DCURL_DISABLE_SOCKETPAIR=ON
-DHAVE_BASENAME=NO
 
Last edited:
I took a look at the 5.00 Mechacon firmware.

Some tidbits:
Load at 0x0
Entry point is 0x0
Little endian ARM Thumb
SCMD handler table starting at 0x00 is at 0x33AC0
SCMD handler table starting at 0x40 (Config related) is at 0x33B5C
SCMD handler table starting at 0x80 (Security related) is at 0x33B6C
Referenced by a function at 0x128A8

My goal is to figure out what enables the power button interrupt, and see if it is possible to do so without turning on dev9.
The SCMD for controlling the LED is 0x25. Bitmasks at either 1, 2, 4, 8 will control the corresponding LEDs.
The result will be written into 0x31010C0, which I assume is memory mapped somewhere.
I assume that whatever is written to that address also controls power handling.
Such as when you press the power button while the console is off, the LED turns from red to green: first the red LED is turned off, then the green LED is turned on.
When you press the power button while the console is on, I assume there is some check going on before the LED turns from green to red and the console powers off. I'd like to find out where and what that check does.
 
I'm working on inserting names and definitions, so diffing can be done easily with Diaphora.
Since these modules have already been reversed and reimplemented, doing this isn't too difficult, but it takes a lot of time.

I already did pfs, apa, ata, and dev9 back when I reversed the DVR partition area.
I already did the DVRP set of modules.
I did hdck, hdsk, fsck, fssk from the DESR update utility disc.
I did xfromman and flash. xfromman could probably be diffed against mcman for a better starting point there.

Next up:
mcman
mcserv
padman
libsd
usbd
rmman
udnl
secrman
secrsif
mtapman
sio2man
 
Does xfromman is responsible for internal flash handling? Why they not use mcman/xmcman as it using MCFS like normal memory card?
 
Tips when updating the assembly code to the new toolchain...

* You can guard code that only works the new toolchain:
Code:
.ifdef .gasversion.
# new toolchain
.else
# old toolchain
.endif

* Vector registers e.g. "vi00" and "vf00" require the leading zeros to be removed e.g. "vi0" and "vf0" and a leading dollar to be inserted e.g. "$vi0" and "$vf0"
This is compatible with the old assembler, so you can test there.

* Special registers "ACC", "I", and "Q" need a leading dollar e.g. "$ACC", "$I", and "$Q"
This is not compatible with the old assembler, so you need to use ifdef for compatibility purposes.

* Two operand assumptions need to be changed
Example:
Code:
qfsrv   $reg0, $reg1
change to
Code:
qfsrv   $reg0, $reg0, $reg1

* Stack pointer
The stack pointer is offset by 0x8 bytes compared to the old toolchain. But you don't need to deal with this if you don't have more than 8 arguments.

* t4, t5, t6, t7
These registers have been renamed. Search "as_reg_compat.h" in ps2sdk for an example on how to obtain compatibility with these old names.

* $at handling
The new assembler is more strict on $at register handling. Add ".set at" and ".set noat" as necessarary. You may need to undef the "at" symbol if you used "as_reg_compat.h".

* Instruction aliases
Change the following:
phmaddh -> phmadh
phmsubh -> phmsbh
 
Last edited:
I'm thinking about diffing the ps2sdk binaries against the SCE binaries, instead of manually inserting the types into the SCE binaries.

I used the generic mipsel-unknown-elf 3.2.3 toolchain to compile. I turned on debug symbols using `-ggdb` and removed stripping `-s`.

At the moment, I'm having a little issue loading DWARF debug information with mcman, udnl, ps2hdd, and hdsk, so I'm going to try a newer toolchain and see if the issue persists.
 
Last edited:
Something I've wanted to do for a while is write a PSF2 player, but the IOP code is emulated on the EE.

One thing that would be nice is to port libsd to POSIX so it can be more easily debugged there.
Also, it would be nice if the IOP code can be run on a separate thread than the SPU2 code.
It would also be nice if it was possible to "mock" the SIF and DMAC code, for easier debugging.

So, steps:
* IOP and SPU2 emulation on single thread
* IOP and SPU2 emulation on separate threads <- Current work in progress is here
* IOP and SPU2 emulation on separate processes
* IOP and SPU2 emulation on separate machines
* IOP emulation on one machine and SPU2 passthrough on IOP
* IOP emulation on EE and SPU2 passthrough on IOP
 
Let's talk a little about differences between how code is generated between the old toolchain and the new toolchain, and how to translate the code between the two.

Let's start by an example.

Test C code:
Code:
int test2(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9);

int test(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9)
{
	test2(arg9, arg8, arg7, arg6, arg5, arg4, arg3, arg2, arg1, arg0);
	return 0x74;
}
New GCC 11 toolchain (N32):
Code:
test:

var_30= -0x30
var_28= -0x28
var_20= -0x20
var_1C= -0x1C
var_18= -0x18
var_14= -0x14
var_10= -0x10
var_C= -0xC
var_8= -8
var_4= -4
var_s0=  0
var_s8=  8
arg_0=  0x10
arg_8=  0x18

addiu   $sp, -0x40
sd      $ra, 0x30+var_s8($sp)
sd      $fp, 0x30+var_s0($sp)
move    $fp, $sp
sw      $a0, 0x30+var_20($fp)
sw      $a1, 0x30+var_1C($fp)
sw      $a2, 0x30+var_18($fp)
sw      $a3, 0x30+var_14($fp)
sw      $a4, 0x30+var_10($fp)
sw      $a5, 0x30+var_C($fp)
sw      $a6, 0x30+var_8($fp)
sw      $a7, 0x30+var_4($fp)
lw      $v0, 0x30+var_20($fp)
sw      $v0, 0x30+var_28($sp)
lw      $v0, 0x30+var_1C($fp)
sw      $v0, 0x30+var_30($sp)
lw      $a7, 0x30+var_18($fp)
lw      $a6, 0x30+var_14($fp)
lw      $a5, 0x30+var_10($fp)
lw      $a4, 0x30+var_C($fp)
lw      $a3, 0x30+var_8($fp)
lw      $a2, 0x30+var_4($fp)
lw      $a1, 0x30+arg_0($fp)
lw      $a0, 0x30+arg_8($fp)
jal     test2
nop
li      $v0, 0x74  # 't'
move    $sp, $fp
ld      $ra, 0x30+var_s8($sp)
ld      $fp, 0x30+var_s0($sp)
addiu   $sp, 0x40
jr      $ra
nop
Old GCC 3 toolchain (EABI):
Code:
test:

var_40= -0x40
var_38= -0x38
var_30= -0x30
var_2C= -0x2C
var_28= -0x28
var_24= -0x24
var_20= -0x20
var_1C= -0x1C
var_18= -0x18
var_14= -0x14
var_10= -0x10
var_s0=  0
arg_0=  0x10
arg_8=  0x18

addiu   $sp, -0x50
sd      $ra, 0x40+var_s0($sp)
sd      $fp, 0x40+var_10($sp)
move    $fp, $sp
sw      $a0, 0x40+var_30($fp)
sw      $a1, 0x40+var_2C($fp)
sw      $a2, 0x40+var_28($fp)
sw      $a3, 0x40+var_24($fp)
sw      $a4, 0x40+var_20($fp)
sw      $a5, 0x40+var_1C($fp)
sw      $a6, 0x40+var_18($fp)
sw      $a7, 0x40+var_14($fp)
lw      $v0, 0x40+var_2C($fp)
sw      $v0, 0x40+var_40($sp)
lw      $v0, 0x40+var_30($fp)
sw      $v0, 0x40+var_38($sp)
lw      $a0, 0x40+arg_8($fp)
lw      $a1, 0x40+arg_0($fp)
lw      $a2, 0x40+var_14($fp)
lw      $a3, 0x40+var_18($fp)
lw      $a4, 0x40+var_1C($fp)
lw      $a5, 0x40+var_20($fp)
lw      $a6, 0x40+var_24($fp)
jal     test2
lw      $a7, 0x40+var_28($fp)
li      $v0, 0x74  # 't'
move    $sp, $fp
ld      $ra, 0x40+var_s0($sp)
ld      $fp, 0x40+var_10($sp)
jr      $ra
addiu   $sp, 0x50

So, what differences could you quickly glance from this?

* stack pointer is offset by -0x10
* GCC 3 is more aggressive in using the jump delay slot

Now, let's clean up the code a bit and label it...

GCC 11:
Code:
test_new:

var_30= -0x30
var_28= -0x28
var_20= -0x20 # arg0
var_1C= -0x1C # arg1
var_18= -0x18 # arg2
var_14= -0x14 # arg3
var_10= -0x10 # arg4
var_0C= -0x0C # arg5
var_08= -0x08 # arg6
var_04= -0x04 # arg7
var_s0=  0
var_s8=  8
arg_00=  0x10 # arg8
arg_08=  0x18 # arg9

addiu   $sp, -0x40
sd      $ra, 0x30+var_s8($sp)
sd      $fp, 0x30+var_s0($sp)
move    $fp, $sp
sw      $a0, 0x30+var_20($fp)
sw      $a1, 0x30+var_1C($fp)
sw      $a2, 0x30+var_18($fp)
sw      $a3, 0x30+var_14($fp)
sw      $a4, 0x30+var_10($fp)
sw      $a5, 0x30+var_0C($fp)
sw      $a6, 0x30+var_08($fp)
sw      $a7, 0x30+var_04($fp)
lw      $v0, 0x30+var_1C($fp)
sw      $v0, 0x30+var_30($sp)
lw      $v0, 0x30+var_20($fp)
sw      $v0, 0x30+var_28($sp)
lw      $a0, 0x30+arg_08($fp)
lw      $a1, 0x30+arg_00($fp)
lw      $a2, 0x30+var_04($fp)
lw      $a3, 0x30+var_08($fp)
lw      $a4, 0x30+var_0C($fp)
lw      $a5, 0x30+var_10($fp)
lw      $a6, 0x30+var_14($fp)
lw      $a7, 0x30+var_18($fp)
jal     test2
nop
li      $v0, 0x74  # 't'
move    $sp, $fp
ld      $ra, 0x30+var_s8($sp)
ld      $fp, 0x30+var_s0($sp)
addiu   $sp, 0x40
jr      $ra
nop

GCC 3:
Code:
test_old:

var_40= -0x40
var_38= -0x38
var_30= -0x30 # arg0
var_2C= -0x2C # arg1
var_28= -0x28 # arg2
var_24= -0x24 # arg3
var_20= -0x20 # arg4
var_1C= -0x1C # arg5
var_18= -0x18 # arg6
var_14= -0x14 # arg7
var_10= -0x10
var_s0=  0
arg_00=  0x10 # arg8
arg_08=  0x18 # arg9

addiu   $sp, -0x50
sd      $ra, 0x40+var_s0($sp)
sd      $fp, 0x40+var_10($sp)
move    $fp, $sp
sw      $a0, 0x40+var_30($fp)
sw      $a1, 0x40+var_2C($fp)
sw      $a2, 0x40+var_28($fp)
sw      $a3, 0x40+var_24($fp)
sw      $a4, 0x40+var_20($fp)
sw      $a5, 0x40+var_1C($fp)
sw      $a6, 0x40+var_18($fp)
sw      $a7, 0x40+var_14($fp)
lw      $v0, 0x40+var_2C($fp)
sw      $v0, 0x40+var_40($sp)
lw      $v0, 0x40+var_30($fp)
sw      $v0, 0x40+var_38($sp)
lw      $a0, 0x40+arg_08($fp)
lw      $a1, 0x40+arg_00($fp)
lw      $a2, 0x40+var_14($fp)
lw      $a3, 0x40+var_18($fp)
lw      $a4, 0x40+var_1C($fp)
lw      $a5, 0x40+var_20($fp)
lw      $a6, 0x40+var_24($fp)
lw      $a7, 0x40+var_28($fp)
jal     test2
nop
li      $v0, 0x74  # 't'
move    $sp, $fp
ld      $ra, 0x40+var_s0($sp)
ld      $fp, 0x40+var_10($sp)
addiu   $sp, 0x50
jr      $ra
nop

So, with this labeled, there are some more differences we can make out…

* $ra gets stored in different places (GCC 11: +0x08 / GCC 3: +0x00)
* $fp gets stored in different places (GCC 11: +0x00 / GCC 3: -0x10)

So summming this up... The callling convention is mostly compatible when mixing EABI64 and N32 and using integers / pointers. (Floats not checked)
Mix and match all you want.
However, debuggers are not going to work really well when doing that due to differences in how $ra and $fp are stored.

So, in order to fix that in assembly (EABI64 to N32):
* Add +0x08 to where $ra gets stored
* Add +0x10 to where $fp gets stored
 
I mostly fixed Simple Media System to compile on the new toolchain. It compiles, but doesn't work properly (black screen).

I guess I'll take a look at it again once I finished my debugging tools...

For reference, here is how to encode MPEG 4 videos for playing with Simple Media System with ffmpeg: https://trac.ffmpeg.org/wiki/Encode/MPEG-4

---

It seems like there is an archive of Mercurial projects that were removed from Bitbucket once they deprecated it. Here is some relevant ones:

https://bitbucket-archive.softwareheritage.org/projects/if/ifcaro/open-ps2-loader.html
https://bitbucket-archive.softwareh...n-ps2-loader-0.9.3-documentation-project.html
 
The following libraries have issues with mx4sio and mcemu, due to moving the MC transfer function from sio2man to their respective libraries:
Code:
mc2_d.irx    3100
mc2_s1.irx   3030
mc2_s1.irx   3100
mcman.irx    3030
mcman.irx    3031
mcman.irx    3100

One thing that would be nice is creating a "universal" mcman/padman/libmc2/libpad2…

Also, another thing to note is MCMAN in the 220a ROM is has a version of 1631. Lower versions forgot to fill in the version number (they are 4 periods)
 
Last edited:
I've managed to get my implementation of sysclib down to 9,208 bytes, compared to 10,045 bytes in the 3.0.0 original.

However, this may affect performance, so I plan to add multiple configurations of sysclib. One uses the assembly versions of memcpy/memset etc. while the other uses the C versions which are smaller.
 
I wonder how "Remote Control" "Gameplay Function On" actually works… Does Mechacon inject commands into sio2?

---

I managed to get unpatched cURL compiling with a modified ps2sdk. For now, mbedtls needs to be patched (hardcoded entropy source, disable ipv6, platform ifdef checks).

These changes will be upstreamed later.
 

Similar threads

Back
Top