Rust on Dreamcast: Difference between revisions

From dreamcast.wiki
Jump to navigation Jump to search
 
(61 intermediate revisions by the same user not shown)
Line 1: Line 1:
[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]
[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]


'''WIP''': This article is currently under construction. The repos linked to below are not yet live.
'''Rust''' is a popular modern programming language focusing on performance, memory safety, and program correctness. As a "blazingly fast" low-level language, it is viable for running on a Dreamcast console. Doing so presents a bit of a challenge, however, as the official Rust compiler is based on the [https://llvm.org/ LLVM] toolchain infrastructure, which does not support the Dreamcast CPU's SuperH architecture. Dreamcast programming is instead typically done with [https://gcc.gnu.org/ GCC], the GNU Compiler Collection. There are currently two viable solutions to this challenge:


'''Rust''' is a systems programming language rising in popularity which emphasizes memory safety and performance. Due to its operating at a low level, it is an ideal candidate for running on the Dreamcast. Doing so presents a bit of a challenge, however, as the official Rust compiler is based on the [https://llvm.org/ LLVM] toolchain infrastructure, which does not support the Dreamcast CPU's SuperH architecture. Dreamcast programming is instead typically done with [https://gcc.gnu.org/ GCC], the GNU Compiler Collection. There are currently two viable solutions to this challenge:
* '''rustc_codegen_gcc''', the current preferred method: An experimental codegen backend for the official Rust '''rustc''' compiler which interfaces with GCC's libgccjit API
* '''gccrs''': an experimental Rust frontend for GCC


* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)
Neither solution is complete at this time, but '''rustc_codegen_gcc''' is much further along and is quite usable with some patience with its current limitations. On the other hand, '''gccrs''' can compile for Dreamcast, but is in an earlier state. Below we will focus on using '''rustc_codegen_gcc'''. For more information on using gccrs, see the [[gccrs]] page.
* '''gccrs''': a Rust frontend for GCC


Neither solution is complete at this time, and both are under active development. Using either of them to target the Dreamcast should be considered experimental. '''rustc_codegen_gcc''' is quite further along, however, and is quite usable with some patience with its current limitations and rapid change. On the other hand, while '''gccrs''' can compile for Dreamcast, it is in a very early stage, with much of the language unimplemented and no '''libcore''' support. Below we will focus on using '''rustc_codegen_gcc'''. For more information on using '''gccrs''', see the [[gccrs]] page.
=Current Status=
* The full '''std''' library is supported.
* A [https://github.com/dreamcast-rs/KallistiOS custom version of KallistiOS] v2.1.0 (latest stable release) is used.
** This version includes minor changes for Rust and a beta libpthread addon layer to interface with the Rust standard library.
** Wrappers are provided to invoke '''cargo''' and '''rustc''' in a familiar manner to compile projects.
* A [https://github.com/dreamcast-rs/kos-sys '''kos-sys'''] raw/unsafe bindings crate is provided for easily linking KallistiOS and its unsafe C API into Rust crates. This crate is in active development, but with a large portion of KallistiOS functionality available now via C FFI.
* A [https://github.com/dreamcast-rs/kos-rs '''kos-rs'''] Rust crate with safe idiomatic interfaces is available but is in very early stages.
** Documentation for the bindings is available at [https://kos-rs.dreamcast.wiki/ kos-rs.dreamcast.wiki].
** If bindings do not yet exist for desired functionality, wrapping of KallistiOS C functions is required to get proper use of KallistiOS at this time. See the [https://doc.rust-lang.org/nomicon/ffi.html Foreign Function Interface page] in the Rustonomicon for further understanding.
** A custom [https://github.com/dreamcast-rs/libc/tree/libc-0.2-kos '''libc''' crate with KallistiOS support] is provided.
* Code generation uses '''rustc_codegen_gcc''', so its limitations apply here. See the [https://blog.antoyo.xyz/ development blog] for more information on its progress.
** rustc_codegen_gcc is pinned to a particular Rust nightly version, but is synced regularly. The current version in use is the '''2024-08-10''' nightly.
** A [https://github.com/dreamcast-rs/rust/tree/kos-2024-08-10 KallistiOS-patched version of the latest nightly] is maintained and updated regularly.
** Panic unwinding, debug info, LTO, etc. are not currently available.
** Architectures dependent on rustc_codegen_gcc are not yet able to be added to the rustc frontend, so a workaround is necessary: MIPS is falsely selected for KallistiOS, causing rustc to emit objects with a MIPS header and a provided wrapper is used when linking to rewrite these MIPS headers to SuperH before passing the compiled objects to the linker.
* A separate [https://github.com/rust-lang/gcc development version of the GCC toolchain] must be used.
** This version of GCC contains the latest libgccjit patches necessary to interface with rustc_codegen_gcc, and will be built to work with the custom KallistiOS's libpthread layer.
** New updates to rustc_codegen_gcc often require new updates to GCC, so recompiling GCC regularly will be necessary to keep up with the latest updates.
** The development version of GCC does not always compile well on every platform. Using a modern Linux platform is recommended for maximum compatibility.
** The custom KallistiOS and GCC environment can still be used to compile code or projects written in C, C++, etc.
* KallistiOS and all related libraries and tools must be compiled using the <code>-m4-single</code> ABI. This is already set up for you in the guide below, but if external libraries are being linked to Rust projects, make sure that code is compiled using the proper ABI.


=Using rustc_codegen_gcc to develop on Dreamcast=
=Setting up a Rust environment for Dreamcast development=
With [https://github.com/rust-lang/rustc_codegen_gcc rustc_codegen_gcc], we can interface the standard '''rustc''' compiler frontend with '''libgccjit''', a GCC code-generation API. With the help of the [https://github.com/darcagn/rust-for-dreamcast '''Rust-for-Dreamcast''' repo] and the [https://github.com/darcagn/kos-rs '''kos-rs''' crate] containing [[KallistiOS]] bindings, we can set up '''rustc_codegen_gcc''' to compile Rust programs with [https://doc.rust-lang.org/core/ '''core'''] and [https://doc.rust-lang.org/alloc/ '''alloc'''] support (but not the entirety of [https://doc.rust-lang.org/std/ '''std''']). Rust-for-Dreamcast includes wrapper scripts to invoke the rustc and '''cargo''' tools in a familiar way. The familiar borrow checker still works, and one can import and use <code>no_std</code> crates. Despite this support, '''rustc_codegen_gcc''' is still in active development, so if using such a setup, expect that things may change rapidly over time. We will need to use some patches and workarounds to make this solution work. See the rustc_codegen_gcc [https://blog.antoyo.xyz/ progress reports] for more information on the project's progress.
The following guide will provide instructions for setting up a Rust development environment for Dreamcast.  


'''What Works'''
''If you run into any errors or other challenges while following this tutorial, or simply need clarification on any of the steps, feel free to ask for assistance on the [https://dcemulation.org/phpBB/viewforum.php?f=29 message board] or [https://discord.gg/wDEfkUh63h our Discord server] and we would be happy to aid you and update the guide for the benefit of future readers and others in the community.''
* '''libcore''' -- the core components of the language for running on bare metal (basics like integers, floats, enums, bools, chars, tuples, arrays, slices, closures, iterators, etc.)
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''
* including <code>no_std</code> crates with the <code>cargo</code> build system
'''Future Goals'''
* libc support -- Adding KallistiOS support to Rust's libc crate
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS
* Inclusion as a tier 3 target officially
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images


==Prerequisites==
==Prerequisites==
First, we will need to set up the prerequisites.


We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:
* You must have the [https://rustup.rs/ rustup] tool installed for your operating system to manage Rust toolchain installations. It will automatically manage the latest Rust nightly installations for you. You cannot use a Rust toolchain that may be provided by your operating system vendor.
* You must already have a KallistiOS development environment set up. This means you have installed the typical dependencies, you have created a cross-compiling toolchain for SH4, you have set up your KallistiOS <code>environ.sh</code> file, and you have built KallistiOS with it. Ideally, you will already have at least some familiarity with KallistiOS dev already. See [[Getting Started with Dreamcast development]] for more information, as well as the [https://kos-docs.dreamcast.wiki/ KallistiOS Doxygen].
* Install the dependency packages for your operating system. These are listed in the '''Dependencies''' section on the [[Getting Started with Dreamcast development#Dependencies]] page, with lists provided for most common operating systems.
** For the purposes of this guide, we will assume you are using the standard paths for Dreamcast development tools; i.e. your environment is set up in  <code>/opt/toolchains/dc</code>. Some included scripts and examples may assume this.
* We will install our custom Rust toolchain environment within <code>/opt/toolchains/dc/rust</code>. If it does not already exist, create the <code>/opt/toolchains/dc</code> directory and grant it proper permissions using the following commands.  
** Your KallistiOS installation will need its <code>KOS_SH4_PRECISION</code> setting set to <code>-m4-single</code>. At this time, rustc_codegen_gcc support will not compile with KallistiOS's default <code>-m4-single-only</code> setting. This setting can be changed in KallistiOS's <code>environ.sh</code>, but changing the setting may require you to rebuild your toolchain if you have not built it with <code>m4-single</code> support (which is off by default, but can be enabled in the <code>config.mk</code> file). Once you modify the setting in your <code>environ.sh</code> and re-source the <code>environ.sh</code>, you'll need to rebuild KallistiOS with a <code>make clean</code> and <code>make</code> for the changes to take effect. '''kos-ports''' being used will also need rebuilding with <code>-m4-single</code>. Keep in mind, however, that because KallistiOS doesn't officially support <code>-m4-single</code> yet, some things may be broken, especially libraries in kos-ports that haven't been heavily tested with this setting.
sudo mkdir -p /opt/toolchains/dc
* You must already have a relatively up-to-date Rust installation, either using your operating system's package manager or [https://rustup.rs/ rustup]. Ideally, you will already have some familiarity with Rust's tools.
sudo chmod -R 755 /opt/toolchains/dc
sudo chown -R $(id -u):$(id -g) /opt/toolchains/dc
* Clone the [https://github.com/dreamcast-rs/rust-for-dreamcast/ Rust for Dreamcast] repo containing necessary support files to <code>/opt/toolchains/dc/rust</code>:
git clone https://github.com/dreamcast-rs/rust-for-dreamcast.git /opt/toolchains/dc/rust


''If you run into any errors or other challenges while following this tutorial, or simply need clarification on any of the steps, feel free to ask for assistance on the [https://dcemulation.org/phpBB/viewforum.php?f=29 message board] and we would be happy to aid you and update the guide for the benefit of future readers and others in the community.''
==Installing our custom KallistiOS and GCC toolchain==
Clone the git repository for our custom Rust-patched version of KallistiOS v2.1.0 stable:
git clone https://github.com/dreamcast-rs/KallistiOS /opt/toolchains/dc/rust/kos


==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==
Enter the dc-chain directory:
Before we can use '''rustc_codegen_gcc''', we must compile <code>libgccjit.so</code>, the '''libgccjit''' library, for your system. This entails building a unique copy of the SH4 toolchain in its own directory under <code>/opt/toolchains/dc/rust</code>, using a forked version of GCC with enhancements made to '''libgccjit'''. The forked version is based on the latest GCC 14.0.1 development branch.
cd /opt/toolchains/dc/rust/kos/utils/dc-chain
* '''NOTE''': This forked version of GCC 14.0.1 with libgccjit changes is actively developed alongside rustc_codegen_gcc itself, so if you update your rustc_codegen_gcc installation, you may also need to rebuild libgccjit to pull down changes rustc_codegen_gcc depends upon.


We will first clone the <code>rust-for-dreamcast</code> repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the <code>rust-for-dreamcast</code> repository to <code>/opt/toolchains/dc/rust</code>:
The <code>Makefile.default.cfg</code> file in this directory has been customized for you, selecting the proper toolchain profile, paths, and build options necessary for building a Rust toolchain using this guide. It is not recommended to adjust the defaults.
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust
Enter your KallistiOS installation's <code>dc-chain</code> directory:
cd /opt/toolchains/dc/kos/utils/dc-chain
Clear out any existing build files:
make clean-keep-archives
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/
Copy the '''rustc_codegen_gcc''' configuration file into place:
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk
Make any desired changes to this <code>config.mk</code> configuration file (e.g., change <code>makejobs=-j2</code> to the number of CPU threads you'd like to use during compilation), and then compile the SH4 toolchain:
make build-sh4
When this command is completed successfully, a new SH4 cross-compiler toolchain will exist at <code>/opt/toolchains/dc/rust/sh-elf</code> and your  <code>libgccjit.so</code> will be installed to <code>/opt/toolchains/dc/rust/sh-elf/lib/libgccjit.so</code>.


==Building rustc_codegen_gcc==
To build the GCC toolchain using 2 CPU cores, simply type:
Clone the '''rustc_codegen_gcc''' to your rust directory:
  make makejobs=2
  git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc
Adjust the <code>makejobs=...</code> value to use the number of CPU cores available to you on your system in order to speed up compilation. This may take a while to build, so be patient.
'''rustc_codegen_gcc''' needs a <code>config.toml</code> file that specifies the location of <code>libgccjit.so</code>. Let's write the the <code>gcc-path</code> to the location of our <code>libgccjit.so</code> library file in this file:
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml
The <code>rust-for-dreamcast</code> repository contains scripts and wrappers to assist you in building '''rustc_codegen_gcc''' and using it in conjunction with <code>cargo</code> and <code>rustc</code>. We'll need to add the path to those scripts to our <code>PATH</code> environment variable:
export PATH="/opt/toolchains/dc/rust/bin:$PATH"
You may also want to add the above lines to your shell's startup file or else you'll need to run them every time you open a new shell.


Now we can use the included scripts to set up '''rustc_codegen_gcc'''. Various patches need to be applied to '''rustc_codegen_gcc''' for it to compile properly for our target platform. Let's apply them:
Once the GCC toolchain is built, source the <code>environ.sh</code> KallistiOS environment settings. A custom <code>environ.sh</code> file has been provided for you, containing settings pre-adjusted for Rust development using this guide.
rcg-dc patch
Now we can prepare and build '''rustc_codegen_gcc'''!
rcg-dc prepare
rcg-dc build


==Using Rust for Dreamcast==
You will need to run the source command to apply the KallistiOS environment settings to your currently running shell. Run the following now, '''and''' ''whenever'' you open a new shell to work on Dreamcast projects:
If all went well, rustc_codegen_gcc will have built successfully.
source /opt/toolchains/dc/rust/misc/environ.sh


You can now use the scripts included in the Rust for Dreamcast repo:
Enter the KallistiOS directory:
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it
cd /opt/toolchains/dc/rust/kos
* <code>rustc-dc</code> script can be used to compile Rust modules
* <code>cargo-dc</code> script can be used to build Rust crates


Examples are included with the Rust for Dreamcast repo to help you get started:
Build KallistiOS:
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code>
make
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project


These examples rely on the [https://github.com/darcagn/kos-rs '''kos-rs'''] crate being present on your computer locally. This is in a separate repo, so let's pull it down now:
==Building rustc_codegen_gcc and Rust sysroot==
  git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs
Now that we have a working KallistiOS environment suitable for Rust development set up, we can build the Rust compiler compiler and sysroot components. A script is provided which will download the necessary components from the respective repositories and compile them for you. Run the installer script:
 
/opt/toolchains/dc/rust/misc/install-rust.sh
 
If all goes well, you'll see '''Rust for KallistiOS/Dreamcast installed!''' message!
 
==Building KOS ports libraries==
If desired, you may also built the KOS ports collection of libraries for use with your Rust environment. It is recommended to maintain a separate KOS ports installation for linking with Rust projects in <code>/opt/toolchains/dc/rust/kos-ports</code>. This is because the Rust environment is using the <code>-m4-single</code> ABI, but the KallistiOS default installation will be using the <code>-m4-single-only</code>, which cannot be mixed and matched.
 
Clone the kos-ports repository to your system:
  git clone https://github.com/KallistiOS/kos-ports /opt/toolchains/dc/rust/kos-ports
An individual port can be built and installed by entering its directory and running the proper command. For example, installing GLdc:
cd /opt/toolchains/dc/rust/kos-ports/libGL
make install
Or, instead, you may run the script to build all of the included ports:
/opt/toolchains/dc/rust/kos-ports/utils/build-all.sh
 
=Using Rust for Dreamcast=
If all went well, you will now have a working Rust for Dreamcast environment!
 
* '''cargo''' can be invoked to target Dreamcast using the <code>kos-cargo</code> command.
** <code>kos-cargo run</code> can be used to build and run code on a Dreamcast console using [[dcload-ip]] or [[dcload-serial]]. You will need to set up the <code>KOS_LOADER</code> variable with the necessary command in the <code>/opt/toolchains/dc/rust/misc/environ.sh</code> file (and then re-source the file for the change to take effect).
* '''rustc''' can be invoked to target Dreamcast using the <code>kos-rustc</code> command.
 
==Examples==
Examples are included with the Rust for Dreamcast repo to help you get started.
* [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/hello-cargo <code>hello-cargo</code>] demonstrates how to create a simple "Hello, world!" application using cargo.
* [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/hello-make <code>hello-make</code>] demonstrates how to create a simple "Hello, world!" application using standard Makefiles.
* [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/gldc-cube <code>gldc-cube</code>] demonstrates a Rust project using the GLdc KOS port.
* [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/addlib <code>addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project.
* [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/atomics <code>atomics</code>] demonstrates the use of atomic functions.
* [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/filesystem-io <code>filesystem-io</code>] demonstrates listing directory contents and opening files.
* [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/network-time <code>network-time</code>] demonstrates displaying the system time and retrieving the current UTC time from an NTP server using the [[Broadband adapter]] or [[LAN adapter]].
* [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/pvr_strips_direct <code>pvr_strips_direct</code>] demonstrates the use of PVR direct rendering
* [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/threads <code>threads</code>] demonstrates the use of threads.
* [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/tokio-async <code>tokio-async</code>] demonstrates the use of the '''tokio''' async runtime.


==Creating a new Rust project with Cargo==
==Creating a new Rust project with Cargo==
We'll demonstrate creating a new "Hello, world!" project with <code>cargo</code>. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] example included in the <code>rust-for-dreamcast</code> repo.  
First, we'll demonstrate creating a new "Hello, world!" project with <code>kos-cargo</code>. This will follow the [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/hello-cargo <code>cargo-hello</code>] example included in the Rust-for-Dreamcast repo.  


In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:
In a directory of your choosing, let's invoke <code>kos-cargo</code> to create a new project and then enter the directory:
  cargo-dc new hellow
  kos-cargo new hello
  cd hellow
  cd hello


Let's add our '''kos-rs''' crate to gain access to current KallistiOS bindings. Open <code>Cargo.toml</code> in your text editor and add:
Let's add our kos-rs crate to gain access to current KallistiOS bindings. Open <code>Cargo.toml</code> in your text editor and add:
<syntaxhighlight lang="toml">
<syntaxhighlight lang="toml">
[dependencies]
[dependencies]
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }
kos = { package = "kos-rs", git = "https://github.com/dreamcast-rs/kos-rs" }</syntaxhighlight>
</syntaxhighlight>
 
Next, we'll need to let Cargo know about our custom link wrapper script. Create a <code>.cargo</code> directory, and within it, a new <code>config</code> file:
mkdir .cargo
touch .cargo/config
 
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:
<syntaxhighlight lang="toml">
[target.sh-elf]
linker = "sh-link-wrapper"
</syntaxhighlight>


Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:
<syntaxhighlight lang="rust" line>
<syntaxhighlight lang="rust" line>
#![no_std]
fn main() {
#![no_main]
     println!("Hello, world from Rust!");
extern crate alloc;
use kos::println;
 
#[no_mangle]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
     println!("Hello, world!");
    return 0;
}
}
</syntaxhighlight>
</syntaxhighlight>


* <syntaxhighlight lang="rust" inline>#![no_std]</syntaxhighlight> and <syntaxhighlight lang="rust" inline>#![no_main]</syntaxhighlight> tell Rust that our project does not use the standard library and we will not have Rust use a <code>main</code> function as an entry point -- this will be handled by KallistiOS.
Now we can use <code>kos-cargo build</code> to build our project. If all goes well, there will be a <code>target/sh-elf/debug/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.
* <syntaxhighlight lang="rust" inline>extern crate alloc;</syntaxhighlight> tells Rust to use the alloc crate to gain access to heap-allocated types (in our case, <code>String</code>).
If you have <code>KOS_LOADER</code> set in the <code>environ.sh</code> file, you can invoke it directly with <code>kos-cargo run</code>.
* <syntaxhighlight lang="rust" inline>use kos::println!;</syntaxhighlight> tells Rust to use the <code>println!</code> macro defined in the kos-rs crate. With this, we can print output to our <code>dc-tool</code> console.
* <syntaxhighlight lang="rust" inline>#[no_mangle]</syntaxhighlight> tells Rust to disable name mangling so that the <code>main</code> function can be used by KallistiOS.
* Finally, we have a <syntaxhighlight lang="rust" inline>fn main</syntaxhighlight> with the function signature of a typical C <code>main</code> function, containing a basic "Hello, world!" exclamation.
 
Now we can use <code>cargo-dc build</code> to build our project. If all goes well, there will be a <code>target/sh-elf/debug/hellow.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.


==Creating a Rust project using kos-ports libraries==
==Creating a Rust project using kos-ports libraries==
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the <code>rust-for-dreamcast</code> repo demonstrates creating a rotating 3D cube using Rust as the primary language, while calling C functions provided by the '''GLdc''' library available in kos-ports. This project's initial setup is done the same as the above <code>cargo-hello</code> example.
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]
 
The [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/gldc-cube <code>gldc-cube</code>] example demonstrates creating a rotating 3D cube using Rust as the primary language, while calling C functions provided by the '''GLdc''' library available in kos-ports. This project's initial setup is done the same as the above <code>hello</code> example.
'' '''NOTE''': There is a currently a bug in GLdc when using <code>-m4-single</code>, which is required when using Rust. A [https://gitlab.com/simulant/GLdc/-/merge_requests/114 merge request] has been submitted upstream for the issue. In the mean time, the cube will not appear correctly unless you apply the small changes shown in the merge request and rebuild the GLdc kos-port. To do this, change directory to <code>$KOS_PORTS/libGL</code>, run <code>make uninstall clean</code>, then run <code>make fetch</code> to retrieve fresh sources. Open <code>dist/libGL-1.0.0/GL/matrix.c</code> in your text editor and [https://gitlab.com/simulant/GLdc/-/merge_requests/114/diffs make these changes], then run <code>make install</code> to compile and install the fixed GLdc.''


We'll be using the '''GLdc''' graphics and '''libm''' math libraries, so we need to tell <code>cargo-dc</code> to link them in. To do this, we'll edit our <code>build.rs</code> file to add:
For this example, we'll need to convert JPEG textures to VQ-encoded textures at compile time using the <code>vqenc</code> utility includes with KallistiOS. To do this, we'll add a <code>build.rs</code> file to the root of the crate with the following code (abridged to show the conversion of only one texture):
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
println!("cargo:rustc-link-lib=GL");
fn main() {
println!("cargo:rustc-link-lib=m");
    let kos_base = std::env::var("KOS_BASE").expect("Missing $KOS_BASE -- KallistiOS environment not sourced!");
    let vqenc_cmd = kos_base + "/utils/vqenc/vqenc";
    Command::new(&vqenc_cmd)
        .arg("-t")
        .arg("-v")
        .arg("rsrc/tex_claw.jpg")
        .output()
        .expect("vqenc on tex_claw.jpg failed!");
}
</syntaxhighlight>
</syntaxhighlight>
The texture files are then included in our project using the <syntaxhighlight lang="rust" inline>include_bytes!</syntaxhighlight> macro.


We don't need to add the paths to these libraries, because this is already done for us in the kos-rs crate. However, if in your project you need to include a separate unique library path, you can do that like so:
We will need to link the GLdc library in this example, but we don't need to do anything special to add the paths to this library, because adding the common paths to KallistiOS libraries is already done for us in the kos-rs crate. However, if in your project you need to include a separate unique library path, you can add it in your <code>build.rs</code> like so:
<syntaxhighlight lang="rust">
<syntaxhighlight lang="rust">
println!("cargo:rustc-link-search=native={}", lib_path);
println!("cargo:rustc-link-search=native={}", lib_path);
</syntaxhighlight>
</syntaxhighlight>


While not shown here, our <code>build.rs</code> also demonstrates how to use a build script to convert JPG images to VQ-compressed textures with the <code>vqenc</code> tool included with KallistiOS. These texture files are then included in our project using the <syntaxhighlight lang="rust" inline>include_bytes!</syntaxhighlight> macro.
The workings of this example's source code are too great to detail here line-by-line, but the example demonstrates how to use GLdc via the [https://github.com/dreamcast-rs/gldc-sys gldc-sys] crate, which contains unsafe/raw bindings to GLdc. Check out this crate's source code for an example of declaring and binding external C functions, constants, and structures so they can be used in Rust code. Since the entirety of the gldc-cube example uses C FFI functions without safe wrappers, the <code>main()</code> source is wrapped in <code>unsafe {}</code>. In the future, this would be much less necessary as higher level safe Rust bindings to KallistiOS and other libraries become mature.


The workings of this example's source code are too great to detail here line-by-line, but the example demonstrates declaring and binding external C functions, constants, and structures and then using them in Rust code. Since the entirety of the example is C interop, the <code>main()</code> source is wrapped in <code>unsafe {}</code>. In the future, this would be much less necessary as higher level safe Rust bindings to KallistiOS and other libraries become mature.
==Creating a Rust library==
Next, we'll demonstrate creating a Rust library with <code>kos-cargo</code> that can be included in other Dreamcast code. This will follow the [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/addlib <code>addlib</code>] example. Once again, this project's initial setup is done the same as the above <code>hello</code> example, but you'll create the new project using <code>kos-cargo new --lib addlib</code> to specify that we're creating a library named <code>addlib</code>. You'll also need to add the following text to this project's <code>Cargo.toml</code> file:


==Creating a Rust library for Dreamcast==
<syntaxhighlight lang="toml">
* This will follow the <code>cargo-addlib</code> example
[lib]
crate-type = ["staticlib"]
</syntaxhighlight>
 
This tells Rust to build a static <code>.a</code> library archive file from our code, which is located in <code>src/lib.rs</code>:
 
<syntaxhighlight lang="rust" line>
#[no_mangle]
pub extern "C" fn print_added(a: isize, b: isize) {
    print!("{}", a + b);
}


==Compiling individual modules into object files with rustc==
#[no_mangle]
* This will follow the <code>rustc-hello</code> example
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {
To incorporate Rust source files into a standard KallistiOS <code>Makefile</code>-based project, you can use the <code>rustc-dc</code> wrapper. If we assume the Rust module file is named <code>example.rs</code>, you'll need to add <code>example.o</code> as an object file in your <code>Makefile</code>'s <code>OBJS =</code> declaration. Additionally, you'll need to add the following lines so that <code>make</code> knows how to compile Rust modules into <code>.o</code> object files:
    a + b
<syntaxhighlight lang="make">
}
%.o: %.rs
rustc-dc $< -o $@
</syntaxhighlight>
</syntaxhighlight>
Alternatively, you can add those lines to your KallistiOS <code>Makefile.rules</code> file to avoid having to place it in every project's <code>Makefile</code>.


An example "Hello, world!" program built in this style which also demonstrates basic C interoperation is included with the Rust-for-Dreamcast repository, located at [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>examples/rustc-hello</code>].
The source code here starts similarly to the "Hello, world!" example, except we don't need to specify <syntaxhighlight lang="rust" inline>#![no_main]</syntaxhighlight> as this is a library which wouldn't have a <code>main()</code> function anyway.
 
Two simple functions are provided: one for adding two integers and returning the result, and another for adding two integers and printing the result as text. Because these functions use <syntaxhighlight lang="rust" inline>#[no_mangle]</syntaxhighlight> and are declared <syntaxhighlight lang="rust" inline>extern "C"</syntaxhighlight>, they can be called by name in C code that links this library.


==Creating a new project using Cargo==
When built using <code>kos-cargo build</code>, a <code>target/sh-elf/debug/libaddlib.a</code> file will be generated. This can be linked into other projects to gain the use of these functions.
<code>cargo-dc</code> simplifies invoking <code>cargo</code> and creating Dreamcast crates. When using <code>cargo</code> in this setup, we will need to compile our program and all crate code into a static library <code>.a</code> file, and link it with a KallistiOS trampoline function to start the Rust code. Your Rust code will start with the function you specify as <code>rust_main()</code>. Once you <code>cargo-dc build</code> your Dreamcast code into a <code>.a</code> file, use <code>cargo-dc link</code> to automatically link it with this KallistiOS trampoline function and generate an ELF file. Instructions to do this follow.


First, let's clone the [https://github.com/darcagn/kos-rs '''kos-rs'''] repo:
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs
<syntaxhighlight lang="make">
Create a new crate using <code>cargo-dc</code>:
$(TARGET): $(OBJS)
cargo-dc new example --lib
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib
Change the crate to a static library in <code>Cargo.toml</code> by changing the <code>crate-type</code> as follows:
<syntaxhighlight lang="toml">
crate-type = ["staticlib"]
</syntaxhighlight>
</syntaxhighlight>
Add the '''kos-rs''' crate to your <code>Cargo.toml</code> file:
 
<syntaxhighlight lang="toml">
Then, we can use the code in our C source:
[dependencies]
<syntaxhighlight lang="c">
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }
/* Declare the external function from the Rust library */
int add_integers(int a, int b);
 
/* Use the function */
printf("Five plus six is %d\n", add_integers(5, 6));
</syntaxhighlight>
</syntaxhighlight>


Add the following function to your crate's <code>src/lib.rs</code> file:
==Compiling individual modules into object files with rustc==
<syntaxhighlight lang="rust">
If we'd like to mix C and Rust code in the same <code>Makefile</code>-based KallistiOS project without building an entirely separate library, we can do that as well. This is demonstrated in the [https://github.com/dreamcast-rs/rust-for-dreamcast/tree/master/examples/no_std/hello-make <code>hello-make</code>] example.
#[no_mangle]
 
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {
Instead of using <code>kos-cargo</code>, we can invoke the <code>kos-rustc</code> via a <code>Makefile</code> to build Rust modules. If we assume the Rust file is named <code>example.rs</code>, you'll need to add <code>example.o</code> as an object file in your <code>Makefile</code>'s <code>OBJS =</code> declaration. For example, if the project has two source files <code>hello_c.c</code> and <code>hello_rust.rs</code>, our <code>Makefile</code> would have a line like this:
    [...]
<syntaxhighlight lang="make">
    return 0;
OBJS = hello_c.o hello_rust.o
}
</syntaxhighlight>
</syntaxhighlight>
The <code>rust_main()</code> function will serve as the entry point to your Rust code.


An example "Hello, world!" style program built using '''kos-rs''' and <code>cargo-dc</code> is included with the Rust-for-Dreamcast repository, located at [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>examples/cargo-hello</code>]. Type <code>cargo-dc build</code> to build the project, then <code>cargo-dc link</code> to link against KallistiOS and generate a <code>cargo-hello.elf</code>. Make sure you have your KallistiOS <code>environ.sh</code> sourced in your terminal before running the link command.
The example code demonstrates starting a C <code>main()</code> function to call a Rust function which builds a <code>String</code> containing the "Hello, world!" text which is passed back to a C function which prints <code>String</code>s.
 
==Using the libc crate with KallistiOS==
If any of the crates in your project's dependency tree requires the '''libc''' crate, you will need to override fetching the default crate with our custom libc crate with KallistiOS/Dreamcast definitions. This custom version was copied to <code>/opt/toolchains/dc/rust/libc</code> when building the Rust sysroot earlier. Add the following text to your <code>Cargo.toml</code>:
<syntaxhighlight lang="toml">
[patch.crates-io]
libc = { path = "/opt/toolchains/dc/rust/libc" }
</syntaxhighlight>


==Integrating a Cargo project with a KallistiOS project==
==Adjusting build flags==
We can also build a crate based on '''kos-rs''' and integrate the Rust code with other C code and KOS libraries. An example rotating 3D cube program built using '''kos-rs''' and <code>cargo-dc</code> combined with a <code>Makefile</code>-based KallistiOS project is included with the '''Rust-for-Dreamcast''' repository, located at [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rust_cube <code>examples/rust_cube</code>]. Type <code>cargo-dc build</code> to build the project, then invoke <code>make</code> to build the KallistiOS project and link the Rust code within it.
Build settings can be adjusted through the <code>KOS_RCG_RUSTFLAGS</code> variable in the <code>environ.sh</code> file under the <code>### Rust Flags</code> section.
* To pass arguments to the '''rustc''' frontend, e.g. to disable debug assertions:
** <syntaxhighlight lang="bash" inline>export KOS_RCG_RUSTFLAGS="${KOS_RCG_RUSTFLAGS} -Cdebug-assertions=no"</syntaxhighlight>
* To pass arguments to the GCC backend, e.g. to enable the <code>-freorder-blocks-algorithm=simple</code> optimization for Rust code:
** <syntaxhighlight lang="bash" inline>export KOS_RCG_RUSTFLAGS="${KOS_RCG_RUSTFLAGS} -Cllvm-args=-freorder-blocks-algorithm=simple"</syntaxhighlight>

Latest revision as of 00:28, 22 November 2024

Ferris holding his Dreamcast controller

Rust is a popular modern programming language focusing on performance, memory safety, and program correctness. As a "blazingly fast" low-level language, it is viable for running on a Dreamcast console. Doing so presents a bit of a challenge, however, as the official Rust compiler is based on the LLVM toolchain infrastructure, which does not support the Dreamcast CPU's SuperH architecture. Dreamcast programming is instead typically done with GCC, the GNU Compiler Collection. There are currently two viable solutions to this challenge:

  • rustc_codegen_gcc, the current preferred method: An experimental codegen backend for the official Rust rustc compiler which interfaces with GCC's libgccjit API
  • gccrs: an experimental Rust frontend for GCC

Neither solution is complete at this time, but rustc_codegen_gcc is much further along and is quite usable with some patience with its current limitations. On the other hand, gccrs can compile for Dreamcast, but is in an earlier state. Below we will focus on using rustc_codegen_gcc. For more information on using gccrs, see the gccrs page.

Current Status

  • The full std library is supported.
  • A custom version of KallistiOS v2.1.0 (latest stable release) is used.
    • This version includes minor changes for Rust and a beta libpthread addon layer to interface with the Rust standard library.
    • Wrappers are provided to invoke cargo and rustc in a familiar manner to compile projects.
  • A kos-sys raw/unsafe bindings crate is provided for easily linking KallistiOS and its unsafe C API into Rust crates. This crate is in active development, but with a large portion of KallistiOS functionality available now via C FFI.
  • A kos-rs Rust crate with safe idiomatic interfaces is available but is in very early stages.
  • Code generation uses rustc_codegen_gcc, so its limitations apply here. See the development blog for more information on its progress.
    • rustc_codegen_gcc is pinned to a particular Rust nightly version, but is synced regularly. The current version in use is the 2024-08-10 nightly.
    • A KallistiOS-patched version of the latest nightly is maintained and updated regularly.
    • Panic unwinding, debug info, LTO, etc. are not currently available.
    • Architectures dependent on rustc_codegen_gcc are not yet able to be added to the rustc frontend, so a workaround is necessary: MIPS is falsely selected for KallistiOS, causing rustc to emit objects with a MIPS header and a provided wrapper is used when linking to rewrite these MIPS headers to SuperH before passing the compiled objects to the linker.
  • A separate development version of the GCC toolchain must be used.
    • This version of GCC contains the latest libgccjit patches necessary to interface with rustc_codegen_gcc, and will be built to work with the custom KallistiOS's libpthread layer.
    • New updates to rustc_codegen_gcc often require new updates to GCC, so recompiling GCC regularly will be necessary to keep up with the latest updates.
    • The development version of GCC does not always compile well on every platform. Using a modern Linux platform is recommended for maximum compatibility.
    • The custom KallistiOS and GCC environment can still be used to compile code or projects written in C, C++, etc.
  • KallistiOS and all related libraries and tools must be compiled using the -m4-single ABI. This is already set up for you in the guide below, but if external libraries are being linked to Rust projects, make sure that code is compiled using the proper ABI.

Setting up a Rust environment for Dreamcast development

The following guide will provide instructions for setting up a Rust development environment for Dreamcast.

If you run into any errors or other challenges while following this tutorial, or simply need clarification on any of the steps, feel free to ask for assistance on the message board or our Discord server and we would be happy to aid you and update the guide for the benefit of future readers and others in the community.

Prerequisites

First, we will need to set up the prerequisites.

  • You must have the rustup tool installed for your operating system to manage Rust toolchain installations. It will automatically manage the latest Rust nightly installations for you. You cannot use a Rust toolchain that may be provided by your operating system vendor.
  • Install the dependency packages for your operating system. These are listed in the Dependencies section on the Getting Started with Dreamcast development#Dependencies page, with lists provided for most common operating systems.
  • We will install our custom Rust toolchain environment within /opt/toolchains/dc/rust. If it does not already exist, create the /opt/toolchains/dc directory and grant it proper permissions using the following commands.
sudo mkdir -p /opt/toolchains/dc
sudo chmod -R 755 /opt/toolchains/dc
sudo chown -R $(id -u):$(id -g) /opt/toolchains/dc
  • Clone the Rust for Dreamcast repo containing necessary support files to /opt/toolchains/dc/rust:
git clone https://github.com/dreamcast-rs/rust-for-dreamcast.git /opt/toolchains/dc/rust

Installing our custom KallistiOS and GCC toolchain

Clone the git repository for our custom Rust-patched version of KallistiOS v2.1.0 stable:

git clone https://github.com/dreamcast-rs/KallistiOS /opt/toolchains/dc/rust/kos

Enter the dc-chain directory:

cd /opt/toolchains/dc/rust/kos/utils/dc-chain

The Makefile.default.cfg file in this directory has been customized for you, selecting the proper toolchain profile, paths, and build options necessary for building a Rust toolchain using this guide. It is not recommended to adjust the defaults.

To build the GCC toolchain using 2 CPU cores, simply type:

make makejobs=2

Adjust the makejobs=... value to use the number of CPU cores available to you on your system in order to speed up compilation. This may take a while to build, so be patient.

Once the GCC toolchain is built, source the environ.sh KallistiOS environment settings. A custom environ.sh file has been provided for you, containing settings pre-adjusted for Rust development using this guide.

You will need to run the source command to apply the KallistiOS environment settings to your currently running shell. Run the following now, and whenever you open a new shell to work on Dreamcast projects:

source /opt/toolchains/dc/rust/misc/environ.sh

Enter the KallistiOS directory:

cd /opt/toolchains/dc/rust/kos

Build KallistiOS:

make

Building rustc_codegen_gcc and Rust sysroot

Now that we have a working KallistiOS environment suitable for Rust development set up, we can build the Rust compiler compiler and sysroot components. A script is provided which will download the necessary components from the respective repositories and compile them for you. Run the installer script:

/opt/toolchains/dc/rust/misc/install-rust.sh

If all goes well, you'll see Rust for KallistiOS/Dreamcast installed! message!

Building KOS ports libraries

If desired, you may also built the KOS ports collection of libraries for use with your Rust environment. It is recommended to maintain a separate KOS ports installation for linking with Rust projects in /opt/toolchains/dc/rust/kos-ports. This is because the Rust environment is using the -m4-single ABI, but the KallistiOS default installation will be using the -m4-single-only, which cannot be mixed and matched.

Clone the kos-ports repository to your system:

git clone https://github.com/KallistiOS/kos-ports /opt/toolchains/dc/rust/kos-ports

An individual port can be built and installed by entering its directory and running the proper command. For example, installing GLdc:

cd /opt/toolchains/dc/rust/kos-ports/libGL
make install

Or, instead, you may run the script to build all of the included ports:

/opt/toolchains/dc/rust/kos-ports/utils/build-all.sh

Using Rust for Dreamcast

If all went well, you will now have a working Rust for Dreamcast environment!

  • cargo can be invoked to target Dreamcast using the kos-cargo command.
    • kos-cargo run can be used to build and run code on a Dreamcast console using dcload-ip or dcload-serial. You will need to set up the KOS_LOADER variable with the necessary command in the /opt/toolchains/dc/rust/misc/environ.sh file (and then re-source the file for the change to take effect).
  • rustc can be invoked to target Dreamcast using the kos-rustc command.

Examples

Examples are included with the Rust for Dreamcast repo to help you get started.

  • hello-cargo demonstrates how to create a simple "Hello, world!" application using cargo.
  • hello-make demonstrates how to create a simple "Hello, world!" application using standard Makefiles.
  • gldc-cube demonstrates a Rust project using the GLdc KOS port.
  • addlib demonstrates how to create a Rust library that can be included with a KallistiOS project.
  • atomics demonstrates the use of atomic functions.
  • filesystem-io demonstrates listing directory contents and opening files.
  • network-time demonstrates displaying the system time and retrieving the current UTC time from an NTP server using the Broadband adapter or LAN adapter.
  • pvr_strips_direct demonstrates the use of PVR direct rendering
  • threads demonstrates the use of threads.
  • tokio-async demonstrates the use of the tokio async runtime.

Creating a new Rust project with Cargo

First, we'll demonstrate creating a new "Hello, world!" project with kos-cargo. This will follow the cargo-hello example included in the Rust-for-Dreamcast repo.

In a directory of your choosing, let's invoke kos-cargo to create a new project and then enter the directory:

kos-cargo new hello
cd hello

Let's add our kos-rs crate to gain access to current KallistiOS bindings. Open Cargo.toml in your text editor and add:

[dependencies]
kos = { package = "kos-rs", git = "https://github.com/dreamcast-rs/kos-rs" }

Now we can open up src/main.rs and write our "Hello, world!" example code:

fn main() {
    println!("Hello, world from Rust!");
}

Now we can use kos-cargo build to build our project. If all goes well, there will be a target/sh-elf/debug/hello.elf file that can be sent to the Dreamcast with dc-tool. If you have KOS_LOADER set in the environ.sh file, you can invoke it directly with kos-cargo run.

Creating a Rust project using kos-ports libraries

cube example in action

The gldc-cube example demonstrates creating a rotating 3D cube using Rust as the primary language, while calling C functions provided by the GLdc library available in kos-ports. This project's initial setup is done the same as the above hello example.

For this example, we'll need to convert JPEG textures to VQ-encoded textures at compile time using the vqenc utility includes with KallistiOS. To do this, we'll add a build.rs file to the root of the crate with the following code (abridged to show the conversion of only one texture):

fn main() {
    let kos_base = std::env::var("KOS_BASE").expect("Missing $KOS_BASE -- KallistiOS environment not sourced!");
    let vqenc_cmd = kos_base + "/utils/vqenc/vqenc";
    Command::new(&vqenc_cmd)
        .arg("-t")
        .arg("-v")
        .arg("rsrc/tex_claw.jpg")
        .output()
        .expect("vqenc on tex_claw.jpg failed!");
}

The texture files are then included in our project using the include_bytes! macro.

We will need to link the GLdc library in this example, but we don't need to do anything special to add the paths to this library, because adding the common paths to KallistiOS libraries is already done for us in the kos-rs crate. However, if in your project you need to include a separate unique library path, you can add it in your build.rs like so:

println!("cargo:rustc-link-search=native={}", lib_path);

The workings of this example's source code are too great to detail here line-by-line, but the example demonstrates how to use GLdc via the gldc-sys crate, which contains unsafe/raw bindings to GLdc. Check out this crate's source code for an example of declaring and binding external C functions, constants, and structures so they can be used in Rust code. Since the entirety of the gldc-cube example uses C FFI functions without safe wrappers, the main() source is wrapped in unsafe {}. In the future, this would be much less necessary as higher level safe Rust bindings to KallistiOS and other libraries become mature.

Creating a Rust library

Next, we'll demonstrate creating a Rust library with kos-cargo that can be included in other Dreamcast code. This will follow the addlib example. Once again, this project's initial setup is done the same as the above hello example, but you'll create the new project using kos-cargo new --lib addlib to specify that we're creating a library named addlib. You'll also need to add the following text to this project's Cargo.toml file:

[lib]
crate-type = ["staticlib"]

This tells Rust to build a static .a library archive file from our code, which is located in src/lib.rs:

#[no_mangle]
pub extern "C" fn print_added(a: isize, b: isize) {
    print!("{}", a + b);
}

#[no_mangle]
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {
    a + b
}

The source code here starts similarly to the "Hello, world!" example, except we don't need to specify #![no_main] as this is a library which wouldn't have a main() function anyway.

Two simple functions are provided: one for adding two integers and returning the result, and another for adding two integers and printing the result as text. Because these functions use #[no_mangle] and are declared extern "C", they can be called by name in C code that links this library.

When built using kos-cargo build, a target/sh-elf/debug/libaddlib.a file will be generated. This can be linked into other projects to gain the use of these functions.

For example, this can be added to a standard Makefile-based KallistiOS project by editing the Makefile:

$(TARGET): $(OBJS)
	kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib

Then, we can use the code in our C source:

/* Declare the external function from the Rust library */
int add_integers(int a, int b);

/* Use the function */
printf("Five plus six is %d\n", add_integers(5, 6));

Compiling individual modules into object files with rustc

If we'd like to mix C and Rust code in the same Makefile-based KallistiOS project without building an entirely separate library, we can do that as well. This is demonstrated in the hello-make example.

Instead of using kos-cargo, we can invoke the kos-rustc via a Makefile to build Rust modules. If we assume the Rust file is named example.rs, you'll need to add example.o as an object file in your Makefile's OBJS = declaration. For example, if the project has two source files hello_c.c and hello_rust.rs, our Makefile would have a line like this:

OBJS = hello_c.o hello_rust.o

The example code demonstrates starting a C main() function to call a Rust function which builds a String containing the "Hello, world!" text which is passed back to a C function which prints Strings.

Using the libc crate with KallistiOS

If any of the crates in your project's dependency tree requires the libc crate, you will need to override fetching the default crate with our custom libc crate with KallistiOS/Dreamcast definitions. This custom version was copied to /opt/toolchains/dc/rust/libc when building the Rust sysroot earlier. Add the following text to your Cargo.toml:

[patch.crates-io]
libc = { path = "/opt/toolchains/dc/rust/libc" }

Adjusting build flags

Build settings can be adjusted through the KOS_RCG_RUSTFLAGS variable in the environ.sh file under the ### Rust Flags section.

  • To pass arguments to the rustc frontend, e.g. to disable debug assertions:
    • export KOS_RCG_RUSTFLAGS="${KOS_RCG_RUSTFLAGS} -Cdebug-assertions=no"
  • To pass arguments to the GCC backend, e.g. to enable the -freorder-blocks-algorithm=simple optimization for Rust code:
    • export KOS_RCG_RUSTFLAGS="${KOS_RCG_RUSTFLAGS} -Cllvm-args=-freorder-blocks-algorithm=simple"