Skip to content

Commit

Permalink
bios: Add gamepad support using IRQ handler
Browse files Browse the repository at this point in the history
  • Loading branch information
ayrtonm committed Feb 17, 2023
1 parent 158554e commit 5a92d8f
Show file tree
Hide file tree
Showing 6 changed files with 234 additions and 25 deletions.
4 changes: 3 additions & 1 deletion examples/bios/src/boot.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::allocator::HEAP;
use crate::exceptions::exception_vec;
use crate::exceptions::{exception_vec, irq_auto_ack};
use crate::global::Global;
use crate::handlers::{a0_fn_vec, b0_fn_vec, c0_fn_vec};
use crate::main;
Expand All @@ -9,6 +9,7 @@ use core::arch::asm;
use core::intrinsics::{volatile_copy_nonoverlapping_memory, volatile_set_memory};
use core::mem::{size_of, transmute};
use psx::constants::*;
use psx::irq::IRQ;
use psx::CriticalSection;

// This is the entry point which is placed at 0xBFC0_0000 by the linker script
Expand Down Expand Up @@ -38,6 +39,7 @@ extern "C" fn start() -> ! {
init_vectors();
init_ram(cs);
init_threads(cs);
irq_auto_ack(true, IRQ::Vblank, cs);
main();

// TODO: Add a proper executable loader
Expand Down
47 changes: 34 additions & 13 deletions examples/bios/src/exceptions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::thread::{ThreadControlBlock, CURRENT_THREAD};
use alloc::boxed::Box;
use core::arch::asm;
use core::ptr;
use core::ptr::NonNull;
use psx::hw::cop0;
use psx::hw::cop0::{Excode, IntSrc};
use psx::hw::irq;
Expand Down Expand Up @@ -180,10 +181,16 @@ pub struct IRQCtxt<'a> {
pub tcb: *mut ThreadControlBlock,
pub stat: &'a mut irq::Status,
pub mask: &'a mut irq::Mask,
pub active_irqs: [Option<IRQ>; NUM_IRQS],
pub cs: &'a mut CriticalSection,
}

impl IRQCtxt<'_> {
pub fn is_active(&self, irq: IRQ) -> bool {
let active_irqs = self.mask.active_irqs(self.stat);
active_irqs.iter().any(|&x| x == Some(irq))
}
}

