initial commit

This commit is contained in:
Corwin Kuiper 2021-03-06 17:58:59 +00:00
commit 0790f78103
21 changed files with 1018 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/target
/out
/.vscode

16
Cargo.lock generated Normal file
View file

@ -0,0 +1,16 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "gba"
version = "0.1.0"
dependencies = [
"bitflags",
]

19
Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "gba"
version = "0.1.0"
authors = ["Corwin Kuiper <corwin@kuiper.dev>"]
edition = "2018"
[profile.dev]
panic = "abort"
lto = true
[profile.release]
panic = "abort"
lto = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
bitflags = "1.0"

44
Makefile Normal file
View file

@ -0,0 +1,44 @@
BINUTILS_PREFIX=arm-none-eabi-
CC=$(BINUTILS_PREFIX)as
ARCH = -mthumb-interwork -mthumb
RUSTFILES=$(shell find . -name '*.rs')
.ONESHELL:
out/debug/%.gba: cargo-debug-%
@mkdir -p $(dir $@)
@OUTNAME=$(patsubst out/debug/%.gba,%,$@)
@$(BINUTILS_PREFIX)objcopy -O binary target/gba/debug/examples/$${OUTNAME} out/debug/$${OUTNAME}.gba
@gbafix $@
out/release/%.gba: cargo-release-%
@mkdir -p $(dir $@)
@OUTNAME=$(patsubst out/release/%.gba,%,$@)
@$(BINUTILS_PREFIX)objcopy -O binary target/gba/release/examples/$${OUTNAME} out/release/$${OUTNAME}.gba
@gbafix $@
d-%: out/debug/%.gba
@OUTNAME=$(patsubst d-%,%,$@)
@mgba-qt $<
@rm out/debug/$${OUTNAME}.sav
r-%: out/release/%.gba
@OUTNAME=$(patsubst r-%,%,$@)
@mgba-qt $<
@rm out/release/$${OUTNAME}.sav
cargo-release-%: $(RUSTFILES) out/crt0.o
@OUTNAME=$(patsubst cargo-release-%,%, $@)
@rustup run nightly cargo xbuild --release --target=gba.json --example=$${OUTNAME}
cargo-debug-%: $(RUSTFILES) out/crt0.o
@OUTNAME=$(patsubst cargo-debug-%,%, $@)
@rustup run nightly cargo xbuild --target=gba.json --example=$${OUTNAME}
out/crt0.o: crt0.s interrupt_simple.s
@mkdir $(dir $@)
@$(CC) $(ARCH) -o out/crt0.o crt0.s
clippy:
rustup run nightly cargo xclippy --target=gba.json

16
README.md Normal file
View file

