178 lines
4.0 KiB
C++
178 lines
4.0 KiB
C++
/*
|
|
Copyright 2023 flyinghead
|
|
|
|
This file is part of Flycast.
|
|
|
|
Flycast is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Flycast is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "touchscreen.h"
|
|
#include "hw/sh4/sh4_sched.h"
|
|
#include "hw/sh4/modules/modules.h"
|
|
#include "hw/maple/maple_cfg.h"
|
|
#include "hw/maple/maple_devs.h"
|
|
#include "input/gamepad.h"
|
|
#include "serialize.h"
|
|
|
|
#include <algorithm>
|
|
#include <deque>
|
|
#include <memory>
|
|
|
|
namespace touchscreen
|
|
{
|
|
|
|
//
|
|
// 837-14672 touchscreen sensor board
|
|
// used by Manic Panic Ghosts and Touch De Zunou
|
|
//
|
|
class TouchscreenPipe final : public SerialPort::Pipe
|
|
{
|
|
public:
|
|
TouchscreenPipe()
|
|
{
|
|
schedId = sh4_sched_register(0, schedCallback, this);
|
|
SCIFSerialPort::Instance().setPipe(this);
|
|
}
|
|
|
|
~TouchscreenPipe() override
|
|
{
|
|
SCIFSerialPort::Instance().setPipe(nullptr);
|
|
sh4_sched_unregister(schedId);
|
|
}
|
|
|
|
void write(u8 data) override
|
|
{
|
|
if (data == 0x39)
|
|
{
|
|
// status request
|
|
constexpr u8 status[] = { 0xaa, 0x39, 0 };
|
|
send(status, sizeof(status));
|
|
}
|
|
if (!schedulerStarted)
|
|
{
|
|
sh4_sched_request(schedId, FRAME_CYCLES);
|
|
schedulerStarted = true;
|
|
}
|
|
}
|
|
|
|
int available() override {
|
|
return toSend.size();
|
|
}
|
|
|
|
u8 read() override
|
|
{
|
|
u8 data = toSend.front();
|
|
toSend.pop_front();
|
|
return data;
|
|
}
|
|
|
|
void serialize(Serializer& ser)
|
|
{
|
|
ser << schedulerStarted;
|
|
sh4_sched_serialize(ser, schedId);
|
|
ser << (int)toSend.size();
|
|
for (u8 b : toSend)
|
|
ser << b;
|
|
}
|
|
|
|
void deserialize(Deserializer& deser)
|
|
{
|
|
deser >> schedulerStarted;
|
|
sh4_sched_deserialize(deser, schedId);
|
|
int size;
|
|
deser >> size;
|
|
toSend.resize(size);
|
|
for (int i = 0; i < size; i++)
|
|
deser >> toSend[i];
|
|
}
|
|
|
|
private:
|
|
void send(const u8 *msg, int size)
|
|
{
|
|
if (toSend.size() >= 32)
|
|
return;
|
|
toSend.insert(toSend.end(), &msg[0], &msg[size]);
|
|
toSend.push_back(calcChecksum(msg, size));
|
|
SCIFSerialPort::Instance().updateStatus();
|
|
}
|
|
|
|
u8 calcChecksum(const u8 *data, int size)
|
|
{
|
|
u8 c = 0;
|
|
for (int i = 0; i < size; i++)
|
|
c += data[i];
|
|
return 256 - c;
|
|
}
|
|
|
|
static int schedCallback(int tag, int cycles, int jitter, void *arg)
|
|
{
|
|
TouchscreenPipe *instance = (TouchscreenPipe *)arg;
|
|
u32 pack[2];
|
|
for (size_t i = 0; i < std::size(pack); i++)
|
|
{
|
|
int x = std::clamp(mapleInputState[i].absPos.x, 0, 1023);
|
|
int y = std::clamp(mapleInputState[i].absPos.y, 0, 1023);
|
|
#ifdef LIBRETRO
|
|
int hit = (mapleInputState[i].kcode & NAOMI_BTN0_KEY) == 0;
|
|
int charge = (mapleInputState[i].kcode & NAOMI_BTN1_KEY) == 0;
|
|
#else
|
|
int hit = (mapleInputState[i].kcode & DC_BTN_A) == 0;
|
|
int charge = (mapleInputState[i].kcode & DC_BTN_B) == 0;
|
|
#endif
|
|
// touches require bits 20, 21 and 22
|
|
// drag needs bit 22 off
|
|
// bit 23 is charge
|
|
pack[i] = (charge << 23) | (hit << 21) | (hit << 20) | (y << 10) | x;
|
|
if (!instance->touch[i])
|
|
pack[i] |= hit << 22;
|
|
instance->touch[i] = hit;
|
|
}
|
|
u8 msg[] = { 0xaa, 0x10,
|
|
u8(pack[0] >> 16), u8(pack[0] >> 8), u8(pack[0]),
|
|
u8(pack[1] >> 16), u8(pack[1] >> 8), u8(pack[1]) };
|
|
instance->send(msg, sizeof(msg));
|
|
|
|
return FRAME_CYCLES;
|
|
}
|
|
|
|
std::deque<u8> toSend;
|
|
int schedId = -1;
|
|
bool schedulerStarted = false;
|
|
bool touch[2] {};
|
|
|
|
static constexpr int FRAME_CYCLES = SH4_MAIN_CLOCK / 60;
|
|
};
|
|
|
|
std::unique_ptr<TouchscreenPipe> touchscreen;
|
|
|
|
void init() {
|
|
touchscreen = std::make_unique<TouchscreenPipe>();
|
|
}
|
|
|
|
void term() {
|
|
touchscreen.reset();
|
|
}
|
|
|
|
void serialize(Serializer& ser)
|
|
{
|
|
if (touchscreen)
|
|
touchscreen->serialize(ser);
|
|
}
|
|
void deserialize(Deserializer& deser)
|
|
{
|
|
if (touchscreen)
|
|
touchscreen->deserialize(deser);
|
|
}
|
|
|
|
}
|