// It would've been nice to make this generic over the return type for handlers
// that don't switch threads, but the handler chain is a linked list so it
// would've either required `dyn IRQHandlerFn` or been all one type
Expand All @@ -197,7 +204,7 @@ pub struct IRQHandler {
static HANDLER_CHAIN: Global<Option<IRQHandler>> = Global::new(None);

// This inserts the new handler at the end so it's really more of a stack...
pub fn enqueue_handler(func: IRQHandlerFn, cs: &mut CriticalSection) {
pub fn push_handler(func: IRQHandlerFn, cs: &mut CriticalSection) {
let chain = HANDLER_CHAIN.borrow(cs);
let handler = IRQHandler { func, next: None };
match chain {
Expand All @@ -212,7 +219,7 @@ pub fn enqueue_handler(func: IRQHandlerFn, cs: &mut CriticalSection) {
}
}

pub fn dequeue_handler(cs: &mut CriticalSection) {
pub fn pop_handler(cs: &mut CriticalSection) {
let chain = HANDLER_CHAIN.borrow(cs);
if let Some(root) = chain {
match &mut root.next {
Expand All @@ -228,10 +235,15 @@ pub fn dequeue_handler(cs: &mut CriticalSection) {
};
}

static AUTO_ACK: Global<bool> = Global::new(true);
static AUTO_ACK: Global<[Option<IRQ>; NUM_IRQS]> = Global::new([None; NUM_IRQS]);

pub fn irq_auto_ack(ack: bool, cs: &mut CriticalSection) {
*AUTO_ACK.borrow(cs) = ack;
pub fn irq_auto_ack(ack: bool, irq: IRQ, cs: &mut CriticalSection) {
let auto_ack = &mut AUTO_ACK.borrow(cs)[irq as usize];
if ack {
*auto_ack = Some(irq);
} else {
*auto_ack = None;
}
}

#[inline(always)]
Expand All @@ -243,30 +255,39 @@ fn call_irq_handlers(

let mut new_tcb = ptr::null_mut();
if let Some(root) = HANDLER_CHAIN.borrow(cs) {
let active_irqs = mask.active_irqs(&stat);
let ctxt = IRQCtxt {
tcb,
stat: &mut stat,
mask: &mut mask,
active_irqs,
cs,
};
new_tcb = (root.func)(ctxt);
if let Some(tcb) = NonNull::new((root.func)(ctxt)) {
new_tcb = tcb.as_ptr();
};
let mut next_handler = &root.next;
while let Some(next) = next_handler {
let ctxt = IRQCtxt {
tcb: new_tcb,
stat: &mut stat,
mask: &mut mask,
active_irqs,
cs,
};
new_tcb = (next.func)(ctxt);
if let Some(tcb) = NonNull::new((next.func)(ctxt)) {
new_tcb = tcb.as_ptr();
};
next_handler = &next.next;
}
};
if *AUTO_ACK.borrow(cs) {
stat.ack_all().store();
let mut auto_acked = false;
for &mut irq in AUTO_ACK.borrow(cs) {
if let Some(irq) = irq {
stat.ack(irq);
auto_acked = true;
}
}
// This volatile write only needs to occur if there was an auto ack
if auto_acked {
stat.store();
}
new_tcb
}
Expand Down
57 changes: 48 additions & 9 deletions examples/bios/src/gamepad.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
use crate::exceptions::{push_handler, IRQCtxt};
use crate::global::Global;
use crate::println;
use crate::thread;
use crate::thread::Thread;
use crate::thread::ThreadControlBlock;
use core::mem::size_of;
use core::ptr;
use core::ptr::NonNull;
use psx::hw::pad::{BaudFactor, CharLength, RxIntMode};
use psx::hw::Register;
use psx::hw::{cop0, pad};
use psx::irq::IRQ;

#[repr(C)]
struct GamepadBuffer {
Expand Down Expand Up @@ -38,16 +44,49 @@ pub fn init_pad(buf1: &mut [u16], buf2: &mut [u16]) -> u32 {
};
let ctxt = GamepadCtxt {
buffer1: NonNull::new(buf1).unwrap().cast(),
buffer2: NonNull::new(buf1).unwrap().cast(),
buffer2: NonNull::new(buf2).unwrap().cast(),
};
let mut t = Thread::create_with_arg(gamepad_thread, ctxt).unwrap();
t.unpark();

pad::Baud::skip_load().set_bits(0x88).store();
pad::Control::skip_load()
.enable_tx()
.enable_output()
.enable_rx_interrupt()
.enable_ack_interrupt()
.set_rx_interrupt_mode(RxIntMode::Bits1)
.store();
pad::Mode::skip_load()
.set_baud_factor(BaudFactor::Mul1)
.set_char_length(CharLength::Bits8)
.store();

cop0::Status::new().critical_section(|cs| {
push_handler(poll_gamepad, cs);
});

pad::TxData::send(0x01);

0
}

extern "C" fn gamepad_thread(_ctxt: GamepadCtxt) {
println!("Started gamepad thread");
loop {
thread::resume_main();
fn poll_gamepad(ctxt: IRQCtxt) -> *mut ThreadControlBlock {
const SEQ: [u8; 5] = [0x01, 0x42, 0, 0, 0];
static N: Global<usize> = Global::new(0);
let n = N.borrow(ctxt.cs);
if ctxt.is_active(IRQ::ControllerMemoryCard) {
*n += 1;
if *n == SEQ.len() {
*n = 0;
let mut ctrl = pad::Control::new();
ctrl.disable_output().store();
ctrl.enable_output().store();
}
println!("Received {:x?}", pad::RxData::recv());
pad::TxData::send(SEQ[*n]);

ctxt.stat.ack(IRQ::ControllerMemoryCard).store();
pad::Control::new().ack().store();
}

ptr::null_mut()
}
4 changes: 2 additions & 2 deletions examples/bios/src/thread.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![allow(dead_code)]

extern crate alloc;
use crate::exceptions::{enqueue_handler, IRQCtxt};
use crate::exceptions::{push_handler, IRQCtxt};
use crate::global::Global;
use alloc::boxed::Box;
use alloc::vec;
Expand Down Expand Up @@ -343,7 +343,7 @@ pub fn init_threads(cs: &mut CriticalSection) {
reschedule_threads(ctxt.tcb, ctxt.cs)
}

enqueue_handler(switch_threads, cs);
push_handler(switch_threads, cs);
}

#[no_mangle]
Expand Down
1 change: 1 addition & 0 deletions psx/src/hw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub mod gpu;
pub mod gte;
pub mod irq;
pub mod mmio;
pub mod pad;

use mmio::MemRegister;

Expand Down
146 changes: 146 additions & 0 deletions psx/src/hw/pad.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#![allow(missing_docs, dead_code)]

//! Gamepad and memory card registers
use crate::hw::{MemRegister, Register};

pub struct TxData(MemRegister<u8, 0x1F80_1040>);
pub struct RxData(MemRegister<u8, 0x1F80_1040>);
pub type Status = MemRegister<u32, 0x1F80_1044>;
pub type Mode = MemRegister<u16, 0x1F80_1048>;
pub type Control = MemRegister<u16, 0x1F80_104A>;
pub type Baud = MemRegister<u16, 0x1F80_104E>;

impl TxData {
/// Sends data to the I/O port, whether it's ready for writes or not.
pub fn send(x: u8) {
let mut tx = Self(MemRegister::skip_load());
tx.0.assign(x).store();
}
}

impl RxData {
/// Gets the data at the I/O port, whether it's ready to be read or not.
pub fn recv() -> u8 {
let rx = Self(MemRegister::new());
rx.0.to_bits()
}
}

impl Status {
const TX_READY: u32 = 0;
const RX_NOT_EMPTY: u32 = 1;
const RX_PARITY_ERROR: u32 = 3;
const ACK_INPUT: u32 = 7;
const IRQ: u32 = 9;
const BAUD_TIMER: u32 = 11;

pub fn tx_ready(&self) -> bool {
self.all_set(1 << Self::TX_READY)
}

pub fn rx_not_empty(&self) -> bool {
self.all_set(1 << Self::RX_NOT_EMPTY)
}
}

#[repr(u16)]
pub enum BaudFactor {
Mul1 = 1,
Mul16 = 2,
Mul64 = 3,
}

#[repr(u16)]
pub enum CharLength {
Bits5 = 0,
Bits6 = 1,
Bits7 = 2,
Bits8 = 3,
}

#[repr(u16)]
pub enum RxIntMode {
Bits1 = 0,
Bits2 = 1,
Bits4 = 2,
Bits8 = 3,
}

impl Mode {
const BAUD_RELOAD_FACTOR: u32 = 0;
const CHAR_LENGTH: u32 = 2;
const PARITY_ENABLE: u32 = 4;
const PARITY_TYPE: u32 = 5;
const CLK_OUTPUT_POLARITY: u32 = 8;

pub fn set_baud_factor(&mut self, baud_factor: BaudFactor) -> &mut Self {
self.clear_bits(0b11).set_bits(baud_factor as u16)
}

pub fn set_char_length(&mut self, char_length: CharLength) -> &mut Self {
self.clear_bits(0b11 << Self::CHAR_LENGTH)
.set_bits((char_length as u16) << Self::CHAR_LENGTH)
}
}

impl Control {
const TX_ENABLE: u32 = 0;
const JOYN_OUTPUT: u32 = 1;
const RX_ENABLE: u32 = 2;
const ACK: u32 = 4;
const RESET: u32 = 6;
const RX_INT_MODE: u32 = 8;
const TX_INT_ENABLE: u32 = 10;
const RX_INT_ENABLE: u32 = 11;
const ACK_INT_ENABLE: u32 = 12;
const SLOT_NUM: u32 = 13;

pub fn enable_tx(&mut self) -> &mut Self {
self.set_bits(1 << Self::TX_ENABLE)
}

pub fn disable_tx(&mut self) -> &mut Self {
self.clear_bits(1 << Self::TX_ENABLE)
}

pub fn enable_output(&mut self) -> &mut Self {
self.set_bits(1 << Self::JOYN_OUTPUT)
}

pub fn disable_output(&mut self) -> &mut Self {
self.clear_bits(1 << Self::JOYN_OUTPUT)
}

pub fn select_p1(&mut self) -> &mut Self {
self.clear_bits(1 << Self::SLOT_NUM)
}

pub fn select_p2(&mut self) -> &mut Self {
self.set_bits(1 << Self::SLOT_NUM)
}

pub fn ack(&mut self) -> &mut Self {
self.set_bits(1 << Self::ACK)
}

pub fn enable_rx_interrupt(&mut self) -> &mut Self {
self.set_bits(1 << Self::RX_INT_ENABLE)
}

pub fn disable_rx_interrupt(&mut self) -> &mut Self {
self.clear_bits(1 << Self::RX_INT_ENABLE)
}

pub fn enable_ack_interrupt(&mut self) -> &mut Self {
self.set_bits(1 << Self::ACK_INT_ENABLE)
}

pub fn disable_ack_interrupt(&mut self) -> &mut Self {
self.clear_bits(1 << Self::ACK_INT_ENABLE)
}

pub fn set_rx_interrupt_mode(&mut self, mode: RxIntMode) -> &mut Self {
self.clear_bits(0b11 << Self::RX_INT_MODE)
.set_bits((mode as u16) << Self::RX_INT_MODE)
}
}

0 comments on commit 5a92d8f

Please sign in to comment.