@ -0,0 +1,16 @@
# Rust for the Gameboy Advance
This is my in development library for rust on the gameboy advance. It uses
information from GbaTek, Tonc, and the existing
[rust-console/gba](https://github.com/rust-console/gba). Namely the gba.json
file comes from [rust-console/gba](https://github.com/rust-console/gba).
Note that this currently contains no documentation of any kind, unless you count
examples as documentation.
## Requirements
* Nightly rust, probably quite a recent version.
* Devkitarm.
This is probably easiest to do in a container.

19
crt0.s Normal file
View file

@ -0,0 +1,19 @@
.arm
.global __start
__start:
b .Initialise
@ Filled in by gbafix
.fill 188, 1, 0
.Initialise:
@ Set interrupt handler
ldr r0, =InterruptHandlerSimple
ldr r1, =0x03007FFC
str r0, [r1]
@ load main and branch
ldr r0, =main
bx r0
.include "interrupt_simple.s"

39
examples/bitmap3.rs Normal file
View file

@ -0,0 +1,39 @@
#![no_std]
#![feature(start)]
extern crate gba;
use gba::display;
struct Vector2D {
x: i32,
y: i32,
}
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let gba = gba::Gba::new();
let bitmap = gba.display.bitmap3();
let mut input = gba::input::ButtonController::new();
let mut pos = Vector2D {
x: display::WIDTH / 2,
y: display::HEIGHT / 2,
};
gba::interrupt::enable(gba::interrupt::Interrupt::VBlank);
gba::interrupt::enable_interrupts();
gba::display::enable_VBlank_interrupt();
loop {
gba::display::wait_for_VBlank();
input.update();
pos.x += input.x_tri() as i32;
pos.y += input.y_tri() as i32;
pos.x = pos.x.clamp(0, display::WIDTH - 1);
pos.y = pos.y.clamp(0, display::HEIGHT - 1);
bitmap.draw_point(pos.x, pos.y, 0x001F);
}
}

42
examples/bitmap4.rs Normal file
View file

@ -0,0 +1,42 @@
#![no_std]
#![feature(start)]
extern crate gba;
use gba::display;
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let gba = gba::Gba::new();
let bitmap = gba.display.bitmap4();
bitmap.set_palette_entry(1, 0x001F);
bitmap.set_palette_entry(2, 0x03E0);
bitmap.draw_point_page(
display::WIDTH / 2,
display::HEIGHT / 2,
1,
display::Page::Front,
);
bitmap.draw_point_page(
display::WIDTH / 2 + 5,
display::HEIGHT / 2,
2,
display::Page::Back,
);
gba::interrupt::enable(gba::interrupt::Interrupt::VBlank);
gba::interrupt::enable_interrupts();
gba::display::enable_VBlank_interrupt();
let mut count = 0;
loop {
gba::display::wait_for_VBlank();
count += 1;
if count % 6 == 0 {
bitmap.flip_page();
}
}
}

21
examples/panic.rs Normal file
View file

@ -0,0 +1,21 @@
#![no_std]
#![feature(start)]
extern crate gba;
use gba::display;
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let gba = gba::Gba::new();
let bitmap = gba.display.bitmap3();
let mut input = gba::input::ButtonController::new();
loop {
input.update();
if input.is_just_pressed(gba::input::Button::A) {
bitmap.draw_point(display::WIDTH, 0, 0x05);
}
}
}

21
examples/syscall.rs Normal file
View file

@ -0,0 +1,21 @@
#![no_std]
#![feature(start)]
extern crate gba;
use gba::{display, syscall};
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let gba = gba::Gba::new();
let bitmap = gba.display.bitmap3();
for x in 0..display::WIDTH {
let y = syscall::sqrt(x << 6);
let y = (display::HEIGHT - y).clamp(0, display::HEIGHT - 1);
bitmap.draw_point(x, y, 0x001F);
}
loop {
syscall::halt();
}
}

35
gba.json Normal file
View file

@ -0,0 +1,35 @@
{
"abi-blacklist": [
"stdcall",
"fastcall",
"vectorcall",
"thiscall",
"win64",
"sysv64"
],
"arch": "arm",
"atomic-cas": false,
"cpu": "arm7tdmi",
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"emit-debug-gdb-scripts": false,
"env": "agb",
"executables": true,
"features": "+soft-float,+strict-align",
"linker": "arm-none-eabi-ld",
"linker-flavor": "ld",
"linker-is-gnu": true,
"llvm-target": "thumbv4-none-eabi",
"os": "none",
"panic-strategy": "abort",
"pre-link-args": {
"ld": [
"out/crt0.o",
"-Tgba.ld"
]
},
"relocation-model": "static",
"target-c-int-width": "32",
"target-endian": "little",
"target-pointer-width": "32",
"vendor": "nintendo"
}

51
gba.ld Normal file
View file

