mirror of
https://github.com/transistorfet/moa.git
synced 2024-11-25 15:33:08 +00:00
Improved genesis controllers a bit
Some games still broken, but sonic 2 works now
This commit is contained in:
parent
a5e4f51469
commit
dd0e84fa51
@ -1,6 +1,7 @@
|
|||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::devices::{Address, Addressable, Transmutable};
|
use crate::system::System;
|
||||||
|
use crate::devices::{Clock, ClockElapsed, Address, Addressable, Steppable, Transmutable};
|
||||||
use crate::host::controllers::{ControllerDevice, ControllerEvent};
|
use crate::host::controllers::{ControllerDevice, ControllerEvent};
|
||||||
use crate::host::traits::{Host, ControllerUpdater, SharedData};
|
use crate::host::traits::{Host, ControllerUpdater, SharedData};
|
||||||
|
|
||||||
@ -23,11 +24,11 @@ pub struct GenesisControllerPort {
|
|||||||
/// Data contains bits:
|
/// Data contains bits:
|
||||||
/// 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
|
/// 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
|
||||||
/// X | Y | Z | MODE | START | A | C | B | RIGHT | LEFT | DOWN | UP
|
/// X | Y | Z | MODE | START | A | C | B | RIGHT | LEFT | DOWN | UP
|
||||||
pub data: SharedData<u16>,
|
pub buttons: SharedData<u16>,
|
||||||
|
|
||||||
pub ctrl: u8,
|
pub ctrl: u8,
|
||||||
|
pub outputs: u8,
|
||||||
pub th_count: u8,
|
pub th_count: u8,
|
||||||
pub next_read: u8,
|
|
||||||
|
|
||||||
pub s_ctrl: u8,
|
pub s_ctrl: u8,
|
||||||
}
|
}
|
||||||
@ -35,35 +36,40 @@ pub struct GenesisControllerPort {
|
|||||||
impl GenesisControllerPort {
|
impl GenesisControllerPort {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
data: SharedData::new(0xffff),
|
buttons: SharedData::new(0xffff),
|
||||||
ctrl: 0,
|
ctrl: 0,
|
||||||
|
outputs: 0,
|
||||||
th_count: 0,
|
th_count: 0,
|
||||||
next_read: 0,
|
|
||||||
s_ctrl: 0,
|
s_ctrl: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_data(&mut self, outputs: u8) {
|
pub fn get_data(&mut self) -> u8 {
|
||||||
let prev_th = self.next_read & 0x40;
|
let inputs = self.buttons.get();
|
||||||
self.next_read = outputs & self.ctrl;
|
let th_state = (self.outputs & 0x40) != 0;
|
||||||
|
|
||||||
if ((self.next_read & 0x40) ^ prev_th) != 0 {
|
match (th_state, self.th_count) {
|
||||||
|
(true, 0) => self.outputs | ((inputs & 0x003F) as u8),
|
||||||
|
(false, 0) => self.outputs | (((inputs & 0x00C0) >> 2) as u8) | ((inputs & 0x0003) as u8),
|
||||||
|
(true, 1) => self.outputs | ((inputs & 0x003F) as u8),
|
||||||
|
(false, 1) => self.outputs | (((inputs & 0x00C0) >> 2) as u8),
|
||||||
|
(true, 2) => self.outputs | ((inputs & 0x0030) as u8) | (((inputs & 0x0F00) >> 8) as u8),
|
||||||
|
(false, 2) => self.outputs | (((inputs & 0x00C0) >> 2) as u8) | 0x0F,
|
||||||
|
(true, 3) => self.outputs | ((inputs & 0x003F) as u8),
|
||||||
|
(false, 3) => self.outputs | (((inputs & 0x00C0) >> 2) as u8) | ((inputs & 0x0003) as u8),
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_data(&mut self, outputs: u8) {
|
||||||
|
let prev_th = self.outputs & 0x40;
|
||||||
|
self.outputs = outputs;
|
||||||
|
|
||||||
|
if ((outputs & 0x40) ^ prev_th) != 0 {
|
||||||
// TH bit was toggled
|
// TH bit was toggled
|
||||||
let inputs = self.data.get();
|
|
||||||
self.next_read = match self.th_count {
|
|
||||||
0 => self.next_read | ((inputs & 0x003F) as u8),
|
|
||||||
1 => self.next_read | (((inputs & 0x00C0) >> 2) as u8) | ((inputs & 0x0003) as u8),
|
|
||||||
2 => self.next_read | ((inputs & 0x003F) as u8),
|
|
||||||
3 => self.next_read | (((inputs & 0x00C0) >> 2) as u8),
|
|
||||||
4 => self.next_read | ((inputs & 0x0030) as u8) | (((inputs & 0x0F00) >> 8) as u8),
|
|
||||||
5 => self.next_read | (((inputs & 0x00C0) >> 2) as u8) | 0x0F,
|
|
||||||
6 => self.next_read | ((inputs & 0x003F) as u8),
|
|
||||||
7 => self.next_read | (((inputs & 0x00C0) >> 2) as u8) | ((inputs & 0x0003) as u8),
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.th_count += 1;
|
self.th_count += 1;
|
||||||
if self.th_count > 7 {
|
if self.th_count > 3 {
|
||||||
self.th_count = 0;
|
self.th_count = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,6 +79,10 @@ impl GenesisControllerPort {
|
|||||||
self.ctrl = ctrl;
|
self.ctrl = ctrl;
|
||||||
self.th_count = 0;
|
self.th_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reset_count(&mut self) {
|
||||||
|
self.th_count = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GenesisControllerUpdater(SharedData<u16>, SharedData<bool>);
|
pub struct GenesisControllerUpdater(SharedData<u16>, SharedData<bool>);
|
||||||
@ -107,6 +117,8 @@ pub struct GenesisController {
|
|||||||
pub port_2: GenesisControllerPort,
|
pub port_2: GenesisControllerPort,
|
||||||
pub expansion: GenesisControllerPort,
|
pub expansion: GenesisControllerPort,
|
||||||
pub interrupt: SharedData<bool>,
|
pub interrupt: SharedData<bool>,
|
||||||
|
pub last_clock: Clock,
|
||||||
|
pub last_write: Clock,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GenesisController {
|
impl GenesisController {
|
||||||
@ -116,15 +128,17 @@ impl GenesisController {
|
|||||||
port_2: GenesisControllerPort::new(),
|
port_2: GenesisControllerPort::new(),
|
||||||
expansion: GenesisControllerPort::new(),
|
expansion: GenesisControllerPort::new(),
|
||||||
interrupt: SharedData::new(false),
|
interrupt: SharedData::new(false),
|
||||||
|
last_clock: 0,
|
||||||
|
last_write: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
|
pub fn create<H: Host>(host: &mut H) -> Result<Self, Error> {
|
||||||
let controller = GenesisController::new();
|
let controller = GenesisController::new();
|
||||||
|
|
||||||
let controller1 = Box::new(GenesisControllerUpdater(controller.port_1.data.clone(), controller.interrupt.clone()));
|
let controller1 = Box::new(GenesisControllerUpdater(controller.port_1.buttons.clone(), controller.interrupt.clone()));
|
||||||
host.register_controller(ControllerDevice::A, controller1)?;
|
host.register_controller(ControllerDevice::A, controller1)?;
|
||||||
let controller2 = Box::new(GenesisControllerUpdater(controller.port_2.data.clone(), controller.interrupt.clone()));
|
let controller2 = Box::new(GenesisControllerUpdater(controller.port_2.buttons.clone(), controller.interrupt.clone()));
|
||||||
host.register_controller(ControllerDevice::B, controller2)?;
|
host.register_controller(ControllerDevice::B, controller2)?;
|
||||||
|
|
||||||
Ok(controller)
|
Ok(controller)
|
||||||
@ -150,9 +164,9 @@ impl Addressable for GenesisController {
|
|||||||
|
|
||||||
match addr {
|
match addr {
|
||||||
REG_VERSION => { data[i] = 0xA0; } // Overseas Version, NTSC, No Expansion
|
REG_VERSION => { data[i] = 0xA0; } // Overseas Version, NTSC, No Expansion
|
||||||
REG_DATA1 => { data[i] = self.port_1.next_read; },
|
REG_DATA1 => { data[i] = self.port_1.get_data(); },
|
||||||
REG_DATA2 => { data[i] = self.port_2.next_read; },
|
REG_DATA2 => { data[i] = self.port_2.get_data(); },
|
||||||
REG_DATA3 => { data[i] = self.expansion.next_read; },
|
REG_DATA3 => { data[i] = self.expansion.get_data(); },
|
||||||
REG_CTRL1 => { data[i] = self.port_1.ctrl; },
|
REG_CTRL1 => { data[i] = self.port_1.ctrl; },
|
||||||
REG_CTRL2 => { data[i] = self.port_2.ctrl; },
|
REG_CTRL2 => { data[i] = self.port_2.ctrl; },
|
||||||
REG_CTRL3 => { data[i] = self.expansion.ctrl; },
|
REG_CTRL3 => { data[i] = self.expansion.ctrl; },
|
||||||
@ -166,6 +180,14 @@ impl Addressable for GenesisController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
|
fn write(&mut self, addr: Address, data: &[u8]) -> Result<(), Error> {
|
||||||
|
// TODO this causes sonic2 to read incorrect inputs, but works without the reset
|
||||||
|
//if self.last_clock - self.last_write >= 1_500_000 {
|
||||||
|
// self.port_1.reset_count();
|
||||||
|
// self.port_2.reset_count();
|
||||||
|
// self.expansion.reset_count();
|
||||||
|
//}
|
||||||
|
//self.last_write = self.last_clock;
|
||||||
|
|
||||||
debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
debug!("{}: write to register {:x} with {:x}", DEV_NAME, addr, data[0]);
|
||||||
match addr {
|
match addr {
|
||||||
REG_DATA1 => { self.port_1.set_data(data[0]); }
|
REG_DATA1 => { self.port_1.set_data(data[0]); }
|
||||||
@ -183,12 +205,21 @@ impl Addressable for GenesisController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO make a step function to reset the TH count after 1.5ms
|
impl Steppable for GenesisController {
|
||||||
|
fn step(&mut self, system: &System) -> Result<ClockElapsed, Error> {
|
||||||
|
self.last_clock = system.clock;
|
||||||
|
Ok(100_000) // Update every 100us
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Transmutable for GenesisController {
|
impl Transmutable for GenesisController {
|
||||||
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
|
fn as_addressable(&mut self) -> Option<&mut dyn Addressable> {
|
||||||
Some(self)
|
Some(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_steppable(&mut self) -> Option<&mut dyn Steppable> {
|
||||||
|
Some(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user