-bus/a2bus: Added Apple II Parallel Printer Interface Card.

-frontend/mame/audit.cpp: Fixed another annoying edge case.

-Cleaned up RawInput code slightly.
This commit is contained in:
Vas Crabb 2020-11-01 00:37:28 +11:00
parent da9213faaa
commit cf7fe1e04d
10 changed files with 538 additions and 181 deletions

View File

@ -2330,6 +2330,8 @@ if (BUSES["A2BUS"]~=null) then
MAME_DIR .. "src/devices/bus/a2bus/a2midi.h",
MAME_DIR .. "src/devices/bus/a2bus/a2mockingboard.cpp",
MAME_DIR .. "src/devices/bus/a2bus/a2mockingboard.h",
MAME_DIR .. "src/devices/bus/a2bus/a2parprn.cpp",
MAME_DIR .. "src/devices/bus/a2bus/a2parprn.h",
MAME_DIR .. "src/devices/bus/a2bus/a2pic.cpp",
MAME_DIR .. "src/devices/bus/a2bus/a2pic.h",
MAME_DIR .. "src/devices/bus/a2bus/a2sam.cpp",

View File

@ -0,0 +1,233 @@
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
#include "emu.h"
#include "a2parprn.h"
//#define VERBOSE 1
//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"
namespace {
// FIXME: get proper PROM dumps.
/*
There are most likely multiple revisions.
6309 and 74471 PROMs were used.
Example silkscreen:
SINGAPORE
341-0005-00
cAPPLE 78-02
SN74S471N
Example label:
APPLE
©1978
PROM image here is from entering a listing and zero-filling unused areas.
********************************
* *
* PRINTER CARD I FIRMWARE *
* *
* WOZ 11/1/77 *
* APPLE COMPUTER INC. *
* ALL RIGHTS RESERVED *
* *
********************************
*/
ROM_START(parprn)
ROM_REGION(0x100, "prom", 0)
ROM_LOAD( "prom.b4", 0x0000, 0x0100, BAD_DUMP CRC(00b742ca) SHA1(c67888354aa013f9cb882eeeed924e292734e717) )
ROM_END
INPUT_PORTS_START(parprn)
PORT_START("CFG")
PORT_CONFNAME(0x01, 0x00, "Acknowledge latching edge")
PORT_CONFSETTING( 0x00, "Falling (/Y-B)")
PORT_CONFSETTING( 0x01, "Rising (Y-B)")
PORT_CONFNAME(0x06, 0x02, "Printer ready")
PORT_CONFSETTING( 0x00, "Always (S5-C-D)")
PORT_CONFSETTING( 0x02, "Acknowledge latch (Z-C-D)")
PORT_CONFSETTING( 0x04, "ACK (Y-C-D)")
PORT_CONFSETTING( 0x06, "/ACK (/Y-C-D)")
PORT_CONFNAME(0x08, 0x00, "Strobe polarity")
PORT_CONFSETTING( 0x00, "Negative (S5-A-/X, GND-X)")
PORT_CONFSETTING( 0x08, "Positive (S5-X, GND-A-/X)")
PORT_CONFNAME(0x10, 0x10, "Character width")
PORT_CONFSETTING( 0x00, "7-bit")
PORT_CONFSETTING( 0x10, "8-bit")
INPUT_PORTS_END
} // anonymous namespace
DEFINE_DEVICE_TYPE(A2BUS_PARPRN, a2bus_parprn_device, "a2parprn", "Apple II Parallel Printer Interface Card")
a2bus_parprn_device::a2bus_parprn_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock) :
device_t(mconfig, A2BUS_PARPRN, tag, owner, clock),
device_a2bus_card_interface(mconfig, *this),
m_printer_conn(*this, "prn"),
m_printer_out(*this, "prn_out"),
m_input_config(*this, "CFG"),
m_prom(*this, "prom"),
m_strobe_timer(nullptr),
m_next_strobe(0U),
m_ack_latch(0U),
m_ack_in(1U)
{
}
//----------------------------------------------
// device_a2bus_card_interface implementation
//----------------------------------------------
u8 a2bus_parprn_device::read_c0nx(u8 offset)
{
if (!machine().side_effects_disabled())
{
LOG("Read C0n%01X (effective open-bus write)\n", offset);
// R/W is ignored, so it has the same effect as a write
// the bus is open, but 74LS logic has low-impedance inputs so it's likely to latch 0xff
write_c0nx(offset, 0xffU);
}
return 0x00U;
}
void a2bus_parprn_device::write_c0nx(u8 offset, u8 data)
{
LOG("Write C0n%01X=%02X\n", offset, data);
if (m_ack_latch)
LOG("Clearing acknowledge latch\n");
else
LOG("Previous data not acknowledged\n");
ioport_value const cfg(m_input_config->read());
m_printer_out->write(data & (BIT(cfg, 8) ? 0xffU : 0x7fU));
m_printer_conn->write_strobe(BIT(~cfg, 3));
m_next_strobe = BIT(cfg, 3);
m_ack_latch = 0U;
m_strobe_timer->adjust(attotime::from_ticks(1, clock()));
}
u8 a2bus_parprn_device::read_cnxx(u8 offset)
{
ioport_value const cfg(m_input_config->read());
if (BIT(cfg, 2))
{
if (!BIT(offset, 6) || (BIT(offset, 7) && (BIT(cfg, 1) != m_ack_in)))
offset |= 0x40U;
else
offset &= 0xbfU;
}
else if (BIT(cfg, 1))
{
if (!BIT(offset, 6) || (BIT(offset, 7) && !m_ack_latch))
offset |= 0x40U;
else
offset &= 0xbfU;
}
else
{
offset ^= 0x40U;
}
return m_prom[offset];
}
//----------------------------------------------
// device_t implementation
//----------------------------------------------
tiny_rom_entry const *a2bus_parprn_device::device_rom_region() const
{
return ROM_NAME(parprn);
}
void a2bus_parprn_device::device_add_mconfig(machine_config &config)
{
CENTRONICS(config, m_printer_conn, centronics_devices, "printer");
m_printer_conn->ack_handler().set(FUNC(a2bus_parprn_device::ack_w));
OUTPUT_LATCH(config, m_printer_out);
m_printer_conn->set_output_latch(*m_printer_out);
}
ioport_constructor a2bus_parprn_device::device_input_ports() const
{
return INPUT_PORTS_NAME(parprn);
}
void a2bus_parprn_device::device_start()
{
m_strobe_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(a2bus_parprn_device::update_strobe), this));
save_item(NAME(m_next_strobe));
save_item(NAME(m_ack_latch));
save_item(NAME(m_ack_in));
}
void a2bus_parprn_device::device_reset()
{
m_ack_latch = 1U;
}
//----------------------------------------------
// printer status inputs
//----------------------------------------------
WRITE_LINE_MEMBER(a2bus_parprn_device::ack_w)
{
if (bool(state) != bool(m_ack_in))
{
m_ack_in = state ? 1U : 0U;
LOG("ACK=%u\n", m_ack_in);
if (started() && (m_ack_in == BIT(m_input_config->read(), 0)))
{
LOG("Active ACK edge\n");
m_ack_latch = 1U;
}
}
}
//----------------------------------------------
// timer handlers
//----------------------------------------------
TIMER_CALLBACK_MEMBER(a2bus_parprn_device::update_strobe)
{
ioport_value const cfg(m_input_config->read());
LOG("Output /STROBE=%u\n", m_next_strobe);
m_printer_conn->write_strobe(m_next_strobe);
if (m_next_strobe == BIT(cfg, 3))
{
LOG("Start strobe timer\n");
m_next_strobe = BIT(~cfg, 3);
m_strobe_timer->adjust(attotime::from_ticks(1, clock()));
}
}