@ -0,0 +1,51 @@
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(__start)
MEMORY {
ewram (w!x) : ORIGIN = 0x2000000, LENGTH = 256K
iwram (w!x) : ORIGIN = 0x3000000, LENGTH = 32K
rom (rx) : ORIGIN = 0x8000000, LENGTH = 32M
}
__text_start = ORIGIN(rom);
SECTIONS {
. = __text_start;
.crt0 : {
KEEP (*(.crt0));
. = ALIGN(4);
} > rom
.text : {
*(.text .text*);
. = ALIGN(4);
} > rom
__text_end = .;
.rodata : {
*(.rodata .rodata.*);
. = ALIGN(4);
} > rom
__data_start = .;
.data : {
*(.data .data.*);
. = ALIGN(4);
} > iwram
__data_end = .;
.bss : {
*(.bss .bss.*);
. = ALIGN(4);
} > iwram
.stack 0x80000 : {
*(.stack)
}
/* discard anything not already mentioned */
/DISCARD/ : { *(*) }
}

17
interrupt_simple.s Normal file
View file

@ -0,0 +1,17 @@
@ An interrupt handler that simply acknowledges all interrupts
.arm
.global InterruptHandlerSimple
.align
InterruptHandlerSimple:
ldr r2, =0x04000200
ldrh r1, [r2]
ldrh r3, [r2, #2]
and r0, r1, r3
strh r0, [r2, #2]
ldr r2, =0x03007FF8
ldrh r1, [r2]
orr r1, r1, r0
strh r1, [r2]
bx lr

196
src/display.rs Normal file
View file

@ -0,0 +1,196 @@
use crate::{
memory_mapped::{MemoryMapped, MemoryMapped1DArray, MemoryMapped2DArray},
single::{Single, SingleToken},
};
use bitflags::bitflags;
use core::convert::TryInto;
const DISPLAY_CONTROL: MemoryMapped<u16> = MemoryMapped::new(0x0400_0000);
const DISPLAY_STATUS: MemoryMapped<u16> = MemoryMapped::new(0x0400_0004);
const VCOUNT: MemoryMapped<u16> = MemoryMapped::new(0x0400_0006);
const PALETTE_BACKGROUND: MemoryMapped1DArray<u16, 256> = MemoryMapped1DArray::new(0x0500_0000);
const PALETTE_SPRITE: MemoryMapped1DArray<u16, 256> = MemoryMapped1DArray::new(0x0500_0200);
const BITMAP_MODE_3: MemoryMapped2DArray<u16, { WIDTH as usize }, { HEIGHT as usize }> =
MemoryMapped2DArray::new(0x600_0000);
const BITMAP_PAGE_FRONT_MODE_4: MemoryMapped2DArray<
u16,
{ (WIDTH / 2) as usize },
{ HEIGHT as usize },
> = MemoryMapped2DArray::new(0x600_0000);
const BITMAP_PAGE_BACK_MODE_4: MemoryMapped2DArray<
u16,
{ (WIDTH / 2) as usize },
{ HEIGHT as usize },
> = MemoryMapped2DArray::new(0x600_A000);
pub const WIDTH: i32 = 240;
pub const HEIGHT: i32 = 160;
pub enum DisplayMode {
Tiled0 = 0,
Tiled1 = 1,
Tiled2 = 2,
Bitmap3 = 3,
Bitmap4 = 4,
Bitmap5 = 5,
}
pub enum Page {
Front = 0,
Back = 1,
}
bitflags! {
pub struct GraphicsSettings: u16 {
const PAGE_SELECT = 1 << 0x4;
const OAM_HBLANK = 1 << 0x5;
const SPRITE1_D = 1 << 0x6;
const SCREEN_BLANK = 1 << 0x7;
const LAYER_BG0 = 1 << 0x8;
const LAYER_BG1 = 1 << 0x9;
const LAYER_BG2 = 1 << 0xA;
const LAYER_BG3 = 1 << 0xB;
const LAYER_OBJ = 1 << 0xC;
const WINDOW0 = 1 << 0xD;
const WINDOW1 = 1 << 0xE;
const WINDOW_OBJECT = 1 << 0xF;
}
}
pub struct Display {
in_mode: Single,
}
impl Default for Display {
fn default() -> Self {
Self::new()
}
}
impl Display {
pub(crate) const fn new() -> Self {
Display {
in_mode: Single::new(),
}
}
pub fn bitmap3(&self) -> Bitmap3 {
Bitmap3::new(
self.in_mode
.take()
.expect("Cannot create new mode as mode already taken"),
)
}
pub fn bitmap4(&self) -> Bitmap4 {
Bitmap4::new(
self.in_mode
.take()
.expect("Cannot create new mode as mode already taken"),
)
}
}
pub struct Bitmap3<'a> {
_in_mode: SingleToken<'a>,
}
impl<'a> Bitmap3<'a> {
fn new(in_mode: SingleToken<'a>) -> Self {
set_graphics_mode(DisplayMode::Bitmap3);
set_graphics_settings(GraphicsSettings::LAYER_BG2);
Bitmap3 { _in_mode: in_mode }
}
pub fn draw_point(&self, x: i32, y: i32, colour: u16) {
let x = x.try_into().unwrap();
let y = y.try_into().unwrap();
BITMAP_MODE_3.set(x, y, colour)
}
}
pub struct Bitmap4<'a> {
_in_mode: SingleToken<'a>,
}
impl<'a> Bitmap4<'a> {
fn new(in_mode: SingleToken<'a>) -> Self {
set_graphics_mode(DisplayMode::Bitmap4);
set_graphics_settings(GraphicsSettings::LAYER_BG2);
Bitmap4 { _in_mode: in_mode }
}
pub fn draw_point_page(&self, x: i32, y: i32, colour: u8, page: Page) {
let addr = match page {
Page::Front => BITMAP_PAGE_FRONT_MODE_4,
Page::Back => BITMAP_PAGE_BACK_MODE_4,
};
let x_in_screen = (x / 2) as usize;
let y_in_screen = y as usize;
let c = addr.get(x_in_screen, y_in_screen);
if x & 0b1 != 0 {
addr.set(x_in_screen, y_in_screen, c | (colour as u16) << 8);
} else {
addr.set(x_in_screen, y_in_screen, c | colour as u16);
}
}
pub fn draw_point(&self, x: i32, y: i32, colour: u8) {
let disp = DISPLAY_CONTROL.get();
let page = if disp & GraphicsSettings::PAGE_SELECT.bits() != 0 {
Page::Back
} else {
Page::Front
};
self.draw_point_page(x, y, colour, page)
}
pub fn set_palette_entry(&self, entry: u32, colour: u16) {
PALETTE_BACKGROUND.set(entry as usize, colour);
}
pub fn flip_page(&self) {
let disp = DISPLAY_CONTROL.get();
let swapped = disp ^ GraphicsSettings::PAGE_SELECT.bits();
DISPLAY_CONTROL.set(swapped);
}
}
fn set_graphics_mode(mode: DisplayMode) {
let current = DISPLAY_CONTROL.get();
let current = current & (!0b111);
let s = current | (mode as u16 & 0b111);
DISPLAY_CONTROL.set(s);
}
pub fn set_graphics_settings(settings: GraphicsSettings) {
let current = DISPLAY_CONTROL.get();
// preserve display mode
let current = current & 0b111;
let s = settings.bits() | current;
DISPLAY_CONTROL.set(s);
}
#[allow(non_snake_case)]
pub fn busy_wait_for_VBlank() {
while VCOUNT.get() >= 160 {}
while VCOUNT.get() < 160 {}
}
#[allow(non_snake_case)]
pub fn enable_VBlank_interrupt() {
let status = DISPLAY_STATUS.get() | (1 << 3);
DISPLAY_STATUS.set(status);
}
#[allow(non_snake_case)]
pub fn wait_for_VBlank() {
crate::syscall::wait_for_VBlank();
}

