/* 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 . */ #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 #include #include 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 toSend; int schedId = -1; bool schedulerStarted = false; bool touch[2] {}; static constexpr int FRAME_CYCLES = SH4_MAIN_CLOCK / 60; }; std::unique_ptr touchscreen; void init() { touchscreen = std::make_unique(); } void term() { touchscreen.reset(); } void serialize(Serializer& ser) { if (touchscreen) touchscreen->serialize(ser); } void deserialize(Deserializer& deser) { if (touchscreen) touchscreen->deserialize(deser); } }