Get all the code lined up

This commit is contained in:
Lokathor 2019-02-14 19:39:34 -07:00
parent 89b7cc0eaa
commit 3b688b907c
6 changed files with 62 additions and 114 deletions

View file

@ -1,61 +0,0 @@
#![no_std]
#![feature(start)]
#![forbid(unsafe_code)]
use gba::{
io::{
display::{spin_until_vblank, spin_until_vdraw, DisplayControlSetting, DisplayMode, DISPCNT},
keypad::read_key_input,
},
vram::bitmap::Mode3,
Color,
};
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
loop {}
}
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
const SETTING: DisplayControlSetting = DisplayControlSetting::new().with_mode(DisplayMode::Mode3).with_bg2(true);
DISPCNT.write(SETTING);
let mut px = Mode3::WIDTH / 2;
let mut py = Mode3::HEIGHT / 2;
let mut color = Color::from_rgb(31, 0, 0);
loop {
// read the input for this frame
let this_frame_keys = read_key_input();
// adjust game state and wait for vblank
px = px.wrapping_add(2 * this_frame_keys.column_direction() as usize);
py = py.wrapping_add(2 * this_frame_keys.row_direction() as usize);
spin_until_vblank();
// draw the new game and wait until the next frame starts.
const BLACK: Color = Color::from_rgb(0, 0, 0);
if px >= Mode3::WIDTH || py >= Mode3::HEIGHT {
// out of bounds, reset the screen and position.
Mode3::clear_to(BLACK);
color = color.rotate_left(5);
px = Mode3::WIDTH / 2;
py = Mode3::HEIGHT / 2;
} else {
let color_here = Mode3::read(px, py);
if color_here != Some(BLACK) {
// crashed into our own line, reset the screen
Mode3::dma_clear_to(BLACK);
color = color.rotate_left(5);
} else {
// draw the new part of the line
Mode3::write(px, py, color);
Mode3::write(px, py + 1, color);
Mode3::write(px + 1, py, color);
Mode3::write(px + 1, py + 1, color);
}
}
spin_until_vdraw();
}
}

View file

@ -4,11 +4,13 @@
use gba::{ use gba::{
fatal, fatal,
io::display::{DisplayControlSetting, DisplayMode, DISPCNT}, io::{
display::{DisplayControlSetting, DisplayMode, DISPCNT, VBLANK_SCANLINE, VCOUNT},
keypad::read_key_input,
},
vram::bitmap::Mode3, vram::bitmap::Mode3,
Color, Color,
}; };
use gba::io::keypad::read_key_input;
#[panic_handler] #[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! { fn panic(info: &core::panic::PanicInfo) -> ! {
@ -33,9 +35,51 @@ fn main(_argc: isize, _argv: *const *const u8) -> isize {
// read our keys for this frame // read our keys for this frame
let this_frame_keys = read_key_input(); let this_frame_keys = read_key_input();
gba::io::display::spin_until_vblank(); // adjust game state and wait for vblank
Mode5::dma_clear_to(Page::Zero, Color(111)); px = px.wrapping_add(2 * this_frame_keys.x_tribool() as usize);
Mode5::draw_line(Page::Zero, 5, 5, 100, 100, Color(0b0_11111_11111_11111)); py = py.wrapping_add(2 * this_frame_keys.y_tribool() as usize);
gba::io::display::spin_until_vdraw(); if this_frame_keys.l() {
color = Color(color.0.rotate_left(5));
}
if this_frame_keys.r() {
color = Color(color.0.rotate_right(5));
}
// now we wait
spin_until_vblank();
// draw the new game and wait until the next frame starts.
const BLACK: Color = Color::from_rgb(0, 0, 0);
if px >= Mode3::WIDTH || py >= Mode3::HEIGHT {
// out of bounds, reset the screen and position.
Mode3::dma_clear_to(BLACK);
px = Mode3::WIDTH / 2;
py = Mode3::HEIGHT / 2;
} else {
// draw the new part of the line
Mode3::write(px, py, color);
Mode3::write(px, py + 1, color);
Mode3::write(px + 1, py, color);
Mode3::write(px + 1, py + 1, color);
}
// now we wait again
spin_until_vdraw();
} }
} }
/// Performs a busy loop until VBlank starts.
///
/// This is very inefficient, and please keep following the lessons until we
/// cover how interrupts work!
pub fn spin_until_vblank() {
while VCOUNT.read() < VBLANK_SCANLINE {}
}
/// Performs a busy loop until VDraw starts.
///
/// This is very inefficient, and please keep following the lessons until we
/// cover how interrupts work!
pub fn spin_until_vdraw() {
while VCOUNT.read() >= VBLANK_SCANLINE {}
}

View file