99
src/input.rs Normal file
View file

@ -0,0 +1,99 @@
use bitflags::bitflags;
use core::convert::From;
pub enum Tri {
Positive = 1,
Zero = 0,
Negative = -1,
}
impl From<(bool, bool)> for Tri {
fn from(a: (bool, bool)) -> Tri {
let b1 = a.0 as i8;
let b2 = a.1 as i8;
unsafe { core::mem::transmute(b2 - b1) }
}
}
bitflags! {
pub struct Button: u32 {
const A = 1 << 0;
const B = 1 << 1;
const SELECT = 1 << 2;
const START = 1 << 3;
const RIGHT = 1 << 4;
const LEFT = 1 << 5;
const UP = 1 << 6;
const DOWN = 1 << 7;
const R = 1 << 8;
const L = 1 << 9;
}
}
const KEY_MASK: u16 = 0b1111111111;
const BUTTON_INPUT: *mut u16 = (0x04000130) as *mut u16;
// const BUTTON_INTURRUPT: *mut u16 = (0x04000132) as *mut u16;
pub struct ButtonController {
previous: u16,
current: u16,
}
impl Default for ButtonController {
fn default() -> Self {
ButtonController::new()
}
}
impl ButtonController {
pub fn new() -> Self {
ButtonController {
previous: 0,
current: 0,
}
}
pub fn update(&mut self) {
self.previous = self.current;
self.current = unsafe { BUTTON_INPUT.read_volatile() } ^ KEY_MASK;
}
pub fn x_tri(&self) -> Tri {
let left = self.is_pressed(Button::LEFT);
let right = self.is_pressed(Button::RIGHT);
(left, right).into()
}
pub fn y_tri(&self) -> Tri {
let up = self.is_pressed(Button::UP);
let down = self.is_pressed(Button::DOWN);
(up, down).into()
}
pub fn is_pressed(&self, keys: Button) -> bool {
let currently_pressed = self.current as u32;
let keys = keys.bits();
(currently_pressed & keys) != 0
}
pub fn is_released(&self, keys: Button) -> bool {
!self.is_pressed(keys)
}
pub fn is_just_pressed(&self, keys: Button) -> bool {
let current = self.current as u32;
let previous = self.previous as u32;
let keys = keys.bits();
((current & keys) != 0) && ((previous & keys) == 0)
}
pub fn is_just_released(&self, keys: Button) -> bool {
let current = self.current as u32;
let previous = self.previous as u32;
let keys = keys.bits();
((current & keys) == 0) && ((previous & keys) != 0)
}
}

