flycast/core/network/alienfnt_modem.cpp

161 lines
3.1 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 "alienfnt_modem.h"
#include "hw/sh4/sh4_sched.h"
#include "hw/sh4/modules/modules.h"
#include "picoppp.h"
#include <vector>
#include <deque>
#include <memory>
struct ModemEmu : public SerialPort::Pipe
{
ModemEmu() {
SCIFSerialPort::Instance().setPipe(this);
schedId = sh4_sched_register(0, schedCallback);
}
~ModemEmu() {
sh4_sched_unregister(schedId);
stop_pico();
SCIFSerialPort::Instance().setPipe(nullptr);
}
u8 read() override
{
if (!toSend.empty())
{
char c = toSend.front();
toSend.pop_front();
return c;
}
else if (dataMode)
return read_pico();
else
return 0;
}
int available() override
{
if (!toSend.empty())
return toSend.size();
else if (dataMode)
return pico_available();
else
return 0;
}
void write(u8 data) override
{
if (dataMode)
{
if (pluses == 3)
{
if (sh4_sched_now64() - plusTime >= SH4_MAIN_CLOCK)
{
dataMode = false;
send("OK");
recvBuf.push_back(data);
}
else
{
write_pico('+');
write_pico('+');
write_pico('+');
write_pico(data);
}
pluses = 0;
plusTime = 0;
}
else if (data == '+')
{
if (++pluses == 3)
plusTime = sh4_sched_now64();
}
else
{
while (pluses > 0) {
write_pico('+');
pluses--;
}
write_pico(data);
}
}
else if (data == '\r' || data == '\n')
handleCmd();
else
recvBuf.push_back(data);
}
private:
void handleCmd()
{
if (recvBuf.empty())
return;
std::string line(recvBuf.begin(), recvBuf.end());
recvBuf.clear();
if (line.substr(0, 4) == "ATDT") {
send("CONNECT 14400");
start_pico();
dataMode = true;
sh4_sched_request(schedId, SH4_MAIN_CLOCK / 60);
}
if (line.substr(0, 3) == "ATH")
{
stop_pico();
send("OK");
}
else if (line.substr(0, 2) == "AT")
send("OK");
else if (!line.empty())
send("ERROR");
}
void send(const std::string& l)
{
toSend.insert(toSend.end(), l.begin(), l.end());
toSend.push_back('\n');
SCIFSerialPort::Instance().updateStatus();
}
static int schedCallback(int tag, int cycles, int lag, void *arg)
{
SCIFSerialPort::Instance().updateStatus();
return SH4_MAIN_CLOCK / 60;
}
std::deque<char> toSend;
std::vector<char> recvBuf;
int schedId = -1;
int pluses = 0;
u64 plusTime = 0;
bool dataMode = false;
};
static std::unique_ptr<ModemEmu> modemEmu;
void serialModemInit() {
modemEmu = std::make_unique<ModemEmu>();
}
void serialModemTerm() {
modemEmu.reset();
}