diff options
Diffstat (limited to 'src/devices/bus/sms_ctrl/diypaddle.cpp')
-rw-r--r-- | src/devices/bus/sms_ctrl/diypaddle.cpp | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/src/devices/bus/sms_ctrl/diypaddle.cpp b/src/devices/bus/sms_ctrl/diypaddle.cpp new file mode 100644 index 00000000000..7ab811b7ca4 --- /dev/null +++ b/src/devices/bus/sms_ctrl/diypaddle.cpp @@ -0,0 +1,227 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/********************************************************************** + + Raphaƫl Assenat's DIY SMS/Mark III Paddle Controller + + English description: + https://www.raphnet.net/electronique/sms_paddle/index_en.php + + French description: + https://www.raphnet.net/electronique/sms_paddle/index.php + + Firmware source: + https://github.com/raphnet/sms_paddle + + TODO: + * Add ATmega8 variant - need CPU core. + +**********************************************************************/ + +#include "emu.h" +#include "diypaddle.h" + +#include "cpu/avr8/avr8.h" + + +namespace { + +ROM_START( sms_diy_paddle ) + ROM_REGION(0x4000, "u1", ROMREGION_ERASEFF) + ROM_LOAD( "sms_paddle.bin", 0x0000, 0x0138, CRC(97c7d334) SHA1(dd9e20f2447c43dbe29a49bb3a4fd8049775acd3) ) + + ROM_REGION(0x200, "eeprom", ROMREGION_ERASEFF) +ROM_END + + +INPUT_PORTS_START( sms_diy_paddle ) + PORT_START("BUTTON") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) + + PORT_START("PADDLE") + PORT_BIT( 0x3ff, 0x200, IPT_PADDLE) PORT_MINMAX(0, 1023) PORT_REVERSE PORT_SENSITIVITY(160) PORT_KEYDELTA(20) PORT_CENTERDELTA(0) + + PORT_START("MODE") + PORT_CONFNAME( 0x01, 0x00, "Mode" ) + PORT_CONFSETTING( 0x00, "HPD-200" ) + PORT_CONFSETTING( 0x01, "Export paddle" ) +INPUT_PORTS_END + + + +class sms_diy_paddle_device : public device_t, public device_sms_control_interface +{ +public: + sms_diy_paddle_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock); + + virtual u8 in_r() override; + virtual void out_w(u8 data, u8 mem_mask) override; + +protected: + virtual tiny_rom_entry const *device_rom_region() const override { return ROM_NAME(sms_diy_paddle); } + virtual ioport_constructor device_input_ports() const override { return INPUT_PORTS_NAME(sms_diy_paddle); } + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + virtual void device_start() override ATTR_COLD; + +private: + void program_map(address_map &map) ATTR_COLD; + void data_map(address_map &map) ATTR_COLD; + + u8 portb_in(); + u8 portc_in(); + u8 portd_in(); + void portb_out(u8 data); + void portc_out(u8 data); + + TIMER_CALLBACK_MEMBER(set_lines); + TIMER_CALLBACK_MEMBER(set_portd); + + required_device<atmega168_device> m_mcu; + required_ioport m_button; + required_ioport m_paddle; + required_ioport m_mode; + + u8 m_lines; + u8 m_portb; + u8 m_portc; + u8 m_portd; +}; + + +sms_diy_paddle_device::sms_diy_paddle_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) : + device_t(mconfig, SMS_DIY_PADDLE, tag, owner, clock), + device_sms_control_interface(mconfig, *this), + m_mcu(*this, "u1"), + m_button(*this, "BUTTON"), + m_paddle(*this, "PADDLE"), + m_mode(*this, "MODE"), + m_lines(0x2f), + m_portb(0xff), + m_portc(0xff), + m_portd(0xff) +{ +} + + +u8 sms_diy_paddle_device::in_r() +{ + if (!machine().side_effects_disabled()) + machine().scheduler().add_quantum(attotime::from_usec(5), attotime::from_usec(100)); + + // button connected to TL directly + return m_lines | (BIT(m_button->read(), 0) << 4); +} + + +void sms_diy_paddle_device::out_w(u8 data, u8 mem_mask) +{ + // TH connected to PD2 + machine().scheduler().synchronize( + timer_expired_delegate(FUNC(sms_diy_paddle_device::set_portd), this), + 0xfb | (BIT(data, 6) << 2)); + machine().scheduler().add_quantum(attotime::from_usec(1), attotime::from_usec(20)); +} + + +void sms_diy_paddle_device::device_add_mconfig(machine_config &config) +{ + ATMEGA168(config, m_mcu, 8'000'000); // internally-generated clock + m_mcu->set_addrmap(AS_PROGRAM, &sms_diy_paddle_device::program_map); + m_mcu->set_addrmap(AS_DATA, &sms_diy_paddle_device::data_map); + m_mcu->set_eeprom_tag("eeprom"); + m_mcu->set_low_fuses(0xe2); + m_mcu->set_high_fuses(0xdd); + m_mcu->set_extended_fuses(0xf9); + m_mcu->gpio_in<atmega168_device::GPIOB>().set(FUNC(sms_diy_paddle_device::portb_in)); + m_mcu->gpio_in<atmega168_device::GPIOC>().set(FUNC(sms_diy_paddle_device::portc_in)); + m_mcu->gpio_in<atmega168_device::GPIOD>().set(FUNC(sms_diy_paddle_device::portd_in)); + m_mcu->gpio_out<atmega168_device::GPIOB>().set(FUNC(sms_diy_paddle_device::portb_out)); + m_mcu->gpio_out<atmega168_device::GPIOC>().set(FUNC(sms_diy_paddle_device::portc_out)); + m_mcu->adc_in<atmega168_device::ADC0>().set_ioport("PADDLE"); +} + + +void sms_diy_paddle_device::device_start() +{ + m_lines = 0x2f; + m_portb = 0xff; + m_portc = 0xff; + m_portd = 0xff; + + save_item(NAME(m_lines)); + save_item(NAME(m_portb)); + save_item(NAME(m_portc)); + save_item(NAME(m_portd)); +} + + +void sms_diy_paddle_device::program_map(address_map &map) +{ + map(0x0000, 0x3fff).rom(); +} + + +void sms_diy_paddle_device::data_map(address_map &map) +{ + map(0x0100, 0x04ff).ram(); +} + + +u8 sms_diy_paddle_device::portb_in() +{ + // PB0 looped back to PB1 via mode switch + if (BIT(m_mode->read(), 0)) + return 0xfc | bitswap<2>(m_portb, 0, 0); + else + return 0xfe | BIT(m_portb, 0); +} + + +u8 sms_diy_paddle_device::portc_in() +{ + return (m_portc & 0x3e) | 0xc0 | ((m_paddle->read() > 0x1ff) ? 0x01 : 0x00); +} + + +u8 sms_diy_paddle_device::portd_in() +{ + return m_portd; +} + + +void sms_diy_paddle_device::portb_out(u8 data) +{ + m_portb = data; +} + + +void sms_diy_paddle_device::portc_out(u8 data) +{ + // PC1 -> TR, PC2 -> Up, PC3 -> Down, PC4 -> Left, PC5 -> Right + if (data != m_portc) + { + m_portc = data; + machine().scheduler().synchronize( + timer_expired_delegate(FUNC(sms_diy_paddle_device::set_lines), this), + (BIT(data, 1) << 5) | BIT(data, 2, 4)); + machine().scheduler().add_quantum(attotime::from_usec(1), attotime::from_usec(20)); + } +} + + +TIMER_CALLBACK_MEMBER(sms_diy_paddle_device::set_lines) +{ + m_lines = param & 0x2f; +} + + +TIMER_CALLBACK_MEMBER(sms_diy_paddle_device::set_portd) +{ + m_portd = param & 0xff; +} + +} // anonymous namespace + + + +DEFINE_DEVICE_TYPE_PRIVATE(SMS_DIY_PADDLE, device_sms_control_interface, sms_diy_paddle_device, "sms_diypaddle", "raphnet DIY SMS/Mark III Paddle Controller") |