Skip to content

PlayStation 1 (PSX) MIPS assembly programming ... with Cargo builds.

Notifications You must be signed in to change notification settings

hornc/psx-asm-cargo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

51 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PSX assembler toolchain using Cargo-psx

This is an experiment in setting up an assembler toolchain for the original Playstation, using modern tools.

Using:

Cargo (Rust's build system) via cargo-psx + LLVM with Rust's asm_experimental_arch to write pure MIPS32el assembly for the PSX's R3000 chip.

Runner:

Inspirations:

  • https://github.com/PeterLemon/PSX (I had the idea to do revisit PSX asm, then went looking for prior and recent art, this is what I found)
  • psx-sdk-rs, although I'm bypassing all the helpful Rust by using asm! and global-asm!
  • Hitmen's Greentro!!!

Toolchain setup

(Linux x86 host specific)

Rust

Add Rust nightly to access the unstable asm-experimental-arch features: (cargo-psx may handle this for us...)

rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu

Install cargo-psx following the instructions in the README. This tool generates PSX-EXE format binaries, instead of ELF. (See https://doc.rust-lang.org/nightly/rustc/platform-support/mipsel-sony-psx.html)

Optional at this stage, the NOP example doesn't need it: Install Mednafen, a multi-system emulator. We'll be using its PSX emulation for testing, although this build output should work on other emulators and original PSX hardware (I have not yet tested). psx-sdk-rs' Mednafen integration is very smooth, and I am just using that for the moment.

New project setup (NOP)

This is how to compile a single MIPS NOP as a minimal test of the toolchain.

cargo new nop
cd nop

To set a specific project to use the asm-experimental-features,

rustup override set nightly

Add the psx dependency to Cargo.toml

[dependencies]
psx = "0.1.6"

The main reason for this appears to be to allow cargo-psx to find the psexe.ld linker script to produce PSX executables from the compiled output.

Now, lets make a minimal Rust main.rs that simply includes a MIPS assembly source file:

#![feature(asm_experimental_arch)]
#![no_std]
#![no_main]

use core::arch::global_asm;
use core::panic::PanicInfo;

#[panic_handler]
fn on_panic(_info: &PanicInfo) -> ! {
    loop {};
}

global_asm!(include_str!("main.s"));

This adds just enough to allow the use of the global_asm! macro, and sets up a panic handler, which is the minimum Rust we need to build something that works.

Continuing the minimal theme, let's create a minimal assembly source file src/main.s:

// Minimal MIPS assembly to test compilation.
.global __start
__start:
    nop

Now we can test everything is set up correctly by building with:

$ cargo psx build

Output:

   Compiling compiler_builtins v0.1.87
   Compiling core v0.0.0 (/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core)
   Compiling psx v0.1.6
warning: MIPS-I support is experimental
   Compiling rustc-std-workspace-core v1.99.0 (/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/rustc-std-workspace-core)
warning: MIPS-I support is experimental
warning: MIPS-I support is experimental
   Compiling alloc v0.0.0 (/home/user/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc)
warning: MIPS-I support is experimental
warning: MIPS-I support is experimental
   Compiling nop v0.1.0 (/home/user/psx-asm-cargo/nop)
warning: MIPS-I support is experimental
    Finished release [optimized] target(s) in 6.53s

There should now be a 4096 byte PSX executable at target/mipsel-sony-psx/release/nop.exe

$ stat target/mipsel-sony-psx/release/nop.exe
  File: target/mipsel-sony-psx/release/nop.exe
  Size: 4096      	Blocks: 8          IO Block: 4096   regular file
...

And confim the Magic number / string of the PSX executable format:

$ head -c8 target/mipsel-sony-psx/release/nop.exe
PS-X EXE

Examples

A super minimal NOP example (described above) to test the toolchain is set up correctly and works with cargo-psx.

Takes the previous NOP example and strips it back to the minimum: only uses the PSX EXE linker script from the Rust psx crate. Builds and runs with standard cargo, and also tests that the assembly source can be built directly with clang and LLVM's lld, just to confirm that we are only using the Rust build environment for convenience.

C.Horn, 2023.

Inspired by Lameguy64's graphics tutorial (in C) http://lameguy64.net/tutorials/pstutorials/chapter1/2-graphics.html , part of Lameguy64's PlayStation Programming Series.

Draw a single graphics primitive (yellow square) on a purple background.

Uses Silpheed/HITMEN's silph.inc PSX helpful asm routines, taken from the classic Greentro intro source, and converted (by me) from spASM syntax to a more standard style which is compatible with GNU as and MARS MIPS assembly.

C.Horn, ~1999.

A simple asm graphics demo using some of the Net Yaroze libps library functions, developed on an Amiga. Conversion script to get it compiling with this toolchain.

Silpheed/Hitmen, 1998.

Source conversion of the classic 1998 Greentro spASM intro into the form that compiles with this toolchain.

Most conversions made programatically using sed (see commit comments for details).

Original Hitmen PSX-EXE release available from Hitmen, along with the official source code release.

To compile and run this conversion:

cd hit-greensrc
cargo psx run

From the intro:

Now go take a look at
the source for this
little thing and get
coding!!
- Silpheed
HITMEN - Ruling in 1998!

About

PlayStation 1 (PSX) MIPS assembly programming ... with Cargo builds.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published