commit c591b2c2725fad878c6bf5961967231b8f1ebf59 Author: Zenn Date: Sun Aug 6 18:14:49 2023 +0200 first commit diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..e67948f --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,19 @@ +[build] +target = "arduboy.json" +target-dir = "./target" + +[profile.release] +codegen-units = 1 +lto = "fat" +opt-level = "z" +panic = "abort" + +[profile.dev] +codegen-units = 1 +lto = "fat" +opt-level = "z" +panic = "abort" + +[unstable] +build-std = ["core", "compiler_builtins"] +build-std-features = ["compiler-builtins-mem"] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dc220e8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/target +/.vscode + +/Wrapper-Project/.pio +/Wrapper-Project/lib/*.a diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..458f675 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,95 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "arduboy-rust" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e80037f297e2a5db1e58804b7a3b1b10f448b104623422db1c94b185b45b1ea5" +dependencies = [ + "panic-halt", +] + +[[package]] +name = "demo2" +version = "0.1.0" +dependencies = [ + "arduboy-rust", +] + +[[package]] +name = "demo3" +version = "0.1.0" +dependencies = [ + "arduboy-rust", +] + +[[package]] +name = "demo4" +version = "0.1.0" +dependencies = [ + "arduboy-rust", +] + +[[package]] +name = "demo5" +version = "0.1.0" +dependencies = [ + "arduboy-rust", +] + +[[package]] +name = "demo6" +version = "0.1.0" +dependencies = [ + "arduboy-rust", +] + +[[package]] +name = "eeprom-demo" +version = "0.1.0" +dependencies = [ + "arduboy-rust", +] + +[[package]] +name = "game" +version = "0.1.0" +dependencies = [ + "arduboy-rust", +] + +[[package]] +name = "panic-halt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de96540e0ebde571dc55c73d60ef407c653844e6f9a1e2fdbd40c07b9252d812" + +[[package]] +name = "pong" +version = "0.1.0" +dependencies = [ + "arduboy-rust", +] + +[[package]] +name = "rustacean" +version = "0.1.0" +dependencies = [ + "arduboy-rust", +] + +[[package]] +name = "snake" +version = "0.1.0" +dependencies = [ + "arduboy-rust", +] + +[[package]] +name = "tone" +version = "0.1.0" +dependencies = [ + "arduboy-rust", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..52c05dc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[workspace] +members = [ + "Examples/Arduboy-Tutorials/eeprom", + "Examples/Arduboy-Tutorials/tone", + "Examples/Arduboy-Tutorials/demo2", + "Examples/Arduboy-Tutorials/demo3", + "Examples/Arduboy-Tutorials/demo4", + "Examples/Arduboy-Tutorials/demo5", + "Examples/Arduboy-Tutorials/demo6", + "Examples/pong", + "Examples/rustacean", + "Examples/snake", + "Project/game", +] +resolver = "2" diff --git a/Examples/Arduboy-Tutorials/demo2/Cargo.toml b/Examples/Arduboy-Tutorials/demo2/Cargo.toml new file mode 100644 index 0000000..a51e739 --- /dev/null +++ b/Examples/Arduboy-Tutorials/demo2/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "demo2" +version = "0.1.0" +authors = ["ZennDev "] +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +arduboy-rust = "1.0.0" diff --git a/Examples/Arduboy-Tutorials/demo2/src/lib.rs b/Examples/Arduboy-Tutorials/demo2/src/lib.rs new file mode 100644 index 0000000..23f8f6b --- /dev/null +++ b/Examples/Arduboy-Tutorials/demo2/src/lib.rs @@ -0,0 +1,18 @@ +#![no_std] +//Include the Arduboy Library +//Initialize the arduboy object +use arduboy_rust::prelude::*; +//The setup() function runs once when you turn your Arduboy on +#[no_mangle] +pub unsafe extern "C" fn setup() { + // put your setup code here, to run once: + arduboy.begin(); + arduboy.clear(); + arduboy.print("Holmes is cool!\0"); + arduboy.display(); +} +#[no_mangle] +#[export_name = "loop"] +pub unsafe extern "C" fn loop_() { + // put your main code here, to run repeatedly: +} diff --git a/Examples/Arduboy-Tutorials/demo3/Cargo.toml b/Examples/Arduboy-Tutorials/demo3/Cargo.toml new file mode 100644 index 0000000..c469dc7 --- /dev/null +++ b/Examples/Arduboy-Tutorials/demo3/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "demo3" +version = "0.1.0" +authors = ["ZennDev "] +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +arduboy-rust = "1.0.0" diff --git a/Examples/Arduboy-Tutorials/demo3/src/lib.rs b/Examples/Arduboy-Tutorials/demo3/src/lib.rs new file mode 100644 index 0000000..34e6c47 --- /dev/null +++ b/Examples/Arduboy-Tutorials/demo3/src/lib.rs @@ -0,0 +1,38 @@ +#![no_std] +//Include the Arduboy Library +//Initialize the arduboy object +use arduboy_rust::prelude::*; +//Initialize our counter variable +#[allow(non_upper_case_globals)] +static mut counter: c_int = 0; +//The setup() function runs once when you turn your Arduboy on +#[no_mangle] +pub unsafe extern "C" fn setup() { + //Start the Arduboy properly and display the Arduboy logo + arduboy.begin(); + //Get rid of the Arduboy logo and clear the screen + arduboy.clear(); + //Assign our counter variable to be equal to 0 + counter = 0; + //Set framerate to 30 + arduboy.set_frame_rate(30); +} +//The loop() function repeats forever after setup() is done +#[no_mangle] +#[export_name = "loop"] +pub unsafe extern "C" fn loop_() { + //Skip cycles not in the framerate + if !arduboy.next_frame() { + return; + } + //Clear whatever is printed on the screen + arduboy.clear(); + //Move the cursor to the position 30, 30 of the screen + arduboy.set_cursor(0, 0); + //Increase counter's value by 1 + counter += 1; + //Print out the value of counter + arduboy.print(counter); + //Refresh the screen to show whatever's printed to it + arduboy.display(); +} diff --git a/Examples/Arduboy-Tutorials/demo4/Cargo.toml b/Examples/Arduboy-Tutorials/demo4/Cargo.toml new file mode 100644 index 0000000..8675d4f --- /dev/null +++ b/Examples/Arduboy-Tutorials/demo4/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "demo4" +version = "0.1.0" +authors = ["ZennDev "] +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +arduboy-rust = "1.0.0" diff --git a/Examples/Arduboy-Tutorials/demo4/src/lib.rs b/Examples/Arduboy-Tutorials/demo4/src/lib.rs new file mode 100644 index 0000000..6ed0ec8 --- /dev/null +++ b/Examples/Arduboy-Tutorials/demo4/src/lib.rs @@ -0,0 +1,54 @@ +#![no_std] +#![allow(non_upper_case_globals)] +//Include the Arduboy Library +//Initialize the arduboy object +use arduboy_rust::prelude::*; +//Initialize our counter variable + +static mut counter: c_int = 0; +//The setup() function runs once when you turn your Arduboy on +#[no_mangle] +pub unsafe extern "C" fn setup() { + //Start the Arduboy properly and display the Arduboy logo + arduboy.begin(); + //Get rid of the Arduboy logo and clear the screen + arduboy.clear(); + //Assign our counter variable to be equal to 0 + counter = 0; + //Set framerate to 10 + arduboy.set_frame_rate(10); +} +//The loop() function repeats forever after setup() is done +#[no_mangle] +#[export_name = "loop"] +pub unsafe extern "C" fn loop_() { + //Skip cycles not in the framerate + if !arduboy.next_frame() { + return; + } + //Clear whatever is printed on the screen + arduboy.clear(); + //Check if the UP_BUTTON is being pressed + if UP.pressed() { + //Increase counter by 1 + counter = counter + 1; + } + //Check if the DOWN_BUTTON is being pressed + if DOWN.pressed() { + //Decrease counter + counter = counter - 1; + } + //Check if counter is equal to 36 + if counter == 36 { + //Move the cursor to the position 30, 30 of the screen + arduboy.set_cursor(30, 30); + //Printing the yay (important always put the \0 at the end for &str) + arduboy.print("Yay!\0"); + } + //Move the cursor back to the top-left of the screen + arduboy.set_cursor(0, 0); + //Print out the value of counter + arduboy.print(counter); + //Refresh the screen to show whatever's printed to it + arduboy.display(); +} diff --git a/Examples/Arduboy-Tutorials/demo5/Cargo.toml b/Examples/Arduboy-Tutorials/demo5/Cargo.toml new file mode 100644 index 0000000..d0fcedb --- /dev/null +++ b/Examples/Arduboy-Tutorials/demo5/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "demo5" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +arduboy-rust = "1.0.0" diff --git a/Examples/Arduboy-Tutorials/demo5/src/lib.rs b/Examples/Arduboy-Tutorials/demo5/src/lib.rs new file mode 100644 index 0000000..fd6be95 --- /dev/null +++ b/Examples/Arduboy-Tutorials/demo5/src/lib.rs @@ -0,0 +1,95 @@ +#![no_std] +#![allow(non_upper_case_globals)] + +//Include the Arduboy Library +//Initialize the arduboy object +use arduboy_rust::prelude::*; + +//Initialize variables used in this game +static mut playerwin: c_int = 0; +static mut attempts: c_int = 0; +static mut guessednumber: c_int = 0; +static mut randomnumber: c_int = 0; +static mut lastguess: c_int = 0; + +//The setup() function runs once when you turn your Arduboy on +#[no_mangle] +pub unsafe extern "C" fn setup() { + // put your setup code here, to run once: + arduboy.begin(); + arduboy.clear(); + arduboy.init_random_seed(); + randomnumber = random_between(1, 101) as i16; +} +//The loop() function repeats forever after setup() is done +#[no_mangle] +#[export_name = "loop"] +pub unsafe extern "C" fn loop_() { + // put your main code here, to run repeatedly: + arduboy.clear(); + arduboy.poll_buttons(); + if playerwin == 0 { + //Ask the player for a number and play the game + if attempts == 7 { + //Game Over screen + arduboy.set_cursor(0, 0); + arduboy.print("You lost!\0"); + arduboy.print("\n\0"); + arduboy.print("Correct Number: \0"); + arduboy.print(randomnumber); + if A.just_pressed() { + randomnumber = random_between(1, 101) as i16; + attempts = 0; + playerwin = 0; + } + } else { + //Player has more attempts + if UP.just_pressed() { + guessednumber += 1; + } + if DOWN.just_pressed() { + guessednumber -= 1; + } + if A.just_pressed() { + if guessednumber == randomnumber { + playerwin += 1; + } else { + attempts += 1; + lastguess = guessednumber + } + } + arduboy.set_cursor(0, 0); + arduboy.print("Attempt: \0"); + arduboy.print(attempts); + arduboy.print("\n\0"); + arduboy.print("Number to guess: \0"); + arduboy.print(guessednumber); + arduboy.print("\n\0"); + if attempts == 0 { + arduboy.print("Good luck!\0"); + } else { + arduboy.print(lastguess); + if lastguess > randomnumber { + arduboy.print(" is too high!\0"); + } + if lastguess < randomnumber { + arduboy.print(" is too low!\0"); + } + } + } + } else { + //Tell the player that they won! + arduboy.set_cursor(0, 0); + arduboy.print("You won!\0"); + arduboy.print("\n\0"); + arduboy.print("Correct Number: \0"); + arduboy.print(randomnumber); + + if A.just_pressed() { + randomnumber = random_between(1, 101) as c_int; + attempts = 0; + playerwin = 0; + } + } + arduboy.display(); +} diff --git a/Examples/Arduboy-Tutorials/demo6/Cargo.toml b/Examples/Arduboy-Tutorials/demo6/Cargo.toml new file mode 100644 index 0000000..9cec495 --- /dev/null +++ b/Examples/Arduboy-Tutorials/demo6/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "demo6" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +arduboy-rust = "1.0.0" diff --git a/Examples/Arduboy-Tutorials/demo6/src/lib.rs b/Examples/Arduboy-Tutorials/demo6/src/lib.rs new file mode 100644 index 0000000..e8e322d --- /dev/null +++ b/Examples/Arduboy-Tutorials/demo6/src/lib.rs @@ -0,0 +1,76 @@ +#![no_std] +#![allow(non_upper_case_globals)] +//Include the Arduboy Library +//Initialize the arduboy object +#[allow(unused_imports)] +use arduboy_rust::prelude::*; + +#[link_section = ".progmem.data"] +static background_sprite: [u8; 10] = [8, 8, 0x81, 0x00, 0x12, 0x40, 0x04, 0x11, 0x00, 0x04]; +#[link_section = ".progmem.data"] +static player_sprite1: [u8; 34] = [ + 16, 16, 0xfe, 0x01, 0x3d, 0x25, 0x25, 0x3d, 0x01, 0x01, 0xc1, 0x01, 0x3d, 0x25, 0x25, 0x3d, + 0x01, 0xfe, 0x7f, 0x80, 0x9c, 0xbc, 0xb0, 0xb0, 0xb2, 0xb2, 0xb3, 0xb0, 0xb0, 0xb0, 0xbc, 0x9c, + 0x80, 0x7f, +]; +#[link_section = ".progmem.data"] +static player_sprite2: [u8; 34] = [ + 16, 16, 0xfc, 0x02, 0x19, 0x25, 0x25, 0x19, 0x01, 0x01, 0x01, 0x01, 0x19, 0x25, 0x25, 0x19, + 0x02, 0xfc, 0x3f, 0x40, 0x80, 0x98, 0x8c, 0x86, 0x82, 0x82, 0x82, 0x82, 0x86, 0x8c, 0x98, 0x80, + 0x40, 0x3f, +]; + +// Put your variables here +static mut playerx: c_int = 5; +static mut playery: c_int = 10; +static mut playermode: bool = false; +// The setup() function runs once when you turn your Arduboy on +#[no_mangle] +pub unsafe extern "C" fn setup() { + // put your setup code here, to run once: + arduboy.begin(); + arduboy.clear(); + arduboy.set_frame_rate(60); +} +// The loop() function repeats forever after setup() is done +#[no_mangle] +#[export_name = "loop"] +pub unsafe extern "C" fn loop_() { + if !arduboy.next_frame() { + return; + } + // put your main code here, to run repeatedly: + arduboy.clear(); + arduboy.poll_buttons(); + + if arduboy.pressed(LEFT) { + playerx -= 1; + } + if arduboy.pressed(RIGHT) { + playerx += 1; + } + if arduboy.pressed(UP) { + playery -= 1; + } + if arduboy.pressed(DOWN) { + playery += 1; + } + if arduboy.just_pressed(A) { + playermode = !playermode + } + + for i in (0..WIDTH).step_by(8) { + for j in (0..HEIGHT).step_by(8) { + sprites::draw_override(i.into(), j.into(), get_sprite_addr!(background_sprite), 0) + } + } + + if playermode { + sprites::draw_override(playerx, playery, get_sprite_addr!(player_sprite1), 0); + } else { + sprites::draw_override(playerx, playery, get_sprite_addr!(player_sprite2), 0); + } + + arduboy.set_cursor(0, 0); + arduboy.display(); +} diff --git a/Examples/Arduboy-Tutorials/eeprom/Cargo.toml b/Examples/Arduboy-Tutorials/eeprom/Cargo.toml new file mode 100644 index 0000000..7ab7e00 --- /dev/null +++ b/Examples/Arduboy-Tutorials/eeprom/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "eeprom-demo" +version = "0.1.0" +authors = ["ZennDev "] +license = "MIT OR Apache-2.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +arduboy-rust = "1.0.0" diff --git a/Examples/Arduboy-Tutorials/eeprom/src/lib.rs b/Examples/Arduboy-Tutorials/eeprom/src/lib.rs new file mode 100644 index 0000000..650463e --- /dev/null +++ b/Examples/Arduboy-Tutorials/eeprom/src/lib.rs @@ -0,0 +1,59 @@ +#![no_std] +#![allow(non_upper_case_globals)] +//Include the Arduboy Library +//Initialize the arduboy object +#[allow(unused_imports)] +use arduboy_rust::prelude::*; + +// #[link_section = ".progmem.data"] + +// Setup eeprom memory +static mut eeprom: EEPROM = EEPROM::new(10); + +static mut count: u8 = 0; +static mut MEM: u8 = 0; + +// The setup() function runs once when you turn your Arduboy on +#[no_mangle] +pub unsafe extern "C" fn setup() { + // put your setup code here, to run once: + arduboy.begin(); + eeprom.init(); + arduboy.clear(); + arduboy.set_frame_rate(30); +} +// The loop() function repeats forever after setup() is done +#[no_mangle] +#[export_name = "loop"] +pub unsafe extern "C" fn loop_() { + // put your main code here, to run repeatedly: + if !arduboy.next_frame() { + return; + } + arduboy.clear(); + arduboy.poll_buttons(); + if arduboy.just_pressed(UP) { + count += 1; + } + if arduboy.just_pressed(DOWN) { + count -= 1; + } + if arduboy.just_pressed(A) { + unsafe { eeprom.put(count) } + } + if arduboy.just_pressed(B) { + eeprom.get(&mut MEM) + } + arduboy.set_cursor(0, 0); + arduboy.print(count as u16); + + arduboy.set_cursor(0, 30); + arduboy.print("Memory:\0"); + arduboy.print(MEM as u16); + arduboy.set_cursor(0, 40); + arduboy.print("eeprom:\0"); + + arduboy.print(eeprom.read() as u16); + + arduboy.display(); +} diff --git a/Examples/Arduboy-Tutorials/tone/Cargo.toml b/Examples/Arduboy-Tutorials/tone/Cargo.toml new file mode 100644 index 0000000..f66ee77 --- /dev/null +++ b/Examples/Arduboy-Tutorials/tone/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "tone" +version = "0.1.0" +authors = ["ZennDev "] +license = "MIT OR Apache-2.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +arduboy-rust = "1.0.0" diff --git a/Examples/Arduboy-Tutorials/tone/src/lib.rs b/Examples/Arduboy-Tutorials/tone/src/lib.rs new file mode 100644 index 0000000..ab7eb78 --- /dev/null +++ b/Examples/Arduboy-Tutorials/tone/src/lib.rs @@ -0,0 +1,490 @@ +#![no_std] +#![allow(non_upper_case_globals)] +//Include the Arduboy Library +//Initialize the arduboy object +use arduboy_rust::arduboy_tone_pitch::*; +#[allow(unused_imports)] +use arduboy_rust::prelude::*; + +#[link_section = ".progmem.data"] +static allNotes: [u16; 241] = [ + NOTE_C0H, + NDUR, + NOTE_CS0, + NDUR, + NOTE_D0, + NDUR, + NOTE_DS0, + NDUR, + NOTE_E0, + NDUR, + NOTE_F0, + NDUR, + NOTE_FS0, + NDUR, + NOTE_G0, + NDUR, + NOTE_GS0, + NDUR, + NOTE_A0, + NDUR, + NOTE_AS0, + NDUR, + NOTE_B0, + NDUR, + NOTE_C1H, + NDUR, + NOTE_CS1, + NDUR, + NOTE_D1, + NDUR, + NOTE_DS1, + NDUR, + NOTE_E1, + NDUR, + NOTE_F1, + NDUR, + NOTE_FS1, + NDUR, + NOTE_G1, + NDUR, + NOTE_GS1, + NDUR, + NOTE_A1, + NDUR, + NOTE_AS1, + NDUR, + NOTE_B1, + NDUR, + NOTE_C2H, + NDUR, + NOTE_CS2, + NDUR, + NOTE_D2, + NDUR, + NOTE_DS2, + NDUR, + NOTE_E2, + NDUR, + NOTE_F2, + NDUR, + NOTE_FS2, + NDUR, + NOTE_G2, + NDUR, + NOTE_GS2, + NDUR, + NOTE_A2, + NDUR, + NOTE_AS2, + NDUR, + NOTE_B2, + NDUR, + NOTE_C3H, + NDUR, + NOTE_CS3, + NDUR, + NOTE_D3, + NDUR, + NOTE_DS3, + NDUR, + NOTE_E3, + NDUR, + NOTE_F3, + NDUR, + NOTE_FS3, + NDUR, + NOTE_G3, + NDUR, + NOTE_GS3, + NDUR, + NOTE_A3, + NDUR, + NOTE_AS3, + NDUR, + NOTE_B3, + NDUR, + NOTE_C4H, + NDUR, + NOTE_CS4, + NDUR, + NOTE_D4, + NDUR, + NOTE_DS4, + NDUR, + NOTE_E4, + NDUR, + NOTE_F4, + NDUR, + NOTE_FS4, + NDUR, + NOTE_G4, + NDUR, + NOTE_GS4, + NDUR, + NOTE_A4, + NDUR, + NOTE_AS4, + NDUR, + NOTE_B4, + NDUR, + NOTE_C5H, + NDUR, + NOTE_CS5, + NDUR, + NOTE_D5, + NDUR, + NOTE_DS5, + NDUR, + NOTE_E5, + NDUR, + NOTE_F5, + NDUR, + NOTE_FS5, + NDUR, + NOTE_G5, + NDUR, + NOTE_GS5, + NDUR, + NOTE_A5, + NDUR, + NOTE_AS5, + NDUR, + NOTE_B5, + NDUR, + NOTE_C6H, + NDUR, + NOTE_CS6, + NDUR, + NOTE_D6, + NDUR, + NOTE_DS6, + NDUR, + NOTE_E6, + NDUR, + NOTE_F6, + NDUR, + NOTE_FS6, + NDUR, + NOTE_G6, + NDUR, + NOTE_GS6, + NDUR, + NOTE_A6, + NDUR, + NOTE_AS6, + NDUR, + NOTE_B6, + NDUR, + NOTE_C7H, + NDUR, + NOTE_CS7, + NDUR, + NOTE_D7, + NDUR, + NOTE_DS7, + NDUR, + NOTE_E7, + NDUR, + NOTE_F7, + NDUR, + NOTE_FS7, + NDUR, + NOTE_G7, + NDUR, + NOTE_GS7, + NDUR, + NOTE_A7, + NDUR, + NOTE_AS7, + NDUR, + NOTE_B7, + NDUR, + NOTE_C8H, + NDUR, + NOTE_CS8, + NDUR, + NOTE_D8, + NDUR, + NOTE_DS8, + NDUR, + NOTE_E8, + NDUR, + NOTE_F8, + NDUR, + NOTE_FS8, + NDUR, + NOTE_G8, + NDUR, + NOTE_GS8, + NDUR, + NOTE_A8, + NDUR, + NOTE_AS8, + NDUR, + NOTE_B8, + NDUR, + NOTE_C9H, + NDUR, + NOTE_CS9, + NDUR, + NOTE_D9, + NDUR, + NOTE_DS9, + NDUR, + NOTE_E9, + NDUR, + NOTE_F9, + NDUR, + NOTE_FS9, + NDUR, + NOTE_G9, + NDUR, + NOTE_GS9, + NDUR, + NOTE_A9, + NDUR, + NOTE_AS9, + NDUR, + NOTE_B9, + NDUR, + TONES_REPEAT, +]; +#[link_section = ".progmem.data"] +static sound1: [u16; 73] = [ + NOTE_C1, 500, NOTE_C1H, 500, NOTE_G1, 500, NOTE_G1H, 500, NOTE_C2, 500, NOTE_C2H, 500, NOTE_G2, + 500, NOTE_G2H, 500, NOTE_C3, 500, NOTE_C3H, 500, NOTE_G3, 500, NOTE_G3H, 500, NOTE_C4, 500, + NOTE_C4H, 500, NOTE_G4, 500, NOTE_G4H, 500, NOTE_C5, 500, NOTE_C5H, 500, NOTE_G5, 500, + NOTE_G5H, 500, NOTE_C6, 500, NOTE_C6H, 500, NOTE_G6, 500, NOTE_G6H, 500, NOTE_C7, 500, + NOTE_C7H, 500, NOTE_G7, 500, NOTE_G7H, 500, NOTE_C8, 500, NOTE_C8H, 500, NOTE_G8, 500, + NOTE_G8H, 500, NOTE_C9, 500, NOTE_C9H, 500, NOTE_G9, 500, NOTE_G9H, 500, TONES_END, +]; + +static mut circle_pos: i16 = 7; +const BUTTON_DELAY: u32 = 200; + +// The setup() function runs once when you turn your Arduboy on +#[no_mangle] +pub unsafe extern "C" fn setup() { + // put your setup code here, to run once: + arduboy.begin(); +} +// The loop() function repeats forever after setup() is done +#[no_mangle] +#[export_name = "loop"] +pub unsafe extern "C" fn loop_() { + let mut in_ram: [u16; 43] = [ + NOTE_E4, + 400, + NOTE_D4, + 200, + NOTE_C4, + 400, + NOTE_REST, + 200, + NOTE_D4, + 200, + NOTE_C4, + 300, + NOTE_REST, + 100, + NOTE_C4, + 300, + NOTE_REST, + 100, + NOTE_E4, + 300, + NOTE_REST, + 100, + NOTE_G4, + 300, + NOTE_REST, + 100, + NOTE_F4, + 300, + NOTE_REST, + 100, + NOTE_A4, + 300, + NOTE_REST, + 100, + NOTE_D5H, + 200, + NOTE_REST, + 200, + NOTE_D5H, + 200, + NOTE_REST, + 1500, + TONES_REPEAT, + ]; + // put your main code here, to run repeatedly: + let mut new_notes: bool; + display_audio(); + loop { + move_circle(); + if arduboy.pressed(UP) { + arduboy.audio_on(); + display_audio(); + } + if arduboy.pressed(DOWN) { + arduboy.audio_off(); + display_audio(); + } + if arduboy.pressed(B) { + delay(BUTTON_DELAY); + break; + } + } + + sound.tone(1000, 0); + arduboy.clear(); + arduboy.print("tone(1000)\n\nB: no_tone()\n delay(1000)\n break\0"); + while sound.playing() { + move_circle(); + if arduboy.pressed(B_BUTTON) { + sound.no_tone(); + delay(1000); + break; + } + } + + sound.tone(500, 4000); + arduboy.clear(); + arduboy.print("tone(500, 4000)\n\nB: break\0"); + while sound.playing() { + move_circle(); + if arduboy.pressed(B_BUTTON) { + delay(BUTTON_DELAY); + break; + } + } + + sound.tone2(NOTE_C4, 500, NOTE_C5H, 5000); + arduboy.clear(); + arduboy.print("tone(C4,500,C5H,5000)\n\nB: no_tone(), break\0"); + while sound.playing() { + move_circle(); + if arduboy.pressed(B_BUTTON) { + sound.no_tone(); + delay(BUTTON_DELAY); + break; + } + } + + sound.tone3(NOTE_C7H, 500, NOTE_REST, 1000, NOTE_C6, 5000); + arduboy.clear(); + arduboy.print("tone(C7H,500,\n REST,1000,\n C6,6000)\n\nB: no_tone(), break\0"); + while sound.playing() { + move_circle(); + if arduboy.pressed(B_BUTTON) { + sound.no_tone(); + delay(BUTTON_DELAY); + break; + } + } + + sound.tones(get_tones_addr!(allNotes)); + arduboy.clear(); + arduboy.print("tones(allNotes)\n\nA: no_tone(), again\nUP: again\nB: break\0"); + while sound.playing() { + move_circle(); + if arduboy.pressed(A_BUTTON) { + sound.no_tone(); + sound.tones(get_tones_addr!(allNotes)); + } + if arduboy.pressed(UP_BUTTON) { + sound.tones(get_tones_addr!(allNotes)); + } + if arduboy.pressed(B_BUTTON) { + delay(BUTTON_DELAY); + break; + } + } + + new_notes = false; + sound.tones_in_ram(get_tones_addr!(in_ram) as *mut u32); + arduboy.clear(); + arduboy.print("tonesInRAM(inRAM)\n\nA: change notes\nB: break\0"); + while sound.playing() { + move_circle(); + if arduboy.pressed(A_BUTTON) { + new_notes = !new_notes; + if new_notes { + in_ram[34] = in_ram[38]; + in_ram[38] = NOTE_C5H; + } else { + in_ram[34] = in_ram[38]; + in_ram[38] = NOTE_D5H; + } + delay(BUTTON_DELAY); + } + if arduboy.pressed(B) { + delay(BUTTON_DELAY); + break; + } + } + + sound.tones(get_tones_addr!(sound1)); + arduboy.clear(); + arduboy.print("volume_mode(IN_TONES)\ntones(sound1)\n\nB: break\0"); + while sound.playing() { + move_circle(); + if arduboy.pressed(B) { + delay(BUTTON_DELAY); + break; + } + } + + sound.volume_mode(VOLUME_ALWAYS_NORMAL); + sound.tones(get_tones_addr!(sound1)); + arduboy.clear(); + arduboy.print("volume_mode(NORMAL)\ntones(sound1)\n\nB: no_tone(), break\0"); + while sound.playing() { + move_circle(); + if arduboy.pressed(B) { + sound.no_tone(); + delay(BUTTON_DELAY); + break; + } + } + + sound.volume_mode(VOLUME_ALWAYS_HIGH); + sound.tones(get_tones_addr!(sound1)); + arduboy.clear(); + arduboy.print("volume_mode(HIGH)\ntones(sound1)\n\nB: break\0"); + while sound.playing() { + move_circle(); + if arduboy.pressed(B) { + delay(BUTTON_DELAY); + break; + } + } + sound.volume_mode(VOLUME_IN_TONE); +} + +fn move_circle() { + unsafe { + arduboy.fill_circle(circle_pos, 54, 7, Color::Black); + circle_pos += 8; + if circle_pos > 119 { + circle_pos = 7; + } + arduboy.fill_circle(circle_pos, 54, 7, Color::White); + } + arduboy.display(); + delay(100); +} + +fn display_audio() { + arduboy.clear(); + arduboy.print("Audio enabled: \0"); + if arduboy.audio_enabled() { + arduboy.print("YES\0"); + } else { + arduboy.print("NO\0") + } + + arduboy.print("\n\nUP: enable\nDOWN: disable\nB: break\0"); + arduboy.invert(!arduboy.audio_enabled()); +} diff --git a/Examples/pong/Cargo.toml b/Examples/pong/Cargo.toml new file mode 100644 index 0000000..2a27afa --- /dev/null +++ b/Examples/pong/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "pong" +version = "0.1.0" +authors = ["Brian Bowman "] +license = "MIT OR Apache-2.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +arduboy-rust = "1.0.0" diff --git a/Examples/pong/src/lib.rs b/Examples/pong/src/lib.rs new file mode 100644 index 0000000..109ec2b --- /dev/null +++ b/Examples/pong/src/lib.rs @@ -0,0 +1,230 @@ +#![no_std] + +use arduboy_rust::prelude::*; + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] +enum GameState { + Title, + Gameplay, + Win, + Lose, +} + +struct Ball { + x: i16, + y: i16, + size: u8, + right: bool, + down: bool, +} + +impl Ball { + unsafe fn draw(&self) { + arduboy.fill_rect(self.x.into(), self.y.into(), self.size, self.size, Color::White); + } +} + +struct Paddle { + x: i16, + y: i16, + width: u8, + height: u8, +} + +impl Paddle { + unsafe fn draw(&self) { + arduboy.fill_rect(self.x.into(), self.y.into(), self.width, self.height, Color::White); + } +} + +static mut G: Globals = Globals { + game_state: GameState::Title, + player_score: 0, + ai_score: 0, + ball: Ball { + x: 62, + y: 1, + size: 4, + right: true, + down: true, + }, + player: Paddle { + x: 0, + y: 50, + width: 2, + height: 12, + }, + ai: Paddle { + x: (WIDTH - 4) as i16, + y: 50, + width: 2, + height: 12, + }, +}; + +struct Globals { + game_state: GameState, + player_score: u8, + ai_score: u8, + ball: Ball, + player: Paddle, + ai: Paddle, +} + +#[no_mangle] +pub unsafe extern "C" fn setup() { + arduboy.begin(); + arduboy.init_random_seed(); + arduboy.set_frame_rate(60); +} + +#[no_mangle] +#[export_name = "loop"] +pub unsafe extern "C" fn loop_() { + if !arduboy.next_frame() { + return; + } + + arduboy.poll_buttons(); + + arduboy.clear(); + match G.game_state { + GameState::Title => { + arduboy.set_cursor(52, 10); + arduboy.print(&b"PONG\0"[..]); + arduboy.set_cursor(16, 22); + arduboy.print(&b"Press A to start\0"[..]); + if A.just_pressed() { + G.game_state = GameState::Gameplay; + } + } + GameState::Gameplay => { + gameplay(); + if A.just_pressed() { + reset_game(); + } + } + GameState::Win => { + arduboy.set_cursor(40, 10); + arduboy.print(&b"You Win!\0"[..]); + if A.just_pressed() { + reset_game(); + } + } + GameState::Lose => { + arduboy.set_cursor(37, 10); + arduboy.print(&b"Game Over\0"[..]); + if A.just_pressed() { + reset_game(); + } + } + } + + arduboy.display(); +} + +unsafe fn reset_game() { + G.game_state = GameState::Title; + G.ball.x = (WIDTH / 2) as i16; + G.player_score = 0; + G.ai_score = 0; +} + +unsafe fn gameplay() { + // + // Player movement + // + + if UP.pressed() && G.player.y > 0 { + G.player.y -= 1; + } + + if DOWN.pressed() && G.player.y + (G.player.height as i16) < (HEIGHT - 1) as i16 { + G.player.y += 1; + } + + // + // AI movement + // + + if G.ball.x > 115 || random_between(0, 20) == 0 { + if G.ball.y < G.ai.y { + G.ai.y -= 1; + } else if G.ball.y + (G.ball.size as i16) > G.ai.y + G.ai.height as i16 { + G.ai.y += 1; + } + } + + // + // Ball movement + // + + if G.ball.y == 1 { + G.ball.down = true; + } else if G.ball.y + G.ball.size as i16 == (HEIGHT - 1) as i16 { + G.ball.down = false; + } + + if G.ball.x == G.player.x + G.player.width as i16 + && G.ball.y + G.ball.size as i16 > G.player.y + && G.ball.y < G.player.y + G.player.height as i16 + { + G.ball.right = true; + } + + if G.ball.x + G.ball.size as i16 == G.ai.x + && G.ball.y + G.ball.size as i16 > G.ai.y + && G.ball.y < G.ai.y + G.ai.height as i16 + { + G.ball.right = false; + } + + if G.ball.right { + G.ball.x += 1; + } else { + G.ball.x -= 1; + } + + if G.ball.down { + G.ball.y += 1; + } else { + G.ball.y -= 1; + } + + // + // + + // Scoring + if G.ball.x + G.ball.size as i16 == -10 { + G.ai_score += 1; + G.ball.x = (WIDTH / 2) as i16; + G.ball.right = true; + } else if G.ball.x == (WIDTH + 10) as i16 { + G.player_score += 1; + G.ball.x = (WIDTH / 2) as i16; + G.ball.right = false; + } + + if G.player_score == 5 { + G.game_state = GameState::Win; + } else if G.ai_score == 5 { + G.game_state = GameState::Lose; + } + + // + // Drawing + // + + arduboy.set_cursor(20, 2); + arduboy.print(G.player_score as u16); + + arduboy.set_cursor(101, 2); + arduboy.print(G.ai_score as u16); + + arduboy.draw_fast_hline(0, 0, WIDTH, Color::White); + arduboy.draw_fast_hline(0, (HEIGHT - 1) as i16, WIDTH, Color::White); + + G.player.draw(); + G.ai.draw(); + G.ball.draw(); +} diff --git a/Examples/rustacean/Cargo.toml b/Examples/rustacean/Cargo.toml new file mode 100644 index 0000000..abfe88d --- /dev/null +++ b/Examples/rustacean/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rustacean" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +arduboy-rust = "1.0.0" diff --git a/Examples/rustacean/src/lib.rs b/Examples/rustacean/src/lib.rs new file mode 100644 index 0000000..05ac95b --- /dev/null +++ b/Examples/rustacean/src/lib.rs @@ -0,0 +1,74 @@ +#![no_std] +#![feature(c_size_t)] +use arduboy_rust::prelude::*; + +const BOTTOM: u8 = 64; +const RIGHT_END: u8 = 128; + +const CHAR_WIDTH: u8 = 6; +const CHAR_HEIGHT: u8 = 8; + +const MSG: &str = " I'm now a \0"; +const MSG2: &str = "Rustacean <3\0"; + +struct Environment { + x: u8, + y: u8, + msg_len: u8, +} +impl Environment { + unsafe fn setup(&mut self) { + arduboy.begin(); + arduboy.set_frame_rate(60); + let msg_len = MSG.len(); + debug_assert!(msg_len <= (core::u8::MAX as c_size_t)); + self.msg_len = msg_len as u8; + } + + unsafe fn loop_(&mut self) { + if !arduboy.next_frame() { + return; + } + + if UP.pressed() && self.y > 0 { + self.y -= 1; + } + if RIGHT.pressed() && self.x < RIGHT_END - CHAR_WIDTH * self.msg_len { + self.x += 1; + } + if LEFT.pressed() && self.x > 0 { + self.x -= 1; + } + if DOWN.pressed() && self.y < BOTTOM - CHAR_HEIGHT * 2 { + self.y += 1; + } + + if (A | B).pressed() { + sound.tone(0xff, 0x3f); + } + + arduboy.clear(); + arduboy.set_cursor(self.x.into(), self.y.into()); + arduboy.print(MSG); + arduboy.set_cursor(self.x.into(), (self.y + 9).into()); + arduboy.print(MSG2); + arduboy.display(); + } +} + +static mut E: Environment = Environment { + x: 0, + y: 0, + msg_len: 0, +}; + +#[no_mangle] +pub unsafe extern "C" fn setup() { + E.setup(); +} + +#[no_mangle] +#[export_name = "loop"] +pub unsafe extern "C" fn loop_() { + E.loop_(); +} diff --git a/Examples/snake/Cargo.toml b/Examples/snake/Cargo.toml new file mode 100644 index 0000000..0b35760 --- /dev/null +++ b/Examples/snake/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "snake" +version = "0.1.0" +authors = ["ZennDev "] +license = "MIT OR Apache-2.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +arduboy-rust = "1.0.0" diff --git a/Examples/snake/src/lib.rs b/Examples/snake/src/lib.rs new file mode 100644 index 0000000..4465ad4 --- /dev/null +++ b/Examples/snake/src/lib.rs @@ -0,0 +1,274 @@ +#![no_std] +use arduboy_rust::prelude::*; + +static mut MEM: u8 = 0; +static SCORE: EEPROM = EEPROM::new(100); + +const WORLD_WIDTH: u8 = 32; +const WORLD_HEIGHT: u8 = 16; +const SCALE_FACTOR: u8 = 4; +#[allow(dead_code)] +enum State { + Title, + Game, + Win, + GameOver, + Reset, + Pause, +} +#[derive(PartialEq, Copy, Clone)] +enum Direction { + Up, + Left, + Down, + Right, +} +struct Snake { + game_state: State, + points: u8, + len: u8, + pos: [(u8, u8); 255 as usize], + next_food: (u8, u8), + delta_time: u8, + direction: Direction, + last_direction: Direction, +} +impl Snake { + fn movement(&mut self) { + for i in 0..(self.len - 1) as usize { + self.pos[i] = self.pos[i + 1]; + } + let (x, y) = self.pos[(self.len - 1) as usize]; + match self.direction { + Direction::Up => { + self.pos[(self.len - 1) as usize] = (x, y - 1); + } + Direction::Left => { + self.pos[(self.len - 1) as usize] = (x - 1, y); + } + Direction::Down => { + self.pos[(self.len - 1) as usize] = (x, y + 1); + } + Direction::Right => { + self.pos[(self.len - 1) as usize] = (x + 1, y); + } + } + self.last_direction = self.direction; + } + unsafe fn get_new_dir(&mut self) { + if UP.just_pressed() && self.last_direction != Direction::Down { + self.direction = Direction::Up + } + if LEFT.just_pressed() && self.last_direction != Direction::Right { + self.direction = Direction::Left + } + if DOWN.just_pressed() && self.last_direction != Direction::Up { + self.direction = Direction::Down + } + if RIGHT.just_pressed() && self.last_direction != Direction::Left { + self.direction = Direction::Right + } + } + fn new_food(&mut self) { + self.next_food.0 = 0; + self.next_food.1 = 0; + while self.next_food.0 == 0 && self.next_food.1 == 0 { + self.next_food.0 = random_between(2, (WORLD_WIDTH - 2).into()) as u8; + self.next_food.1 = random_between(2, (WORLD_HEIGHT - 2).into()) as u8; + for i in 0..(self.len) as usize { + if self.pos[i] == self.next_food { + self.next_food.0 = 0; + self.next_food.1 = 0; + } + } + } + } + unsafe fn render(&self) { + arduboy.draw_circle( + ((self.next_food.0 * SCALE_FACTOR) + SCALE_FACTOR / 4) + .try_into() + .unwrap(), + ((self.next_food.1 * SCALE_FACTOR) + SCALE_FACTOR / 4) + .try_into() + .unwrap(), + SCALE_FACTOR / 4, + Color::White, + ); + + self.pos.iter().enumerate().for_each(|(i, p)| { + if i == (self.len) as usize { + return; + } + if *p == (0, 0) { + return; + } + arduboy.fill_rect( + (p.0 as u8 * SCALE_FACTOR).try_into().unwrap(), + (p.1 as u8 * SCALE_FACTOR).try_into().unwrap(), + SCALE_FACTOR / 2, + SCALE_FACTOR / 2, + Color::White, + ); + }); + } + unsafe fn init(&mut self) { + self.points = 0; + self.len = 3; + self.direction = Direction::Right; + self.last_direction = Direction::Left; + self.pos = [(0, 0); 255 as usize]; + self.pos[0] = (3, 4); + self.pos[1] = (4, 4); + self.pos[2] = (5, 4); + } + unsafe fn boarder(&self) { + for x in 0..WORLD_WIDTH as usize { + for y in 0..WORLD_HEIGHT as usize { + if x == 0 + || y == 0 + || x == (WORLD_WIDTH - 1) as usize + || y == (WORLD_HEIGHT - 1) as usize + { + let scale: u8 = 2; + if self.delta_time % 20 == 0 { + //scale = 1; + }; + arduboy.fill_rect( + (x as u8 * SCALE_FACTOR + scale / 2).try_into().unwrap(), + (y as u8 * SCALE_FACTOR + scale / 2).try_into().unwrap(), + SCALE_FACTOR / scale, + SCALE_FACTOR / scale, + Color::White, + ); + } + } + } + } + fn collision_check(&mut self) { + let (x, y) = self.pos[(self.len - 1) as usize]; + if x == 0 || x == 31 || y == 0 || y == 15 { + self.game_state = State::GameOver; + } + self.pos.iter().enumerate().for_each(|(i, p)| { + if p == &(x, y) && i != (self.len - 1) as usize { + self.game_state = State::GameOver; + } + }); + if self.pos[(self.len - 1) as usize] == self.next_food { + self.points += 1; + self.new_food(); + self.pos[self.len as usize] = self.pos[(self.len - 1) as usize]; + self.len += 1; + sound.tone(0xff, 0x3f); + } + } +} +#[allow(non_upper_case_globals)] +static mut snake: Snake = Snake { + game_state: State::Reset, + len: 3, + points: 0, + pos: [(0, 0); 255 as usize], + next_food: (0, 0), + delta_time: 0, + direction: Direction::Right, + last_direction: Direction::Left, +}; + +#[no_mangle] +pub unsafe extern "C" fn setup() { + arduboy.begin(); + SCORE.init(); + arduboy.init_random_seed(); + arduboy.set_frame_rate(60); + arduboy.clear(); +} + +#[no_mangle] +#[export_name = "loop"] +pub unsafe extern "C" fn loop_() { + if !arduboy.next_frame() { + return; + } + arduboy.poll_buttons(); + arduboy.clear(); + match snake.game_state { + State::Reset => { + snake.init(); + snake.new_food(); + snake.game_state = State::Title; + } + State::Title => { + arduboy.set_cursor(0, 0); + arduboy.set_text_size(2); + arduboy.print(&b"RustySnake\n\0"[..]); + arduboy.set_text_size(1); + arduboy.print(&b"\nControls: \nB for Pause\nA&B for reset\n\0"[..]); + arduboy.print(&b"\nZennDev 2023\n\0"[..]); + if A.just_pressed() { + snake.game_state = State::Game; + } + } + State::Game => { + snake.get_new_dir(); + if snake.delta_time % 10 == 0 { + snake.movement(); + snake.collision_check(); + }; + snake.render(); + snake.boarder(); + if B.just_pressed() { + sound.tone(800, 100); + snake.game_state = State::Pause; + } + } + State::Win => (), + State::Pause => { + let msg = &b"[ Break ]\0"[..]; + let l = msg.len() as u8 * FONT_SIZE / 2; + arduboy.set_cursor( + ((WIDTH / 2) as u16 - l as u16).try_into().unwrap(), + ((HEIGHT / 2) as u16).try_into().unwrap(), + ); + snake.render(); + snake.boarder(); + arduboy.print(msg); + if B.just_pressed() { + snake.game_state = State::Game; + sound.tone(800, 100); + } + } + State::GameOver => { + SCORE.get(&mut MEM); + if MEM == 255 { + MEM = 0; + } + if snake.points > MEM { + SCORE.put(snake.points); + } + + arduboy.set_cursor(0, 0); + arduboy.print(&b"Game Over!\0"[..]); + arduboy.print(&b"\n\n\0"[..]); + arduboy.print(&b"Score: \0"[..]); + arduboy.print(snake.points as u16); + arduboy.print(&b"\nHigh Score: \0"[..]); + arduboy.print(MEM as u16); + arduboy.print(&b"\n\n\0"[..]); + arduboy.print(&b"Press A to Play Again\0"[..]); + + if A.just_pressed() { + snake.game_state = State::Reset; + } + } + } + if (A | B).pressed() { + snake.game_state = State::Reset; + } + arduboy.display(); + if snake.delta_time == 60 { + snake.delta_time = 1 + } else { + snake.delta_time += 1; + } +} diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..29f7a38 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,199 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..9cf1062 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,19 @@ +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Project/game/Cargo.toml b/Project/game/Cargo.toml new file mode 100644 index 0000000..809496e --- /dev/null +++ b/Project/game/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "game" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +arduboy-rust = "1.0.0" diff --git a/Project/game/src/lib.rs b/Project/game/src/lib.rs new file mode 100644 index 0000000..dce1d8c --- /dev/null +++ b/Project/game/src/lib.rs @@ -0,0 +1,20 @@ +#![no_std] +#![allow(non_upper_case_globals)] +//Include the Arduboy Library +//Initialize the arduboy object +#[allow(unused_imports)] +use arduboy_rust::prelude::*; + +//#[link_section = ".progmem.data"] + +// The setup() function runs once when you turn your Arduboy on +#[no_mangle] +pub unsafe extern "C" fn setup() { + // put your setup code here, to run once: +} +// The loop() function repeats forever after setup() is done +#[no_mangle] +#[export_name = "loop"] +pub unsafe extern "C" fn loop_() { + // put your main code here, to run repeatedly: +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..625a974 --- /dev/null +++ b/README.md @@ -0,0 +1,199 @@ +# Rust for Arduboy + +Running Rust on the [Arduboy](https://arduboy.com/) miniature game system. + +The most important first. +I didn't create this project from scratch, @Seeker14491 the legend has already done a lot in his old project [ArduboyRust](https://github.com/Seeker14491/ArduboyRust) + +I just updated and completed the project. + +### What do i mean by completed? + +I provided all the important functions from the [Arduboy2](https://github.com/MLXXXp/Arduboy2) library in a safe Rust API. + +Most of the Arduboy2 funktions can be called the same way like in C. + +# How to use this Project + +First clone the repo + +```bash +git clone https://github.com/ZennDev1337/Rust-for-Arduboy +``` + +Install [PlatformIO Core](https://docs.platformio.org/en/latest/core/installation/methods/pypi.html) on your system. +I recommend the pip install if you don't want to configure a new path variable for pio. +Otherwise you will know how to do that. + +The rust-analyzer will complain about a missing test crate to fix this +add the following rule to the lsp settings : + +```json +{ + "rust-analyzer.checkOnSave.allTargets": false +} +``` + +If your using Visual Studio Code: Create a folder named `.vscode` and a file named `settings.json` inside. Past the setting above in the new file. (I have excluded my `.vscode` folder because I have many different settings there that you don't need) + +Your game is located in the Project/game folder. This is also the folder you are working in + +Inside the game folder you will find a lib.rs file which contains the setup and loop function as you know it from a normal Arduboy C project. + +You can use the following command to open the Rust doc of the arduboy_rust crate + +```bash +cargo doc -p arduboy_rust --open +``` + +Most of the time you will use the prelude. +There is listed witch funktionality you will have. + +And last but not least there are multiple examples inside of the Examples folder which shows you how to use the functions. +I will from time to time also upload my projects to the Example folder so you have as much ressurces as possible. + +## Usage of the run tool + +**Both run scripts** (only works if you use the given folder structure) + +requirements: + +- [PlatformIO Core](https://docs.platformio.org/en/latest/core/installation/methods/pypi.html) must be installed +- The Arduboy must be plugged in +- You are in the root directory of this project + +All builded `.hex` files are saved inside of `Wrapper-Project/.pio/build/.hex` after you uploaded them to the Arduboy. + +To upload your own game to the Arduboy use: + +Linux: + +```bash +./run +``` + +Windows: + +```ps1 +.\run.bat +``` + +## Play the examples + +### Snake + +To upload snake to the Arduboy use: + +Linux: + +```bash +./run snake +``` + +Windows: + +```ps1 +.\run.bat snake +``` + +### Pong + +To upload pong to the Arduboy use: + +Linux: + +```bash +./run pong +``` + +Windows: + +```ps1 +.\run.bat pong +``` + +### I'm now a Rustacean <3 + +To upload rustacean to the Arduboy use: + +Linux: + +```bash +./run rustacean +``` + +Windows: + +```ps1 +.\run.bat rustacean +``` + +### The demo games / tutorials from the official Arduboy forum [Rewritten in Rust] + +- [Make Your Own Arduboy Game: Part 1 - Setting Up Your Computer](https://community.arduboy.com/t/make-your-own-arduboy-game-part-1-setting-up-your-computer/7924/1) +- [demo2] [Make Your Own Arduboy Game: Part 2 - Printing Text](https://community.arduboy.com/t/make-your-own-arduboy-game-part-2-printing-text/7925) +- [demo3] [Make Your Own Arduboy Game: Part 3 - Storing Data & Loops](https://community.arduboy.com/t/make-your-own-arduboy-game-part-3-storing-data-loops/7926) +- [demo4] [Make Your Own Arduboy Game: Part 4 - Questions & Button Input](https://community.arduboy.com/t/make-your-own-arduboy-game-part-4-questions-button-input/7927) +- [demo5] [Make Your Own Arduboy Game: Part 5 - Your First Game!](https://community.arduboy.com/t/make-your-own-arduboy-game-part-5-your-first-game/7928) +- [demo6] [Make Your Own Arduboy Game: Part 6 - Graphics!](https://community.arduboy.com/t/make-your-own-arduboy-game-part-6-graphics/7929) + Link for the [arduboy-tile-converter](https://github.com/Team-ARG-Museum/arduboy-tile-converter) +- not done [demo7] [Make Your Own Arduboy Game: Part 7 - Make Pong From Scratch!](https://community.arduboy.com/t/make-your-own-arduboy-game-part-7-make-pong-from-scratch/7930) +- not done [demo8] [Make Your Own Arduboy Game: Part 8 - Starting DinoSmasher](https://community.arduboy.com/t/make-your-own-arduboy-game-part-8-starting-dinosmasher/7932) +- [eeprom] [Help, I’m struggling with EEPROM!](https://community.arduboy.com/t/help-im-struggling-with-eeprom/7178) +- [tone] [ArduboyTonesTest](https://github.com/MLXXXp/ArduboyTones/blob/master/examples/ArduboyTonesTest/ArduboyTonesTest.ino) + +To upload a demo to the Arduboy use: + +Linux: + +```bash +./run demo2 +./run demo3 +./run demo4 +./run demo5 +./run demo6 +./run eeprom +./run tone +``` + +Windows: + +```ps1 +.\run.bat demo2 +.\run.bat demo3 +.\run.bat demo4 +.\run.bat demo5 +.\run.bat demo6 +.\run.bat eeprom +.\run.bat tone +``` + +## Creating and building an Arduboy crate [Outdated] + +You can find instructions on how all build steps work in detail here [ArduboyRust creating and building an arduboy crate](https://github.com/Seeker14491/ArduboyRust#creating-and-building-an-arduboy-crate) + +## Linking the static library to the Arduino C++ project [Outdated] + +Instructions on how to link all the static libraries in the Arduino C++ project can be found here [ArduboyRust linking the static library to the arduino c++ project](https://github.com/Seeker14491/ArduboyRust#linking-the-static-library-to-the-arduino-c-project) + +## Credits + +Thanks to @Seeker14491 who wrote [ArduboyRust](https://github.com/Seeker14491/ArduboyRust), the starting point for me to create my own project based on this project. (you are a hero <3) + +Thanks to @simon-i1-h who wrote [arduboy-hello-rs](https://github.com/simon-i1-h/arduboy-hello-rs), the proof of concept that inspired me to try Rust on the Arduboy. + +## License + +You can license it under either one of those licenses: + +- Apache License, Version 2.0 + ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) +- MIT license + ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +Whichever you may prefer. + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. diff --git a/Wrapper-Project/include/README b/Wrapper-Project/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/Wrapper-Project/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/Wrapper-Project/lib/README b/Wrapper-Project/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/Wrapper-Project/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/Wrapper-Project/platformio.ini b/Wrapper-Project/platformio.ini new file mode 100644 index 0000000..416f13d --- /dev/null +++ b/Wrapper-Project/platformio.ini @@ -0,0 +1,21 @@ +;PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:arduboy] +platform = atmelavr +board = arduboy +framework = arduino + +build_flags = + -L lib -l libgame + +lib_deps = + mlxxxp/Arduboy2@^6.0.0 + ArduboyTones@^1.0.3 diff --git a/Wrapper-Project/src/library/arduboy/arduboy.cpp b/Wrapper-Project/src/library/arduboy/arduboy.cpp new file mode 100644 index 0000000..f19d9e3 --- /dev/null +++ b/Wrapper-Project/src/library/arduboy/arduboy.cpp @@ -0,0 +1,123 @@ +#include "arduboy.h" +extern Arduboy2 arduboy; + +void arduboy_begin(void) +{ + arduboy.begin(); +} +void arduboy_clear(void) +{ + arduboy.clear(); +} +void arduboy_display(void) +{ + arduboy.display(); +} +void arduboy_display_and_clear_buffer(void) +{ + arduboy.display(CLEAR_BUFFER); +} +void arduboy_draw_fast_hline(int16_t x, int16_t y, uint8_t w, uint8_t color) +{ + arduboy.drawFastHLine(x, y, w, color); +} +void arduboy_draw_fast_vline(int16_t x, int16_t y, uint8_t h, uint8_t color) +{ + arduboy.drawFastVLine(x, y, h, color); +} +void arduboy_draw_pixel(int16_t x, int16_t y, uint8_t color) +{ + arduboy.drawPixel(x, y, color); +} +void arduboy_draw_circle(int16_t x, int16_t y, uint8_t r, uint8_t color) +{ + arduboy.drawCircle(x, y, r, color); +} +void arduboy_fill_circle(int16_t x, int16_t y, uint8_t r, uint8_t color) +{ + arduboy.fillCircle(x, y, r, color); +} +void arduboy_fill_rect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color) +{ + arduboy.fillRect(x, y, w, h, color); +} +unsigned long arduboy_generate_random_seed() +{ + return arduboy.generateRandomSeed(); +} +uint8_t arduboy_get_pixel(uint8_t x, uint8_t y) +{ + return arduboy.getPixel(x, y); +} +void arduboy_init_random_seed(void) +{ + arduboy.initRandomSeed(); +} +bool arduboy_just_pressed(uint8_t button) +{ + return arduboy.justPressed(button); +} +bool arduboy_just_released(uint8_t button) +{ + return arduboy.justReleased(button); +} +bool arduboy_next_frame(void) +{ + return arduboy.nextFrame(); +} +void arduboy_poll_buttons() +{ + arduboy.pollButtons(); +} +bool arduboy_pressed(uint8_t buttons) +{ + return arduboy.pressed(buttons); +} +void arduboy_print_chars(const char *cstr) +{ + arduboy.print(cstr); +} +size_t arduboy_print_char(char c) +{ + return arduboy.print(c); +} +size_t arduboy_print_int(int n, int base) +{ + return arduboy.print(n, base); +} +size_t arduboy_print_long(long n, int base) +{ + return arduboy.print(n, base); +} +size_t arduboy_print_unsigned_char(unsigned char n, int base) +{ + return arduboy.print(n, base); +} +size_t arduboy_print_unsigned_int(unsigned int n, int base) +{ + return arduboy.print(n, base); +} +size_t arduboy_print_unsigned_long(unsigned long n, int base) +{ + return arduboy.print(n, base); +} +void arduboy_set_cursor(int16_t x, int16_t y) +{ + arduboy.setCursor(x, y); +} +void arduboy_set_frame_rate(uint8_t rate) +{ + arduboy.setFrameRate(rate); +} +bool arduboy_not_pressed(uint8_t button) +{ + arduboy.notPressed(button); +} +void arduboy_set_text_size(uint8_t s) +{ + arduboy.setTextSize(s); +} +void arduboy_invert(bool inverse) +{ + arduboy.invert(inverse); +} \ No newline at end of file diff --git a/Wrapper-Project/src/library/arduboy/arduboy.h b/Wrapper-Project/src/library/arduboy/arduboy.h new file mode 100644 index 0000000..723a989 --- /dev/null +++ b/Wrapper-Project/src/library/arduboy/arduboy.h @@ -0,0 +1,36 @@ +#pragma once +#include + +extern "C" +{ + void arduboy_begin(void); + void arduboy_clear(void); + void arduboy_display(void); + void arduboy_display_and_clear_buffer(void); + void arduboy_draw_fast_hline(int16_t x, int16_t y, uint8_t w, uint8_t color); + void arduboy_draw_fast_vline(int16_t x, int16_t y, uint8_t h, uint8_t color); + void arduboy_draw_pixel(int16_t x, int16_t y, uint8_t color); + void arduboy_draw_circle(int16_t x, int16_t y, uint8_t r, uint8_t color); + void arduboy_fill_circle(int16_t x, int16_t y, uint8_t r, uint8_t color); + void arduboy_fill_rect(int16_t x, int16_t y, uint8_t w, uint8_t h, uint8_t color); + unsigned long arduboy_generate_random_seed(); + uint8_t arduboy_get_pixel(uint8_t x, uint8_t y); + void arduboy_init_random_seed(void); + bool arduboy_just_pressed(uint8_t button); + bool arduboy_just_released(uint8_t button); + bool arduboy_next_frame(void); + void arduboy_poll_buttons(); + bool arduboy_pressed(uint8_t buttons); + void arduboy_print_chars(const char *cstr); + size_t arduboy_print_char(char c); + size_t arduboy_print_int(int n, int base); + size_t arduboy_print_long(long n, int base); + size_t arduboy_print_unsigned_char(unsigned char n, int base); + size_t arduboy_print_unsigned_int(unsigned int n, int base); + size_t arduboy_print_unsigned_long(unsigned long n, int base); + void arduboy_set_cursor(int16_t x, int16_t y); + void arduboy_set_frame_rate(uint8_t rate); + bool arduboy_not_pressed(uint8_t button); + void arduboy_set_text_size(uint8_t s); + void arduboy_invert(bool inverse); +} \ No newline at end of file diff --git a/Wrapper-Project/src/library/arduboy/arduboy_tones.cpp b/Wrapper-Project/src/library/arduboy/arduboy_tones.cpp new file mode 100644 index 0000000..e26808a --- /dev/null +++ b/Wrapper-Project/src/library/arduboy/arduboy_tones.cpp @@ -0,0 +1,46 @@ +#include "arduboy_tones.h" + +void arduboy_audio_on() +{ + arduboy.audio.on(); +} +void arduboy_audio_off() +{ + arduboy.audio.off(); +} +bool arduboy_audio_enabled() +{ + return arduboy.audio.enabled(); +} +void sound_tone(unsigned int frequency, unsigned long duration) +{ + sound.tone(frequency, duration); +} +void sound_tone2(unsigned int frequency1, unsigned long duration1, unsigned int frequency2, unsigned long duration2) +{ + sound.tone(frequency1, duration1, frequency2, duration2); +} +void sound_tone3(unsigned int frequency1, unsigned long duration1, unsigned int frequency2, unsigned long duration2, unsigned int frequency3, unsigned long duration3) +{ + sound.tone(frequency1, duration1, frequency2, duration2, frequency3, duration3); +} +void sound_tones(const uint16_t *tones) +{ + sound.tones(tones); +} +void sound_no_tone() +{ + sound.noTone(); +} +bool sound_playing() +{ + sound.playing(); +} +void sound_tones_in_ram(uint16_t *tones) +{ + sound.tonesInRAM(tones); +} +void sound_volume_mode(uint8_t mode) +{ + sound.volumeMode(mode); +} diff --git a/Wrapper-Project/src/library/arduboy/arduboy_tones.h b/Wrapper-Project/src/library/arduboy/arduboy_tones.h new file mode 100644 index 0000000..4d80219 --- /dev/null +++ b/Wrapper-Project/src/library/arduboy/arduboy_tones.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include +extern ArduboyTones sound; +extern Arduboy2 arduboy; + +extern "C" +{ + void arduboy_audio_on(); + void arduboy_audio_off(); + bool arduboy_audio_enabled(); + void sound_tone(unsigned int frequency, unsigned long duration); + void sound_tone2(unsigned int frequency1, unsigned long duration1, unsigned int frequency2, unsigned long duration2); + void sound_tone3(unsigned int frequency1, unsigned long duration1, unsigned int frequency2, unsigned long duration2, unsigned int frequency3, unsigned long duration3); + void sound_tones(const uint16_t *tones); + void sound_no_tone(); + bool sound_playing(); + void sound_tones_in_ram(uint16_t *tones); + void sound_volume_mode(uint8_t mode); +} \ No newline at end of file diff --git a/Wrapper-Project/src/library/arduboy/sprites.cpp b/Wrapper-Project/src/library/arduboy/sprites.cpp new file mode 100644 index 0000000..170a575 --- /dev/null +++ b/Wrapper-Project/src/library/arduboy/sprites.cpp @@ -0,0 +1,6 @@ +#include "sprites.h" + +void arduino_draw_override(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame) +{ + Sprites::drawOverwrite(x, y, bitmap, frame); +} \ No newline at end of file diff --git a/Wrapper-Project/src/library/arduboy/sprites.h b/Wrapper-Project/src/library/arduboy/sprites.h new file mode 100644 index 0000000..bd6ad50 --- /dev/null +++ b/Wrapper-Project/src/library/arduboy/sprites.h @@ -0,0 +1,7 @@ +#pragma once +#include + +extern "C" +{ + void arduino_draw_override(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame); +} \ No newline at end of file diff --git a/Wrapper-Project/src/library/arduino/arduino.cpp b/Wrapper-Project/src/library/arduino/arduino.cpp new file mode 100644 index 0000000..0630437 --- /dev/null +++ b/Wrapper-Project/src/library/arduino/arduino.cpp @@ -0,0 +1,14 @@ +#include "arduino.h" + +long arduino_random_between(long min, long max) +{ + return random(min, max); +} +long arduino_random_less_than(long max) +{ + return random(max); +} +void arduino_delay(unsigned long ms) +{ + delay(ms); +} \ No newline at end of file diff --git a/Wrapper-Project/src/library/arduino/arduino.h b/Wrapper-Project/src/library/arduino/arduino.h new file mode 100644 index 0000000..b3e180e --- /dev/null +++ b/Wrapper-Project/src/library/arduino/arduino.h @@ -0,0 +1,9 @@ +#pragma once +#include + +extern "C" +{ + long arduino_random_between(long min, long max); + long arduino_random_less_than(long max); + void arduino_delay(unsigned long ms); +} diff --git a/Wrapper-Project/src/library/arduino/eeprom.cpp b/Wrapper-Project/src/library/arduino/eeprom.cpp new file mode 100644 index 0000000..11918f0 --- /dev/null +++ b/Wrapper-Project/src/library/arduino/eeprom.cpp @@ -0,0 +1,26 @@ +#include "eeprom.h" + +int eeprom; + +uint8_t arduboy_eeprom_read(int idx) +{ + eeprom = EEPROM.read(idx); + return eeprom; +} +void arduboy_eeprom_update(int idx, uint8_t val) +{ + EEPROM.update(idx, val); +} +void arduboy_eeprom_write(int idx, uint8_t val) +{ + EEPROM.write(idx, val); +} +uint8_t arduboy_eeprom_get(int idx) +{ + EEPROM.get(idx, eeprom); + return eeprom; +} +void arduboy_eeprom_put(int idx, uint8_t val) +{ + EEPROM.put(idx, val); +} \ No newline at end of file diff --git a/Wrapper-Project/src/library/arduino/eeprom.h b/Wrapper-Project/src/library/arduino/eeprom.h new file mode 100644 index 0000000..c9ce39e --- /dev/null +++ b/Wrapper-Project/src/library/arduino/eeprom.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +extern "C" +{ + uint8_t arduboy_eeprom_read(int idx); + void arduboy_eeprom_update(int idx, uint8_t val); + void arduboy_eeprom_write(int idx, uint8_t val); + uint8_t arduboy_eeprom_get(int idx); + void arduboy_eeprom_put(int idx, uint8_t val); +} diff --git a/Wrapper-Project/src/main.cpp b/Wrapper-Project/src/main.cpp new file mode 100644 index 0000000..f29d497 --- /dev/null +++ b/Wrapper-Project/src/main.cpp @@ -0,0 +1,4 @@ +#include "main.h" + +Arduboy2 arduboy; +ArduboyTones sound(arduboy.audio.enabled); diff --git a/Wrapper-Project/src/main.h b/Wrapper-Project/src/main.h new file mode 100644 index 0000000..0b015e5 --- /dev/null +++ b/Wrapper-Project/src/main.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include +#include "./library/arduboy/arduboy.h" +#include "./library/arduboy/sprites.h" +#include "./library/arduboy/arduboy_tones.h" +#include "./library/arduino/arduino.h" +#include "./library/arduino/eeprom.h" diff --git a/Wrapper-Project/test/README b/Wrapper-Project/test/README new file mode 100644 index 0000000..df5066e --- /dev/null +++ b/Wrapper-Project/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html diff --git a/arduboy.json b/arduboy.json new file mode 100644 index 0000000..4c02da8 --- /dev/null +++ b/arduboy.json @@ -0,0 +1,27 @@ +{ + "arch": "avr", + "atomic-cas": false, + "cpu": "atmega32u4", + "data-layout": "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8", + "eh-frame-header": false, + "exe-suffix": ".elf", + "executables": true, + "late-link-args": { + "gcc": [ + "-lgcc" + ] + }, + "linker": "avr-gcc", + "linker-is-gnu": true, + "llvm-target": "avr-unknown-unknown", + "max-atomic-width": 8, + "no-default-libraries": false, + "pre-link-args": { + "gcc": [ + "-mmcu=atmega32u4", + "-Wl,--as-needed" + ] + }, + "target-c-int-width": "16", + "target-pointer-width": "16" +} \ No newline at end of file diff --git a/run b/run new file mode 100755 index 0000000..712fbca --- /dev/null +++ b/run @@ -0,0 +1,41 @@ +#!/bin/bash + +option=$1 + +if [ -z "$option" ] +then + cargo build -p game --release && cp ./target/arduboy/release/libgame.a ./Wrapper-Project/lib/libgame.a && cd Wrapper-Project/ && pio run -t upload && cp ./.pio/build/arduboy/firmware.hex ./.pio/build/game.hex && pio run -t clean && rm lib/libgame.a && cd .. +elif [ "$option" = "snake" ] +then + cargo build -p snake --release && cp ./target/arduboy/release/libsnake.a ./Wrapper-Project/lib/libgame.a && cd Wrapper-Project/ && pio run -t upload && cp ./.pio/build/arduboy/firmware.hex ./.pio/build/snake.hex && pio run -t clean && rm lib/libgame.a && cd .. +elif [ "$option" = "pong" ] +then + cargo build -p pong --release && cp ./target/arduboy/release/libpong.a ./Wrapper-Project/lib/libgame.a && cd Wrapper-Project/ && pio run -t upload && cp ./.pio/build/arduboy/firmware.hex ./.pio/build/pong.hex && pio run -t clean && rm lib/libgame.a && cd .. +elif [ "$option" = "rustacean" ] +then + cargo build -p rustacean --release && cp ./target/arduboy/release/librustacean.a ./Wrapper-Project/lib/libgame.a && cd Wrapper-Project/ && pio run -t upload && cp ./.pio/build/arduboy/firmware.hex ./.pio/build/rustacean.hex && pio run -t clean && rm lib/libgame.a && cd .. +elif [ "$option" = "tone" ] +then + cargo build -p tone --release && cp ./target/arduboy/release/libtone.a ./Wrapper-Project/lib/libgame.a && cd Wrapper-Project/ && pio run -t upload && cp ./.pio/build/arduboy/firmware.hex ./.pio/build/tone.hex && pio run -t clean && rm lib/libgame.a && cd .. +elif [ "$option" = "eeprom" ] +then + cargo build -p eeprom-demo --release && cp ./target/arduboy/release/libeeprom_demo.a ./Wrapper-Project/lib/libgame.a && cd Wrapper-Project/ && pio run -t upload && cp ./.pio/build/arduboy/firmware.hex ./.pio/build/eeprom.hex && pio run -t clean && rm lib/libgame.a && cd .. +elif [ "$option" = "demo2" ] +then + cargo build -p demo2 --release && cp ./target/arduboy/release/libdemo2.a ./Wrapper-Project/lib/libgame.a && cd Wrapper-Project/ && pio run -t upload && cp ./.pio/build/arduboy/firmware.hex ./.pio/build/demo2.hex && pio run -t clean && rm lib/libgame.a && cd .. +elif [ "$option" = "demo3" ] +then + cargo build -p demo3 --release && cp ./target/arduboy/release/libdemo3.a ./Wrapper-Project/lib/libgame.a && cd Wrapper-Project/ && pio run -t upload && cp ./.pio/build/arduboy/firmware.hex ./.pio/build/demo3.hex && pio run -t clean && rm lib/libgame.a && cd .. +elif [ "$option" = "demo4" ] +then + cargo build -p demo4 --release && cp ./target/arduboy/release/libdemo4.a ./Wrapper-Project/lib/libgame.a && cd Wrapper-Project/ && pio run -t upload && cp ./.pio/build/arduboy/firmware.hex ./.pio/build/demo4.hex && pio run -t clean && rm lib/libgame.a && cd .. +elif [ "$option" = "demo5" ] +then + cargo build -p demo5 --release && cp ./target/arduboy/release/libdemo5.a ./Wrapper-Project/lib/libgame.a && cd Wrapper-Project/ && pio run -t upload && cp ./.pio/build/arduboy/firmware.hex ./.pio/build/demo5.hex && pio run -t clean && rm lib/libgame.a && cd .. +elif [ "$option" = "demo6" ] +then + cargo build -p demo6 --release && cp ./target/arduboy/release/libdemo6.a ./Wrapper-Project/lib/libgame.a && cd Wrapper-Project/ && pio run -t upload && cp ./.pio/build/arduboy/firmware.hex ./.pio/build/demo6.hex && pio run -t clean && rm lib/libgame.a && cd .. +else + echo Usage: for uploading your game \|./run.sh + echo Usage: for uploading an example game \| ./run.sh \ +fi diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..3b1d1d6 --- /dev/null +++ b/run.bat @@ -0,0 +1,43 @@ +@echo off + +set option=%1 + +if [%option%]==[] ( + powershell -Command "cargo build -p game --release; cp ./target/arduboy/release/libgame.a ./Wrapper-Project/lib/libgame.a; cd Wrapper-Project/; pio run -t upload; cp ./.pio/build/arduboy/firmware.hex ./.pio/build/game.hex; pio run -t clean; rm lib/libgame.a; cd .." + goto :eof +) + +if %option%==snake ( + powershell -Command "cargo build -p snake --release; cp ./target/arduboy/release/libsnake.a ./Wrapper-Project/lib/libgame.a; cd Wrapper-Project/; pio run -t upload; cp ./.pio/build/arduboy/firmware.hex ./.pio/build/snake.hex; pio run -t clean; rm lib/libgame.a; cd .." + goto :eof +) else if %option%==pong ( + powershell -Command "cargo build -p pong --release; cp ./target/arduboy/release/libpong.a ./Wrapper-Project/lib/libgame.a; cd Wrapper-Project/; pio run -t upload; cp ./.pio/build/arduboy/firmware.hex ./.pio/build/pong.hex; pio run -t clean; rm lib/libgame.a; cd .." + goto :eof +) else if %option%==rustacean ( + powershell -Command "cargo build -p rustacean --release; cp ./target/arduboy/release/librustacean.a ./Wrapper-Project/lib/libgame.a; cd Wrapper-Project/; cp ./.pio/build/arduboy/firmware.hex ./.pio/build/rustacean.hex; pio run -t upload; pio run -t clean; rm lib/libgame.a; cd .." + goto :eof +) else if %option%==tone ( + powershell -Command "cargo build -p tone --release; cp ./target/arduboy/release/libtone.a ./Wrapper-Project/lib/libgame.a; cd Wrapper-Project/; pio run -t upload; cp ./.pio/build/arduboy/firmware.hex ./.pio/build/tone.hex; pio run -t clean; rm lib/libgame.a; cd .." + goto :eof +) else if %option%==eeprom ( + powershell -Command "cargo build -p eeprom-demo --release; cp ./target/arduboy/release/libeeprom_demo.a ./Wrapper-Project/lib/libgame.a; cd Wrapper-Project/; pio run -t upload; cp ./.pio/build/arduboy/firmware.hex ./.pio/build/eeprom.hex; pio run -t clean; rm lib/libgame.a; cd .." + goto :eof +) else if %option%==demo2 ( + powershell -Command "cargo build -p demo2 --release; cp ./target/arduboy/release/libdemo2.a ./Wrapper-Project/lib/libgame.a; cd Wrapper-Project/; pio run -t upload; cp ./.pio/build/arduboy/firmware.hex ./.pio/build/demo2.hex; pio run -t clean; rm lib/libgame.a; cd .." + goto :eof +) else if %option%==demo3 ( + powershell -Command "cargo build -p demo3 --release; cp ./target/arduboy/release/libdemo3.a ./Wrapper-Project/lib/libgame.a; cd Wrapper-Project/; pio run -t upload; cp ./.pio/build/arduboy/firmware.hex ./.pio/build/demo3.hex; pio run -t clean; rm lib/libgame.a; cd .." + goto :eof +) else if %option%==demo4 ( + powershell -Command "cargo build -p demo4 --release; cp ./target/arduboy/release/libdemo4.a ./Wrapper-Project/lib/libgame.a; cd Wrapper-Project/; pio run -t upload; cp ./.pio/build/arduboy/firmware.hex ./.pio/build/demo4.hex; pio run -t clean; rm lib/libgame.a; cd .." + goto :eof +) else if %option%==demo5 ( + powershell -Command "cargo build -p demo5 --release; cp ./target/arduboy/release/libdemo5.a ./Wrapper-Project/lib/libgame.a; cd Wrapper-Project/; pio run -t upload; cp ./.pio/build/arduboy/firmware.hex ./.pio/build/demo5.hex; pio run -t clean; rm lib/libgame.a; cd .." + goto :eof +) else if %option%==demo6 ( + powershell -Command "cargo build -p demo6 --release; cp ./target/arduboy/release/libdemo6.a ./Wrapper-Project/lib/libgame.a; cd Wrapper-Project/; pio run -t upload; cp ./.pio/build/arduboy/firmware.hex ./.pio/build/demo6.hex; pio run -t clean; rm lib/libgame.a; cd .." + goto :eof +) + +@echo Usage: .\run.bat // for uploading your game +@echo Usage: .\run.bat ^ // for uploading an example game \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..1edbb23 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +# We need to pin to this old Rust version due to an LLVM bug: https://github.com/rust-lang/rust/issues/82104 +channel = "nightly" +components = ["rust-src"] diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..3e321ac --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,5 @@ +format_code_in_doc_comments = true +imports_granularity = "Module" +newline_style = "Unix" +use_field_init_shorthand = true +use_try_shorthand = true