View File

@ -0,0 +1,96 @@
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/***********************************************************************
Apple II Parallel Printer Interface Card
20-pin header:
GND 1 2 ACK
SP1* 3 4 F
SP2* 5 6 SP3*
SP4* 7 8 STR
SP5* 9 10 DP0
DP1 11 12 DP2
DP3 13 14 DP4
DP5 15 16 DP6
DP7 17 18 SP6*
SP7* 19 20 GND
*spare, SP1-SP5 brought out to pads for wire mods
DIP jumper B1:
no connection NC 1 16 B acknowledge latch clock
no connection NC 2 15 D printer ready 1
synchronised ACK Y 3 14 C printer ready 2
synchronised /ACK /Y 4 13 A idle strobe
acknowledge latch Q /Z 5 12 /X strobe on access
acknowlwdge latch /Q Z 6 11 S5 +5V via 12 resistor
signal ground GND 7 10 X strobe after one cycle
header pin 4 F 8 9 NC no connection
In real life, the card only samples the ACK input on rising
edges of the phase 1 clock. Acknowledge pulses shorter than one
microsecond may be ignored. This limitation is not emulated.
The card completely ignores the R/W line. The PROM driver uses
and indexed write to access the C0nX region. This produces a
spurious read, causing data to be latched and triggering a
delayed strobe pulse. This is why the delay is important the
write needs to happen on the cycle immediately following the
spurious read to prevent the open bus data from being printed.
The card was designed to allow user modifications. There are
locations at 5A (16-pin) and 5B (20-pin) for additional DIP
packages. Common modifications included stretching strobe
and/or acknowledge pulses to improve reliability.
***********************************************************************/
#ifndef MAME_BUS_A2BUS_A2PARPRN_H
#define MAME_BUS_A2BUS_A2PARPRN_H
#pragma once
#include "a2bus.h"
#include "bus/centronics/ctronics.h"
class a2bus_parprn_device : public device_t, public device_a2bus_card_interface
{
public:
a2bus_parprn_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock);
// device_a2bus_card_interface implementation
virtual u8 read_c0nx(u8 offset) override;
virtual void write_c0nx(u8 offset, u8 data) override;
virtual u8 read_cnxx(u8 offset) override;
protected:
// device_t implementation
virtual tiny_rom_entry const *device_rom_region() const override;
virtual void device_add_mconfig(machine_config &config) override;
virtual ioport_constructor device_input_ports() const override;
virtual void device_start() override;
virtual void device_reset() override;
private:
// printer status inputs
DECLARE_WRITE_LINE_MEMBER(ack_w);
// timer handlers
TIMER_CALLBACK_MEMBER(update_strobe);
required_device<centronics_device> m_printer_conn;
required_device<output_latch_device> m_printer_out;
required_ioport m_input_config;
required_region_ptr<u8> m_prom;
emu_timer * m_strobe_timer;
u8 m_next_strobe; // B3 pin 13
u8 m_ack_latch; // B2 pin 6
u8 m_ack_in; // pin 2
};
DECLARE_DEVICE_TYPE(A2BUS_PARPRN, a2bus_parprn_device)
#endif // MAME_BUS_A2BUS_A2PARPRN_H

