Compare commits

...

8 Commits
master ... rust

12 changed files with 957 additions and 88 deletions

2
.gitignore vendored
View File

@ -4,6 +4,7 @@
*.o
*.bin
*.dfs
*/target/
## OSX junk
.DS_Store
@ -17,3 +18,4 @@
*properties.json
/.vs
/.vscode
*/.idea/

View File

@ -15,13 +15,28 @@ HEADERTITLE = "EverDrive OS"
SRCDIR = ./src
INCDIR = ./inc
RESDIR = ./res
OBJDIR = ./obj
BINDIR = ./bin
BINDIR = ./target
OBJDIR = $(BINDIR)/obj
TOOLSDIR = ./tools
LINK_FLAGS = -O1 -L$(ROOTDIR)/lib -L$(ROOTDIR)/mips64-elf/lib -ldragon -lmad -lyaml -lc -lm -ldragonsys -lnosys $(LIBS) -Tn64ld.x
PROG_NAME = OS64P
CFLAGS = -std=gnu99 -march=vr4300 -mtune=vr4300 -O1 -I$(INCDIR) -I$(ROOTDIR)/include -I$(ROOTDIR)/mips64-elf/include -lpthread -lrt -D_REENTRANT -DUSE_TRUETYPE $(SET_DEBUG)
RUST_DIR = ./rust
RUST_TARGET_DIR = target/mips-nintendo64-none/release
RUST_FULL_TARGET_DIR = $(RUST_DIR)/$(RUST_TARGET_DIR)
RUST_DEPS := $(wildcard $(RUST_DIR)/src/*) $(wildcard $(RUST_DIR)/Cargo.*)
RUST_BIN_DEPS := $(RUST_DEPS) $(RUST_DIR)/mips-nintendo64-none.json
RUST_H_DEPS := $(RUST_DEPS) $(RUST_DIR)/cbindgen.toml
CFLAGS = -std=gnu99 -march=vr4300 -mtune=vr4300 -O1 -I$(INCDIR) -I$(ROOTDIR)/include -I$(ROOTDIR)/mips64-elf/include -I$(RUST_FULL_TARGET_DIR) -lpthread -lrt -D_REENTRANT -DUSE_TRUETYPE $(SET_DEBUG)
ifdef USE_YAML
CFLAGS += -DUSE_YAML
LIBS += -lyaml
else
endif
LIBS += -laltra64
LINK_FLAGS = -O1 -L$(ROOTDIR)/lib -L$(ROOTDIR)/mips64-elf/lib -L$(RUST_FULL_TARGET_DIR) -ldragon -lmad $(LIBS) -lc -lm -ldragonsys -lnosys -Tn64ld.x
PROG_NAME = $(BINDIR)/OS64P
ASFLAGS = -mtune=vr4300 -march=vr4300
CC = $(GCCN64PREFIX)gcc
AS = $(GCCN64PREFIX)as
@ -31,17 +46,23 @@ OBJCOPY = $(GCCN64PREFIX)objcopy
SOURCES := $(wildcard $(SRCDIR)/*.c)
OBJECTS = $(SOURCES:$(SRCDIR)/%.c=$(OBJDIR)/%.o)
$(PROG_NAME).v64: $ $(PROG_NAME).elf $(PROG_NAME).dfs
$(OBJCOPY) $(BINDIR)/$(PROG_NAME).elf $(BINDIR)/$(PROG_NAME).bin -O binary
rm -f $(BINDIR)/$(PROG_NAME).v64
$(N64TOOL) -l 4M -t $(HEADERTITLE) -h $(RESDIR)/$(HEADERNAME) -o $(BINDIR)/$(PROG_NAME).v64 $(BINDIR)/$(PROG_NAME).bin -s 1M $(BINDIR)/$(PROG_NAME).dfs
$(CHKSUM64PATH) $(BINDIR)/$(PROG_NAME).v64
$(PROG_NAME).v64: $(PROG_NAME).elf $(PROG_NAME).dfs
$(OBJCOPY) $(PROG_NAME).elf $(PROG_NAME).bin -O binary
rm -f $(PROG_NAME).v64
$(N64TOOL) -l 4M -t $(HEADERTITLE) -h $(RESDIR)/$(HEADERNAME) -o $(PROG_NAME).v64 $(PROG_NAME).bin -s 1M $(PROG_NAME).dfs
$(CHKSUM64PATH) $(PROG_NAME).v64
$(PROG_NAME).elf : $(OBJECTS)
$(RUST_FULL_TARGET_DIR)/libaltra64.a: $(RUST_BIN_DEPS)
cd $(RUST_DIR) && cargo build --release --verbose --target mips-nintendo64-none.json -Z build-std=core && touch $(RUST_TARGET_DIR)/libaltra64.a
$(RUST_FULL_TARGET_DIR)/altra64.h: $(RUST_H_DEPS)
cd $(RUST_DIR) && cbindgen -o $(RUST_TARGET_DIR)/altra64.h && touch $(RUST_TARGET_DIR)/altra64.h
$(PROG_NAME).elf : $(OBJECTS) $(RUST_FULL_TARGET_DIR)/libaltra64.a
@mkdir -p $(BINDIR)
$(LD) -o $(BINDIR)/$(PROG_NAME).elf $(OBJECTS) $(LINK_FLAGS)
$(LD) -o $(PROG_NAME).elf $(OBJECTS) $(LINK_FLAGS)
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c
$(OBJECTS): $(OBJDIR)/%.o : $(SRCDIR)/%.c $(RUST_FULL_TARGET_DIR)/altra64.h
@mkdir -p $(OBJDIR)
$(CC) $(CFLAGS) -c $< -o $@
@ -49,7 +70,8 @@ copy: $(PROG_NAME).v64
sh $(TOOLSDIR)/upload.sh
$(PROG_NAME).dfs:
$(MKDFSPATH) $(BINDIR)/$(PROG_NAME).dfs $(RESDIR)/filesystem/
@mkdir -p $(BINDIR)
$(MKDFSPATH) $(PROG_NAME).dfs $(RESDIR)/filesystem/
all: $(PROG_NAME).v64
@ -58,4 +80,4 @@ debug: $(PROG_NAME).v64
debug: SET_DEBUG=-DDEBUG
clean:
rm -f $(BINDIR)/*.v64 $(BINDIR)/*.elf $(OBJDIR)/*.o $(BINDIR)/*.bin $(BINDIR)/*.dfs
rm -rf ./target ./rust/target

View File

@ -14,7 +14,7 @@ The original version overwrote 1 save file per game on system reset, but if you
If you want to build the menu, you need an n64 toolchain. This is terrible to build, I ended up creating a Dockerfile in the docker folder, instructions included in it.
Or if you trust me, you can use the one I built and pushed to docker hub, [moparisthebest/altra64-dev](https://hub.docker.com/r/moparisthebest/altra64-dev)
Or if you trust me, you can use the one I built and pushed to docker hub, [moparisthebest/altra64-rust-dev](https://hub.docker.com/r/moparisthebest/altra64-rust-dev)
### Build `Altra64`
@ -23,7 +23,7 @@ To build the Rom
from the projects root directory, with docker installed
```
$ docker run --rm -v "$(pwd):/build" moparisthebest/altra64-dev make
$ docker run --rm -v "$HOME/.cargo/registry:/root/.cargo/registry" -v "$(pwd):/build" moparisthebest/altra64-rust-dev make
```
If it all worked, you will find `OS64.v64` in the `bin` directory.
@ -34,7 +34,7 @@ Finally, we can clean the build objects from the project
from the projects root directory
```
$ docker run --rm -v "$(pwd):/build" moparisthebest/altra64-dev make clean
$ docker run --rm -v "$(pwd):/build" moparisthebest/altra64-rust-dev make clean
```
Enjoy!

View File

@ -0,0 +1,165 @@
#!/bin/bash
#
# Copyright (c) 2017 The Altra64 project contributors
# See LICENSE file in the project root for full license information.
#
set -euxo pipefail
# Download and install latest updates for the system [sudo req.]
apt-get update
apt-get -y upgrade
# Install essential packages [sudo req.]
apt-get -y install wget build-essential git texinfo libc6 libgmp-dev libmpfr-dev libmpc-dev libpng-dev zlib1g-dev libtool autoconf
# change to the users root directory
cd ~/
# add a system variable and make it perminent
# echo 'N64_INST=/usr/local/libdragon' >> /etc/environment
# echo 'export N64_INST=/usr/local/libdragon' >> ~/.bashrc
export N64_INST=/usr/local/libdragon
# EDIT THIS LINE TO CHANGE YOUR INSTALL PATH!
export INSTALL_PATH=/usr/local/libdragon
# Set up path for newlib to compile later
export PATH=$PATH:$INSTALL_PATH/bin
# Versions
export BINUTILS_V=2.27
export GCC_V=6.2.0
export NEWLIB_V=2.4.0
export BINUTILS_V=2.37
#export GCC_V=11.2.0
#export NEWLIB_V=4.1.0
export GCC_V=4.6.4
# make a build folder for libdragon
mkdir build_gcc
cd build_gcc
# Download stage
#wget -c ftp://sourceware.org/pub/binutils/releases/binutils-$BINUTILS_V.tar.bz2
wget -c https://sourceware.org/pub/binutils/releases/binutils-$BINUTILS_V.tar.xz
wget -c https://sourceware.org/pub/gcc/releases/gcc-$GCC_V/gcc-$GCC_V.tar.bz2
#wget -c https://sourceware.org/pub/gcc/releases/gcc-$GCC_V/gcc-$GCC_V.tar.xz
wget -c https://sourceware.org/pub/newlib/newlib-$NEWLIB_V.tar.gz
# Extract stage
test -d binutils-$BINUTILS_V || tar -xvJf binutils-$BINUTILS_V.tar.xz || tar -xvjf binutils-$BINUTILS_V.tar.bz2
test -d gcc-$GCC_V || tar -xvJf gcc-$GCC_V.tar.xz || tar -xvjf gcc-$GCC_V.tar.bz2
test -d newlib-$NEWLIB_V || tar -xvzf newlib-$NEWLIB_V.tar.gz
# Binutils and newlib support compiling in source directory, GCC does not
# Compile binutils
cd binutils-$BINUTILS_V
./configure --prefix=${INSTALL_PATH} --target=mips64-elf --with-cpu=mips64vr4300 --disable-werror
make -j9
make install
cd ..
# Compile gcc (pass 1)
rm -rf gcc_compile
mkdir gcc_compile
cd gcc_compile
CFLAGS_FOR_TARGET="-G0 -mabi=32 -march=vr4300 -mtune=vr4300 -O2" ../gcc-$GCC_V/configure --prefix=${INSTALL_PATH} --target=mips64-elf --enable-languages=c --without-headers --with-newlib --with-system-zlib --disable-libssp --enable-multilib --disable-shared --with-gcc --with-gnu-ld --with-gnu-as --disable-threads --disable-win32-registry --disable-nls --disable-debug --disable-libmudflap --disable-werror
make -j9
make install
cd ..
# hacky hack hack
mv /usr/local/libdragon/bin/mips64-elf-gcc /usr/local/libdragon/bin/mips64-elf-gcc.orig
cat > /usr/local/libdragon/bin/mips64-elf-gcc <<EOF
#!/bin/bash
set +x
exec /usr/local/libdragon/bin/mips64-elf-gcc.orig -mabi=32 -mtune=vr4300 -march=vr4300 "\$@"
EOF
chmod +x /usr/local/libdragon/bin/mips64-elf-gcc
# Compile newlib
cd newlib-$NEWLIB_V
CFLAGS_FOR_TARGET="-G0 -march=vr4300 -mtune=vr4300 -O2" CFLAGS="-O2" CXXFLAGS="-O2" ./configure --target=mips64-elf --prefix=${INSTALL_PATH} --with-cpu=mips64vr4300 --disable-threads --disable-libssp --disable-werror
make -j9
make install
cd ..
# Compile gcc (pass 2)
#rm -rf gcc_compile
#mkdir gcc_compile
#cd gcc_compile
#CFLAGS_FOR_TARGET="-G0 -mabi=32 -march=vr4300 -mtune=vr4300 -O2" CXXFLAGS_FOR_TARGET="-G0 -mabi=32 -march=vr4300 -mtune=vr4300 -O2" ../gcc-#$GCC_V/configure --prefix=${INSTALL_PATH} --target=mips64-elf --enable-languages=c,c++ --with-newlib --with-system-zlib --disable-libssp --#enable-multilib --disable-shared --with-gcc --with-gnu-ld --with-gnu-as --disable-threads --disable-win32-registry --disable-nls --disable-#debug --disable-libmudflap
#make -j9
#make install
#cd ..
export CFLAGS="-std=gnu99 -mabi=32 -march=vr4300 -mtune=vr4300"
export CFLAGS="-std=gnu99 -march=vr4300 -mtune=vr4300"
# Pull the latest libdragon source code and make a build directory
git clone https://github.com/dragonminded/libdragon.git
# set to correct commit
cd libdragon
git checkout -f b26fce6
# fix issues with the build scripts
sed -i -- 's|${N64_INST:-/usr/local}|/usr/local/libdragon|g' tools/build
sed -i -- 's|--with-newlib|--with-newlib --with-system-zlib|g' tools/build
sed -i -- 's| -lpng|\nLDLIBS = -lpng|g' tools/mksprite/Makefile
sed -i -- 's| -Werror| -w|g' tools/mksprite/Makefile
# run the install script
#find -type f -name Makefile -print0 | xargs -0 sed -i 's/ -mtune=vr4300 / -mabi=32 -mtune=vr4300 /'
make -j9
make install
make -j9 tools
make tools-install
export LDFLAGS="-L$N64_INST/lib -Tn64ld.x"
export LIBS="-ldragon -lc -ldragonsys -lnosys"
cd ..
# install libmikmod (custom version)
git clone https://github.com/n64-tools/libmikmod
cd libmikmod/n64
make -j9
make install
cd .. # we have are in a subfolder, this is not a duplicate...
cd ..
# install libyaml
git clone https://github.com/yaml/libyaml
cd libyaml
./bootstrap
./configure --host=mips64-elf --prefix=$N64_INST
make -j9
make install
cd ..
# install libmad (custom version)
git clone https://github.com/n64-tools/libmad
cd libmad
./configure --host=mips64-elf --prefix=$N64_INST
# needed for GCC 4.6.4
sed -i 's/-fforce-mem//' Makefile
make -j9
make install
cd ..
# Perform cleanup
apt-get -y autoremove
apt-get autoclean
find /usr/local/libdragon/bin /usr/local/libdragon/mips64-elf/bin /usr/local/libdragon/libexec/gcc/mips64-elf -type f -print0 | xargs -0 strip || true
# these are ENV in Dockerfile now
#echo 'export N64_INST=/usr/local/libdragon' >> ~/.bashrc
#echo 'export PATH="$PATH:$N64_INST/bin"' >> ~/.bashrc

7
rust/Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "altra64"
version = "0.1.0"

25
rust/Cargo.toml Normal file
View File

@ -0,0 +1,25 @@
[package]
name = "altra64"
version = "0.1.0"
authors = ["moparisthebest <admin@moparisthebest.com>"]
edition = "2018"
[dependencies]
#libc = {version = "0.2", default-features = false}
#no-std-compat = { version = "0.4.1", features = [ "alloc" ] }
#libdragon-bindings = { git = "https://github.com/DagothBob/libdragon-bindings" }
#cstr_core = {version = "0.2", default-features = false}
# the profile used for `cargo build`
[profile.dev]
panic = "abort" # disable stack unwinding on panic
lto = "off"
# the profile used for `cargo build --release`
[profile.release]
panic = "abort" # disable stack unwinding on panic
lto = "off"
[lib]
name = "altra64"
crate-type = ["staticlib"]

16
rust/Dockerfile Normal file
View File

@ -0,0 +1,16 @@
# to build and test:
# docker build -t altra64-rust-dev . && docker run --rm -v "$HOME/.cargo/registry:/root/.cargo/registry" -v "$(pwd):/build" -it altra64-rust-dev
# to use to compile altra64 (or other n64 stuff I guess)
# docker run --rm -v "$HOME/.cargo/registry:/root/.cargo/registry" -v "$(pwd):/build" -it altra64-rust-dev ./build.sh
FROM moparisthebest/altra64-dev
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/libdragon/bin:/root/.cargo/bin
RUN apt-get -y install curl && \
curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --profile minimal --default-toolchain nightly --component rust-src && \
apt-get -y purge curl && \
cargo install cbindgen && \
rm -rf ~/.cargo/registry/

19
rust/cbindgen.toml Normal file
View File

@ -0,0 +1,19 @@
# This is a template cbindgen.toml file with all of the default values.
# Some values are commented out because their absence is the real default.
#
# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml
# for detailed documentation of every option here.
language = "C"
############## Options for Wrapping the Contents of the Header #################
# header = "/* Text to put at the beginning of the generated file. Probably a license. */"
# trailer = "/* Text to put at the end of the generated file */"
include_guard = "ALTRA64_H"
#pragma_once = true
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
include_version = false
[export]
exclude = ["strtoul", "screen_text", "test_c_print", "screen_text_num", "screen_text_ptr"]

View File

@ -0,0 +1,25 @@
{
"arch": "mips",
"cpu": "mips3",
"data-layout": "E-m:m-p:64:64-i8:8:32-i16:16:32-i64:64-n64-S64",
"disable-redzone": true,
"env": "unknown",
"executables": true,
"features": "+mips3,+gp64,+noabicalls",
"linker": "rust-lld",
"linker-flavor": "ld.lld",
"llvm-target": "mips-unknown-unknown",
"llvm-abiname": "n64",
"os": "none",
"panic-strategy": "abort",
"pre-link-args": {
"ld.lld": [
"--script={}"
]
},
"relocation-model": "static",
"target-c-int-width": "64",
"target-endian": "big",
"target-pointer-width": "64",
"vendor": "nintendo64"
}

453
rust/src/lib.rs Normal file
View File

@ -0,0 +1,453 @@
#![no_std]
#![allow(dead_code,unused_variables)]
use core::slice;
use core::iter::Iterator;
const WHITESPACE: &[u8] = b" \t\n\r";
pub trait SliceSubsequence<T> {
fn trim_start(&self, needle: &[T]) -> &[T];
fn trim_end(&self, needle: &[T]) -> &[T];
fn first_index_of(&self, needle: &[T]) -> Option<usize>;
//fn extract_between(&self, before: &[T], after: &[T]) -> Option<&[T]>;
fn lines<'a>(&'a self, needle: &'a [T]) -> LineIterator<'a, T>;
fn trim(&self, needle: &[T]) -> &[T];
fn contains_seq(&self, needle: &[T]) -> bool {
self.first_index_of(needle).is_some()
}
}
pub struct LineIterator<'a, T> {
inner: &'a [T],
needle: &'a [T],
pos: usize,
}
impl<'a, T: PartialEq> Iterator for LineIterator<'a, T> {
type Item = &'a [T];
fn next(&mut self) -> Option<Self::Item> {
if self.pos == self.inner.len() {
return None;
}
let search = &self.inner[self.pos..];
match search.first_index_of(self.needle) {
None => {
let pos = self.pos;
self.pos = self.inner.len();
Some(&self.inner[pos..])
}
Some(idx) => {
self.pos += idx + self.needle.len();
Some(&search[..idx])
}
}
}
}
fn last_index_of<T: PartialEq>(s: &[T], needle: &[T]) -> usize {
let mut len = 0;
for i in s {
if needle.contains(i) {
len += 1;
} else {
break;
}
}
len
}
fn last_index_of_rev<T: PartialEq>(s: &[T], needle: &[T]) -> usize {
let mut len = s.len();
for i in s.iter().rev() {
if needle.contains(i) {
len -= 1;
} else {
break;
}
}
len
}
impl<T: PartialEq> SliceSubsequence<T> for &[T] {
fn trim_start(&self, needle: &[T]) -> &[T] {
&self[last_index_of(self, needle)..]
}
fn trim_end(&self, needle: &[T]) -> &[T] {
&self[..last_index_of_rev(self, needle)]
}
fn first_index_of(&self, needle: &[T]) -> Option<usize> {
if self.len() >= needle.len() {
for i in 0..self.len() - needle.len() + 1 {
if self[i..i + needle.len()] == needle[..] {
return Some(i);
}
}
}
None
}
fn lines<'a>(&'a self, needle: &'a [T]) -> LineIterator<'a, T> {
LineIterator {
inner: self,
needle,
pos: 0,
}
}
fn trim(&self, needle: &[T]) -> &[T] {
let start = last_index_of(self, needle);
let end = last_index_of_rev(self, needle);
if start >= end {
// empty
&self[0..0]
} else {
&self[start..end]
}
}
}
/*
// for some reason, passing actual pointers like this freezes the n64
#[no_mangle]
pub extern "C" fn parse_cheats_ffi(
cheat_file: *mut u8, cheat_file_len: usize,
boot_cheats: *mut u32, boot_cheats_len: usize,
in_game_cheats: *mut u32, in_game_cheats_len: usize,
) -> u8 {
let cheat_file = unsafe { slice::from_raw_parts(cheat_file, cheat_file_len) };
let boot_cheats = unsafe { slice::from_raw_parts_mut(boot_cheats, boot_cheats_len) };
let in_game_cheats = unsafe { slice::from_raw_parts_mut(in_game_cheats, in_game_cheats_len) };
parse_cheats(cheat_file, boot_cheats, in_game_cheats)
}
*/
// in C world, usize is actually u32, no idea why but that's a problem for another day...
// but if we put u32 here it crashes, can only send usize
#[no_mangle]
pub extern "C" fn parse_cheats_ffi(
cheat_file: usize, cheat_file_len: usize,
boot_cheats: usize, boot_cheats_len: usize,
in_game_cheats: usize, in_game_cheats_len: usize,
) -> u8 {
/*
unsafe {
let cheat_file = cheat_file as *mut u8;
let cheat_file = slice::from_raw_parts_mut(cheat_file, 4);
cheat_file[1] = b'a';
0
}
*/
//unsafe { screen_text_ptr(b"from rust woot" as *const u8 as u32); }
//let cheat_file = unsafe { slice::from_raw_parts(cheat_file as *const u8, cheat_file_len as usize) };
let cheat_file = unsafe { slice::from_raw_parts_mut(cheat_file as *mut u8, cheat_file_len as usize) };
let boot_cheats = unsafe { slice::from_raw_parts_mut(boot_cheats as *mut u32, boot_cheats_len as usize) };
let in_game_cheats = unsafe { slice::from_raw_parts_mut(in_game_cheats as *mut u32, in_game_cheats_len as usize) };
dbg(100);
//cheat_file[1] = b'a';
//0
parse_cheats(cheat_file, boot_cheats, in_game_cheats)
}
const SUCCESS: u8 = 0;
const INVALID_CODE_LINE: u8 = 1;
const INVALID_LINE: u8 = 2;
pub fn parse_cheats(cheat_file: &[u8], boot_cheats: &mut [u32], in_game_cheats: &mut [u32]) -> u8 {
dbg(101);
let mut repeater = false;
let mut boot_cheats_idx = 0;
let mut in_game_cheats_idx = 0;
dbg(102);
for line in cheat_file.lines(b"\n") {
dbg(200);
let line = line.trim(WHITESPACE);
if line.is_empty() || line.starts_with(b"#") || line == b"---" {
continue; // empty or comment or whatever the starting thing is
} else if line.ends_with(b":") {
repeater = false;
} else if line.starts_with(b"- ") {
// we found the start of a code
let line = line.trim_start(b"- ");
let line = line.trim(WHITESPACE);
let mut line = line.lines(b" ");
match (line.next(), line.next(), line.next()) {
(Some(address), Some(value), None) => {
// proper line
let address = hex_to_u32(address.trim(WHITESPACE));
let value = hex_to_u32(value.trim(WHITESPACE));
//println!("address: {:X}, value: {:X}", address, value);
//println!("dec address: {}, value: {}", address, value);
// Do not check code types within "repeater data"
if repeater {
repeater = false;
in_game_cheats[in_game_cheats_idx] = address;
in_game_cheats_idx += 1;
in_game_cheats[in_game_cheats_idx] = value;
in_game_cheats_idx += 1;
continue;
}
//println!("address >> 24: {:X}", address >> 24);
// Determine destination cheat_list for the code type
match address >> 24
{
// Uncessary code types
0x20 | // Clear code list
0xCC | // Exception Handler Selection
0xDE => // Entry Point
continue,
// Boot-time cheats
0xEE | // Disable Expansion Pak
0xF0 | // 8-bit Boot-Time Write
0xF1 | // 16-bit Boot-Time Write
0xFF => { // Cheat Engine Location
boot_cheats[boot_cheats_idx] = address;
boot_cheats_idx += 1;
boot_cheats[boot_cheats_idx] = value;
boot_cheats_idx += 1;
}
// In-game cheats
0x50 => { // Repeater/Patch
// Validate repeater count
if (address & 0x0000FF00) == 0 {
repeater = true;
in_game_cheats[in_game_cheats_idx] = address;
in_game_cheats_idx += 1;
in_game_cheats[in_game_cheats_idx] = value;
in_game_cheats_idx += 1;
}
}
// todo: was fallthrough from default in C, does that even work?
0xD0 | // 8-bit Equal-To Conditional
0xD1 | // 16-bit Equal-To Conditional
0xD2 | // 8-bit Not-Equal-To Conditional
0xD3 => { // 16-bit Not-Equal-To Conditional
// Validate 16-bit codes
if (address & 0x01000001) == 0x01000001 {
continue; // todo: or error
}
in_game_cheats[in_game_cheats_idx] = address;
in_game_cheats_idx += 1;
in_game_cheats[in_game_cheats_idx] = value;
in_game_cheats_idx += 1;
}
// Everything else
_ => {
if address != 0
{
// TODO: Support special code types! :)
}
// Validate 16-bit codes
if (address & 0x01000001) == 0x01000001 {
continue; // todo: or error
}
in_game_cheats[in_game_cheats_idx] = address;
in_game_cheats_idx += 1;
in_game_cheats[in_game_cheats_idx] = value;
in_game_cheats_idx += 1;
}
}
}
_ => return INVALID_CODE_LINE,
}
} else {
//println!("bad line: '{}'", String::from_utf8_lossy(line));
return INVALID_LINE;
}
}
dbg(103);
SUCCESS
}
/// This function is called on panic.
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
fn hex_to_u32(str: &[u8]) -> u32 {
use core::ptr::null_mut;
unsafe { strtoul(str.as_ptr(), null_mut(), 16) }
}
#[no_mangle]
pub extern "C" fn test_rust_print() {
//unsafe { test_c_print(); }
//unsafe { screen_text_num(9); }
// sent 18446744073709551515 = 4294967195 = FFFF_FF9B
// sent 0xffff_ffff_ffff_ffff = 4294967295 = FFFF_FFFF
unsafe { screen_text_num(0xffff_ffff_ffff_ffff); }
//unsafe { screen_text(b"rust test\0".as_ptr()); }
}
#[no_mangle]
pub extern "C" fn rust_call_test(num: usize) {
unsafe { screen_text_num(num); }
}
#[no_mangle]
pub extern "C" fn rust_call_test_ptr(num: usize) -> u8 {
unsafe {
let cheat_file = num as *mut u8;
let cheat_file = slice::from_raw_parts_mut(cheat_file, 4);
cheat_file[1] = b'a';
//screen_text_num(cheat_file[0].into());
0
}
}
fn dbg(num: usize) {
#[cfg(not(test))]
unsafe { screen_text_num(num); }
#[cfg(test)]
println!("dbg: {}", num);
}
/*
#[no_mangle]
pub extern "C" fn rust_call_test_bla() {
//rust_call_test(b"c rust test\0".as_ptr());
rust_call_test([99, 32, 114, 117, 115, 116, 32, 116, 101, 115, 116, 0].as_ptr());
}
*/
extern "C" {
fn strtoul(s: *const u8, endp: *mut *mut u8, base: i32) -> u32; // todo: is base i32 ?
fn screen_text(msg: *const u8);
fn screen_text_num(num: usize);
fn screen_text_ptr(ptr: u32);
fn test_c_print();
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn test_hex_to_u32() {
println!("i: {:?}", b"c rust test\0");
assert_eq!(hex_to_u32(b"0x09"), 9);
}
#[test]
fn test_trim_start() {
let buf = &b" bla"[..];
let buf = buf.trim_start(WHITESPACE);
assert_eq!(buf, b"bla");
let buf = &b"\n\t\r \rbla"[..];
let buf = buf.trim_start(WHITESPACE);
assert_eq!(buf, b"bla");
}
#[test]
fn test_trim_end() {
let buf = &b"bla "[..];
let buf = buf.trim_end(WHITESPACE);
assert_eq!(buf, b"bla");
let buf = &b"bla\n\t\r \r"[..];
let buf = buf.trim_end(WHITESPACE);
assert_eq!(buf, b"bla");
}
#[test]
fn test_trim() {
let buf = &b" bla "[..];
let buf = buf.trim(WHITESPACE);
assert_eq!(buf, b"bla");
let buf = &b"\n\t\r \rbla\n\t\r \r"[..];
let buf = buf.trim(WHITESPACE);
assert_eq!(buf, b"bla");
}
#[test]
fn test_lines() {
let lines = &b"\
line1
line2\r
line3
"[..];
let mut lines = lines.lines(b"\n");
assert_eq!(lines.next(), Some(&b"line1"[..]));
assert_eq!(lines.next(), Some(&b"line2\r"[..]));
assert_eq!(lines.next(), Some(&b"line3"[..]));
assert_eq!(lines.next(), None);
assert_eq!(lines.next(), None);
let lines = &b"F10004E4 2400"[..];
let mut lines = lines.lines(b" ");
assert_eq!(lines.next(), Some(&b"F10004E4"[..]));
assert_eq!(lines.next(), Some(&b"2400"[..]));
assert_eq!(lines.next(), None);
}
#[test]
fn test_parse_cheats() {
let cheats_file = &b"\
---
A:
- F10004E4 2400
- EE000000 0000
B:
- 8138EDA0 2400
"[..];
let mut boot_cheats = [0u32; 6];
let mut in_game_cheats = [0u32; 6];
let ok = parse_cheats(cheats_file, &mut boot_cheats, &mut in_game_cheats);
assert_eq!(ok, SUCCESS);
assert_eq!(boot_cheats, [0xF10004E4, 0x2400, 0xEE000000, 0x0000, 0, 0]);
assert_eq!(in_game_cheats, [0x8138EDA0, 0x2400, 0, 0, 0, 0]);
let cheats_file = &b"\
---
# Legend of Zelda, The - Ocarina of Time (USA) (Rev 2)
Master Code:
- F10004E4 2400
- EE000000 0000
In Health (ASM):
- 8138EDA0 2400
"[..];
let mut boot_cheats = [0u32; 6];
let mut in_game_cheats = [0u32; 6];
let ok = parse_cheats(cheats_file, &mut boot_cheats, &mut in_game_cheats);
assert_eq!(ok, SUCCESS);
assert_eq!(boot_cheats, [0xF10004E4, 0x2400, 0xEE000000, 0x0000, 0, 0]);
assert_eq!(in_game_cheats, [0x8138EDA0, 0x2400, 0, 0, 0, 0]);
let cheats_file = &b"wootwootwootwootwootwootwootwoot\0"[..];
let cheats_file = &b"wootwootwootwootwootwootwootwoot"[..];
let mut boot_cheats = [0u32; 6];
let mut in_game_cheats = [0u32; 6];
let ok = parse_cheats(cheats_file, &mut boot_cheats, &mut in_game_cheats);
assert_eq!(ok, INVALID_LINE);
assert_eq!(boot_cheats, [0, 0, 0, 0, 0, 0]);
assert_eq!(in_game_cheats, [0, 0, 0, 0, 0, 0]);
}
}