56
src/interrupt.rs Normal file
View file

@ -0,0 +1,56 @@
use crate::memory_mapped::MemoryMapped;
pub enum Interrupt {
VBlank,
HBlank,
VCounter,
Timer0,
Timer1,
Timer2,
Timer3,
Serial,
Dma0,
Dma1,
Dma2,
Dma3,
Keypad,
Gamepak,
}
const ENABLED_INTERRUPTS: MemoryMapped<u16> = MemoryMapped::new(0x04000200);
const INTERRUPTS_ENABLED: MemoryMapped<u16> = MemoryMapped::new(0x04000208);
pub fn enable(interrupt: Interrupt) {
let _interrupt_token = temporary_interrupt_disable();
let interrupt = interrupt as usize;
let enabled = ENABLED_INTERRUPTS.get() | (1 << (interrupt as u16));
ENABLED_INTERRUPTS.set(enabled);
}
pub fn disable(interrupt: Interrupt) {
let _interrupt_token = temporary_interrupt_disable();
let interrupt = interrupt as usize;
let enabled = ENABLED_INTERRUPTS.get() & !(1 << (interrupt as u16));
ENABLED_INTERRUPTS.set(enabled);
}
pub struct Disable {}
impl Drop for Disable {
fn drop(&mut self) {
enable_interrupts();
}
}
pub fn temporary_interrupt_disable() -> Disable {
disable_interrupts();
Disable {}
}
pub fn enable_interrupts() {
INTERRUPTS_ENABLED.set(1);
}
fn disable_interrupts() {
INTERRUPTS_ENABLED.set(0);
}

