308 lines
7.2 KiB
C++
308 lines
7.2 KiB
C++
/*
|
|
Dreamcast serial port.
|
|
This is missing most of the functionality, but works for KOS (And thats all that uses it)
|
|
*/
|
|
#include <cerrno>
|
|
#include <cstdlib>
|
|
#include <fcntl.h>
|
|
#ifndef _WIN32
|
|
#include <unistd.h>
|
|
#include <sys/ioctl.h>
|
|
#else
|
|
#include <windows.h>
|
|
#include <io.h>
|
|
#endif
|
|
#include "types.h"
|
|
#include "hw/sh4/sh4_mmr.h"
|
|
#include "hw/sh4/sh4_interrupts.h"
|
|
#include "cfg/option.h"
|
|
#include "modules.h"
|
|
|
|
static SerialPipe *serialPipe;
|
|
|
|
SCIF_SCFSR2_type SCIF_SCFSR2;
|
|
SCIF_SCSCR2_type SCIF_SCSCR2;
|
|
|
|
/*
|
|
//SCIF SCSMR2 0xFFE80000 0x1FE80000 16 0x0000 0x0000 Held Held Pclk
|
|
SCSMR2_type SCIF_SCSMR2;
|
|
|
|
//SCIF SCBRR2 0xFFE80004 0x1FE80004 8 0xFF 0xFF Held Held Pclk
|
|
u8 SCIF_SCBRR2;
|
|
|
|
//SCIF SCSCR2 0xFFE80008 0x1FE80008 16 0x0000 0x0000 Held Held Pclk
|
|
SCSCR2_type SCIF_SCSCR2;
|
|
|
|
//SCIF SCFTDR2 0xFFE8000C 0x1FE8000C 8 Undefined Undefined Held Held Pclk
|
|
u8 SCIF_SCFTDR2;
|
|
|
|
//SCIF SCFSR2 0xFFE80010 0x1FE80010 16 0x0060 0x0060 Held Held Pclk
|
|
SCSCR2_type SCIF_SCFSR2;
|
|
|
|
//SCIF SCFRDR2 0xFFE80014 0x1FE80014 8 Undefined Undefined Held Held Pclk
|
|
//Read OLNY
|
|
u8 SCIF_SCFRDR2;
|
|
|
|
//SCIF SCFCR2 0xFFE80018 0x1FE80018 16 0x0000 0x0000 Held Held Pclk
|
|
SCFCR2_type SCIF_SCFCR2;
|
|
|
|
//Read OLNY
|
|
//SCIF SCFDR2 0xFFE8001C 0x1FE8001C 16 0x0000 0x0000 Held Held Pclk
|
|
SCFDR2_type SCIF_SCFDR2;
|
|
|
|
//SCIF SCSPTR2 0xFFE80020 0x1FE80020 16 0x0000 0x0000 Held Held Pclk
|
|
SCSPTR2_type SCIF_SCSPTR2;
|
|
|
|
//SCIF SCLSR2 0xFFE80024 0x1FE80024 16 0x0000 0x0000 Held Held Pclk
|
|
SCLSR2_type SCIF_SCLSR2;
|
|
*/
|
|
|
|
static void Serial_UpdateInterrupts()
|
|
{
|
|
InterruptPend(sh4_SCIF_TXI, SCIF_SCFSR2.TDFE);
|
|
InterruptMask(sh4_SCIF_TXI, SCIF_SCSCR2.TIE);
|
|
|
|
InterruptPend(sh4_SCIF_RXI, SCIF_SCFSR2.RDF);
|
|
InterruptMask(sh4_SCIF_RXI, SCIF_SCSCR2.RIE);
|
|
}
|
|
|
|
static void SerialWrite(u32 addr, u32 data)
|
|
{
|
|
//DEBUG_LOG(COMMON, "serial %02x", data);
|
|
if (serialPipe != nullptr)
|
|
serialPipe->write(data);
|
|
|
|
SCIF_SCFSR2.TDFE = 1;
|
|
SCIF_SCFSR2.TEND = 1;
|
|
|
|
Serial_UpdateInterrupts();
|
|
}
|
|
|
|
//SCIF_SCFSR2 read
|
|
static u32 ReadSerialStatus(u32 addr)
|
|
{
|
|
if (serialPipe != nullptr)
|
|
{
|
|
constexpr int trigLevels[] { 1, 4, 8, 14 };
|
|
int avail = serialPipe->available();
|
|
|
|
if (avail >= trigLevels[SCIF_SCFCR2.RTRG1 * 2 + SCIF_SCFCR2.RTRG0])
|
|
return SCIF_SCFSR2.full | 3; // RDF | DR
|
|
else if (avail >= 1)
|
|
return SCIF_SCFSR2.full | 1; // DR
|
|
}
|
|
return SCIF_SCFSR2.full;
|
|
}
|
|
|
|
static void WriteSerialStatus(u32 addr,u32 data)
|
|
{
|
|
if (!SCIF_SCFSR2.BRK)
|
|
data &= ~0x10;
|
|
|
|
SCIF_SCFSR2.full = data & 0x00f0;
|
|
|
|
SCIF_SCFSR2.TDFE = 1;
|
|
SCIF_SCFSR2.TEND = 1;
|
|
|
|
Serial_UpdateInterrupts();
|
|
}
|
|
|
|
//SCIF_SCFDR2 - 16b
|
|
static u32 Read_SCFDR2(u32 addr)
|
|
{
|
|
if (serialPipe != nullptr && serialPipe->available() > 0)
|
|
return 1; // TODO more?
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
//SCIF_SCFRDR2
|
|
static u32 ReadSerialData(u32 addr)
|
|
{
|
|
u8 data = 0;
|
|
if (serialPipe != nullptr)
|
|
data = serialPipe->read();
|
|
|
|
return data;
|
|
}
|
|
|
|
//SCSCR2
|
|
|
|
static u32 SCSCR2_read(u32 addr)
|
|
{
|
|
return SCIF_SCSCR2.full;
|
|
}
|
|
|
|
static void SCSCR2_write(u32 addr, u32 data)
|
|
{
|
|
SCIF_SCSCR2.full = data & 0x00fa;
|
|
|
|
Serial_UpdateInterrupts();
|
|
}
|
|
|
|
struct PTYPipe : public SerialPipe
|
|
{
|
|
void write(u8 data) override {
|
|
if (config::SerialConsole)
|
|
::write(tty, &data, 1);
|
|
}
|
|
|
|
int available() override {
|
|
int count = 0;
|
|
#if defined(__unix__) || defined(__APPLE__)
|
|
if (config::SerialConsole && tty != 1)
|
|
ioctl(tty, FIONREAD, &count);
|
|
#endif
|
|
return count;
|
|
}
|
|
|
|
u8 read() override {
|
|
u8 data = 0;
|
|
if (tty != 1)
|
|
::read(tty, &data, 1);
|
|
return data;
|
|
}
|
|
|
|
void init()
|
|
{
|
|
if (config::SerialConsole && config::SerialPTY && tty == 1)
|
|
{
|
|
#if defined(__unix__) || defined(__APPLE__)
|
|
tty = open("/dev/ptmx", O_RDWR | O_NDELAY | O_NOCTTY | O_NONBLOCK);
|
|
if (tty < 0)
|
|
{
|
|
ERROR_LOG(BOOT, "Cannot open /dev/ptmx: errno %d", errno);
|
|
tty = 1;
|
|
}
|
|
else
|
|
{
|
|
grantpt(tty);
|
|
unlockpt(tty);
|
|
NOTICE_LOG(BOOT, "Pseudoterminal is at %s", ptsname(tty));
|
|
}
|
|
#elif defined(_WIN32)
|
|
if (AllocConsole())
|
|
{
|
|
SetConsoleTitle(TEXT("Flycast Serial Output"));
|
|
|
|
// Pipe stdout
|
|
HANDLE hStd = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
tty = _open_osfhandle((intptr_t)hStd, _O_TEXT);
|
|
_dup2(tty, fileno(stdout));
|
|
SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(fileno(stdout)));
|
|
}
|
|
else
|
|
{
|
|
ERROR_LOG(BOOT, "Cannot AllocConsole(): errno %d", GetLastError());
|
|
}
|
|
#endif
|
|
}
|
|
serial_setPipe(this);
|
|
}
|
|
|
|
void term()
|
|
{
|
|
if (tty != 1)
|
|
{
|
|
::close(tty);
|
|
tty = 1;
|
|
}
|
|
}
|
|
|
|
private:
|
|
int tty = 1;
|
|
};
|
|
static PTYPipe ptyPipe;
|
|
|
|
//Init term res
|
|
void serial_init()
|
|
{
|
|
// Serial Communication Interface with FIFO
|
|
|
|
//SCIF SCSMR2 0xFFE80000 0x1FE80000 16 0x0000 0x0000 Held Held Pclk
|
|
sh4_rio_reg_wmask<SCIF, SCIF_SCSMR2_addr, 0x007b>();
|
|
|
|
//SCIF SCBRR2 0xFFE80004 0x1FE80004 8 0xFF 0xFF Held Held Pclk
|
|
sh4_rio_reg8<SCIF, SCIF_SCBRR2_addr>();
|
|
|
|
//SCIF SCSCR2 0xFFE80008 0x1FE80008 16 0x0000 0x0000 Held Held Pclk
|
|
sh4_rio_reg(SCIF, SCIF_SCSCR2_addr, RIO_FUNC, SCSCR2_read, SCSCR2_write);
|
|
|
|
//Write only
|
|
//SCIF SCFTDR2 0xFFE8000C 0x1FE8000C 8 Undefined Undefined Held Held Pclk
|
|
sh4_rio_reg(SCIF, SCIF_SCFTDR2_addr, RIO_WF, nullptr, SerialWrite);
|
|
|
|
//SCIF SCFSR2 0xFFE80010 0x1FE80010 16 0x0060 0x0060 Held Held Pclk
|
|
sh4_rio_reg(SCIF, SCIF_SCFSR2_addr, RIO_FUNC, ReadSerialStatus, WriteSerialStatus);
|
|
|
|
//READ only
|
|
//SCIF SCFRDR2 0xFFE80014 0x1FE80014 8 Undefined Undefined Held Held Pclk
|
|
sh4_rio_reg(SCIF, SCIF_SCFRDR2_addr, RIO_RO_FUNC, ReadSerialData);
|
|
|
|
//SCIF SCFCR2 0xFFE80018 0x1FE80018 16 0x0000 0x0000 Held Held Pclk
|
|
sh4_rio_reg_wmask<SCIF, SCIF_SCFCR2_addr, 0x00ff>();
|
|
|
|
//Read only
|
|
//SCIF SCFDR2 0xFFE8001C 0x1FE8001C 16 0x0000 0x0000 Held Held Pclk
|
|
sh4_rio_reg(SCIF, SCIF_SCFDR2_addr, RIO_RO_FUNC, Read_SCFDR2);
|
|
|
|
//SCIF SCSPTR2 0xFFE80020 0x1FE80020 16 0x0000 0x0000 Held Held Pclk
|
|
sh4_rio_reg_wmask<SCIF, SCIF_SCSPTR2_addr, 0x00f3>();
|
|
|
|
//SCIF SCLSR2 0xFFE80024 0x1FE80024 16 0x0000 0x0000 Held Held Pclk
|
|
sh4_rio_reg_wmask<SCIF, SCIF_SCLSR2_addr, 1>();
|
|
|
|
// Serial Communication Interface
|
|
sh4_rio_reg8<SCI, SCI_SCSMR1_addr>();
|
|
sh4_rio_reg8<SCI, SCI_SCBRR1_addr>();
|
|
sh4_rio_reg8<SCI, SCI_SCSCR1_addr>();
|
|
sh4_rio_reg8<SCI, SCI_SCTDR1_addr>();
|
|
sh4_rio_reg_wmask<SCI, SCI_SCSSR1_addr, 0xf9>();
|
|
sh4_rio_reg(SCI, SCI_SCRDR1_addr, RIO_RO);
|
|
sh4_rio_reg_wmask<SCI, SCI_SCSPTR1_addr, 0x8f>();
|
|
}
|
|
|
|
void serial_reset(bool hard)
|
|
{
|
|
/*
|
|
SCIF SCSMR2 H'FFE8 0000 H'1FE8 0000 16 H'0000 H'0000 Held Held Pclk
|
|
SCIF SCBRR2 H'FFE8 0004 H'1FE8 0004 8 H'FF H'FF Held Held Pclk
|
|
SCIF SCSCR2 H'FFE8 0008 H'1FE8 0008 16 H'0000 H'0000 Held Held Pclk
|
|
SCIF SCFTDR2 H'FFE8 000C H'1FE8 000C 8 Undefined Undefined Held Held Pclk
|
|
SCIF SCFSR2 H'FFE8 0010 H'1FE8 0010 16 H'0060 H'0060 Held Held Pclk
|
|
SCIF SCFRDR2 H'FFE8 0014 H'1FE8 0014 8 Undefined Undefined Held Held Pclk
|
|
SCIF SCFCR2 H'FFE8 0018 H'1FE8 0018 16 H'0000 H'0000 Held Held Pclk
|
|
SCIF SCFDR2 H'FFE8 001C H'1FE8 001C 16 H'0000 H'0000 Held Held Pclk
|
|
SCIF SCSPTR2 H'FFE8 0020 H'1FE8 0020 16 H'0000*2 H'0000*2 Held Held Pclk
|
|
SCIF SCLSR2 H'FFE8 0024 H'1FE8 0024 16 H'0000 H'0000 Held Held Pclk
|
|
*/
|
|
SCIF_SCSMR2.full=0x0000;
|
|
SCIF_SCBRR2=0xFF;
|
|
SCIF_SCFSR2.full=0x060;
|
|
SCIF_SCFCR2.full=0x000;
|
|
SCIF_SCSPTR2.full=0x000;
|
|
SCIF_SCLSR2.full=0x000;
|
|
SCIF_SCSCR2.full = 0;
|
|
|
|
SCI_SCSMR1 = 0;
|
|
SCI_SCBRR1 = 0xff;
|
|
SCI_SCSCR1 = 0;
|
|
SCI_SCTDR1 = 0xff;
|
|
SCI_SCSSR1 = 0x84;
|
|
SCI_SCRDR1 = 0;
|
|
SCI_SCSPTR1 = 0;
|
|
|
|
if (hard)
|
|
ptyPipe.init();
|
|
}
|
|
|
|
void serial_term()
|
|
{
|
|
ptyPipe.term();
|
|
}
|
|
|
|
void serial_setPipe(SerialPipe *pipe)
|
|
{
|
|
serialPipe = pipe;
|
|
}
|