gb/
io.rs

1//! Helpers for GameBoy I/O. including buttons, texts, and else.
2//!
3//! This modules contains a helper for simple input and output. you can print
4//! text or read joypad input as bytes.
5
6use core::{ffi::c_char, fmt::{Error, Write}};
7
8use crate::mmio::JOYP;
9
10#[allow(unused_imports)]
11use super::{drawing::{DmgColor, TILE_HEIGHT, TILE_WIDTH}, gbdk_c::{console::{cls, gotoxy}, font::{font_color, font_ibm, font_init, font_load, font_set, font_t}, stdio::putchar}};
12
13// Imports for docs
14#[allow(unused_imports)]
15use crate::gbdk_c;
16
17/// Prints to the GameBoy screen, with a newline.
18/// If you've ever used `println!` macro in `std`, you'll familiar with this.
19///
20/// The `println!` macro will work with default `GbStream`. So, texts that
21/// written with your custom GbStream will removed.
22///
23/// # Warning
24///
25/// Since the compiled fmt function is very large, care must be taken not to
26/// exceed the ROM capacity of GameBoy.
27///
28/// In addition, compilation will fail if formatting is attempted for floating points
29/// and integers over 32bits. Attempts to use `Debug` trait (`{:?}`) will also fail.
30/// 
31/// # Examples
32///
33/// ```
34/// println!(); //prints just a newline
35/// println!("Hello, Rust-GB!");
36/// println!("Answer!: {}", 42);
37/// ```
38#[macro_export]
39macro_rules! println {
40    () => {
41        $crate::print!("\n")
42    };
43    ($($arg:tt)*) => {{
44        use core::fmt::Write;
45        let mut s = gb::io::GbStream::stream();
46        s.write_fmt(core::format_args_nl!($($arg)*)).unwrap();
47    }};
48}
49
50/// Prints to the GameBoy screen.
51/// If you've ever used `print!` macro in `std`, you'll familiar with this.
52///
53/// Equivalent to the [`println!`] macro except that newline is not printed at
54/// the end of the message.
55///
56/// The `print!` macro will work with default `GbStream`. So, texts that
57/// written with your custom GbStream will removed.
58///
59/// # Warning
60///
61/// Since the compiled fmt function is very large, care must be taken not to
62/// exceed the ROM capacity of GameBoy.
63///
64/// In addition, compilation will fail if formatting is attempted for floating points
65/// and integers over 32bits. Attempts to use `Debug` trait (`{:?}`) will also fail.
66///
67/// # Examples
68/// ```
69/// print!("Hello, ");
70/// print!("Rust-GB!\n");
71/// print!("Answer!: {}", 42);
72/// ```
73#[macro_export]
74macro_rules! print {
75    ($($arg:tt)*) => {{
76        use core::fmt::Write;
77        let mut s = gb::io::GbStream::stream();
78        s.write_fmt(core::format_args!($($arg)*)).unwrap();
79    }};
80}
81
82/// Byte print stream of GameBoy.
83///
84/// Currently, GbStream prints bytes one by one using GBDK's `putchar` function.
85/// In the long run, it is likely to change to RustGB own implementation.
86///
87/// Optionally, GbStream can have font and color.
88///
89/// # Examples
90/// ```
91/// use core::fmt::Write;
92///
93/// let mut w = GbStream::new();
94/// write!(w, "Hello, World!");
95/// ```
96pub struct GbStream {
97    private: ()
98}
99
100impl GbStream {
101    /// Get GameBoy console stream.
102    pub fn stream() -> Self {
103        GbStream { private: () }
104    }
105
106    /// Clear GameBoy console.
107    pub fn clear() {
108        unsafe { cls();}
109    }
110
111    /// Set cursor of [`GbStream`].
112    ///
113    /// # Panics
114    ///
115    /// Panics if coordinate parameter out of bounds.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// cursor(0, 1); //prints to second line.
121    /// print!("Hello, cursor!");
122    ///
123    /// ```
124    pub fn cursor(x: u8, y: u8) {
125        if x >= TILE_WIDTH {
126            panic!("Cursor x outbounded");
127        }
128
129        if y >= TILE_HEIGHT {
130            panic!("Cursor y outbounded");
131        }
132
133        unsafe {gotoxy(x, y)};
134    }
135
136    /// Set a default font and custom color.
137    ///
138    /// # Caution
139    ///
140    /// It will clear GameBoy console and reset the cursor.
141    #[cfg(feature="prototype")]
142    pub fn set_color(foreground: DmgColor, background: DmgColor) {
143        unsafe { 
144            cls();
145            font_init();
146            font_color(foreground as u8, background as u8);
147        }
148        let font = unsafe { font_load(font_ibm) };
149        unsafe { font_set(font) };
150    }
151
152    /// Set a GbStream with custom font.
153    ///
154    /// # Caution
155    ///
156    /// It will clear GameBoy console and reset the cursor.
157    ///
158    /// # Safety
159    ///
160    /// If an invalid font address is entered, it causes an Undefined Behavior.
161    #[cfg(feature="prototype")]
162    pub unsafe fn set_font_and_color(font: font_t, foreground: DmgColor, background: DmgColor) {
163        unsafe { 
164            cls();
165            font_init();
166            font_color(foreground as u8, background as u8) 
167        }
168        let font = unsafe { font_load(font) };
169        unsafe { font_set(font) };
170    }
171
172    /// Writes a byte into this writer, returning whether the write succeeded.
173    ///
174    /// write_char assumes that the input is valid Unicode character. However,
175    /// GBDK maps one byte to one character or symbol.
176    ///
177    /// Therefore, `write_byte` is recommended when you want to print one
178    /// character to the GameBoy.
179    ///
180    /// # Errors
181    ///
182    /// This function will return an instance of `Error` on error.
183    pub fn write_byte(&mut self, b: u8) -> Result<(), Error> {
184        unsafe { putchar(b as c_char) }
185        Ok(())
186    }
187}
188
189impl Write for GbStream {
190    #[inline(never)]
191    fn write_str(&mut self, s: &str) -> core::fmt::Result {
192        for c in s.bytes() {
193            unsafe { putchar(c as c_char) }
194        }
195        Ok(())
196    }
197}
198
199#[repr(u8)]
200enum JoypadMode {
201    None = 0x30,
202    DPad = 0x20,
203    Button = 0x10,
204    All = 0x00
205}
206
207/// Joypad key enum.
208///
209/// The value of the keys returned by the [`Joypad`] struct.
210/// Same as the `J_*` consts in GBDK.
211#[repr(u8)]
212#[derive(Clone, Copy, PartialEq)]
213pub enum JoypadKey {
214    Right = 0x01,
215    Left = 0x02,
216    Up = 0x04,
217    Down = 0x08,
218    A = 0x10,
219    B = 0x20,
220    Select = 0x40,
221    Start = 0x80
222}
223
224/// Joypad input struct.
225///
226/// Joypad (Buttons and D-Pad key) can be checked by writing and reading the [`JOYP`]
227/// register. (`0xFF00`) This struct makes it easy and safe.
228///
229/// The most recommended use is to receive key information on all keys at once using the [`Joypad::read()`]
230/// function for once in each frame, and then check the key press using a method.
231///
232/// # Examples
233/// ```
234/// let key = Joypad::read();
235/// if key.a() {
236///     println!("A pressed!");
237/// }
238/// if key.b() {
239///     println!("B pressed!");
240/// }
241/// ```
242#[derive(Clone, Copy)]
243pub struct Joypad(u8);
244
245impl Joypad {
246    fn change_mode(mode: JoypadMode) {
247        unsafe {JOYP.write(mode as u8)};
248        // 1 instruction delay is required after writing JOYP
249        JOYP.read();
250    }
251
252    /// Get all buttons status.
253    ///
254    /// Internally, write and read twice in the [`JOYP`] register, and returns 
255    /// [`Joypad`] tuple struct with bitwise OR of [`JoypadKey`] values.
256    pub fn read() -> Self {
257        Joypad::change_mode(JoypadMode::Button);
258        let buttons = (JOYP.read() << 4) | 0xF;
259        Joypad::change_mode(JoypadMode::DPad);
260        let d_pad = JOYP.read() | 0xF0;
261
262        Joypad(!(buttons & d_pad))
263    }
264
265    /// Check if A button is pressed.
266    pub fn a(&self) -> bool {
267        self.0 & (JoypadKey::A as u8) != 0
268    }
269
270    /// Check if B button is pressed.
271    pub fn b(&self) -> bool {
272        self.0 & (JoypadKey::B as u8) != 0
273    }
274
275    /// Check if Select button is pressed.
276    pub fn select(&self) -> bool {
277        self.0 & (JoypadKey::Select as u8) != 0
278    }
279
280    /// Check if Start button is pressed.
281    pub fn start(&self) -> bool {
282        self.0 & (JoypadKey::Start as u8) != 0
283    }
284
285    /// Check if Right of d-pad is pressed.
286    pub fn right(&self) -> bool {
287        self.0 & (JoypadKey::Right as u8) != 0
288    }
289
290    /// Check if Left of d-pad is pressed.
291    pub fn left(&self) -> bool {
292        self.0 & (JoypadKey::Left as u8) != 0
293    }
294
295    /// Check if Up of d-pad is pressed.
296    pub fn up(&self) -> bool {
297        self.0 & (JoypadKey::Up as u8) != 0
298    }
299
300    /// Check if Down of d-pad is pressed.
301    pub fn down(&self) -> bool {
302        self.0 & (JoypadKey::Down as u8) != 0
303    }
304
305    /// Waits until any key pressed.
306    pub fn wait_until_press() -> Self {
307        let mut current = Joypad::read();
308        while u8::from(current) == 0 {
309            current = Joypad::read();
310        }
311        return current;
312    }
313
314    /// Waits until all key released.
315    pub fn wait_until_release() {
316        let mut current = Joypad::read();
317        while u8::from(current) != 0 {
318            current = Joypad::read();
319        }
320    }
321}
322
323impl From<Joypad> for u8 {
324    fn from(value: Joypad) -> Self {
325        value.0
326    }
327}