49
src/lib.rs Normal file
View file

@ -0,0 +1,49 @@
#![no_std]
#![feature(asm)]
#![deny(clippy::all)]
use core::fmt::Write;
pub mod display;
pub mod input;
pub mod interrupt;
mod memory_mapped;
mod mgba;
mod single;
pub mod syscall;
#[panic_handler]
#[allow(unused_must_use)]
fn panic_implementation(info: &core::panic::PanicInfo) -> ! {
if let Some(mut mgba) = mgba::Mgba::new() {
write!(mgba, "{}", info);
mgba.set_level(mgba::DebugLevel::Fatal);
}
loop {}
}
static mut GBASINGLE: single::Singleton<Gba> = single::Singleton::new(Gba::single_new());
pub struct Gba {
pub display: display::Display,
}
impl Gba {
pub fn new() -> Self {
unsafe { GBASINGLE.take() }
}
const fn single_new() -> Self {
Self {
display: display::Display::new(),
}
}
}
impl Default for Gba {
fn default() -> Self {
Self::new()
}
}

56
src/memory_mapped.rs Normal file
View file

@ -0,0 +1,56 @@
pub struct MemoryMapped<T> {
address: *mut T,
}
impl<T> MemoryMapped<T> {
pub const fn new(address: usize) -> Self {
MemoryMapped {
address: address as *mut T,
}
}
pub fn get(&self) -> T {
unsafe { self.address.read_volatile() }
}
pub fn set(&self, val: T) {
unsafe { self.address.write_volatile(val) }
}
}
pub struct MemoryMapped1DArray<T, const N: usize> {
array: *mut [T; N],
}
#[allow(dead_code)]
impl<T, const N: usize> MemoryMapped1DArray<T, N> {
pub const fn new(address: usize) -> Self {
MemoryMapped1DArray {
array: address as *mut [T; N],
}
}
pub fn get(&self, n: usize) -> T {
unsafe { (&mut (*self.array)[n] as *mut T).read_volatile() }
}
pub fn set(&self, n: usize, val: T) {
unsafe { (&mut (*self.array)[n] as *mut T).write_volatile(val) }
}
}
pub struct MemoryMapped2DArray<T, const X: usize, const Y: usize> {
array: *mut [[T; X]; Y],
}
impl<T, const X: usize, const Y: usize> MemoryMapped2DArray<T, X, Y> {
pub const fn new(address: usize) -> Self {
MemoryMapped2DArray {
array: address as *mut [[T; X]; Y],
}
}
pub fn get(&self, x: usize, y: usize) -> T {
unsafe { (&mut (*self.array)[y][x] as *mut T).read_volatile() }
}
pub fn set(&self, x: usize, y: usize, val: T) {
unsafe { (&mut (*self.array)[y][x] as *mut T).write_volatile(val) }
}
}

67
src/mgba.rs Normal file
View file