View File

@ -1,6 +1,6 @@
// license:BSD-3-Clause
// copyright-holders:Vas Crabb
/*********************************************************************
/***********************************************************************
Apple II Parallel Interface Card (670-0021)
@ -34,7 +34,18 @@
*wired to 500ns strobe but connector hole is blocked
*********************************************************************/
This card has significant flaws:
* The strobe pulse begins on the same rising edge of the phase 1
clock as the data is latched. The parallel load input ito the
strobe time counter (6A) is delayed slightly, but the load
happens on the rising phase 1 clock edge. This could glitch
on a real printer. MAME always sets the data outputs before
starting the strobe pulse.
* Acknowledge is ignored while the strobe output is active. If
the printer acknowledges the data before the end of the strobe
pulse, the card will miss it and wait forever.
***********************************************************************/
#ifndef MAME_BUS_A2BUS_A2PIC_H
#define MAME_BUS_A2BUS_A2PIC_H

View File

@ -45,6 +45,35 @@ class parent_rom_vector : public std::vector<parent_rom>
public:
using std::vector<parent_rom>::vector;
void remove_redundant_parents()
{
while (!empty())
{
// find where the next parent starts
auto const last(
std::find_if(
std::next(cbegin()),
cend(),
[this] (parent_rom const &r) { return &front().type.get() != &r.type.get(); }));
// examine dumped ROMs in this generation
for (auto i = cbegin(); last != i; ++i)
{
if (!i->hashes.flag(util::hash_collection::FLAG_NO_DUMP))
{
auto const match(
std::find_if(
last,
cend(),
[&i] (parent_rom const &r) { return (i->length == r.length) && (i->hashes == r.hashes); }));
if (cend() == match)
return;
}
}
erase(cbegin(), last);
}
}
std::add_pointer_t<device_type> find_shared_device(device_t &current, char const *name, util::hash_collection const &hashes, uint64_t length) const
{
// if we're examining a child device, it will always have a perfect match
@ -172,6 +201,7 @@ media_auditor::summary media_auditor::audit_media(const char *validation)
}
}
}
parentroms.remove_redundant_parents();
// count ROMs required/found
std::size_t found(0);

View File