@ -26,7 +26,8 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
fn start_timers() { fn start_timers() {
let init_val: u16 = u32::wrapping_sub(0x1_0000, 64) as u16; let init_val: u16 = u32::wrapping_sub(0x1_0000, 64) as u16;
const TIMER_SETTINGS: TimerControlSetting = TimerControlSetting::new().with_overflow_irq(true).with_enabled(true); const TIMER_SETTINGS: TimerControlSetting =
TimerControlSetting::new().with_overflow_irq(true).with_enabled(true);
TM0CNT_L.write(init_val); TM0CNT_L.write(init_val);
TM0CNT_H.write(TIMER_SETTINGS.with_tick_rate(TimerTickRate::CPU1024)); TM0CNT_H.write(TIMER_SETTINGS.with_tick_rate(TimerTickRate::CPU1024));
@ -90,8 +91,8 @@ static mut PIXEL: usize = 0;
fn write_pixel(color: Color) { fn write_pixel(color: Color) {
unsafe { unsafe {
Mode3::write_pixel(PIXEL, 0, color); Mode3::write(PIXEL, 0, color);
PIXEL = (PIXEL + 1) % Mode3::SCREEN_PIXEL_COUNT; PIXEL = (PIXEL + 1) % (Mode3::WIDTH * Mode3::HEIGHT);
} }
} }

View file

@ -127,37 +127,6 @@ pub const VCOUNT: ROVolAddress<u16> = unsafe { ROVolAddress::new(0x400_0006) };
/// If the `VCOUNT` register reads equal to or above this then you're in vblank. /// If the `VCOUNT` register reads equal to or above this then you're in vblank.
pub const VBLANK_SCANLINE: u16 = 160; pub const VBLANK_SCANLINE: u16 = 160;
/// Obtains the current `VCOUNT` value.
pub fn vcount() -> u16 {
VCOUNT.read()
}
/// Performs a busy loop until VBlank starts.
///
/// NOTE: This method isn't very power efficient, since it is equivalent to
/// calling "halt" repeatedly. The recommended way to wait for a VBlank or VDraw
/// is to set an IRQ handler with
/// [`io::irq::set_irq_handler`](`io::irq::set_irq_handler`) and using
/// [`bios::vblank_intr_wait`](bios::vblank_interrupt_wait) to sleep the CPU
/// until a VBlank IRQ is generated. See the [`io::irq`](io::irq) module for
/// more details.
pub fn spin_until_vblank() {
while vcount() < VBLANK_SCANLINE {}
}
/// Performs a busy loop until VDraw starts.
///
/// NOTE: This method isn't very power efficient, since it is equivalent to
/// calling "halt" repeatedly. The recommended way to wait for a VBlank or VDraw
/// is to set an IRQ handler with
/// [`io::irq::set_irq_handler`](`io::irq::set_irq_handler`) and using
/// [`bios::vblank_intr_wait`](bios::vblank_interrupt_wait) to sleep the CPU
/// until a VBlank IRQ is generated. See the [`io::irq`](io::irq) module for
/// more details.
pub fn spin_until_vdraw() {
while vcount() >= VBLANK_SCANLINE {}
}
/// Global mosaic effect control. Write-only. /// Global mosaic effect control. Write-only.
pub const MOSAIC: VolAddress<MosaicSetting> = unsafe { VolAddress::new(0x400_004C) }; pub const MOSAIC: VolAddress<MosaicSetting> = unsafe { VolAddress::new(0x400_004C) };

View file

@ -50,9 +50,10 @@ impl KeyInput {
KeyInput(self.0 ^ other.0) KeyInput(self.0 ^ other.0)
} }
/// Gives the arrow pad value as a tribool, with Plus being increased column /// Right/left tribool.
/// value (right). ///
pub fn column_direction(self) -> TriBool { /// Right is Plus and Left is Minus
pub fn x_tribool(self) -> TriBool {
if self.right() { if self.right() {
TriBool::Plus TriBool::Plus
} else if self.left() { } else if self.left() {
@ -62,9 +63,10 @@ impl KeyInput {
} }
} }
/// Gives the arrow pad value as a tribool, with Plus being increased row /// Up/down tribool.
/// value (down). ///
pub fn row_direction(self) -> TriBool { /// Down is Plus and Up is Minus
pub fn y_tribool(self) -> TriBool {
if self.down() { if self.down() {
TriBool::Plus TriBool::Plus
} else if self.up() { } else if self.up() {

View file

@ -71,13 +71,6 @@ impl Color {
pub const fn from_rgb(r: u16, g: u16, b: u16) -> Color { pub const fn from_rgb(r: u16, g: u16, b: u16) -> Color {
Color(b << 10 | g << 5 | r) Color(b << 10 | g << 5 | r)
} }
/// Does a left rotate of the bits.
///
/// This has no particular meaning but is a wild way to cycle colors.
pub const fn rotate_left(self, n: u32) -> Color {
Color(self.0.rotate_left(n))
}
} }
// //