@ -0,0 +1,67 @@
use crate::memory_mapped::MemoryMapped;
#[derive(Eq, PartialEq, Clone, Copy)]
#[repr(u16)]
#[allow(dead_code)]
pub enum DebugLevel {
Fatal = 0,
Error = 1,
Warning = 2,
Info = 3,
Debug = 4,
}
const OUTPUT: *mut u8 = 0x04FF_F600 as *mut u8;
const ENABLE: MemoryMapped<u16> = MemoryMapped::new(0x04FF_F780);
const ENABLE_HANDSHAKE_IN: u16 = 0xC0DE;
const ENABLE_HANDSHAKE_OUT: u16 = 0x1DEA;
const DEBUG_LEVEL: MemoryMapped<u16> = MemoryMapped::new(0x04FF_F700);
const DEBUG_FLAG_CODE: u16 = 0x0100;
fn is_running_in_mgba() -> bool {
ENABLE.set(ENABLE_HANDSHAKE_IN);
ENABLE.get() == ENABLE_HANDSHAKE_OUT
}
pub struct Mgba {
bytes_written: usize,
}
impl Mgba {
pub fn new() -> Option<Self> {
if is_running_in_mgba() {
Some(Mgba { bytes_written: 0 })
} else {
None
}
}
}
impl Mgba {
pub fn set_level(&mut self, level: DebugLevel) {
DEBUG_LEVEL.set(DEBUG_FLAG_CODE | level as u16);
self.bytes_written = 0;
}
}
impl core::fmt::Write for Mgba {
fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> {
unsafe {
let mut current_location = OUTPUT.add(self.bytes_written);
let mut str_iter = s.bytes();
while self.bytes_written < 255 {
match str_iter.next() {
Some(byte) => {
current_location.write(byte);
current_location = current_location.offset(1);
self.bytes_written += 1;
}
None => return Ok(()),
}
}
}
Ok(())
}
}

48
src/single.rs Normal file
View file

@ -0,0 +1,48 @@
use core::cell::Cell;
pub struct Singleton<T> {
single: Option<T>,
}
impl<T> Singleton<T> {
pub const fn new(s: T) -> Self {
Singleton { single: Some(s) }
}
pub fn take(&mut self) -> T {
let g = core::mem::replace(&mut self.single, None);
g.unwrap()
}
}
pub struct Single {
is_taken: Cell<bool>,
}
pub struct SingleToken<'a> {
cell: &'a Cell<bool>,
}
impl Single {
pub const fn new() -> Self {
Single {
is_taken: Cell::new(false),
}
}
pub fn take(&self) -> Result<SingleToken, &'static str> {
if self.is_taken.get() {
Err("Already taken")
} else {
self.is_taken.set(true);
Ok(SingleToken {
cell: &self.is_taken,
})
}
}
}
impl Drop for SingleToken<'_> {
fn drop(&mut self) {
self.cell.set(false);
}
}

104
src/syscall.rs Normal file
View file

@ -0,0 +1,104 @@
#[allow(non_snake_case)]
pub fn halt() {
unsafe {
asm!(
"swi 0x02",
lateout("r0") _,
lateout("r1") _,
lateout("r2") _,
lateout("r3") _
);
}
}
pub fn stop() {
unsafe {
asm!(
"swi 0x03",
lateout("r0") _,
lateout("r1") _,
lateout("r2") _,
lateout("r3") _
);
}
}
pub fn wait_for_interrupt() {
unsafe {
asm!(
"swi 0x04",
lateout("r0") _,
lateout("r1") _,
lateout("r2") _,
lateout("r3") _
);
}
}
#[allow(non_snake_case)]
pub fn wait_for_VBlank() {
unsafe {
asm!(
"swi 0x05",
lateout("r0") _,
lateout("r1") _,
lateout("r2") _,
lateout("r3") _
);
}
}
pub fn div(numerator: i32, denominator: i32) -> (i32, i32, i32) {
let divide: i32;
let modulo: i32;
let abs_divide: i32;
unsafe {
asm!(
"swi 0x06",
in("r0") numerator,
in("r1") denominator,
lateout("r0") divide,
lateout("r1") modulo,
lateout("r3") abs_divide,
);
}
(divide, modulo, abs_divide)
}
pub fn sqrt(n: i32) -> i32 {
let result: i32;
unsafe {
asm!(
"swi 0x08",
in("r0") n,
lateout("r0") result,
);
}
result
}
pub fn arc_tan(n: i16) -> i16 {
let result: i16;
unsafe {
asm!(
"swi 0x09",
in("r0") n,
lateout("r0") result,
);
}
result
}
pub fn arc_tan2(x: i16, y: i32) -> i16 {
let result: i16;
unsafe {
asm!(
"swi 0x09",
in("r0") x,
in("r1") y,
lateout("r0") result,
);
}
result
}