@ -69,6 +69,7 @@ II Plus: RAM options reduced to 16/32/48 KB.
#include "bus/a2bus/a2memexp.h"
#include "bus/a2bus/a2midi.h"
#include "bus/a2bus/a2mockingboard.h"
#include "bus/a2bus/a2parprn.h"
#include "bus/a2bus/a2pic.h"
#include "bus/a2bus/a2sam.h"
#include "bus/a2bus/a2scsi.h"
@ -1308,7 +1309,9 @@ static void apple2_cards(device_slot_interface &device)
device.option_add("ultraterm", A2BUS_ULTRATERM); /* Videx UltraTerm (original) */
device.option_add("ultratermenh", A2BUS_ULTRATERMENH); /* Videx UltraTerm (enhanced //e) */
device.option_add("aevm80", A2BUS_AEVIEWMASTER80); /* Applied Engineering ViewMaster 80 */
device.option_add("parallel", A2BUS_PIC); /* Apple Parallel Interface Card */
device.option_add("parprn", A2BUS_PARPRN); /* Apple II Parallel Printer Interface Card */
device.option_add("parallel", A2BUS_PIC); /* Apple II Parallel Interface Card */
device.option_add("grapplerplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */
device.option_add("corvus", A2BUS_CORVUS); /* Corvus flat-cable HDD interface (see notes in a2corvus.c) */
device.option_add("mcms1", A2BUS_MCMS1); /* Mountain Computer Music System, card 1 of 2 */
device.option_add("mcms2", A2BUS_MCMS2); /* Mountain Computer Music System, card 2 of 2. must be in card 1's slot + 1! */
@ -1326,7 +1329,6 @@ static void apple2_cards(device_slot_interface &device)
device.option_add("applesurance", A2BUS_APPLESURANCE); /* Applesurance Diagnostic Controller */
// device.option_add("magicmusician", A2BUS_MAGICMUSICIAN); /* Magic Musician Card */
device.option_add("byte8251", A2BUS_BYTE8251); /* BYTE Magazine 8251 serial card */
device.option_add("grapplerplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */
}
void apple2_state::apple2_common(machine_config &config)

View File

@ -152,6 +152,7 @@ MIG RAM page 2 $CE02 is the speaker/slot bitfield and $CE03 is the paddle/accele
#include "bus/a2bus/a2memexp.h"
#include "bus/a2bus/a2midi.h"
#include "bus/a2bus/a2mockingboard.h"
#include "bus/a2bus/a2parprn.h"
#include "bus/a2bus/a2pic.h"
#include "bus/a2bus/a2sam.h"
#include "bus/a2bus/a2scsi.h"
@ -4505,7 +4506,9 @@ static void apple2_cards(device_slot_interface &device)
device.option_add("ultraterm", A2BUS_ULTRATERM); /* Videx UltraTerm (original) */
device.option_add("ultratermenh", A2BUS_ULTRATERMENH); /* Videx UltraTerm (enhanced //e) */
device.option_add("aevm80", A2BUS_AEVIEWMASTER80); /* Applied Engineering ViewMaster 80 */
device.option_add("parallel", A2BUS_PIC); /* Apple Parallel Interface Card */
device.option_add("parprn", A2BUS_PARPRN); /* Apple II Parallel Printer Interface Card */
device.option_add("parallel", A2BUS_PIC); /* Apple II Parallel Interface Card */
device.option_add("grapplerplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */
device.option_add("corvus", A2BUS_CORVUS); /* Corvus flat-cable HDD interface (see notes in a2corvus.c) */
device.option_add("mcms1", A2BUS_MCMS1); /* Mountain Computer Music System, card 1 of 2 */
device.option_add("mcms2", A2BUS_MCMS2); /* Mountain Computer Music System, card 2 of 2. must be in card 1's slot + 1! */
@ -4529,7 +4532,6 @@ static void apple2_cards(device_slot_interface &device)
device.option_add("uthernet", A2BUS_UTHERNET); /* A2RetroSystems Uthernet card */
device.option_add("sider2", A2BUS_SIDER2); /* Advanced Tech Systems / First Class Peripherals Sider 2 SASI card */
device.option_add("sider1", A2BUS_SIDER1); /* Advanced Tech Systems / First Class Peripherals Sider 1 SASI card */
device.option_add("grapplerplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */
}
static void apple2eaux_cards(device_slot_interface &device)

View File

@ -86,6 +86,7 @@
#include "bus/a2bus/a2memexp.h"
#include "bus/a2bus/a2midi.h"
#include "bus/a2bus/a2mockingboard.h"
#include "bus/a2bus/a2parprn.h"
#include "bus/a2bus/a2pic.h"
#include "bus/a2bus/a2sam.h"
#include "bus/a2bus/a2scsi.h"
@ -4572,7 +4573,9 @@ static void apple2_cards(device_slot_interface &device)
device.option_add("ultraterm", A2BUS_ULTRATERM); /* Videx UltraTerm (original) */
device.option_add("ultratermenh", A2BUS_ULTRATERMENH); /* Videx UltraTerm (enhanced //e) */
device.option_add("aevm80", A2BUS_AEVIEWMASTER80); /* Applied Engineering ViewMaster 80 */
device.option_add("parallel", A2BUS_PIC); /* Apple Parallel Interface Card */
device.option_add("parprn", A2BUS_PARPRN); /* Apple II Parallel Printer Interface Card */
device.option_add("parallel", A2BUS_PIC); /* Apple II Parallel Interface Card */
device.option_add("grapplerplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */
device.option_add("corvus", A2BUS_CORVUS); /* Corvus flat-cable HDD interface (see notes in a2corvus.c) */
device.option_add("mcms1", A2BUS_MCMS1); /* Mountain Computer Music System, card 1 of 2 */
device.option_add("mcms2", A2BUS_MCMS2); /* Mountain Computer Music System, card 2 of 2. must be in card 1's slot + 1! */
@ -4594,7 +4597,6 @@ static void apple2_cards(device_slot_interface &device)
device.option_add("uthernet", A2BUS_UTHERNET); /* A2RetroSystems Uthernet card */
device.option_add("sider2", A2BUS_SIDER2); /* Advanced Tech Systems / First Class Peripherals Sider 2 SASI card */
device.option_add("sider1", A2BUS_SIDER1); /* Advanced Tech Systems / First Class Peripherals Sider 1 SASI card */
device.option_add("grapplerplus", A2BUS_GRAPPLERPLUS); /* Orange Micro Grappler+ Printer Interface card */
}
void apple2gs_state::apple2gs(machine_config &config)

View File

@ -32,9 +32,8 @@
#include "input_common.h"
#include "input_windows.h"
//============================================================
// MACROS
//============================================================
namespace {
// Typedefs for dynamically loaded functions
typedef UINT (WINAPI *get_rawinput_device_list_ptr)(PRAWINPUTDEVICELIST, PUINT, UINT);
@ -48,17 +47,24 @@ private:
HKEY m_key;
public:
safe_regkey()
: m_key(nullptr)
safe_regkey() : m_key(nullptr) { }
safe_regkey(safe_regkey const &) = delete;
safe_regkey(safe_regkey &&key) : m_key(key.m_key) { key.m_key = nullptr; }
explicit safe_regkey(HKEY key) : m_key(key) { }
~safe_regkey() { close(); }
safe_regkey &operator=(safe_regkey const &) = delete;
safe_regkey &operator=(safe_regkey &&key)
{
close();
m_key = key.m_key;
key.m_key = nullptr;
return *this;
}
explicit safe_regkey(HKEY key)
: m_key(key)
{
}
bool valid() const { return m_key != nullptr; }
explicit operator bool() const { return m_key != nullptr; }
void close()
{
@ -69,70 +75,68 @@ public:
}
}
~safe_regkey()
operator HKEY() const { return m_key; }
safe_regkey open(std::wstring const &subkey) const { return open(m_key, subkey); }
std::wstring enum_key(int index) const
{
close();
WCHAR keyname[MAX_PATH];
DWORD namelen = MAX_PATH;
if (RegEnumKeyEx(m_key, index, keyname, &namelen, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
return std::wstring(keyname, namelen);
else
return std::wstring();
}
operator HKEY() const { return m_key; }
std::wstring query_string(WCHAR const *path) const
{
// first query to get the length
DWORD datalen;
if (RegQueryValueExW(m_key, path, nullptr, nullptr, nullptr, &datalen) != ERROR_SUCCESS)
return std::wstring();
// allocate a buffer
auto buffer = std::make_unique<WCHAR []>((datalen + (sizeof(WCHAR) * 2) - 1) / sizeof(WCHAR));
// now get the actual data
if (RegQueryValueExW(m_key, path, nullptr, nullptr, reinterpret_cast<LPBYTE>(buffer.get()), &datalen) != ERROR_SUCCESS)
return std::wstring();
buffer[datalen / sizeof(WCHAR)] = 0;
return std::wstring(buffer.get());
}
template <typename T> void foreach_subkey(T &&action) const
{
std::wstring name;
for (int i = 0; ; i++)
{
name = enum_key(i);
if (name.empty())
break;
safe_regkey const subkey = open(name);
if (!subkey)
break;
bool const shouldcontinue = action(subkey);
if (!shouldcontinue)
break;
}
}
static safe_regkey open(HKEY basekey, std::wstring const &subkey)
{
HKEY key(nullptr);
if (RegOpenKeyEx(basekey, subkey.c_str(), 0, KEY_READ, &key) == ERROR_SUCCESS)
return safe_regkey(key);
else
return safe_regkey();
}
};
//============================================================
// reg_open_key
//============================================================
static safe_regkey reg_open_key(HKEY basekey, const std::wstring &subkey)
{
HKEY key;
if (RegOpenKeyEx(basekey, subkey.c_str(), 0, KEY_READ, &key) == ERROR_SUCCESS)
return safe_regkey(key);
return safe_regkey();
}
//============================================================
// reg_enum_key
//============================================================
static std::wstring reg_enum_key(HKEY key, int index)
{
WCHAR keyname[MAX_PATH];
DWORD namelen;
if (RegEnumKeyEx(key, index, keyname, &namelen, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
return std::wstring(keyname, namelen);
return std::wstring();
}
//============================================================
// reg_query_string
//============================================================
static std::wstring reg_query_string(HKEY key, const TCHAR *path)
{
DWORD datalen;
LONG result;
// first query to get the length
result = RegQueryValueEx(key, path, nullptr, nullptr, nullptr, &datalen);
if (result != ERROR_SUCCESS)
return std::wstring();
// allocate a buffer
auto buffer = std::make_unique<TCHAR[]>(datalen + sizeof(TCHAR));
buffer[datalen / sizeof(TCHAR)] = 0;
// now get the actual data
result = RegQueryValueEx(key, path, nullptr, nullptr, reinterpret_cast<LPBYTE>(buffer.get()), &datalen);
if (result == ERROR_SUCCESS)
return std::wstring(buffer.get());
// otherwise return an empty string
return std::wstring();
}
static std::wstring trim_prefix(const std::wstring &devicename)
std::wstring trim_prefix(const std::wstring &devicename)
{
// remove anything prior to the final semicolon
auto semicolon_index = devicename.find_last_of(';');
@ -142,22 +146,22 @@ static std::wstring trim_prefix(const std::wstring &devicename)
return devicename;
}
static std::wstring compute_device_regpath(const std::wstring &name)
std::wstring compute_device_regpath(const std::wstring &name)
{
static const std::wstring basepath(L"SYSTEM\\CurrentControlSet\\Enum\\");
// allocate a temporary string and concatenate the base path plus the name
auto regpath_buffer = std::make_unique<TCHAR[]>(basepath.length() + 1 + name.length());
auto regpath_buffer = std::make_unique<WCHAR []>(basepath.length() + 1 + name.length());
wcscpy(regpath_buffer.get(), basepath.c_str());
WCHAR * chdst = regpath_buffer.get() + basepath.length();
WCHAR *chdst = regpath_buffer.get() + basepath.length();
// convert all # to \ in the name
for (int i = 4; i < name.length(); i++)
*chdst++ = (name[i] == '#') ? '\\' : name[i];
*chdst++ = (name[i] == '#') ? L'\\' : name[i];
*chdst = 0;
// remove the final chunk
chdst = wcsrchr(regpath_buffer.get(), '\\');
chdst = wcsrchr(regpath_buffer.get(), L'\\');
if (chdst == nullptr)
return std::wstring();
@ -166,15 +170,15 @@ static std::wstring compute_device_regpath(const std::wstring &name)
return std::wstring(regpath_buffer.get());
}
static std::wstring improve_name_from_base_path(const std::wstring &regpath, bool *hid)
std::wstring improve_name_from_base_path(const std::wstring &regpath, bool *hid)
{
// now try to open the registry key
auto device_key = reg_open_key(HKEY_LOCAL_MACHINE, regpath);
if (!device_key.valid())
auto device_key = safe_regkey::open(HKEY_LOCAL_MACHINE, regpath);
if (!device_key)
return std::wstring();
// fetch the device description; if it exists, we are finished
auto regstring = reg_query_string(device_key, L"DeviceDesc");
auto regstring = device_key.query_string(L"DeviceDesc");
if (!regstring.empty())
return trim_prefix(regstring);
@ -183,25 +187,7 @@ static std::wstring improve_name_from_base_path(const std::wstring &regpath, boo
return std::wstring();
}
static void foreach_subkey(HKEY key, std::function<bool(HKEY)> action)
{
for (int i = 0; ; i++)
{
std::wstring name = reg_enum_key(key, i);
if (name.empty())
break;
safe_regkey subkey = reg_open_key(key, name);
if (!subkey.valid())
break;
bool shouldcontinue = action(subkey);
if (!shouldcontinue)
break;
}
}
static std::wstring improve_name_from_usb_path(const std::wstring &regpath)
std::wstring improve_name_from_usb_path(const std::wstring &regpath)
{
static const std::wstring usbbasepath(L"SYSTEM\\CurrentControlSet\\Enum\\USB");
@ -213,31 +199,33 @@ static std::wstring improve_name_from_usb_path(const std::wstring &regpath)
std::wstring parentid = regpath.substr(last_slash_index + 1);
// open the USB key
auto usb_key = reg_open_key(HKEY_LOCAL_MACHINE, usbbasepath);
if (!usb_key.valid())
auto usb_key = safe_regkey::open(HKEY_LOCAL_MACHINE, usbbasepath);
if (!usb_key)
return std::wstring();
std::wstring regstring;
foreach_subkey(usb_key, [&regstring, &parentid](HKEY subkey)
{
foreach_subkey(subkey, [&regstring, &parentid](HKEY endkey)
{
std::wstring endparentid = reg_query_string(endkey, L"ParentIdPrefix");
usb_key.foreach_subkey(
[&regstring, &parentid] (safe_regkey const &subkey)
{
subkey.foreach_subkey(
[&regstring, &parentid] (safe_regkey const &endkey)
{
std::wstring endparentid = endkey.query_string(L"ParentIdPrefix");
// This key doesn't have a ParentIdPrefix
if (endparentid.empty())
return true;
// This key doesn't have a ParentIdPrefix
if (endparentid.empty())
return true;
// do we have a match?
if (parentid.find(endparentid) == 0)
regstring = reg_query_string(endkey, L"DeviceDesc");
// do we have a match?
if (parentid.find(endparentid) == 0)
regstring = endkey.query_string(L"DeviceDesc");
return regstring.empty();
});
return regstring.empty();
});
return regstring.empty();
});
return regstring.empty();
});
return trim_prefix(regstring);
}
@ -246,7 +234,7 @@ static std::wstring improve_name_from_usb_path(const std::wstring &regpath)
// rawinput_device_improve_name
//============================================================
static std::wstring rawinput_device_improve_name(const std::wstring &name)
std::wstring rawinput_device_improve_name(const std::wstring &name)
{
// The RAW name received is formatted as:
// \??\type-id#hardware-id#instance-id#{DeviceClasses-id}
@ -283,12 +271,11 @@ static std::wstring rawinput_device_improve_name(const std::wstring &name)
class rawinput_device : public event_based_device<RAWINPUT>
{
private:
HANDLE m_handle;
HANDLE m_handle = nullptr;
public:
rawinput_device(running_machine& machine, const char *name, const char *id, input_device_class deviceclass, input_module& module)
: event_based_device(machine, name, id, deviceclass, module),
m_handle(nullptr)
rawinput_device(running_machine &machine, const char *name, const char *id, input_device_class deviceclass, input_module &module) :
event_based_device(machine, name, id, deviceclass, module)
{
}
@ -305,9 +292,9 @@ class rawinput_keyboard_device : public rawinput_device
public:
keyboard_state keyboard;
rawinput_keyboard_device(running_machine& machine, const char *name, const char *id, input_module& module)
: rawinput_device(machine, name, id, DEVICE_CLASS_KEYBOARD, module),
keyboard({{0}})
rawinput_keyboard_device(running_machine &machine, const char *name, const char *id, input_module &module) :
rawinput_device(machine, name, id, DEVICE_CLASS_KEYBOARD, module),
keyboard({{0}})
{
}
@ -341,9 +328,9 @@ private:
public:
mouse_state mouse;
rawinput_mouse_device(running_machine& machine, const char *name, const char *id, input_module& module)
: rawinput_device(machine, name, id, DEVICE_CLASS_MOUSE, module),
mouse({0})
rawinput_mouse_device(running_machine &machine, const char *name, const char *id, input_module &module) :
rawinput_device(machine, name, id, DEVICE_CLASS_MOUSE, module),
mouse({0})
{
}
@ -401,8 +388,8 @@ private:
public:
mouse_state lightgun;
rawinput_lightgun_device(running_machine& machine, const char *name, const char *id, input_module& module)
: rawinput_device(machine, name, id, DEVICE_CLASS_LIGHTGUN, module),
rawinput_lightgun_device(running_machine &machine, const char *name, const char *id, input_module &module) :
rawinput_device(machine, name, id, DEVICE_CLASS_LIGHTGUN, module),
lightgun({0})
{
}
@ -456,19 +443,14 @@ class rawinput_module : public wininput_module
{
private:
osd::dynamic_module::ptr m_user32_dll;
get_rawinput_device_list_ptr get_rawinput_device_list;
get_rawinput_data_ptr get_rawinput_data;
get_rawinput_device_info_ptr get_rawinput_device_info;
register_rawinput_devices_ptr register_rawinput_devices;
get_rawinput_device_list_ptr get_rawinput_device_list = nullptr;
get_rawinput_data_ptr get_rawinput_data = nullptr;
get_rawinput_device_info_ptr get_rawinput_device_info = nullptr;
register_rawinput_devices_ptr register_rawinput_devices = nullptr;
std::mutex m_module_lock;
public:
rawinput_module(const char *type, const char* name)
: wininput_module(type, name),
get_rawinput_device_list(nullptr),
get_rawinput_data(nullptr),
get_rawinput_device_info(nullptr),
register_rawinput_devices(nullptr)
rawinput_module(const char *type, const char *name) : wininput_module(type, name)
{
}
@ -481,13 +463,7 @@ public:
get_rawinput_device_info = m_user32_dll->bind<get_rawinput_device_info_ptr>("GetRawInputDeviceInfoW");
register_rawinput_devices = m_user32_dll->bind<register_rawinput_devices_ptr>("RegisterRawInputDevices");
if (!get_rawinput_device_list || !get_rawinput_data ||
!get_rawinput_device_info || !register_rawinput_devices )
{
return false;
}
return true;
return get_rawinput_device_list && get_rawinput_data && get_rawinput_device_info && register_rawinput_devices;
}
void input_init(running_machine &machine) override
@ -501,7 +477,7 @@ public:
return;
auto rawinput_devices = std::make_unique<RAWINPUTDEVICELIST[]>(device_count);
if ((*get_rawinput_device_list)(rawinput_devices.get(), &device_count, sizeof(RAWINPUTDEVICELIST)) == -1)
if ((*get_rawinput_device_list)(rawinput_devices.get(), &device_count, sizeof(RAWINPUTDEVICELIST)) == UINT(-1))
return;
// iterate backwards through devices; new devices are added at the head
@ -533,7 +509,7 @@ public:
}
protected:
virtual void add_rawinput_device(running_machine& machine, RAWINPUTDEVICELIST * device) = 0;
virtual void add_rawinput_device(running_machine &machine, RAWINPUTDEVICELIST *device) = 0;
virtual USHORT usagepage() = 0;
virtual USHORT usage() = 0;
@ -550,16 +526,15 @@ protected:
}
template<class TDevice>
TDevice* create_rawinput_device(running_machine &machine, PRAWINPUTDEVICELIST rawinputdevice)
TDevice *create_rawinput_device(running_machine &machine, PRAWINPUTDEVICELIST rawinputdevice)
{
TDevice* devinfo;
UINT name_length = 0;
// determine the length of the device name, allocate it, and fetch it if not nameless
UINT name_length = 0;
if ((*get_rawinput_device_info)(rawinputdevice->hDevice, RIDI_DEVICENAME, nullptr, &name_length) != 0)
return nullptr;
std::unique_ptr<TCHAR[]> tname = std::make_unique<TCHAR[]>(name_length + 1);
if (name_length > 1 && (*get_rawinput_device_info)(rawinputdevice->hDevice, RIDI_DEVICENAME, tname.get(), &name_length) == -1)
std::unique_ptr<WCHAR []> tname = std::make_unique<WCHAR []>(name_length + 1);
if (name_length > 1 && (*get_rawinput_device_info)(rawinputdevice->hDevice, RIDI_DEVICENAME, tname.get(), &name_length) == UINT(-1))
return nullptr;
// if this is an RDP name, skip it
@ -575,7 +550,7 @@ protected:
// set device id to raw input name
std::string utf8_id = osd::text::from_wstring(tname.get());
devinfo = devicelist()->create_device<TDevice>(machine, utf8_name.c_str(), utf8_id.c_str(), *this);
TDevice *devinfo = devicelist()->create_device<TDevice>(machine, utf8_name.c_str(), utf8_id.c_str(), *this);
// Add the handle
devinfo->set_handle(rawinputdevice->hDevice);
@ -583,7 +558,7 @@ protected:
return devinfo;
}
bool handle_input_event(input_event eventid, void* eventdata) override
bool handle_input_event(input_event eventid, void *eventdata) override
{
// Only handle raw input data
if (!input_enabled() || eventid != INPUT_EVENT_RAWINPUT)
@ -622,15 +597,18 @@ protected:
auto *input = reinterpret_cast<RAWINPUT*>(data);
// find the device in the list and update
auto target_device = std::find_if(devicelist()->begin(), devicelist()->end(), [input](auto &device)
{
auto devinfo = dynamic_cast<rawinput_device*>(device.get());
return devinfo != nullptr && input->header.hDevice == devinfo->device_handle();
});
auto target_device = std::find_if(
devicelist()->begin(),
devicelist()->end(),
[input] (auto const &device)
{
auto devinfo = dynamic_cast<rawinput_device *>(device.get());
return devinfo && (input->header.hDevice == devinfo->device_handle());
});
if (target_device != devicelist()->end())
{
static_cast<rawinput_device*>((*target_device).get())->queue_events(input, 1);
static_cast<rawinput_device *>((*target_device).get())->queue_events(input, 1);
return true;
}
}
@ -654,7 +632,8 @@ public:
protected:
USHORT usagepage() override { return 1; }
USHORT usage() override { return 6; }
void add_rawinput_device(running_machine& machine, RAWINPUTDEVICELIST * device) override
void add_rawinput_device(running_machine &machine, RAWINPUTDEVICELIST *device) override
{
// make sure this is a keyboard
if (device->dwType != RIM_TYPEKEYBOARD)
@ -671,12 +650,12 @@ protected:
for (int keynum = 0; keynum < MAX_KEYS; keynum++)
{
input_item_id itemid = table.map_di_scancode_to_itemid(keynum);
TCHAR keyname[100];
WCHAR keyname[100];
// generate the name
if (GetKeyNameText(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, ARRAY_LENGTH(keyname)) == 0)
_sntprintf(keyname, ARRAY_LENGTH(keyname), TEXT("Scan%03d"), keynum);
std::string name = osd::text::from_tstring(keyname);
if (GetKeyNameTextW(((keynum & 0x7f) << 16) | ((keynum & 0x80) << 17), keyname, ARRAY_LENGTH(keyname)) == 0)
_snwprintf(keyname, ARRAY_LENGTH(keyname), L"Scan%03d", keynum);
std::string name = osd::text::from_wstring(keyname);
// add the item to the device
devinfo->device()->add_item(name.c_str(), itemid, generic_button_get_state<std::uint8_t>, &devinfo->keyboard.state[keynum]);
@ -699,7 +678,8 @@ public:
protected:
USHORT usagepage() override { return 1; }
USHORT usage() override { return 2; }
void add_rawinput_device(running_machine& machine, RAWINPUTDEVICELIST * device) override
void add_rawinput_device(running_machine &machine, RAWINPUTDEVICELIST *device) override
{
// make sure this is a mouse
if (device->dwType != RIM_TYPEMOUSE)
@ -747,7 +727,8 @@ public:
protected:
USHORT usagepage() override { return 1; }
USHORT usage() override { return 2; }
void add_rawinput_device(running_machine& machine, RAWINPUTDEVICELIST * device) override
void add_rawinput_device(running_machine &machine, RAWINPUTDEVICELIST *device) override
{
// make sure this is a mouse
@ -781,6 +762,8 @@ protected:
}
};
} // anonymous namespace
#else
MODULE_NOT_SUPPORTED(keyboard_input_rawinput, OSD_KEYBOARDINPUT_PROVIDER, "rawinput")
MODULE_NOT_SUPPORTED(mouse_input_rawinput, OSD_MOUSEINPUT_PROVIDER, "rawinput")

View File

@ -40,14 +40,10 @@ struct mouse_state
class wininput_module : public input_module_base
{
protected:
bool m_global_inputs_enabled;
bool m_global_inputs_enabled = false;
public:
wininput_module(const char * type, const char * name)
: input_module_base(type, name),
m_global_inputs_enabled(false)
{
}
wininput_module(const char *type, const char *name) : input_module_base(type, name) { }
virtual ~wininput_module() { }