static uae_u8 *sermap_data;
static bool sermap_master;
static bool sermap_enabled;
+static bool serloop_enabled;
+static bool serempty_enabled;
+static uae_u8 serstatus;
#define SER_MEMORY_MAPPING _T("WinUAE_Serial")
#endif
static int data_in_serdat; /* new data written to SERDAT */
+static evt_t data_in_serdat_delay;
+static evt_t serper_tx_evt;
+static int serper_tx_per, serper_tx_bits, serper_tx_cycles;
static int data_in_serdatr; /* new data received */
static int data_in_sershift; /* data transferred from SERDAT to shift register */
static int break_in_serdatr; /* break state */
static int break_delay;
static uae_u16 serdatshift; /* serial shift register */
+static int serdatshift_bits;
static uae_u16 serdatshift_masked; /* stop bit masked */
+static evt_t serdatshift_start;
static int ovrun;
static int dtr;
static int serial_period_hsyncs, serial_period_hsync_counter;
void serial_close (void);
uae_u16 serper, serdat, serdatr;
+static bool serper_set = false;
static const int allowed_baudrates[] =
-{ 0, 110, 300, 600, 1200, 2400, 4800, 9600, 14400,
-19200, 31400, 38400, 57600, 115200, 128000, 256000, -1 };
+{
+ 0, 110, 300, 600, 1200, 2400, 4800, 9600, 14400,
+ 19200, 31400, 38400, 57600, 115200, 128000, 256000, -1
+};
+
+static void sersend_end(uae_u32 v);
void SERPER (uae_u16 w)
{
int baud = 0, i, per;
+ int oldper = serper;
static int warned;
- if (serper == w) /* don't set baudrate if it's already ok */
+ if (serper == w && serper_set) /* don't set baudrate if it's already ok */
return;
+ serper_set = true;
ninebit = 0;
serper = w;
if (w & 0x8000)
ninebit = 1;
w &= 0x7fff;
- if (w < 13)
- w = 13;
-
per = w;
if (per == 0)
per = 1;
while (allowed_baudrates[i] >= 0 && per > allowed_baudrates[i] * 100 / 97)
i++;
baud = allowed_baudrates[i];
+ if (baud <= 0) {
+ baud = allowed_baudrates[1];
+ }
serial_period_hsyncs = (((serper & 0x7fff) + 1) * (1 + 8 + ninebit + 1 - 1)) / maxhpos;
if (serial_period_hsyncs <= 0)
serial_period_hsync_counter = 0;
- write_log (_T("SERIAL: period=%d, baud=%d, hsyncs=%d, bits=%d, PC=%x\n"), w, baud, serial_period_hsyncs, ninebit ? 9 : 8, M68K_GETPC);
+ if (!serloop_enabled || seriallog > 0) {
+ write_log(_T("SERIAL: period=%d, baud=%d, hsyncs=%d, bits=%d, PC=%x\n"), w, baud, serial_period_hsyncs, ninebit ? 9 : 8, M68K_GETPC);
+ }
if (ninebit)
baud *= 2;
baud = 115200;
serial_period_hsyncs = 1;
}
+
serial_recv_previous = -1;
serial_send_previous = -1;
+
#ifdef SERIAL_PORT
setbaud (baud);
#endif
+
+ // mid transmit period change
+ if ((serloop_enabled || serempty_enabled) && currprefs.cpu_memory_cycle_exact) {
+ evt_t c = get_cycles();
+ evt_t n = serper_tx_evt + serper_tx_cycles * CYCLE_UNIT;
+ if (n > c) {
+ int cycles_transmitted = (int)((c - serper_tx_evt) / CYCLE_UNIT);
+ serper_tx_cycles -= cycles_transmitted;
+ if (serper_tx_cycles >= 0) {
+ int serper_tx_cycles_mod = serper_tx_cycles % serper_tx_per;
+ serper_tx_cycles /= serper_tx_per;
+ serper_tx_per = (serper & 0x7fff) + 1;
+ serper_tx_cycles *= serper_tx_per;
+ serper_tx_cycles += serper_tx_cycles_mod;
+ serper_tx_evt = c;
+ event2_newevent_x_replace_exists(serper_tx_cycles, 0, sersend_end);
+ }
+ }
+ }
+
+}
+
+static void serial_rx_irq(void)
+{
+ // Data in receive buffer
+ data_in_serdatr = 1;
+ if (currprefs.cpu_memory_cycle_exact) {
+ INTREQ_INT(11, 9);
+ } else {
+ INTREQ_INT(11, 0);
+ }
+}
+
+void serial_rethink(void)
+{
+ if (!currprefs.cpu_memory_cycle_exact) {
+ if (data_in_serdatr) {
+ INTREQ_0(0x8000 | (1 << 11));
+ }
+ }
}
static TCHAR docharlog(int v)
serdatr |= 0x200;
else
serdatr |= 0x100;
- data_in_serdatr = 1;
serdatr_last_got = 0;
- serial_check_irq ();
+ serial_rx_irq();
#if SERIALDEBUG > 2
write_log (_T("SERIAL: received %02X (%c)\n"), serdatr & 0xff, dochar (serdatr));
#endif
}
}
- data_in_serdatr = 1;
serdatr_last_got = 0;
- serial_check_irq();
+ serial_rx_irq();
#if SERIALDEBUG > 2
write_log(_T("SERIAL: received %02X (%c)\n"), serdatr & 0xff, dochar(serdatr));
#endif
static void checksend(void)
{
- if (data_in_sershift != 1 && data_in_sershift != 2)
+ if (data_in_sershift != 1 && data_in_sershift != 2) {
+ return;
+ }
+ if (serloop_enabled) {
return;
+ }
#ifdef ARCADIA
if (alg_flag || currprefs.genlock_image >= 7) {
if (cubo_enabled) {
touch_serial_read(serdatshift);
}
+ if (serempty_enabled) {
+ return;
+ }
#ifdef SERIAL_MAP
if (sermap_data && sermap_enabled)
shmem_serial_send(serdatshift);
return false;
}
+static void sersend_end(uae_u32 v)
+{
+ data_in_sershift = 0;
+ serdatcopy();
+ data_in_serdat = 0;
+ data_in_serdat_delay = get_cycles() + CYCLE_UNIT;
+}
+
+static void sersend_serloop(uae_u32 v)
+{
+ serdatr = serdatshift;
+ serial_rx_irq();
+ event2_newevent_xx(-1, v * CYCLE_UNIT, 0, sersend_end);
+}
+
+
static void sersend_ce(uae_u32 v)
{
if (checkshiftempty()) {
{
int bits;
- if (data_in_sershift || !data_in_serdat)
+ if (data_in_sershift > 0 || !data_in_serdat)
return;
serdatshift = serdat;
bits = 8;
if ((serdatshift & 0xff80) == 0x80) {
bits = 7;
}
+ serdatshift_start = get_cycles();
serdatshift_masked = serdatshift & ((1 << bits) - 1);
- data_in_sershift = 1;
data_in_serdat = 0;
if (seriallog > 0 || (consoleopen && seriallog < 0)) {
}
}
+ if (data_in_sershift == 0) {
+ INTREQ_INT(0, 1);
+ }
+ data_in_sershift = 1;
+
+ serdatshift_bits = 16;
+ for (int i = 15; i >= 0; i--) {
+ if (serdatshift & (1 << i))
+ break;
+ serdatshift_bits--;
+ }
+
// if someone uses serial port as some kind of timer..
if (currprefs.cpu_memory_cycle_exact) {
- int per;
+ int sper = (serper & 0x7fff) + 1;
+ int per = sper * (serdatshift_bits + 1);
- bits = 16 + 1;
- for (int i = 15; i >= 0; i--) {
- if (serdatshift & (1 << i))
- break;
- bits--;
+ serper_tx_evt = get_cycles();
+ serper_tx_per = sper;
+ serper_tx_bits = serdatshift_bits + 1;
+ serper_tx_cycles = per;
+
+ // not connected, emulate only TX
+ if (serempty_enabled) {
+ event2_newevent_xx(-1, per * CYCLE_UNIT, 0, sersend_end);
+ return;
}
- // assuming when last bit goes out, transmit buffer
- // becomes empty, not when last bit has finished
- // transmitting.
- per = ((serper & 0x7fff) + 1) * (bits - 1);
+
+ // loopback receive complete
+ if (serloop_enabled) {
+ int recper = sper * (ninebit ? 10 : 9) + (sper - 1) / 2;
+ int perdiff = per - recper;
+ if (perdiff < 0) {
+ perdiff = 0;
+ }
+
+ // TX to RX delay (1488/1489 chip delays)
+ recper += 3;
+
+ event2_newevent_xx(-1, recper * CYCLE_UNIT, perdiff, sersend_serloop);
+ return;
+ }
+
if (lastbitcycle_active_hsyncs) {
// if last bit still transmitting, add remaining time.
- int extraper = (int)(lastbitcycle - get_cycles());
- if (extraper > 0)
- per += extraper / CYCLE_UNIT;
+ int extraper = (int)((lastbitcycle - get_cycles()) / CYCLE_UNIT);
+ per += extraper;
}
- if (per < 4)
- per = 4;
+
event2_newevent_x_replace(per, 0, sersend_ce);
}
- INTREQ(0x8000 | 0x0001);
checksend();
}
int ch = ld_serial_write();
if (ch >= 0) {
serdatr = ch | 0x100;
- data_in_serdatr = 1;
serdatr_last_got = 0;
- serial_check_irq ();
+ serial_rx_irq();
}
}
#endif
int ch = touch_serial_write();
if (ch >= 0) {
serdatr = ch | 0x100;
- data_in_serdatr = 1;
serdatr_last_got = 0;
- serial_check_irq();
+ serial_rx_irq();
}
}
if (seriallog > 1 && !data_in_serdatr && gotlogwrite) {
int ch = read_log();
if (ch > 0) {
serdatr = ch | 0x100;
- data_in_serdatr = 1;
- serial_check_irq ();
+ serial_rx_irq();
}
}
uae_u16 v = shmem_serial_receive();
if (v != 0xffff) {
serdatr = v;
- data_in_serdatr = 1;
- serial_check_irq();
+ serial_rx_irq();
}
}
#endif
}
}
-void SERDAT (uae_u16 w)
+static void SERDAT_send(uae_u32 v)
{
+ uae_u16 w = (uae_u16)v;
#if SERIALDEBUG > 2
write_log(_T("SERIAL: SERDAT write 0x%04x (%c) PC=%x\n"), w, dochar(w), M68K_GETPC);
#endif
- serdatcopy();
+ if (currprefs.cpu_memory_cycle_exact) {
+ serdat = w;
+ data_in_serdat = 1;
+ if (!data_in_sershift) {
+ if (serloop_enabled || serempty_enabled) {
+ data_in_sershift = -1;
+ INTREQ_INT(0, 1);
+ }
+ serdatcopy();
+ }
+
+ } else {
+ serdatcopy();
- serdat = w;
+ serdat = w;
- if (!w) {
+ if (!w) {
#if SERIALDEBUG > 1
- write_log (_T("SERIAL: zero serial word written?! PC=%x\n"), M68K_GETPC);
+ write_log (_T("SERIAL: zero serial word written?! PC=%x\n"), M68K_GETPC);
#endif
- return;
- }
+ return;
+ }
#if SERIALDEBUG > 1
- if (data_in_serdat) {
- write_log (_T("SERIAL: program wrote to SERDAT but old byte wasn't fetched yet\n"));
- }
+ if (data_in_serdat) {
+ write_log (_T("SERIAL: program wrote to SERDAT but old byte wasn't fetched yet\n"));
+ }
#endif
- data_in_serdat = 1;
- serdatcopy();
+ data_in_serdat = 1;
+ serdatcopy();
+ }
}
uae_u16 SERDATR(void)
{
serdatr &= 0x03ff;
- if (!data_in_serdat) {
- serdatr |= 0x2000;
+ if (!data_in_serdat && get_cycles() >= data_in_serdat_delay) {
+ serdatr |= 0x2000; // TBE (Transmit buffer empty)
}
- if (!data_in_sershift) {
- serdatr |= 0x1000;
+ if (!data_in_sershift && (serdatr & 0x2000)) {
+ serdatr |= 0x1000; // TSRE (Transmit shift register empty)
}
if (data_in_serdatr) {
- serdatr |= 0x4000;
+ serdatr |= 0x4000; // RBF (Receive buffer full)
}
if (ovrun) {
- serdatr |= 0x8000;
+ serdatr |= 0x8000; // OVRUN (Receiver overrun)
}
- if (break_in_serdatr <= 0) {
- serdatr |= 0x0800;
+ if (serloop_enabled) {
+ int per = ((serper & 0x7fff) + 1) * CYCLE_UNIT;
+ evt_t sper = per * serdatshift_bits;
+ evt_t c = get_cycles();
+ evt_t diff = serdatshift_start + sper - c;
+ if (break_in_serdatr < 0) {
+ serdatr |= 0x0800;
+ } else if (diff > 0) {
+ int bit = (int)(c - serdatshift_start) / per;
+ if (bit > 0 && !(serdatshift & (1 << (bit - 1)))) {
+ serdatr |= 0x0800;
+ }
+ } else {
+ if (break_in_serdatr <= 0) {
+ serdatr |= 0x0800;
+ }
+ }
+ } else {
+ if (break_in_serdatr <= 0) {
+ serdatr |= 0x0800; // RXD
+ }
}
#if SERIALDEBUG > 2
write_log (_T("SERIAL: read 0x%04x (%c) %x\n"), serdatr, dochar (serdatr), M68K_GETPC);
return serdatr;
}
-void serial_rbf_clear(void)
+void SERDAT(uae_u16 w)
{
- ovrun = 0;
+ if (currprefs.cpu_memory_cycle_exact) {
+ event2_newevent_xx(-1, 1 * CYCLE_UNIT, w, SERDAT_send);
+ } else {
+ SERDAT_send(w);
+ }
}
-void serial_check_irq (void)
+void serial_rbf_clear(void)
{
- // Data in receive buffer
- if (data_in_serdatr)
- INTREQ_0(0x8000 | 0x0800);
+ ovrun = 0;
}
void serial_dtr_on (void)
int status = 0;
uae_u8 serbits = oldserbits;
+ if (serloop_enabled) {
+ if (serstatus & 0x80) { // DTR -> DSR + CD
+ status |= TIOCM_DSR | TIOCM_CAR;
+ }
+ if (serstatus & 0x10) { // RTS -> CTS
+ status |= TIOCM_CTS;
+ }
+ } else {
#ifdef SERIAL_PORT
- getserstat (&status);
+ getserstat(&status);
#endif
+ }
+
if (!(status & TIOCM_CAR)) {
if (!(serbits & 0x20)) {
serbits |= 0x20;
{
static int logcnt = 10;
+ serstatus = newstate & dir;
+
#ifdef SERIAL_PORT
if (((oldserbits ^ newstate) & 0x80) && (dir & 0x80)) {
if (newstate & 0x80)
} else if (!_tcsicmp(currprefs.sername, SERIAL_INTERNAL)) {
sermap_enabled = true;
#endif
+ } else if (!_tcsicmp(currprefs.sername, SERIAL_LOOPBACK)) {
+ serloop_enabled = true;
+ } else if (!currprefs.sername[0]) {
+ serempty_enabled = true;
} else {
if(!openser (currprefs.sername)) {
write_log (_T("SERIAL: Could not open device %s\n"), currprefs.sername);
#endif
}
-void serial_close (void)
+void serial_close(void)
{
#ifdef SERIAL_PORT
- closeser ();
+ closeser();
#ifdef SERIAL_ENET
- enet_close ();
+ enet_close();
#endif
serdev = 0;
#ifdef SERIAL_MAP
sermap_deactivate();
#endif
#endif
+ serloop_enabled = false;
+ serempty_enabled = false;
+ serper_set = false;
}
-void serial_init (void)
+void serial_init(void)
{
#ifdef SERIAL_PORT
- if (!currprefs.use_serial)
+ if (!currprefs.use_serial) {
+ serial_open();
return;
+ }
if (!currprefs.serial_demand)
- serial_open ();
+ serial_open();
#endif
}
#endif
dtr = 0; /* just closes *opened* filehandles which is ok */
oldserbits = 0; /* when exiting. */
+ data_in_serdat = 0;
+ data_in_serdatr = 0;
+ data_in_sershift = 0;
+ break_in_serdatr = 0;
+ ovrun = 0;
+ serdatr_last_got = 0;
}
void serial_uartbreak (int v)