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}