https://dreamcast.wiki/wiki/api.php?action=feedcontributions&user=Darc&feedformat=atomdreamcast.wiki - User contributions [en]2024-03-29T02:35:54ZUser contributionsMediaWiki 1.39.3https://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3508Rust on Dreamcast2024-03-18T02:56:26Z<p>Darc: /* Building rustc_codegen_gcc to develop on Dreamcast */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
While neither solution is complete at this time, '''rustc_codegen_gcc''' is much further along and is quite usable with some patience with its current limitations and rapid change. On the other hand, '''gccrs''' can compile for Dreamcast, but 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.<br />
<br />
=Building rustc_codegen_gcc to develop on Dreamcast=<br />
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 some early basic [[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 provided patches and scripts to make this solution work. See the rustc_codegen_gcc [https://blog.antoyo.xyz/ progress reports] for more information on the upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''In Progress Now'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
'''Future Goals'''<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Upstream libc/libstd support, inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* You must already have a relatively up-to-date Rust installation using [https://rustup.rs/ rustup]. Ideally, you will already have some familiarity with Rust's tools.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/patches/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/misc/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
=Using Rust for Dreamcast=<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
==Adjusting build settings==<br />
Build settings can be adjusted through the <code>CG_GCCFLAGS</code> and <code>CG_RUSTFLAGS</code> environment variables. For example, invoking cargo like so:<br />
CG_GCCFLAGS="-freorder-blocks-algorithm=simple" cargo-dc build<br />
will build the project while passing along the <code>-freorder-blocks-algorithm=simple</code> optimization setting to the GCC backend. <code>rustc-dc</code> will also pass through these settings. If you wish to adjust the default flags passed to GCC, they are specified in the <code>common.sh</code> file.<br />
If you wish to have <code>cargo-dc</code> pass through <code>RUSTFLAGS</code> arguments to the <code>rustc</code> compiler frontend, you can do so by using the <code>CG_RUSTFLAGS</code> environment variable.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3507Rust on Dreamcast2024-03-18T01:05:37Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
While neither solution is complete at this time, '''rustc_codegen_gcc''' is much further along and is quite usable with some patience with its current limitations and rapid change. On the other hand, '''gccrs''' can compile for Dreamcast, but 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.<br />
<br />
=Building rustc_codegen_gcc to develop on Dreamcast=<br />
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 some early basic [[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 provided patches and scripts to make this solution work. See the rustc_codegen_gcc [https://blog.antoyo.xyz/ progress reports] for more information on the upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* You must already have a relatively up-to-date Rust installation using [https://rustup.rs/ rustup]. Ideally, you will already have some familiarity with Rust's tools.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/patches/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/misc/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
=Using Rust for Dreamcast=<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
==Adjusting build settings==<br />
Build settings can be adjusted through the <code>CG_GCCFLAGS</code> and <code>CG_RUSTFLAGS</code> environment variables. For example, invoking cargo like so:<br />
CG_GCCFLAGS="-freorder-blocks-algorithm=simple" cargo-dc build<br />
will build the project while passing along the <code>-freorder-blocks-algorithm=simple</code> optimization setting to the GCC backend. <code>rustc-dc</code> will also pass through these settings. If you wish to adjust the default flags passed to GCC, they are specified in the <code>common.sh</code> file.<br />
If you wish to have <code>cargo-dc</code> pass through <code>RUSTFLAGS</code> arguments to the <code>rustc</code> compiler frontend, you can do so by using the <code>CG_RUSTFLAGS</code> environment variable.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Gccrs&diff=3506Gccrs2024-03-18T00:53:16Z<p>Darc: /* Building a gccrs-enabled toolchain */</p>
<hr />
<div>'''gccrs''' is the Rust compiler frontend for GCC, currently under active development. This Rust compiler is a new implementation of Rust from the ground up using the GCC toolchain infrastructure. This project is in early stages and is targeting the Rust 1.49 revision from December 2020. As of this writing (February 2024), it is not yet able to compile Rust's <code>libcore</code>, so many basic language features are unimplemented or not functional. Additionally, Rust standard tooling like <code>cargo</code> is not available. Borrow checking is not implemented, but the project plans to later use the next-generation Rust borrow checker [https://github.com/rust-lang/polonius Polonius] from the official Rust project.<br />
<br />
At this time, it is recommended to use [[Rust on Dreamcast|'''rustc_codegen_gcc''']] to develop for Dreamcast using Rust instead of gccrs.<br />
<br />
Although it is in early stages and highly experimental for Dreamcast dev purposes, it is possible to use this compiler by building the GCC 14.0.1-dev toolchain or the '''gccrs''' latest toolchain. GCC 14.0.1-dev will get you the latest code upstreamed by the '''gccrs''' team into the main development branch of GCC, while the '''gccrs''' git repo will get you the absolute latest bleeding edge updates to '''gccrs'''. The GCC 14.0.1-dev configuration file is available within the official KallistiOS repo's <code>dc-chain</code> script, while the latest '''gccrs''' configuration is available within the [https://github.com/darcagn/rust-for-dreamcast Rust for Dreamcast] repository. Brief instructions follow for setting up the latest '''gccrs''' toolchain. See [[Getting Started with Dreamcast development]] for more detailed information on how to set up and run <code>dc-chain</code>.<br />
<br />
==Building a gccrs-enabled toolchain==<br />
Follow the [[Getting Started with Dreamcast development]] guide for creating a Dreamcast toolchain until you arrive at the instructions for setting up the <code>dc-chain</code> configuration file. <br />
<br />
Make sure your shell is currently in the correct directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clone the [https://github.com/darcagn/rust-for-dreamcast Rust for Dreamcast] repository:<br />
git clone https://github.com/darcagn/rust-for-dreamcast.git rust<br />
Copy the GCC patch in place:<br />
cp rust/patches/toolchain/gcc-rs-kos.diff patches/<br />
Copy the <code>dc-chain</code> configuration file into place:<br />
cp rust/misc/config.mk.gccrs.sample config.mk<br />
Make any desired changes to the configuration (e.g., change <code>makejobs=-j2</code> to the number of CPU threads you'd like to use during compilation). Note that to avoid conflicting with an existing stable toolchain at the default path (i.e. <code>/opt/toolchains/dc/sh-elf</code>), we will be installing to <code>/opt/toolchains/dc/gccrs/sh-elf</code> instead. To begin compilation and installation, run:<br />
make build-sh4<br />
After building everything, you can clean up the extraneous files in your <code>dc-chain</code> directory by entering:<br />
make clean<br />
<br />
==Setting up Makefiles to compile Rust modules==<br />
As mentioned before, <code>cargo</code> is not available to use with '''gccrs''', so for our example, we will place our <code>.rs</code> modules within a typical KallistiOS <code>Makefile</code> project. If we assume the module file is named <code>example.rs</code>, you'll need to add <code>example.rox</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>rox</code> object files:<br />
<syntaxhighlight lang="make"><br />
%.rox: %.rs<br />
kos-cc -frust-incomplete-and-experimental-compiler-do-not-use $(CFLAGS) -c $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
In your <code>example.rs</code> file, your <code>main</code> function will need to be declared like so:.<br />
<br />
<syntaxhighlight lang="rust"><br />
#[no_mangle]<br />
pub extern fn main() -> i32 {<br />
[...]<br />
}<br />
</syntaxhighlight><br />
<br />
Make sure before you compile your code that you set <code>export KOS_CC_BASE="/opt/toolchains/dc/gccrs/sh-elf"</code> in your KallistiOS <code>environ.sh</code> file or <code>make</code> will not find your '''gccrs''' compiler executable.<br />
<br />
==Example project==<br />
The [https://github.com/darcagn/rust-fortran-cube/ <code>rust-fortran-cube</code>] repository contains an example project using gccrs in conjunction with GFortran, KallistiOS, and GLdc. View [https://www.youtube.com/watch?v=VUiRoEcpvtI footage of the example] running. This example was created by Eric Fradella and used as a demo in the FOSDEM 2024 talk [https://fosdem.org/2024/schedule/event/fosdem-2024-2634-sega-dreamcast-homebrew-with-gcc/ Sega Dreamcast Homebrew with GCC] by [[User:GyroVorbis|Falco Girgis]].</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3505Rust on Dreamcast2024-03-18T00:52:37Z<p>Darc: /* Building a cross-compiling libgccjit.so for rustc_codegen_gcc */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
While neither solution is complete at this time, '''rustc_codegen_gcc''' is much further along and is quite usable with some patience with its current limitations and rapid change. On the other hand, '''gccrs''' can compile for Dreamcast, but 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.<br />
<br />
=Building rustc_codegen_gcc to develop on Dreamcast=<br />
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 some early basic [[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 provided patches and scripts to make this solution work. See the rustc_codegen_gcc [https://blog.antoyo.xyz/ progress reports] for more information on the upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* You must already have a relatively up-to-date Rust installation using [https://rustup.rs/ rustup]. Ideally, you will already have some familiarity with Rust's tools.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/patches/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/misc/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
=Using Rust for Dreamcast=<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''NOTE''': If you already had GLdc built, make sure you pull the latest GLdc and rebuild with <code>-m4-single</code> -- a very recently fixed bug prevented this demo from working properly under <code>-m4-single</code>.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
==Adjusting build settings==<br />
Build settings can be adjusted through the <code>CG_GCCFLAGS</code> and <code>CG_RUSTFLAGS</code> environment variables. For example, invoking cargo like so:<br />
CG_GCCFLAGS="-freorder-blocks-algorithm=simple" cargo-dc build<br />
will build the project while passing along the <code>-freorder-blocks-algorithm=simple</code> optimization setting to the GCC backend. <code>rustc-dc</code> will also pass through these settings. If you wish to adjust the default flags passed to GCC, they are specified in the <code>common.sh</code> file.<br />
If you wish to have <code>cargo-dc</code> pass through <code>RUSTFLAGS</code> arguments to the <code>rustc</code> compiler frontend, you can do so by using the <code>CG_RUSTFLAGS</code> environment variable.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3502Rust on Dreamcast2024-03-06T18:27:12Z<p>Darc: /* Prerequisites */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
While neither solution is complete at this time, '''rustc_codegen_gcc''' is much further along and is quite usable with some patience with its current limitations and rapid change. On the other hand, '''gccrs''' can compile for Dreamcast, but 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.<br />
<br />
=Building rustc_codegen_gcc to develop on Dreamcast=<br />
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 some early basic [[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 provided patches and scripts to make this solution work. See the rustc_codegen_gcc [https://blog.antoyo.xyz/ progress reports] for more information on the upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* You must already have a relatively up-to-date Rust installation using [https://rustup.rs/ rustup]. Ideally, you will already have some familiarity with Rust's tools.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
=Using Rust for Dreamcast=<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''NOTE''': If you already had GLdc built, make sure you pull the latest GLdc and rebuild with <code>-m4-single</code> -- a very recently fixed bug prevented this demo from working properly under <code>-m4-single</code>.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
==Adjusting build settings==<br />
Build settings can be adjusted through the <code>CG_GCCFLAGS</code> and <code>CG_RUSTFLAGS</code> environment variables. For example, invoking cargo like so:<br />
CG_GCCFLAGS="-freorder-blocks-algorithm=simple" cargo-dc build<br />
will build the project while passing along the <code>-freorder-blocks-algorithm=simple</code> optimization setting to the GCC backend. <code>rustc-dc</code> will also pass through these settings. If you wish to adjust the default flags passed to GCC, they are specified in the <code>common.sh</code> file.<br />
If you wish to have <code>cargo-dc</code> pass through <code>RUSTFLAGS</code> arguments to the <code>rustc</code> compiler frontend, you can do so by using the <code>CG_RUSTFLAGS</code> environment variable.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Programming_language_support&diff=3501Programming language support2024-02-24T18:36:51Z<p>Darc: /* Compiled languages */</p>
<hr />
<div>The following languages can be used in Sega Dreamcast programming:<br />
<br />
=Assembly languages=<br />
* '''SH4 assembly''', as the native machine code for the Dreamcast's SuperH processor<br />
* '''ARM assembly''', as the native machine code for the Dreamcast's AICA ARM7DI sound processor<br />
<br />
=Compiled languages=<br />
* '''C''', the predominant language for Sega Dreamcast development, via the GCC C compiler.<br />
* '''C++''', commonly used and very well-supported, via the GCC C++ compiler.<br />
* '''Objective-C''', via the GCC Objective-C compiler. Preliminary support for Foundation framework via [https://gnustep.github.io/ GNUstep] is being developed. See [https://twitter.com/dinobj_c/status/1732399498977276154 Andrew Apperley's X account].<br />
** The GNU '''Objective-C++''' extension is available as well.<br />
* '''Rust''', via rustc_codegen_gcc and libgccjit, or via gccrs. When using rustc_codegen_gcc, the core and alloc libraries are available, with bindings to KallistiOS being developed. See [[Rust on Dreamcast]] for more information.<br />
* '''D''', via the GCC D compiler. Early support, see [https://twitter.com/lunafoxgirlvt/status/1736444629149487494 Luna's X account].<br />
* '''Ada''', via GNAT. [https://github.com/dkm/ada-dreamcast-helloworld Ada 3D cube demo] by [https://poulhies.fr/ Marc Poulhiès].<br />
* '''Fortran''', via GFortran. Requires some work to create a cross-compiler, but does work.<br />
<br />
=Scripting/Interpreted languages=<br />
* Lua (available in [[kos-ports]])<br />
* MicroPython (available in [[kos-ports]])<br />
* Tcl (available in [[kos-ports]])<br />
* mRuby (available in [[kos-ports]])<br />
* AngelScript<br />
* QuickJS</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3500Rust on Dreamcast2024-02-20T23:49:13Z<p>Darc: </p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
While neither solution is complete at this time, '''rustc_codegen_gcc''' is much further along and is quite usable with some patience with its current limitations and rapid change. On the other hand, '''gccrs''' can compile for Dreamcast, but 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.<br />
<br />
=Building rustc_codegen_gcc to develop on Dreamcast=<br />
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 some early basic [[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 provided patches and scripts to make this solution work. See the rustc_codegen_gcc [https://blog.antoyo.xyz/ progress reports] for more information on the upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
=Using Rust for Dreamcast=<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''NOTE''': If you already had GLdc built, make sure you pull the latest GLdc and rebuild with <code>-m4-single</code> -- a very recently fixed bug prevented this demo from working properly under <code>-m4-single</code>.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
==Adjusting build settings==<br />
Build settings can be adjusted through the <code>CG_GCCFLAGS</code> and <code>CG_RUSTFLAGS</code> environment variables. For example, invoking cargo like so:<br />
CG_GCCFLAGS="-freorder-blocks-algorithm=simple" cargo-dc build<br />
will build the project while passing along the <code>-freorder-blocks-algorithm=simple</code> optimization setting to the GCC backend. <code>rustc-dc</code> will also pass through these settings. If you wish to adjust the default flags passed to GCC, they are specified in the <code>common.sh</code> file.<br />
If you wish to have <code>cargo-dc</code> pass through <code>RUSTFLAGS</code> arguments to the <code>rustc</code> compiler frontend, you can do so by using the <code>CG_RUSTFLAGS</code> environment variable.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3499Rust on Dreamcast2024-02-20T23:36:23Z<p>Darc: </p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
While neither solution is complete at this time, '''rustc_codegen_gcc''' is much further along and is quite usable with some patience with its current limitations and rapid change. On the other hand, '''gccrs''' can compile for Dreamcast, but 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.<br />
<br />
=Building rustc_codegen_gcc to develop on Dreamcast=<br />
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 some early basic [[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 provided patches and scripts to make this solution work. See the rustc_codegen_gcc [https://blog.antoyo.xyz/ progress reports] for more information on the upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
=Using Rust for Dreamcast=<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''NOTE''': If you already had GLdc built, make sure you pull the latest GLdc and rebuild with <code>-m4-single</code> -- a very recently fixed bug prevented this demo from working properly under <code>-m4-single</code>.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3498Rust on Dreamcast2024-02-19T12:21:10Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
While neither solution is complete at this time, '''rustc_codegen_gcc''' is much further along and is quite usable with some patience with its current limitations and rapid change. On the other hand, '''gccrs''' can compile for Dreamcast, but 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.<br />
<br />
=Building rustc_codegen_gcc to develop on Dreamcast=<br />
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 some early basic [[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 provided patches and scripts to make this solution work. See the rustc_codegen_gcc [https://blog.antoyo.xyz/ progress reports] for more information on the upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
=Using Rust for Dreamcast=<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''NOTE''': If you already had GLdc built, make sure you pull the latest GLdc and rebuild with <code>-m4-single</code> -- a very recently fixed bug prevented this demo from working properly under <code>-m4-single</code>.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Getting_Started_with_Dreamcast_development&diff=3497Getting Started with Dreamcast development2024-02-19T02:48:51Z<p>Darc: /* Introduction */</p>
<hr />
<div><div style="float:right;">__TOC__</div><br />
=Introduction=<br />
This article will cover the entire beginning process: starting from zero to having a working dev environment with debug link (serial or IP) and self-booting CD-R. This guide will cover the process for the following platforms:<br />
* '''Microsoft Windows 10''' via [https://learn.microsoft.com/en-us/windows/wsl/about Windows Subsystem for Linux]<br />
** Users desiring a native Windows approach, see [[DreamSDK]] instead<br />
* '''macOS''' on Intel or Apple Silicon systems with the [https://brew.sh/ Homebrew] package manager installed<br />
* '''Linux'''-based systems<br />
** '''Debian'''- and '''Ubuntu'''-based distributions using the default '''apt''' package manger<br />
** '''Fedora'''-based distributions using the default '''dnf''' package manager<br />
** '''Arch'''-based distributions using the default '''pacman''' package manager<br />
** '''Alpine'''-based distributions using the default '''apk''' package manager<br />
<br />
===Need help?===<br />
Important note: ''This guide aims to remain up to date and work on all of the above platforms, but keeping instructions for such a variety of platforms up-to-date can be difficult. 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.''<br />
<br />
===Terms===<br />
Before we get started, let's define several terms:<br />
<br />
The '''toolchain''' is a set of programs which turns your code into an executable file for your Dreamcast console. The toolchain includes:<br />
* '''GCC''', a compiler which supports C, C++, Objective-C, and more (see also [[Rust on Dreamcast]] and [[Programming language support]])<br />
* '''binutils''', an assembler and linker<br />
* '''newlib''', a C library<br />
* '''gdb''', a debugger<br />
The toolchain includes a compiler for the Dreamcast's main SH4 CPU, and optionally a compiler for the ARM-based AICA sound processor. Your operating system may already have versions of these programs installed to compile code for your computer, but we will need to build a "cross-compiler" for compiling specifically for the Dreamcast. <br />
<br />
'''KallistiOS''' or ''KOS'' is an open source development library and pseudo-operating system for the Dreamcast console. It is the best documented and most widely used development kit in the homebrew community. KallistiOS's very flexible license allows both homebrew and commercial use with no restrictions other than a requirement to include credit for its use in your project, and indeed almost all commercially sold indie Dreamcast titles use it. There are others in existence, like [[libronin]] and [[libdream]], as well as the older development kits [[Katana]] and [[Windows CE]] created by Sega and Microsoft for use in retail games, but this guide will only cover the setup and use of KallistiOS. <br />
<br />
'''kos-ports''' is a repository including various libraries which integrate with KallistiOS. We will download and compile these libraries as well. <br />
<br />
The '''debug link''' is a generic term referring to a hardware accessory to facilitate quickly running and debugging your programs. IP-based links include the Dreamcast's '''[[Broadband adapter]]''' and '''[[LAN adapter]]''' accessories, and serial-based links include the [[Coder's cable]], which is a cable that can connect the Dreamcast's serial port to your computer via USB or serial. This guide includes instructions for setting up and using the the Broadband adapter and a USB-based coder's cable.<br />
<br />
'''dc-tool''' and '''dcload''' are a pair of programs to facilitate using a debug link. ''dc-tool'' runs on your computer and links to a Dreamcast running ''dcload-ip'' or ''dcload-serial''. With this setup, you can quickly load programs, read console feedback, load assets, transfer data, redirect I/O, handle exceptions, debug problems, and so forth.<br />
<br />
=Choosing a debug link solution=<br />
If you are building the toolchain for the purpose of building existing programs from source with little to no modifications, then a debug link setup might not be necessary for you. You may simply build programs to burn directly to CD-R. However, if you are planning to actively develop for the Dreamcast, then a debug link is a critical component. While Dreamcast emulators are mature and accurate enough to play the vast majority of the system's games library without issue, many critical bugs may show up on a real Dreamcast system, but not on a Dreamcast emulator. Therefore, it is highly recommended to test on a real system as much as possible. It's also possible to load software off of a [[Serial SD card adapter]], but without an active link to a computer, debugging and stepping through programs as they execute is significantly more challenging.<br />
<br />
Presented below is a table comparing the different options available for a debug link. Due to the cost, potential buyers may want to factor in the ability to play multiplayer games with their purchase. Thus, for comparison, we have included information about the [[Modem]] with [[DreamPi]] as well, but understand that the Modem with DreamPi cannot be used as a debug link.<br />
<br />
{| class="wikitable"<br />
!colspan="6" |Comparison of various Dreamcast connectivity options<br />
|-<br />
|style="background-color:#c0c0c0;" width="150" | Device: <br />
|style="background-color:#d0d0d0;" width="400" | [[Broadband adapter]] (HIT-400 or HIT-401) <br />Realtek RTL8139C chipset<br />
|style="background-color:#d0d0d0;" width="400" | [[LAN adapter]] (HIT-300) <br />Fujitsu MB86967 chipset<br />
|style="background-color:#d0d0d0;" width="400" | [[Modem]] with [[DreamPi]]<br />
|style="background-color:#d0d0d0;" width="400" | USB [[Coder's cable]]<br />
|style="background-color:#d0d0d0;" width="400" | Serial [[Coder's cable]]<br />
|-<br />
|style="background-color:#d0d0d0;" | Useful for dev? || Yes, supports dcload-ip || Yes, supports dcload-ip,<br/>but BBA is superior and cheaper || No, only useful for online multiplayer gaming || Yes, supports dcload-serial || Yes, supports dcload-serial<br />
|-<br />
|style="background-color:#d0d0d0;" | Cost || $100 - $200 and up on used markets || $200 and up on used markets,<br/>due to extreme rarity || Kit prices vary, around $100 || See [[Coder's cable]] for vendors<br />darc sells for $45, RetroOnyx sells for $85 || Varies on used markets, uncommonly sold<br />
|-<br />
|style="background-color:#d0d0d0;" | Can make DIY? || No || No || Yes || Yes || Yes<br />
|-<br />
|style="background-color:#d0d0d0;" | Performance || Up to 100 megabits/s || Up to 10 megabits/s || Up to 56 kilobits/s || Up to 1500 kilobits/s || Up to 120 kilobits/s<br />
|-<br />
|style="background-color:#d0d0d0;" | Games support || Some games: Phantasy Star Online, Quake III Arena, Toy Racer, POD SpeedZone, Propellor Arena, Unreal Tournament<br />Some browsers: Broadband Passport, PlanetWeb 3.0 || No games<br />One browser: Dream Passport for LAN || All multiplayer games with network support<br />All web browsers || NO multiplayer games support || NO multiplayer games support <br />
|-<br />
|style="background-color:#d0d0d0;" | Homebrew support || Homebrew utilities like dcload-ip || Homebrew utilities like dcload-ip || Homebrew utilities don't support, only multiplayer games || Homebrew utilities like dcload-serial || Homebrew utilities like dcload-serial <br />
|}<br />
<br />
=Setting up and compiling the toolchain with the dc-chain script=<br />
===Dependencies===<br />
First, we'll need to install dependencies before building the toolchain. Below we have provided commands to install these dependencies on various systems. Many of the packages will likely already be installed on your system, but we have provided an exhaustive list for good measure.<br />
====macOS 13 Ventura on an Intel or Apple Silicon processor====<br />
First, make sure you install Apple Xcode, including the Command Line tools. You will also need to install several other packages for which we'll include instructions assuming you have installed the [https://brew.sh/ Homebrew] package manager on your system.<br />
brew install wget gettext texinfo gmp mpfr libmpc libelf jpeg-turbo libpng meson libisofs<br />
<br />
''Important Note for Apple Silicon users'': On Apple Silicon, Homebrew installs libraries to a path not included by default by the compiler. If you haven't added these to your '''~/.zprofile''', then add the following lines now and reload your session (or run them in your Terminal session whenever you compile KOS):<br />
export CPATH=/opt/homebrew/include<br />
export LIBRARY_PATH=/opt/homebrew/lib<br />
<br />
====Debian/Ubuntu-based Linux====<br />
sudo apt-get update<br />
sudo apt install gawk patch bzip2 tar make libgmp-dev libmpfr-dev libmpc-dev gettext wget libelf-dev texinfo bison flex sed git build-essential diffutils curl libjpeg-dev libpng-dev python3 pkg-config libisofs-dev meson ninja-build<br />
<br />
====Fedora-based Linux====<br />
sudo dnf install gawk patch bzip2 tar make gmp-devel mpfr-devel libmpc-devel gettext wget elfutils-libelf-devel texinfo bison flex sed git diffutils curl libjpeg-turbo-devel libpng-devel gcc-c++ python3 rubygem-rake meson ninja-build<br />
<br />
====Arch-based Linux====<br />
sudo pacman -S --needed gawk patch bzip2 tar make gmp mpfr libmpc gettext wget libelf texinfo bison flex sed git diffutils curl libjpeg-turbo libpng python3 meson<br />
<br />
====Alpine-based Linux====<br />
sudo apk --update add build-base patch bash texinfo gmp-dev libjpeg-turbo-dev libpng-dev elfutils-dev curl wget python3 git<br />
<br />
====Other Linux distributions====<br />
If you're using a different Linux- or Unix-based system besides the one above, you may need to reference your distribution's package database and package manager documentation for the equivalent package names and commands necessary for your system.<br />
<br />
===Creating a space for your toolchain installation===<br />
Create the path where we'll install the toolchain and KOS, and grant it the proper permissions:<br />
sudo mkdir -p /opt/toolchains/dc<br />
sudo chmod -R 755 /opt/toolchains/dc<br />
sudo chown -R $(id -u):$(id -g) /opt/toolchains/dc<br />
===Cloning the KOS git repository===<br />
Clone the KOS git repository to your system:<br />
git clone https://github.com/KallistiOS/KallistiOS.git /opt/toolchains/dc/kos<br />
<br />
===Configuring the dc-chain script===<br />
Enter the dc-chain directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
<br />
We will choose the default '''stable''' configuration for the toolchain, which currently uses GCC 13.2. For advanced users, other configurations are available to you; read the <code>README.md</code> file in the dc-chain directory for more information if you are interested.<br />
cp config/config.mk.stable.sample config.mk<br />
<br />
Now, you may configure config.mk options to your liking by using a text editor. You may alter the <code>makejobs</code> parameter to the number of threads available on your CPU to speed up the compilation, if desired. However, if you run into errors during compilation, you may want to set <code>makejobs=-j1</code>, as on some operating systems the toolchain may fail to build with a higher setting.<br />
<br />
===Downloading and compiling the toolchain===<br />
Now we will run a script to download files and compile the toolchain. At this point, we have the option of building both the main CPU SH4 compiler and the AICA sound processor ARM compiler, or we can skip the ARM compiler and just build the SH4 compiler. Thankfully, KallistiOS includes a prebuilt sound driver, so the ARM compiler is only necessary if you're wanting to make changes to the sound driver or write custom code to run on the sound processor.<br />
To build '''only the SH4 compiler''':<br />
make build-sh4<br />
To build '''both''' the SH4 and the ARM compilers:<br />
make<br />
This will download and unpack the relevant necessary files and then begin the compilation process. The compilation can take anywhere from minutes to a few hours depending on your CPU and number of threads available. When successfully finished, the toolchains will be ready.<br />
<br />
Afterwards, if desired, you may also compile the GNU Debugger (gdb) as well:<br />
make gdb<br />
<br />
The GNU Debugger is now installed along with your toolchains.<br />
<br />
===Cleaning up temporary files===<br />
After building everything, you can clean up the extraneous files in your dc-chain directory by entering:<br />
make clean<br />
<br />
=Configuring and compiling KOS and kos-ports=<br />
===Setting up the environment settings===<br />
Enter the KOS directory:<br />
cd /opt/toolchains/dc/kos<br />
Copy the pre-made environment script into place:<br />
cp doc/environ.sh.sample environ.sh <br />
For most users, the default settings will suffice. However, advanced users may edit the environ.sh to your liking if you'd like to change compile flags or alter paths. If you'd like to have multiple KOS versions installed or multiple toolchain versions installed, you can set up different environ.sh files corresponding to these different configurations by altering the paths. Run the source command on the desired environ.sh file to select that configuration prior to compiling your project. <br />
<br />
You will need to run the source command to apply the KOS environment settings to your currently running shell. Run the following now, '''and''' ''whenever'' you open a new shell to work on Dreamcast projects:<br />
source /opt/toolchains/dc/kos/environ.sh<br />
<br />
===Building KOS===<br />
Build KOS:<br />
make<br />
KOS is now built.<br />
===Building kos-ports===<br />
Clone the kos-ports repository to your system:<br />
git clone --recursive https://github.com/KallistiOS/kos-ports /opt/toolchains/dc/kos-ports<br />
Run the script to build all of the included ports:<br />
/opt/toolchains/dc/kos-ports/utils/build-all.sh<br />
kos-ports is now built.<br />
<br />
===Building the KOS examples===<br />
Enter the KOS examples directory:<br />
cd /opt/toolchains/dc/kos/examples/dreamcast<br />
Build the examples:<br />
make<br />
All of the example programs provided with KallistiOS are now built.<br />
<br />
=Further=<br />
At this point, you will have a cross-compiler built so that you can generate programs for your Dreamcast from code. KallistiOS, the Dreamcast development library and operating system, is built, as is its associated packages within kos-ports, and all of its example programs.<br />
<br />
At this point, you will likely want to view some of the following articles:<br />
* Setting up an IDE for Dreamcast development<br />
** [[Qt Creator Dreamcast Development Environment]]<br />
** [[CLion Debugging]]<br />
** [[Visual Studio Code]]<br />
* [[Creating a first project with KallistiOS]] ('''TODO''': Explain how to create a new DC project folder with Makefile, adding an external library, create a basic program, and compile it)<br />
* [[Using a debug link]]<br />
* [[Burning an example to CD-R]]<br />
<br />
=Running an example program through a debug link=<br />
'''TODO''': ''Give a tutorial on compiling dcload/dc-tool, setting up a serial, USB, or IP debug link, and running 2ndmix demo on real hardware.''<br />
<br />
Download and burn the [[:File:Dcload-2023-06-22.zip|latest versions of dcload-ip or dcload-serial]] -- the IP version includes improved DHCP support, so there is no longer a need to configure things beforehand. <br />
<br />
Run one of the examples from the <code>kos/examples/dreamcast</code> directory with one of the following commands:<br />
dc-tool-ip -t <dreamcast IP address> -x example.elf<br />
<br />
dc-tool-ser -t <serial device path> -x example.elf<br />
<br />
Run <code>dc-tool-ip</code> or <code>dc-tool-ser</code> without any parameters to get additional options.<br />
<br />
=Burning an example program to CD-R=<br />
'''TODO''': ''Explain how to build mkdcdisc and write 2ndmix demo to CD-R and run on a Dreamcast console''<br />
<br />
[https://gitlab.com/simulant/mkdcdisc mkdcdisc] can be used to easily generate a burnable self-boot CDI image.<br />
Dependencies required:<br />
* Homebrew: <code>meson libisofs</code><br />
* Debian/Ubuntu: <code>libisofs-dev meson ninja-build</code><br />
* Fedora: <code>meson ninja-build</code><br />
* Arch: <code>meson</code><br />
<br />
Build <code>mkdcdisc</code>:<br />
git clone https://gitlab.com/simulant/mkdcdisc.git<br />
cd mkdcdisc<br />
meson setup builddir<br />
meson compile -C builddir<br />
./builddir/mkdcdisc -h<br />
and create a CDI image from your compiled ELF like so:<br />
mkdcdisc -e MyProgram.elf -o MyProgram.cdi<br />
Then you can burn the CDI file using DiscJuggler (Windows-only, but also works through [https://www.winehq.org/ WINE]), ImgBurn with the CDI plugin, or the cdiburn *nix script floating around out there. (document this better)</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3496Rust on Dreamcast2024-02-19T02:04:19Z<p>Darc: /* Building rustc_codegen_gcc to develop on Dreamcast */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
While neither solution is complete at this time, '''rustc_codegen_gcc''' is much further along and is quite usable with some patience with its current limitations and rapid change. On the other hand, '''gccrs''' can compile for Dreamcast, but 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.<br />
<br />
=Building rustc_codegen_gcc to develop on Dreamcast=<br />
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 some early basic [[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 provided patches and scripts to make this solution work. See the rustc_codegen_gcc [https://blog.antoyo.xyz/ progress reports] for more information on the upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
=Using Rust for Dreamcast=<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3495Rust on Dreamcast2024-02-19T02:03:23Z<p>Darc: </p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
While neither solution is complete at this time, '''rustc_codegen_gcc''' is much further along and is quite usable with some patience with its current limitations and rapid change. On the other hand, '''gccrs''' can compile for Dreamcast, but 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.<br />
<br />
=Building rustc_codegen_gcc to develop on Dreamcast=<br />
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 provided patches and scripts to make this solution work. See the rustc_codegen_gcc [https://blog.antoyo.xyz/ progress reports] for more information on the upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
=Using Rust for Dreamcast=<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3494Rust on Dreamcast2024-02-19T01:48:19Z<p>Darc: /* Building rustc_codegen_gcc to develop on Dreamcast */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Building rustc_codegen_gcc to develop on Dreamcast=<br />
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 provided patches and scripts to make this solution work. See the rustc_codegen_gcc [https://blog.antoyo.xyz/ progress reports] for more information on the upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
=Using Rust for Dreamcast=<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3493Rust on Dreamcast2024-02-19T01:25:51Z<p>Darc: </p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Building rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
=Using Rust for Dreamcast=<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3492Rust on Dreamcast2024-02-19T01:00:27Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
[[File:Rust-Cube rustc codegen-gcc demo.gif|thumb|cube example in action]]<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=File:Rust-Cube_rustc_codegen-gcc_demo.gif&diff=3491File:Rust-Cube rustc codegen-gcc demo.gif2024-02-19T00:59:39Z<p>Darc: Uploaded own work with UploadWizard</p>
<hr />
<div>=={{int:filedesc}}==<br />
{{Information<br />
|description={{no|1=Rust-Cube rustc_codegen-gcc demo}}<br />
|date=2024-02-18<br />
|source={{own}}<br />
|author=[[User:Darc|Darc]]<br />
|permission=<br />
|other versions=<br />
}}<br />
<br />
=={{int:license-header}}==<br />
{{licensing|generic}}</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3490Rust on Dreamcast2024-02-19T00:54:56Z<p>Darc: </p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3489Rust on Dreamcast2024-02-19T00:24:45Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
While not shown here, in the example 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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3488Rust on Dreamcast2024-02-19T00:24:24Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
We don't need to add the paths to these libraries, 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 do that like so:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3487Rust on Dreamcast2024-02-19T00:23:42Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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 add a <code>build.rs</code> file to the root of the crate with the following code:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3486Rust on Dreamcast2024-02-19T00:22:36Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
fn main() {<br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
}<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3485Rust on Dreamcast2024-02-19T00:20:42Z<p>Darc: /* Creating a new Rust project with Cargo */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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/hello.elf</code> file that can be sent to the Dreamcast with <code>dc-tool</code>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3484Rust on Dreamcast2024-02-19T00:19:56Z<p>Darc: /* Creating a new Rust project with Cargo */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hello<br />
cd hello<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3483Rust on Dreamcast2024-02-19T00:15:34Z<p>Darc: /* Building rustc_codegen_gcc */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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>. You'll need to add the path to those scripts to your <code>PATH</code> environment variable:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3482Rust on Dreamcast2024-02-19T00:12:45Z<p>Darc: /* Compiling individual modules into object files with rustc */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the Rust-for-Dreamcast repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3481Rust on Dreamcast2024-02-19T00:11:43Z<p>Darc: /* Creating a Rust library */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the Rust-for-Dreamcast repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the <code>rust-for-dreamcast</code> repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3480Rust on Dreamcast2024-02-19T00:10:52Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
The [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] example included in the Rust-for-Dreamcast 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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the <code>rust-for-dreamcast</code> repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the <code>rust-for-dreamcast</code> repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3479Rust on Dreamcast2024-02-19T00:09:28Z<p>Darc: /* Using rustc_codegen_gcc to develop on Dreamcast */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 upstream project's progress.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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 later, you may also need to rebuild libgccjit to pull down new changes rustc_codegen_gcc depends upon as well.<br />
<br />
We will first clone the Rust-for-Dreamcast repository, which contains various supporting files needed to create Rust support for Dreamcast. Using <code>git</code>, clone the repository to <code>/opt/toolchains/dc/rust</code>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the rustc_codegen_gcc toolchain configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Now that we have libgccjit built, we can use rustc_codegen_gcc to interface with it to generate SuperH machine code from Rust. Clone the rustc_codegen_gcc repository to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
The Rust-for-Dreamcast 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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
You may also want to add the above line to your shell's startup file or else you'll need to re-run it every time you start a new shell.<br />
<br />
Now we can use the included Rust-for-Dreamcast scripts to set up rustc_codegen_gcc. Patches need to be applied to rustc_codegen_gcc for it to compile properly for our target platform. Let's apply them:<br />
rcg-dc patch<br />
Now we can prepare and build rustc_codegen_gcc!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* the <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* the <code>rustc-dc</code> script can be used to compile Rust modules<br />
* the <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-hello <code>cargo-hello</code>] demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-cube <code>cargo-cube</code>] demonstrates a Rust project using KallistiOS with GLdc<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
==Creating a new Rust project with Cargo==<br />
First, we'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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 Rust-for-Dreamcast repo. <br />
<br />
The Cargo-based 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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the <code>rust-for-dreamcast</code> repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the <code>rust-for-dreamcast</code> repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3478Rust on Dreamcast2024-02-18T23:52:46Z<p>Darc: /* Prerequisites */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 <code>rust-for-dreamcast</code> 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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <code>xxd</code> might be part of <code>vim</code> depending on the organization of your operating system's package manager. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
We'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the <code>rust-for-dreamcast</code> repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the <code>rust-for-dreamcast</code> repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3477Rust on Dreamcast2024-02-18T23:15:27Z<p>Darc: /* Prerequisites */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 <code>rust-for-dreamcast</code> 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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages for your operating system. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
We'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the <code>rust-for-dreamcast</code> repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the <code>rust-for-dreamcast</code> repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3476Rust on Dreamcast2024-02-18T22:15:16Z<p>Darc: /* Prerequisites */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 <code>rust-for-dreamcast</code> 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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
* Install the <code>jq</code> and <code>xxd</code> packages. <br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
We'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the <code>rust-for-dreamcast</code> repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the <code>rust-for-dreamcast</code> repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3475Rust on Dreamcast2024-02-18T22:08:32Z<p>Darc: /* Using rustc_codegen_gcc to develop on Dreamcast */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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 <code>rust-for-dreamcast</code> 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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
We'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the <code>rust-for-dreamcast</code> repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the <code>rust-for-dreamcast</code> repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3474Rust on Dreamcast2024-02-18T22:07:37Z<p>Darc: </p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
We'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the <code>rust-for-dreamcast</code> repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the <code>rust-for-dreamcast</code> repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Getting_Started_with_Dreamcast_development&diff=3473Getting Started with Dreamcast development2024-02-18T21:19:57Z<p>Darc: /* Burning an example program to CD-R */</p>
<hr />
<div><div style="float:right;">__TOC__</div><br />
=Introduction=<br />
This article will cover the entire beginning process: starting from zero to having a working dev environment with debug link (serial or IP) and self-booting CD-R. This guide will cover the process for the following platforms:<br />
* '''Microsoft Windows 10''' via [https://learn.microsoft.com/en-us/windows/wsl/about Windows Subsystem for Linux]<br />
** Users desiring a native Windows approach, see [[DreamSDK]] instead<br />
* '''macOS''' on Intel or Apple Silicon systems with the [https://brew.sh/ Homebrew] package manager installed<br />
* '''Linux'''-based systems<br />
** '''Debian'''- and '''Ubuntu'''-based distributions using the default '''apt''' package manger<br />
** '''Fedora'''-based distributions using the default '''dnf''' package manager<br />
** '''Arch'''-based distributions using the default '''pacman''' package manager<br />
** '''Alpine'''-based distributions using the default '''apk''' package manager<br />
<br />
===Need help?===<br />
Important note: ''This guide aims to remain up to date and work on all of the above platforms, but keeping instructions for such a variety of platforms up-to-date can be difficult. 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.''<br />
<br />
===Terms===<br />
Before we get started, let's define several terms:<br />
<br />
The '''toolchain''' is a set of programs which turns your code into an executable file for your Dreamcast console. The toolchain includes:<br />
* '''GCC''', a compiler which supports C, C++, Objective-C, and Objective-C++ on Dreamcast (with D and Rust support coming soon)<br />
* '''binutils''', an assembler and linker<br />
* '''newlib''', a C library<br />
* '''gdb''', a debugger<br />
The toolchain includes a compiler for the Dreamcast's main SH4 CPU, and optionally a compiler for the ARM-based AICA sound processor. Your operating system may already have versions of these programs installed to compile code for your computer, but we will need to build a "cross-compiler" for compiling specifically for the Dreamcast. <br />
<br />
'''KallistiOS''' or ''KOS'' is an open source development library and pseudo-operating system for the Dreamcast console. It is the best documented and most widely used development kit in the homebrew community. KallistiOS's very flexible license allows both homebrew and commercial use with no restrictions other than a requirement to include credit for its use in your project, and indeed almost all commercially sold indie Dreamcast titles use it. There are others in existence, like [[libronin]] and [[libdream]], as well as the older development kits [[Katana]] and [[Windows CE]] created by Sega and Microsoft for use in retail games, but this guide will only cover the setup and use of KallistiOS. <br />
<br />
'''kos-ports''' is a repository including various libraries which integrate with KallistiOS. We will download and compile these libraries as well. <br />
<br />
The '''debug link''' is a generic term referring to a hardware accessory to facilitate quickly running and debugging your programs. IP-based links include the Dreamcast's '''[[Broadband adapter]]''' and '''[[LAN adapter]]''' accessories, and serial-based links include the [[Coder's cable]], which is a cable that can connect the Dreamcast's serial port to your computer via USB or serial. This guide includes instructions for setting up and using the the Broadband adapter and a USB-based coder's cable.<br />
<br />
'''dc-tool''' and '''dcload''' are a pair of programs to facilitate using a debug link. ''dc-tool'' runs on your computer and links to a Dreamcast running ''dcload-ip'' or ''dcload-serial''. With this setup, you can quickly load programs, read console feedback, load assets, transfer data, redirect I/O, handle exceptions, debug problems, and so forth.<br />
<br />
=Choosing a debug link solution=<br />
If you are building the toolchain for the purpose of building existing programs from source with little to no modifications, then a debug link setup might not be necessary for you. You may simply build programs to burn directly to CD-R. However, if you are planning to actively develop for the Dreamcast, then a debug link is a critical component. While Dreamcast emulators are mature and accurate enough to play the vast majority of the system's games library without issue, many critical bugs may show up on a real Dreamcast system, but not on a Dreamcast emulator. Therefore, it is highly recommended to test on a real system as much as possible. It's also possible to load software off of a [[Serial SD card adapter]], but without an active link to a computer, debugging and stepping through programs as they execute is significantly more challenging.<br />
<br />
Presented below is a table comparing the different options available for a debug link. Due to the cost, potential buyers may want to factor in the ability to play multiplayer games with their purchase. Thus, for comparison, we have included information about the [[Modem]] with [[DreamPi]] as well, but understand that the Modem with DreamPi cannot be used as a debug link.<br />
<br />
{| class="wikitable"<br />
!colspan="6" |Comparison of various Dreamcast connectivity options<br />
|-<br />
|style="background-color:#c0c0c0;" width="150" | Device: <br />
|style="background-color:#d0d0d0;" width="400" | [[Broadband adapter]] (HIT-400 or HIT-401) <br />Realtek RTL8139C chipset<br />
|style="background-color:#d0d0d0;" width="400" | [[LAN adapter]] (HIT-300) <br />Fujitsu MB86967 chipset<br />
|style="background-color:#d0d0d0;" width="400" | [[Modem]] with [[DreamPi]]<br />
|style="background-color:#d0d0d0;" width="400" | USB [[Coder's cable]]<br />
|style="background-color:#d0d0d0;" width="400" | Serial [[Coder's cable]]<br />
|-<br />
|style="background-color:#d0d0d0;" | Useful for dev? || Yes, supports dcload-ip || Yes, supports dcload-ip,<br/>but BBA is superior and cheaper || No, only useful for online multiplayer gaming || Yes, supports dcload-serial || Yes, supports dcload-serial<br />
|-<br />
|style="background-color:#d0d0d0;" | Cost || $100 - $200 and up on used markets || $200 and up on used markets,<br/>due to extreme rarity || Kit prices vary, around $100 || See [[Coder's cable]] for vendors<br />darc sells for $45, RetroOnyx sells for $85 || Varies on used markets, uncommonly sold<br />
|-<br />
|style="background-color:#d0d0d0;" | Can make DIY? || No || No || Yes || Yes || Yes<br />
|-<br />
|style="background-color:#d0d0d0;" | Performance || Up to 100 megabits/s || Up to 10 megabits/s || Up to 56 kilobits/s || Up to 1500 kilobits/s || Up to 120 kilobits/s<br />
|-<br />
|style="background-color:#d0d0d0;" | Games support || Some games: Phantasy Star Online, Quake III Arena, Toy Racer, POD SpeedZone, Propellor Arena, Unreal Tournament<br />Some browsers: Broadband Passport, PlanetWeb 3.0 || No games<br />One browser: Dream Passport for LAN || All multiplayer games with network support<br />All web browsers || NO multiplayer games support || NO multiplayer games support <br />
|-<br />
|style="background-color:#d0d0d0;" | Homebrew support || Homebrew utilities like dcload-ip || Homebrew utilities like dcload-ip || Homebrew utilities don't support, only multiplayer games || Homebrew utilities like dcload-serial || Homebrew utilities like dcload-serial <br />
|}<br />
<br />
=Setting up and compiling the toolchain with the dc-chain script=<br />
===Dependencies===<br />
First, we'll need to install dependencies before building the toolchain. Below we have provided commands to install these dependencies on various systems. Many of the packages will likely already be installed on your system, but we have provided an exhaustive list for good measure.<br />
====macOS 13 Ventura on an Intel or Apple Silicon processor====<br />
First, make sure you install Apple Xcode, including the Command Line tools. You will also need to install several other packages for which we'll include instructions assuming you have installed the [https://brew.sh/ Homebrew] package manager on your system.<br />
brew install wget gettext texinfo gmp mpfr libmpc libelf jpeg-turbo libpng meson libisofs<br />
<br />
''Important Note for Apple Silicon users'': On Apple Silicon, Homebrew installs libraries to a path not included by default by the compiler. If you haven't added these to your '''~/.zprofile''', then add the following lines now and reload your session (or run them in your Terminal session whenever you compile KOS):<br />
export CPATH=/opt/homebrew/include<br />
export LIBRARY_PATH=/opt/homebrew/lib<br />
<br />
====Debian/Ubuntu-based Linux====<br />
sudo apt-get update<br />
sudo apt install gawk patch bzip2 tar make libgmp-dev libmpfr-dev libmpc-dev gettext wget libelf-dev texinfo bison flex sed git build-essential diffutils curl libjpeg-dev libpng-dev python3 pkg-config libisofs-dev meson ninja-build<br />
<br />
====Fedora-based Linux====<br />
sudo dnf install gawk patch bzip2 tar make gmp-devel mpfr-devel libmpc-devel gettext wget elfutils-libelf-devel texinfo bison flex sed git diffutils curl libjpeg-turbo-devel libpng-devel gcc-c++ python3 rubygem-rake meson ninja-build<br />
<br />
====Arch-based Linux====<br />
sudo pacman -S --needed gawk patch bzip2 tar make gmp mpfr libmpc gettext wget libelf texinfo bison flex sed git diffutils curl libjpeg-turbo libpng python3 meson<br />
<br />
====Alpine-based Linux====<br />
sudo apk --update add build-base patch bash texinfo gmp-dev libjpeg-turbo-dev libpng-dev elfutils-dev curl wget python3 git<br />
<br />
====Other Linux distributions====<br />
If you're using a different Linux- or Unix-based system besides the one above, you may need to reference your distribution's package database and package manager documentation for the equivalent package names and commands necessary for your system.<br />
<br />
===Creating a space for your toolchain installation===<br />
Create the path where we'll install the toolchain and KOS, and grant it the proper permissions:<br />
sudo mkdir -p /opt/toolchains/dc<br />
sudo chmod -R 755 /opt/toolchains/dc<br />
sudo chown -R $(id -u):$(id -g) /opt/toolchains/dc<br />
===Cloning the KOS git repository===<br />
Clone the KOS git repository to your system:<br />
git clone https://github.com/KallistiOS/KallistiOS.git /opt/toolchains/dc/kos<br />
<br />
===Configuring the dc-chain script===<br />
Enter the dc-chain directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
<br />
We will choose the default '''stable''' configuration for the toolchain, which currently uses GCC 13.2. For advanced users, other configurations are available to you; read the <code>README.md</code> file in the dc-chain directory for more information if you are interested.<br />
cp config/config.mk.stable.sample config.mk<br />
<br />
Now, you may configure config.mk options to your liking by using a text editor. You may alter the <code>makejobs</code> parameter to the number of threads available on your CPU to speed up the compilation, if desired. However, if you run into errors during compilation, you may want to set <code>makejobs=-j1</code>, as on some operating systems the toolchain may fail to build with a higher setting.<br />
<br />
===Downloading and compiling the toolchain===<br />
Now we will run a script to download files and compile the toolchain. At this point, we have the option of building both the main CPU SH4 compiler and the AICA sound processor ARM compiler, or we can skip the ARM compiler and just build the SH4 compiler. Thankfully, KallistiOS includes a prebuilt sound driver, so the ARM compiler is only necessary if you're wanting to make changes to the sound driver or write custom code to run on the sound processor.<br />
To build '''only the SH4 compiler''':<br />
make build-sh4<br />
To build '''both''' the SH4 and the ARM compilers:<br />
make<br />
This will download and unpack the relevant necessary files and then begin the compilation process. The compilation can take anywhere from minutes to a few hours depending on your CPU and number of threads available. When successfully finished, the toolchains will be ready.<br />
<br />
Afterwards, if desired, you may also compile the GNU Debugger (gdb) as well:<br />
make gdb<br />
<br />
The GNU Debugger is now installed along with your toolchains.<br />
<br />
===Cleaning up temporary files===<br />
After building everything, you can clean up the extraneous files in your dc-chain directory by entering:<br />
make clean<br />
<br />
=Configuring and compiling KOS and kos-ports=<br />
===Setting up the environment settings===<br />
Enter the KOS directory:<br />
cd /opt/toolchains/dc/kos<br />
Copy the pre-made environment script into place:<br />
cp doc/environ.sh.sample environ.sh <br />
For most users, the default settings will suffice. However, advanced users may edit the environ.sh to your liking if you'd like to change compile flags or alter paths. If you'd like to have multiple KOS versions installed or multiple toolchain versions installed, you can set up different environ.sh files corresponding to these different configurations by altering the paths. Run the source command on the desired environ.sh file to select that configuration prior to compiling your project. <br />
<br />
You will need to run the source command to apply the KOS environment settings to your currently running shell. Run the following now, '''and''' ''whenever'' you open a new shell to work on Dreamcast projects:<br />
source /opt/toolchains/dc/kos/environ.sh<br />
<br />
===Building KOS===<br />
Build KOS:<br />
make<br />
KOS is now built.<br />
===Building kos-ports===<br />
Clone the kos-ports repository to your system:<br />
git clone --recursive https://github.com/KallistiOS/kos-ports /opt/toolchains/dc/kos-ports<br />
Run the script to build all of the included ports:<br />
/opt/toolchains/dc/kos-ports/utils/build-all.sh<br />
kos-ports is now built.<br />
<br />
===Building the KOS examples===<br />
Enter the KOS examples directory:<br />
cd /opt/toolchains/dc/kos/examples/dreamcast<br />
Build the examples:<br />
make<br />
All of the example programs provided with KallistiOS are now built.<br />
<br />
=Further=<br />
At this point, you will have a cross-compiler built so that you can generate programs for your Dreamcast from code. KallistiOS, the Dreamcast development library and operating system, is built, as is its associated packages within kos-ports, and all of its example programs.<br />
<br />
At this point, you will likely want to view some of the following articles:<br />
* Setting up an IDE for Dreamcast development<br />
** [[Qt Creator Dreamcast Development Environment]]<br />
** [[CLion Debugging]]<br />
** [[Visual Studio Code]]<br />
* [[Creating a first project with KallistiOS]] ('''TODO''': Explain how to create a new DC project folder with Makefile, adding an external library, create a basic program, and compile it)<br />
* [[Using a debug link]]<br />
* [[Burning an example to CD-R]]<br />
<br />
=Running an example program through a debug link=<br />
'''TODO''': ''Give a tutorial on compiling dcload/dc-tool, setting up a serial, USB, or IP debug link, and running 2ndmix demo on real hardware.''<br />
<br />
Download and burn the [[:File:Dcload-2023-06-22.zip|latest versions of dcload-ip or dcload-serial]] -- the IP version includes improved DHCP support, so there is no longer a need to configure things beforehand. <br />
<br />
Run one of the examples from the <code>kos/examples/dreamcast</code> directory with one of the following commands:<br />
dc-tool-ip -t <dreamcast IP address> -x example.elf<br />
<br />
dc-tool-ser -t <serial device path> -x example.elf<br />
<br />
Run <code>dc-tool-ip</code> or <code>dc-tool-ser</code> without any parameters to get additional options.<br />
<br />
=Burning an example program to CD-R=<br />
'''TODO''': ''Explain how to build mkdcdisc and write 2ndmix demo to CD-R and run on a Dreamcast console''<br />
<br />
[https://gitlab.com/simulant/mkdcdisc mkdcdisc] can be used to easily generate a burnable self-boot CDI image.<br />
Dependencies required:<br />
* Homebrew: <code>meson libisofs</code><br />
* Debian/Ubuntu: <code>libisofs-dev meson ninja-build</code><br />
* Fedora: <code>meson ninja-build</code><br />
* Arch: <code>meson</code><br />
<br />
Build <code>mkdcdisc</code>:<br />
git clone https://gitlab.com/simulant/mkdcdisc.git<br />
cd mkdcdisc<br />
meson setup builddir<br />
meson compile -C builddir<br />
./builddir/mkdcdisc -h<br />
and create a CDI image from your compiled ELF like so:<br />
mkdcdisc -e MyProgram.elf -o MyProgram.cdi<br />
Then you can burn the CDI file using DiscJuggler (Windows-only, but also works through [https://www.winehq.org/ WINE]), ImgBurn with the CDI plugin, or the cdiburn *nix script floating around out there. (document this better)</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3472Rust on Dreamcast2024-02-18T17:59:36Z<p>Darc: /* Compiling individual modules into object files with rustc */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
We'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the <code>rust-for-dreamcast</code> repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
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/darcagn/rust-for-dreamcast/tree/master/examples/rustc-hello <code>rustc-hello</code>] example included in the <code>rust-for-dreamcast</code> repo.<br />
<br />
Instead of using <code>cargo-dc</code>, we can invoke the <code>rustc-dc</code> script in our <code>Makefile</code> to build Rust modules. 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. 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:<br />
<syntaxhighlight lang="make"><br />
OBJS = hello_c.o hello_rust.o<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3471Rust on Dreamcast2024-02-18T17:34:41Z<p>Darc: /* Creating a Rust library */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
We'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the <code>rust-for-dreamcast</code> repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc 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:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3470Rust on Dreamcast2024-02-18T17:30:03Z<p>Darc: /* Creating a Rust library */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
We'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the <code>rust-for-dreamcast</code> repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc new --lib addlib</code> to specify that we're creating a library. You'll also need to add the following text to this project's <code>Cargo.toml</code> file:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
Then, we can use the code in our C source:<br />
<syntaxhighlight lang="c"><br />
/* Declare the external function from the Rust library */<br />
int add_integers(int a, int b);<br />
<br />
/* Use the function */<br />
printf("Five plus six is %d\n", add_integers(5, 6));<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3469Rust on Dreamcast2024-02-18T17:22:48Z<p>Darc: /* Creating a Rust library for Dreamcast */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
We'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library==<br />
Next, we'll demonstrate creating a Rust library with <code>cargo-dc</code> that can be included in other Dreamcast code. This will follow the [https://github.com/darcagn/rust-for-dreamcast/tree/master/examples/cargo-addlib <code>cargo-addlib</code>] example included in the <code>rust-for-dreamcast</code> repo. Once again, this project's initial setup is done the same as the above <code>cargo-hello</code> example, but you'll create the new project using <code>cargo-dc new --lib addlib</code> to specify that we're creating a library. You'll also need to add the following text to this project's <code>Cargo.toml</code> file:<br />
<br />
<syntaxhighlight lang="toml"><br />
[lib]<br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
<br />
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>:<br />
<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
extern crate alloc;<br />
use kos::print;<br />
<br />
#[no_mangle]<br />
pub extern "C" fn print_added(a: isize, b: isize) {<br />
print!("{}", a + b);<br />
}<br />
<br />
#[no_mangle]<br />
pub extern "C" fn add_integers(a: isize, b: isize) -> isize {<br />
a + b<br />
}<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
When built using <code>cargo-dc 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.<br />
<br />
For example, this can be added to a standard <code>Makefile</code>-based KallistiOS project by editing the <code>Makefile</code>:<br />
<br />
<syntaxhighlight lang="make"><br />
$(TARGET): $(OBJS)<br />
kos-cc -o $(TARGET) $(OBJS) -L/opt/toolchains/dc/rust/examples/cargo-addlib/target/sh-elf/debug -laddlib<br />
</syntaxhighlight><br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3468Rust on Dreamcast2024-02-18T16:58:19Z<p>Darc: /* Creating a new Rust project with Cargo */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
We'll demonstrate creating a new "Hello, world!" project with <code>cargo-dc</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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3467Rust on Dreamcast2024-02-18T16:58:05Z<p>Darc: /* Creating a new project using Cargo */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3466Rust on Dreamcast2024-02-18T16:52:12Z<p>Darc: /* Using rustc_codegen_gcc to develop on Dreamcast */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images using metadata specified in <code>Cargo.toml</code><br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].<br />
<br />
==Creating a new project using Cargo==<br />
<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.<br />
<br />
First, let's clone the [https://github.com/darcagn/kos-rs '''kos-rs'''] repo:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
Create a new crate using <code>cargo-dc</code>:<br />
cargo-dc new example --lib<br />
Change the crate to a static library in <code>Cargo.toml</code> by changing the <code>crate-type</code> as follows:<br />
<syntaxhighlight lang="toml"><br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
Add the '''kos-rs''' crate to your <code>Cargo.toml</code> file:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Add the following function to your crate's <code>src/lib.rs</code> file:<br />
<syntaxhighlight lang="rust"><br />
#[no_mangle]<br />
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {<br />
[...]<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
The <code>rust_main()</code> function will serve as the entry point to your Rust code.<br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3465Rust on Dreamcast2024-02-18T16:51:01Z<p>Darc: /* Using rustc_codegen_gcc to develop on Dreamcast */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* '''libc''' support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images<br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].<br />
<br />
==Creating a new project using Cargo==<br />
<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.<br />
<br />
First, let's clone the [https://github.com/darcagn/kos-rs '''kos-rs'''] repo:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
Create a new crate using <code>cargo-dc</code>:<br />
cargo-dc new example --lib<br />
Change the crate to a static library in <code>Cargo.toml</code> by changing the <code>crate-type</code> as follows:<br />
<syntaxhighlight lang="toml"><br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
Add the '''kos-rs''' crate to your <code>Cargo.toml</code> file:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Add the following function to your crate's <code>src/lib.rs</code> file:<br />
<syntaxhighlight lang="rust"><br />
#[no_mangle]<br />
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {<br />
[...]<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
The <code>rust_main()</code> function will serve as the entry point to your Rust code.<br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3464Rust on Dreamcast2024-02-18T16:49:55Z<p>Darc: /* Integrating a Cargo project with a KallistiOS project */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* libc support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images<br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].<br />
<br />
==Creating a new project using Cargo==<br />
<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.<br />
<br />
First, let's clone the [https://github.com/darcagn/kos-rs '''kos-rs'''] repo:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
Create a new crate using <code>cargo-dc</code>:<br />
cargo-dc new example --lib<br />
Change the crate to a static library in <code>Cargo.toml</code> by changing the <code>crate-type</code> as follows:<br />
<syntaxhighlight lang="toml"><br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
Add the '''kos-rs''' crate to your <code>Cargo.toml</code> file:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Add the following function to your crate's <code>src/lib.rs</code> file:<br />
<syntaxhighlight lang="rust"><br />
#[no_mangle]<br />
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {<br />
[...]<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
The <code>rust_main()</code> function will serve as the entry point to your Rust code.<br />
<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3463Rust on Dreamcast2024-02-18T16:46:48Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* libc support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images<br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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.<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].<br />
<br />
==Creating a new project using Cargo==<br />
<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.<br />
<br />
First, let's clone the [https://github.com/darcagn/kos-rs '''kos-rs'''] repo:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
Create a new crate using <code>cargo-dc</code>:<br />
cargo-dc new example --lib<br />
Change the crate to a static library in <code>Cargo.toml</code> by changing the <code>crate-type</code> as follows:<br />
<syntaxhighlight lang="toml"><br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
Add the '''kos-rs''' crate to your <code>Cargo.toml</code> file:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Add the following function to your crate's <code>src/lib.rs</code> file:<br />
<syntaxhighlight lang="rust"><br />
#[no_mangle]<br />
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {<br />
[...]<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
The <code>rust_main()</code> function will serve as the entry point to your Rust code.<br />
<br />
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.<br />
<br />
==Integrating a Cargo project with a KallistiOS project==<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3462Rust on Dreamcast2024-02-18T16:40:41Z<p>Darc: /* Creating a new Rust project with Cargo */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* libc support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images<br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust" line><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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>.<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].<br />
<br />
==Creating a new project using Cargo==<br />
<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.<br />
<br />
First, let's clone the [https://github.com/darcagn/kos-rs '''kos-rs'''] repo:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
Create a new crate using <code>cargo-dc</code>:<br />
cargo-dc new example --lib<br />
Change the crate to a static library in <code>Cargo.toml</code> by changing the <code>crate-type</code> as follows:<br />
<syntaxhighlight lang="toml"><br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
Add the '''kos-rs''' crate to your <code>Cargo.toml</code> file:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Add the following function to your crate's <code>src/lib.rs</code> file:<br />
<syntaxhighlight lang="rust"><br />
#[no_mangle]<br />
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {<br />
[...]<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
The <code>rust_main()</code> function will serve as the entry point to your Rust code.<br />
<br />
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.<br />
<br />
==Integrating a Cargo project with a KallistiOS project==<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3461Rust on Dreamcast2024-02-18T16:38:03Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* libc support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images<br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust"><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
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>.<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].<br />
<br />
==Creating a new project using Cargo==<br />
<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.<br />
<br />
First, let's clone the [https://github.com/darcagn/kos-rs '''kos-rs'''] repo:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
Create a new crate using <code>cargo-dc</code>:<br />
cargo-dc new example --lib<br />
Change the crate to a static library in <code>Cargo.toml</code> by changing the <code>crate-type</code> as follows:<br />
<syntaxhighlight lang="toml"><br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
Add the '''kos-rs''' crate to your <code>Cargo.toml</code> file:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Add the following function to your crate's <code>src/lib.rs</code> file:<br />
<syntaxhighlight lang="rust"><br />
#[no_mangle]<br />
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {<br />
[...]<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
The <code>rust_main()</code> function will serve as the entry point to your Rust code.<br />
<br />
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.<br />
<br />
==Integrating a Cargo project with a KallistiOS project==<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3460Rust on Dreamcast2024-02-18T16:33:27Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* libc support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images<br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust"><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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.<br />
<br />
'' '''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.''<br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
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:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-search=native={}", lib_path);<br />
</syntaxhighlight><br />
<br />
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.<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].<br />
<br />
==Creating a new project using Cargo==<br />
<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.<br />
<br />
First, let's clone the [https://github.com/darcagn/kos-rs '''kos-rs'''] repo:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
Create a new crate using <code>cargo-dc</code>:<br />
cargo-dc new example --lib<br />
Change the crate to a static library in <code>Cargo.toml</code> by changing the <code>crate-type</code> as follows:<br />
<syntaxhighlight lang="toml"><br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
Add the '''kos-rs''' crate to your <code>Cargo.toml</code> file:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Add the following function to your crate's <code>src/lib.rs</code> file:<br />
<syntaxhighlight lang="rust"><br />
#[no_mangle]<br />
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {<br />
[...]<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
The <code>rust_main()</code> function will serve as the entry point to your Rust code.<br />
<br />
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.<br />
<br />
==Integrating a Cargo project with a KallistiOS project==<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3459Rust on Dreamcast2024-02-18T16:13:41Z<p>Darc: /* Creating a Rust project using kos-ports libraries */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* libc support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images<br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust"><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
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 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.<br />
<br />
In <code>build.rs</code>, it's necessary to specify to <code>cargo-dc</code> that our project needs '''libGL''' and '''libm''' linked:<br />
<syntaxhighlight lang="rust"><br />
println!("cargo:rustc-link-lib=GL");<br />
println!("cargo:rustc-link-lib=m");<br />
</syntaxhighlight><br />
<br />
While not shown here, our <code>build.rs</code> also demonstrates how to convert JPG images to VQ-compressed textures with <code>vqenc</code> (included with KallistiOS) to be included in our project when <code>cargo-dc build</code> is invoked. <br />
<br />
'''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.<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].<br />
<br />
==Creating a new project using Cargo==<br />
<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.<br />
<br />
First, let's clone the [https://github.com/darcagn/kos-rs '''kos-rs'''] repo:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
Create a new crate using <code>cargo-dc</code>:<br />
cargo-dc new example --lib<br />
Change the crate to a static library in <code>Cargo.toml</code> by changing the <code>crate-type</code> as follows:<br />
<syntaxhighlight lang="toml"><br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
Add the '''kos-rs''' crate to your <code>Cargo.toml</code> file:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Add the following function to your crate's <code>src/lib.rs</code> file:<br />
<syntaxhighlight lang="rust"><br />
#[no_mangle]<br />
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {<br />
[...]<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
The <code>rust_main()</code> function will serve as the entry point to your Rust code.<br />
<br />
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.<br />
<br />
==Integrating a Cargo project with a KallistiOS project==<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3458Rust on Dreamcast2024-02-18T15:35:13Z<p>Darc: /* Creating a new Rust project with Cargo */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* libc support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images<br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
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. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust"><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
* This will follow the <code>cargo-cube</code> example<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].<br />
<br />
==Creating a new project using Cargo==<br />
<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.<br />
<br />
First, let's clone the [https://github.com/darcagn/kos-rs '''kos-rs'''] repo:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
Create a new crate using <code>cargo-dc</code>:<br />
cargo-dc new example --lib<br />
Change the crate to a static library in <code>Cargo.toml</code> by changing the <code>crate-type</code> as follows:<br />
<syntaxhighlight lang="toml"><br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
Add the '''kos-rs''' crate to your <code>Cargo.toml</code> file:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Add the following function to your crate's <code>src/lib.rs</code> file:<br />
<syntaxhighlight lang="rust"><br />
#[no_mangle]<br />
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {<br />
[...]<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
The <code>rust_main()</code> function will serve as the entry point to your Rust code.<br />
<br />
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.<br />
<br />
==Integrating a Cargo project with a KallistiOS project==<br />
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.</div>Darchttps://dreamcast.wiki/wiki/index.php?title=Rust_on_Dreamcast&diff=3457Rust on Dreamcast2024-02-18T15:32:19Z<p>Darc: /* Creating a new Rust project with Cargo */</p>
<hr />
<div>[[File:Rust-dc-logo.png|thumb|Ferris holding his Dreamcast controller]]<br />
<br />
'''WIP''': This article is currently under construction. The repos linked to below are not yet live.<br />
<br />
'''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:<br />
<br />
* '''rustc_codegen_gcc''': A libgccjit-based codegen backend for rustc (preferred method)<br />
* '''gccrs''': a Rust frontend for GCC<br />
<br />
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.<br />
<br />
=Using rustc_codegen_gcc to develop on Dreamcast=<br />
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.<br />
<br />
'''What Works'''<br />
* '''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.)<br />
* '''liballoc''' -- the core components of the language that require a heap, including collections (Vec, String, Box, etc.)<br />
* linking to KallistiOS -- KallistiOS and kos-ports can be used if one manually manages interoperating with C via '''unsafe'''<br />
* including <code>no_std</code> crates with the <code>cargo</code> build system<br />
'''Future Goals'''<br />
* libc support -- Adding KallistiOS support to Rust's libc crate<br />
* '''libstd''' support -- built-in language support for I/O, networking, threads, time and date, HashMap/HashSet, unwinding on panic, etc.<br />
* KallistiOS bindings -- properly idiomatic Rust support for KallistiOS<br />
* Inclusion as a tier 3 target officially<br />
* Expansion of <code>cargo-dc</code> to support more dcdev-specific functionallity like generating Dreamcast disc images<br />
<br />
==Prerequisites==<br />
<br />
We will build rustc_codegen_gcc support for the Dreamcast in the instructions below. Before we begin, though:<br />
* 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].<br />
** 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.<br />
** 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.<br />
* 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.<br />
<br />
''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.''<br />
<br />
==Building a cross-compiling libgccjit.so for rustc_codegen_gcc==<br />
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.<br />
* '''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.<br />
<br />
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>:<br />
git clone https://github.com/darcagn/rust-for-dreamcast /opt/toolchains/dc/rust<br />
Enter your KallistiOS installation's <code>dc-chain</code> directory:<br />
cd /opt/toolchains/dc/kos/utils/dc-chain<br />
Clear out any existing build files:<br />
make clean-keep-archives<br />
Copy the necessary toolchain patches to your <code>dc-chain</code> setup:<br />
cp /opt/toolchains/dc/rust/toolchain/*.diff patches/<br />
Copy the '''rustc_codegen_gcc''' configuration file into place:<br />
cp /opt/toolchains/dc/rust/toolchain/config.mk.rustc.sample config.mk<br />
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:<br />
make build-sh4<br />
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>.<br />
<br />
==Building rustc_codegen_gcc==<br />
Clone the '''rustc_codegen_gcc''' to your rust directory:<br />
git clone https://github.com/rust-lang/rustc_codegen_gcc.git /opt/toolchains/dc/rust/rustc_codegen_gcc<br />
'''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:<br />
echo 'gcc-path = "/opt/toolchains/dc/rust/sh-elf/lib"' > /opt/toolchains/dc/rust/rustc_codegen_gcc/config.toml<br />
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:<br />
export PATH="/opt/toolchains/dc/rust/bin:$PATH"<br />
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.<br />
<br />
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:<br />
rcg-dc patch<br />
Now we can prepare and build '''rustc_codegen_gcc'''!<br />
rcg-dc prepare<br />
rcg-dc build<br />
<br />
==Using Rust for Dreamcast==<br />
If all went well, rustc_codegen_gcc will have built successfully.<br />
<br />
You can now use the scripts included in the Rust for Dreamcast repo:<br />
* <code>rcg-dc</code> script can be used to rebuild the rustc_codegen_gcc code after updating or editing it<br />
* <code>rustc-dc</code> script can be used to compile Rust modules<br />
* <code>cargo-dc</code> script can be used to build Rust crates<br />
<br />
Examples are included with the Rust for Dreamcast repo to help you get started:<br />
* <code>cargo-hello</code> demonstrates how to create a simple "Hello, world!" application with KallistiOS using <code>cargo</code><br />
* <code>cargo-cube</code> demonstrates a Rust project using KallistiOS with GLdc<br />
* <code>cargo-addlib</code> demonstrates how to create a Rust library that can be included with a KallistiOS project<br />
* <code>rustc-hello</code> demonstrates how to compile and include a Rust module into a standard KallistiOS <code>Makefile</code>-based project<br />
<br />
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:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
<br />
==Creating a new Rust project with Cargo==<br />
We'll demonstrate creating a new "Hello, world!" project with <code>cargo</code>. This will follow the <code>cargo-hello</code> example included in the <code>rust-for-dreamcast</code> repo. <br />
<br />
In a directory of your choosing, let's invoke <code>cargo-dc</code> to create a new project and then enter the directory:<br />
cargo-dc new hellow<br />
cd hellow<br />
<br />
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:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
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:<br />
mkdir .cargo<br />
touch .cargo/config<br />
<br />
Open this new <code>.cargo/config</code> file in your text editor, add the following entry:<br />
<syntaxhighlight lang="toml"><br />
[target.sh-elf]<br />
linker = "sh-link-wrapper"<br />
</syntaxhighlight><br />
<br />
Now we can open up <code>src/main.rs</code> and write our "Hello, world!" example code:<br />
<syntaxhighlight lang="rust"><br />
#![no_std]<br />
#![no_main]<br />
extern crate alloc;<br />
use kos::println;<br />
<br />
#[no_mangle]<br />
fn main(_argc: isize, _argv: *const *const u8) -> isize {<br />
println!("Hello, world!");<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
<br />
* <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.<br />
* <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>).<br />
* <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.<br />
* <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.<br />
* 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.<br />
<br />
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>.<br />
If you have <code>KOS_LOADER</code> set in your KallistiOS environment, you can invoke it directly with <code>cargo-dc run</code>.<br />
<br />
==Creating a Rust project using kos-ports libraries==<br />
* This will follow the <code>cargo-cube</code> example<br />
<br />
==Creating a Rust library for Dreamcast==<br />
* This will follow the <code>cargo-addlib</code> example<br />
<br />
==Compiling individual modules into object files with rustc==<br />
* This will follow the <code>rustc-hello</code> example<br />
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:<br />
<syntaxhighlight lang="make"><br />
%.o: %.rs<br />
rustc-dc $< -o $@<br />
</syntaxhighlight><br />
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>.<br />
<br />
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>].<br />
<br />
==Creating a new project using Cargo==<br />
<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.<br />
<br />
First, let's clone the [https://github.com/darcagn/kos-rs '''kos-rs'''] repo:<br />
git clone https://github.com/darcagn/kos-rs /opt/toolchains/dc/rust/kos-rs<br />
Create a new crate using <code>cargo-dc</code>:<br />
cargo-dc new example --lib<br />
Change the crate to a static library in <code>Cargo.toml</code> by changing the <code>crate-type</code> as follows:<br />
<syntaxhighlight lang="toml"><br />
crate-type = ["staticlib"]<br />
</syntaxhighlight><br />
Add the '''kos-rs''' crate to your <code>Cargo.toml</code> file:<br />
<syntaxhighlight lang="toml"><br />
[dependencies]<br />
kos = { package = "kos-rs", path = "/opt/toolchains/dc/rust/kos-rs" }<br />
</syntaxhighlight><br />
<br />
Add the following function to your crate's <code>src/lib.rs</code> file:<br />
<syntaxhighlight lang="rust"><br />
#[no_mangle]<br />
pub extern "C" fn rust_main(_argc: i32, _argv: *const u8) -> i32 {<br />
[...]<br />
return 0;<br />
}<br />
</syntaxhighlight><br />
The <code>rust_main()</code> function will serve as the entry point to your Rust code.<br />
<br />
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.<br />
<br />
==Integrating a Cargo project with a KallistiOS project==<br />
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.</div>Darc