xpectrum for the SPMP8000
A week or so ago I have ported
xpectrum (also known as gp2xpectrum, iXpectrum, Xpectroid, and Aunt Irma
[citation needed]) to the SPMP8000 platform. There is no support for ZIP files (if built with zlib all files, even non-compressed ones, fail to load), and the scaling in full-screen mode is done in software, and not very well. Other than that, it works quite nicely, sound and virtual keyboard included. You can
download it here, source code is
at the usual place.
The A3300 is... different.
I actually wanted to fix the two issues with xpectrum before announcing it here, but I didn't find the time because I've been had. My JXD A3300 has arrived at about the same time I finished the xpectrum port, and when I tried to run the emulator on it, it didn't work! Apparently, the A3300 is not an SPMP8000 device at all, but based on some obscure MIPS controller by "
Actions Semiconductor". A little online research indicated that nobody has done any homebrewing on this platform yet. Bummer.
Not so fast!
I was unwilling to write off this purchase, though, so I went ahead and extracted the data from the encrypted firmware update image for the A3300.
(Load it into the firmware update tool, attach OllyDbg, find the largest data segment, dump it, remove some superfluous bytes, and you've got yourself an SQLite3 database file.)
As it turned out, this system is a very, very different beast, but in many ways every bit as crappy as the good old SPMP8000/eCos platform.
The Actions Semiconductor OS
The Actions Semi OS appears to be based on
uC/OS-II. It features a dynamic loader and virtual memory, but nonetheless almost all interfaces are implemented using system calls, even the GUI and libraries like SQLite. There even is a
memcpy() syscall, although most applications have the good sense not to use it. On top of that they put something that seems to borrow many conventions and architectural features from Unix/Linux, such as devices as files, the
POSIX threads API, or file name endings (".so" for dynamically loaded object and ".ko" for kernel modules), but it is clearly not derived from Linux. It's probably a custom job, either grown in-house at Actions Semi, or contracted from a third party. The latter seems more likely to me because the strings found in the binary files are almost entirely void of
Chinglish, very much unlike the SPMP8000 OS.
Running our own code
From what I could tell there is no mechanism similar to the "native game" loading on the SPMP8000 that would allow you to run third-party native applications. There is, however, an update mechanism that checks if there are new emulators on the externally accessible flash partition. To check for updates, the OS tries to load certain files (
libp1.so,
libg1.so, etc.) with
dlopen() and inspects an exported symbol to get a version number. If the number is higher than the one of the installed emulator (or there is no emulator of that type installed yet), it will copy the new file over the existing one on an internal flash partition and use it as an emulator for the corresponding types of files from now on.
The neat thing here is that
dlopen() automatically runs the code in the
.init section of the binary, so I wrote a little program ("installer") that resides entirely in the
.init section, and if you put a file called
new_libp1.so on the external flash, it will copy that file over the internal PS1 emulator, before any version numbers are being checked. Now all we need is a MIPS ELF binary that conforms to a few simple standards, and we can run your own code on this "closed" platform by choosing any file that is configured to be run by the PS1 emulator in the file browser.
Getting comfortable
Of course this is not very convenient: You don't want to copy files around and run the installer every time you want to use a different program. So I wrote another program ("launcher") tailored to the Actions Semi emulator interface that pretends to be an emulator up until the point where the emulator framework tells it what "ROM" it is supposed to play. At that point it opens that file and checks if it is an ELF binary. If it is, it
dlopen()s the file and relays all information it has previously received from the emulator framework. At this point, the new binary has hooked itself into the emulator framework and takes over. If the file to be played is not an ELF binary, the same thing happens, with the exception that it's the PlayStation emulator that is loaded. Now we can simply copy our homebrew code to the console and run it from the file browser, provided we give it a PlayStation-ey file extension;
.bin seems like the obvious choice.
Doing something useful
What we need now is a substantial demo. Since I had played around with xpectrum anyway, I just went ahead and ported that as the first homebrew program for the Actions Semiconductors platform. It works every bit as well as the SPMP8000 port, except that it's even faster if you disable throttling. Here's how to try it:
WARNING: This is alpha-quality code, use it at your own risk. It is unlikely to cause any serious damage to your system, but I am not making any promises. It may also work on devices like the JXD 3000, 5000, and M1000, but it has only been tested on my JXD A3300.
- Download installer.elf and put it on the internal flash drive as "libp1.so".
- Download launcher.elf and put it on the internal flash drive as "new_libp1.so".
- Choose the game icon in the main menu to bring up the file browser; this will trigger the installation process.
- Connect the console to your computer. What you should see is that the file new_libp1.so has been deleted, and the file backup_libp1.so (and possibly backup_libp1+.so) have been created.
- Download real_libps1.so and put it on the internal flash drive. This is the PS1 emulator that will be used from now on. (You could use the backup files created previously instead, but the emulator has to be a specific version (R1.06), so it' s better to use the downloaded one.)
- Put xpectrum in the GAME directory.
Now you should be able to launch xpectrum from the file browser.
The Code
- libactsemi: A support library that provides access to the system call interface, which includes most standard C library functions and a lot of other things, from everyday stuff like semaphores and threads to a complete GUI interface, property lists, and text encoding functions, although many of them still lack prototypes and data type definitions.
- Baselibc: A customized version of the small C library Baselibc that provides the stuff that the operating system doesn't, such as string functions. I chose it over newlib because the Actions Semi OS already provides a rather high-level interface through system calls, and it's not easy to make newlib cooperate with another C library.
- installer: The binary injection program that replaces the PS1 emulator with our own code.
- launcher: A replacement for the PS1 emulator to be installed by launcher that is able to launch both native ELF binaries and a PS1 game images.
- demo: A simple moving-bar demo that also emits a lot of random debug output. I use it to test OS features.
The xpectrum code can be found in the
actsemi branch of the SPMP8000 git repo.
To build all this goodness, you need a MIPS32 cross-toolchain. I built mine using
crosstool-ng and
this configuration file. Unfortunately, crosstool-ng doesn't pass the target CFLAGS that you set in the config file correctly when building newlib. While we don't use the newlib
libc.a, this will still cause us grief when linking with the newlib math library. I worked around this by building newlib manually with
CFLAGS_FOR_TARGET="-march=mips32r2 -G0 -Os".
Have fun!