#![no_std]
#![no_main]
use gba::prelude::*;
const SCREEN_WIDTH: u16 = 240;
const SCREEN_HEIGHT: u16 = 160;
const PADDLE_WIDTH: u16 = 4;
const PADDLE_HEIGHT: u16 = 20;
const BALL_SIZE: u16 = 2;
struct Paddle {
x: u16,
y: u16,
}
struct Ball {
x: u16,
y: u16,
dx: i16,
dy: i16,
}
impl Paddle {
fn new(x: u16, y: u16) -> Self {
Self { x, y }
}
fn update(&mut self) {
let keys = KEYINPUT.read();
if keys.up() && self.y > 1 {
self.y -= 1;
}
if keys.down() && self.y + PADDLE_HEIGHT + 1 < SCREEN_HEIGHT {
self.y += 1;
}
}
}
impl Ball {
fn new(x: u16, y: u16) -> Self {
Self { x, y, dx: 1, dy: 1 }
}
fn update(&mut self, paddle1: &Paddle, paddle2: &Paddle) {
if self.y <= 0 || self.y + BALL_SIZE >= SCREEN_HEIGHT {
self.dy = -self.dy;
}
if self.x + BALL_SIZE >= paddle1.x
&& self.x <= paddle1.x + PADDLE_WIDTH
&& self.y + BALL_SIZE >= paddle1.y
&& self.y <= paddle1.y + PADDLE_HEIGHT
{
self.dx = -self.dx;
self.dy = self.dy;
}
if self.x + BALL_SIZE >= paddle2.x
&& self.x <= paddle2.x + PADDLE_WIDTH
&& self.y + BALL_SIZE >= paddle2.y
&& self.y <= paddle2.y + PADDLE_HEIGHT
{
self.dx = -self.dx;
self.dy = self.dy;
}
if self.x + BALL_SIZE <= 1 + BALL_SIZE {
self.x = SCREEN_WIDTH / 2 - BALL_SIZE / 2;
self.y = SCREEN_HEIGHT / 2 - BALL_SIZE / 2;
self.dx = 1;
self.dy = 1;
}
if self.x >= SCREEN_WIDTH - BALL_SIZE - 1 {
self.x = SCREEN_WIDTH / 2 - BALL_SIZE / 2;
self.y = SCREEN_HEIGHT / 2 - BALL_SIZE / 2;
self.dx = -1;
self.dy = 1;
}
self.x = (self.x as i16 + self.dx) as u16;
self.y = (self.y as i16 + self.dy) as u16;
}
}
static SPRITE_POSITIONS: [GbaCell<u16>; 6] = [
GbaCell::new(0),
GbaCell::new(0),
GbaCell::new(0),
GbaCell::new(0),
GbaCell::new(0),
GbaCell::new(0),
];
#[panic_handler]
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
loop {}
}
#[no_mangle]
fn main() -> ! {
DISPCNT.write(
DisplayControl::new().with_video_mode(VideoMode::_3).with_show_bg2(true),
);
RUST_IRQ_HANDLER.write(Some(draw_sprites));
DISPSTAT.write(DisplayStatus::new().with_irq_vblank(true));
IE.write(IrqBits::VBLANK);
IME.write(true);
let mut left_paddle =
Paddle::new(10, SCREEN_HEIGHT as u16 / 2 - PADDLE_HEIGHT / 2);
let mut right_paddle = Paddle::new(
SCREEN_WIDTH as u16 - 10 - PADDLE_WIDTH,
SCREEN_HEIGHT as u16 / 2 - PADDLE_HEIGHT / 2,
);
let mut ball = Ball::new(SCREEN_WIDTH as u16 / 2, SCREEN_HEIGHT as u16 / 2);
loop {
left_paddle.update();
right_paddle.update();
ball.update(&left_paddle, &right_paddle);
SPRITE_POSITIONS[0].write(left_paddle.x);
SPRITE_POSITIONS[1].write(left_paddle.y);
SPRITE_POSITIONS[2].write(right_paddle.x);
SPRITE_POSITIONS[3].write(right_paddle.y);
SPRITE_POSITIONS[4].write(ball.x);
SPRITE_POSITIONS[5].write(ball.y);
VBlankIntrWait();
}
}
#[link_section = ".iwram.draw_sprites"]
extern "C" fn draw_sprites(_bits: IrqBits) {
video3_clear_to(Color::BLACK);
draw_rect(
SPRITE_POSITIONS[0].read(),
SPRITE_POSITIONS[1].read(),
PADDLE_WIDTH,
PADDLE_HEIGHT,
Color::WHITE,
);
draw_rect(
SPRITE_POSITIONS[2].read(),
SPRITE_POSITIONS[3].read(),
PADDLE_WIDTH,
PADDLE_HEIGHT,
Color::WHITE,
);
draw_rect(
SPRITE_POSITIONS[4].read(),
SPRITE_POSITIONS[5].read(),
BALL_SIZE,
BALL_SIZE,
Color::WHITE,
);
}
#[link_section = ".iwram.draw_rect"]
fn draw_rect(x: u16, y: u16, width: u16, height: u16, color: Color) {
for i in 0..width {
for j in 0..height {
VIDEO3_VRAM.index((x + i) as usize, (y + j) as usize).write(color);
}
}
}