View File

@ -37,8 +37,13 @@
#include "sound.h"
#include "mp3.h"
#ifdef USE_YAML
// YAML parser
#include <yaml.h>
#else
#endif /* USE_YAML */
#include <altra64.h>
#include "constants.h"
#include "debug.h"
@ -56,6 +61,10 @@
#define MAX_LIST 20
uint8_t get_c_u8(void) {
return 20;
}
struct glyph
{
int xoff;
@ -249,6 +258,50 @@ int page = 0;
int cursor = 0;
direntry_t *list;
display_context_t global_disp_ctx;
void screen_text(u8 *msg) {
printText(msg, 3, -1, global_disp_ctx);
display_show(global_disp_ctx);
sleep(1000);
}
void screen_text_num(size_t num) {
char num_str[64];
sprintf(num_str, "dbg: %u", num);
screen_text(num_str);
}
void screen_text_ptr(size_t ptr) {
u8 *msg = (u8*)ptr;
screen_text(msg);
}
void call_test(size_t num) {
screen_text_num(num);
}
/*
00006644 <call_test>:
6644: 27bdffd8 addiu sp,sp,-40
6648: ffbf0020 sd ra,32(sp)
664c: 0c000000 jal 0 <ttULONG>
6650: 00000000 nop
6654: dfbf0020 ld ra,32(sp)
6658: 03e00008 jr ra
665c: 27bd0028 addiu sp,sp,40
*/
void call_test_bla() {
call_test(5);
}
void test_c_print() {
screen_text("c rust test");
screen_text_num(5);
screen_text("c rust test2");
}
int filesize(FILE *pFile)
{
fseek(pFile, 0, SEEK_END);
@ -1751,9 +1804,9 @@ int saveTypeToSd(display_context_t disp, char *rom_name, int stype)
if (result == FR_OK)
{
//for savegame
uint8_t cartsave_data[size]; //TODO: bring back old initialisation if this doesn't work
uint8_t cartsave_data[size];
// zero the memory so we can detect+abort if we can't get a save from the cart
memset(cartsave_data, 0, size * sizeof(cartsave_data[0]));
TRACEF(disp, "cartsave_data=%p", &cartsave_data);
@ -2022,77 +2075,26 @@ void readSDcard(display_context_t disp, char *directory)
display_dir(list, cursor, page, MAX_LIST, count, disp);
}
/*
* Returns two cheat lists:
* - One for the "at boot" cheats
* - Another for the "in-game" cheats
*/
int readCheatFile(TCHAR *filename, u32 *cheat_lists[2])
{
#ifdef USE_YAML
int parse_cheats_yaml(char *cheatfile, u32 *list1, u32 *list2) {
// YAML parser
yaml_parser_t parser;
yaml_event_t event;
// State for YAML parser
int is_code = 0;
int code_on = 1;
int done = 0;
u32 *list1;
u32 *list2;
char *next;
int repeater = 0;
u32 address;
u32 value;
yaml_parser_initialize(&parser);
FRESULT result;
FIL file;
UINT bytesread;
result = f_open(&file, filename, FA_READ);
if (result == FR_OK)
{
int fsize = f_size(&file);
char *cheatfile = malloc(fsize);
if (!cheatfile)
{
return -2; // Out of memory
}
/*
* Size of the cheat list can never be more than half the size of the YAML
* Minimum YAML example:
* A:-80001234 FFFF
* Which is exactly 16 bytes.
* The cheat list in this case fits into exactly 8 bytes (2 words):
* 0x80001234, 0x0000FFFF
*/
list1 = calloc(1, fsize + 2 * sizeof(u32)); // Plus 2 words to be safe
if (!list1)
{
// Free
free(cheatfile);
return -2; // Out of memory
}
list2 = &list1[fsize / sizeof(u32) / 2];
cheat_lists[0] = list1;
cheat_lists[1] = list2;
result =
f_read (
&file, /* [IN] File object */
cheatfile, /* [OUT] Buffer to store read data */
fsize, /* [IN] Number of bytes to read */
&bytesread /* [OUT] Number of bytes read */
);
f_close(&file);
yaml_parser_set_input_string(&parser, cheatfile, strlen(cheatfile));
char *next;
int repeater = 0;
yaml_parser_initialize(&parser);
yaml_parser_set_input_string(&parser, cheatfile, strlen(cheatfile));
do
{
@ -2100,12 +2102,7 @@ int readCheatFile(TCHAR *filename, u32 *cheat_lists[2])
{
// Free
yaml_parser_delete(&parser);
yaml_event_delete(&event);
free(cheatfile);
free(cheat_lists[0]);
cheat_lists[0] = 0;
cheat_lists[1] = 0;
yaml_event_delete(&event);
return -3; // Parse error
}
@ -2217,7 +2214,89 @@ int readCheatFile(TCHAR *filename, u32 *cheat_lists[2])
// Free
yaml_parser_delete(&parser);
return repeater;
}
#endif /* USE_YAML */
/*
* Returns two cheat lists:
* - One for the "at boot" cheats
* - Another for the "in-game" cheats
*/
int readCheatFile(TCHAR *filename, u32 *cheat_lists[2])
{
u32 *list1;
u32 *list2;
int repeater = 0;
FRESULT result;
FIL file;
UINT bytesread;
result = f_open(&file, filename, FA_READ);
screen_text("in readCheatFile");
screen_text_num(0xffffffffffffffff);
test_rust_print();
screen_text("called rust");
if (result == FR_OK)
{
int fsize = f_size(&file);
char *cheatfile = malloc(fsize);
if (!cheatfile)
{
return -2; // Out of memory
}
/*
* Size of the cheat list can never be more than half the size of the YAML
* Minimum YAML example:
* A:-80001234 FFFF
* Which is exactly 16 bytes.
* The cheat list in this case fits into exactly 8 bytes (2 words):
* 0x80001234, 0x0000FFFF
*/
size_t both_list_sizes = fsize + 2 * sizeof(u32);
list1 = calloc(1, both_list_sizes); // Plus 2 words to be safe
if (!list1)
{
// Free
free(cheatfile);
return -2; // Out of memory
}
size_t list2_size = fsize / sizeof(u32) / 2;
list2 = &list1[list2_size];
cheat_lists[0] = list1;
cheat_lists[1] = list2;
result =
f_read (
&file, /* [IN] File object */
cheatfile, /* [OUT] Buffer to store read data */
fsize, /* [IN] Number of bytes to read */
&bytesread /* [OUT] Number of bytes read */
);
f_close(&file);
#ifdef USE_YAML
repeater = parse_cheats_yaml(cheatfile, list1, list2);
#else
// use rust
size_t list1_size = both_list_sizes - list2_size;
repeater = parse_cheats_ffi((size_t)cheatfile, fsize, (size_t)list1, list1_size, (size_t)list2, list2_size);
#endif /* USE_YAML */
free(cheatfile);
if (repeater != 0) {
// this is what original yaml code did
free(cheat_lists[0]);
cheat_lists[0] = 0;
cheat_lists[1] = 0;
}
return repeater; // Ok or repeater error
@ -2242,10 +2321,12 @@ void bootRom(display_context_t disp, int silent)
char cheat_filename[64];
sprintf(cheat_filename, "/"ED64_FIRMWARE_PATH"/CHEATS/%s.yml", rom_filename);
global_disp_ctx = disp;
int ok = readCheatFile(cheat_filename, cheat_lists);
if (ok == 0)
{
printText("cheats found...", 3, -1, disp);
sleep(10000);
}
else
{
@ -4559,6 +4640,59 @@ int main(void)
drawBg(disp); //new
drawBoxNumber(disp, 1); //new
/*
*/
global_disp_ctx = disp;
screen_text("in MAIN");
screen_text_num(SIZE_MAX);
screen_text("in MAIN2");
screen_text_num((size_t)-1);
screen_text("in MAIN3");
screen_text_num(0xffffffffffffffff);
test_rust_print();
screen_text_ptr((size_t)"called rust");
rust_call_test(SIZE_MAX);
screen_text_ptr((size_t)"called rust2");
char* woot = "wootwootwootwootwootwootwootwoot";
size_t fsize = 32;
//u8 repeater = rust_call_test_ptr((size_t) woot);
/*
*/
u32 *list1;
u32 *list2;
size_t both_list_sizes = fsize + 2 * sizeof(u32);
list1 = calloc(1, both_list_sizes); // Plus 2 words to be safe
if (!list1)
{
screen_text("oom");
while(true) { sleep(1000); }
}
size_t list2_size = fsize / sizeof(u32) / 2;
list2 = &list1[list2_size];
//cheat_lists[0] = list1;
//cheat_lists[1] = list2;
size_t list1_size = both_list_sizes - list2_size;
screen_text_num(list1_size);
screen_text_num(list2_size);
screen_text_ptr((size_t)"called rust2.5");
u8 repeater = parse_cheats_ffi((size_t)woot, fsize
, (size_t)list1, list1_size
, (size_t)list2, list2_size
);
screen_text("called rust3");
screen_text(woot);
screen_text("called rust4");
while(true) { sleep(1000); }
uint32_t *buffer = (uint32_t *)__get_buffer(disp); //fg disp = 2

View File

@ -1,6 +1,7 @@
#include <libdragon.h>
#include <stdio.h>
#include "types.h"
#include "menu.h"
#include "version.h"