Serial receive/transmit rework (uses the new fifo.c API) and a small GDB stub fix.
This commit is contained in:
582
src/fifo.c
Normal file
582
src/fifo.c
Normal file
@@ -0,0 +1,582 @@
|
||||
/*
|
||||
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
||||
* running old operating systems and software designed for IBM
|
||||
* PC systems and compatibles from 1981 through fairly recent
|
||||
* system designs based on the PCI bus.
|
||||
*
|
||||
* This file is part of the 86Box distribution.
|
||||
*
|
||||
* FIFO infrastructure.
|
||||
*
|
||||
* Authors: Miran Grca, <mgrca8@gmail.com>
|
||||
*
|
||||
* Copyright 2023 Miran Grca.
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef FIFO_STANDALONE
|
||||
#define fatal printf
|
||||
#define pclog_ex printf
|
||||
#define pclog printf
|
||||
#include "include/86box/fifo.h"
|
||||
#else
|
||||
#define HAVE_STDARG_H
|
||||
#include <86box/86box.h>
|
||||
#include <86box/fifo.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_FIFO_LOG
|
||||
int fifo_do_log = ENABLE_FIFO_LOG;
|
||||
|
||||
static void
|
||||
fifo_log(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (fifo_do_log) {
|
||||
va_start(ap, fmt);
|
||||
pclog_ex(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#else
|
||||
# define fifo_log(fmt, ...)
|
||||
#endif
|
||||
|
||||
int
|
||||
fifo_get_count(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
int ret = fifo->len;
|
||||
|
||||
if (fifo->end == fifo->start)
|
||||
ret = fifo->full ? fifo->len : 0;
|
||||
else
|
||||
ret = abs(fifo->end - fifo->start);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
fifo_write(uint8_t val, void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->d_full = fifo->d_empty = 0;
|
||||
fifo->d_ready = fifo->d_overrun = 0;
|
||||
|
||||
if (fifo->full)
|
||||
fifo->overrun = 1;
|
||||
else {
|
||||
fifo->buf[fifo->end] = val;
|
||||
fifo->end = (fifo->end + 1) & 0x0f;
|
||||
|
||||
if (fifo->end == fifo->start)
|
||||
fifo->full = 1;
|
||||
|
||||
fifo->empty = 0;
|
||||
|
||||
if (fifo_get_count(fifo) >= fifo->trigger_len)
|
||||
fifo->ready = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
fifo_write_evt(uint8_t val, void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->d_full = fifo->d_empty = 0;
|
||||
fifo->d_ready = fifo->d_overrun = 0;
|
||||
|
||||
if (fifo->full) {
|
||||
fifo->d_overrun = (fifo->overrun != 1);
|
||||
fifo->overrun = 1;
|
||||
if (fifo->d_overrun && (fifo->d_overrun_evt != NULL))
|
||||
fifo->d_overrun_evt(fifo->priv);
|
||||
} else {
|
||||
fifo->buf[fifo->end] = val;
|
||||
fifo->end = (fifo->end + 1) & 0x0f;
|
||||
|
||||
if (fifo->end == fifo->start) {
|
||||
fifo->d_full = (fifo->full != 1);
|
||||
fifo->full = 1;
|
||||
if (fifo->d_full && (fifo->d_full_evt != NULL))
|
||||
fifo->d_full_evt(fifo->priv);
|
||||
}
|
||||
|
||||
fifo->d_empty = (fifo->empty != 0);
|
||||
fifo->empty = 0;
|
||||
if (fifo->d_empty && (fifo->d_empty_evt != NULL))
|
||||
fifo->d_empty_evt(fifo->priv);
|
||||
|
||||
if (fifo_get_count(fifo) >= fifo->trigger_len) {
|
||||
fifo->d_ready = (fifo->ready != 1);
|
||||
fifo->ready = 1;
|
||||
if (fifo->d_ready && (fifo->d_ready_evt != NULL))
|
||||
fifo->d_ready_evt(fifo->priv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fifo_read(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
uint8_t ret = 0x00;
|
||||
int count;
|
||||
|
||||
if (!fifo->empty) {
|
||||
ret = fifo->buf[fifo->start];
|
||||
fifo->start = (fifo->start + 1) & 0x0f;
|
||||
|
||||
fifo->full = 0;
|
||||
|
||||
count = fifo_get_count(fifo);
|
||||
|
||||
if (count < fifo->trigger_len) {
|
||||
fifo->ready = 0;
|
||||
|
||||
if (count == 0)
|
||||
fifo->empty = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
fifo_read_evt(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
uint8_t ret = 0x00;
|
||||
int count;
|
||||
|
||||
fifo->d_full = fifo->d_empty = 0;
|
||||
fifo->d_ready = 0;
|
||||
|
||||
if (!fifo->empty) {
|
||||
ret = fifo->buf[fifo->start];
|
||||
fifo->start = (fifo->start + 1) & 0x0f;
|
||||
|
||||
fifo->d_full = (fifo->full != 0);
|
||||
fifo->full = 0;
|
||||
if (fifo->d_full && (fifo->d_full_evt != NULL))
|
||||
fifo->d_full_evt(fifo->priv);
|
||||
|
||||
count = fifo_get_count(fifo);
|
||||
|
||||
if (count < fifo->trigger_len) {
|
||||
fifo->d_ready = (fifo->ready != 0);
|
||||
fifo->ready = 0;
|
||||
if (fifo->d_ready && (fifo->d_ready_evt != NULL))
|
||||
fifo->d_ready_evt(fifo->priv);
|
||||
|
||||
if (count == 0) {
|
||||
fifo->d_empty = (fifo->empty != 1);
|
||||
fifo->empty = 1;
|
||||
if (fifo->d_empty && (fifo->d_empty_evt != NULL))
|
||||
fifo->d_empty_evt(fifo->priv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
fifo_clear_overrun(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->d_overrun = (fifo->overrun != 0);
|
||||
fifo->overrun = 0;
|
||||
}
|
||||
|
||||
int
|
||||
fifo_get_full(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
return fifo->full;
|
||||
}
|
||||
|
||||
int
|
||||
fifo_get_d_full(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
int ret = fifo->d_full;
|
||||
|
||||
fifo->d_full = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
fifo_get_empty(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
return fifo->empty;
|
||||
}
|
||||
|
||||
int
|
||||
fifo_get_d_empty(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
int ret = fifo->d_empty;
|
||||
|
||||
fifo->d_empty = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
fifo_get_overrun(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
return fifo->overrun;
|
||||
}
|
||||
|
||||
int
|
||||
fifo_get_d_overrun(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
int ret = fifo->d_overrun;
|
||||
|
||||
fifo->d_overrun = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
fifo_get_ready(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
return fifo->ready;
|
||||
}
|
||||
|
||||
int
|
||||
fifo_get_d_ready(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
int ret = fifo->d_ready;
|
||||
|
||||
fifo->d_ready = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
fifo_get_trigger_len(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
return fifo->trigger_len;
|
||||
}
|
||||
|
||||
void
|
||||
fifo_set_trigger_len(void *priv, int trigger_len)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->trigger_len = trigger_len;
|
||||
}
|
||||
|
||||
void
|
||||
fifo_set_len(void *priv, int len)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->len = len;
|
||||
}
|
||||
|
||||
void
|
||||
fifo_set_d_full_evt(void *priv, void (*d_full_evt)(void *))
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->d_full_evt = d_full_evt;
|
||||
}
|
||||
|
||||
void
|
||||
fifo_set_d_empty_evt(void *priv, void (*d_empty_evt)(void *))
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->d_empty_evt = d_empty_evt;
|
||||
}
|
||||
|
||||
void
|
||||
fifo_set_d_overrun_evt(void *priv, void (*d_overrun_evt)(void *))
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->d_overrun_evt = d_overrun_evt;
|
||||
}
|
||||
|
||||
void
|
||||
fifo_set_d_ready_evt(void *priv, void (*d_ready_evt)(void *))
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->d_ready_evt = d_ready_evt;
|
||||
}
|
||||
|
||||
void
|
||||
fifo_set_priv(void *priv, void *sub_priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->priv = sub_priv;
|
||||
}
|
||||
|
||||
void
|
||||
fifo_reset(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->start = fifo->end = 0;
|
||||
fifo->full = fifo->overrun = 0;
|
||||
fifo->empty = 1;
|
||||
fifo->ready = 0;
|
||||
}
|
||||
|
||||
void
|
||||
fifo_reset_evt(void *priv)
|
||||
{
|
||||
fifo_t *fifo = (fifo_t *) priv;
|
||||
|
||||
fifo->start = fifo->end = 0;
|
||||
fifo->full = fifo->overrun = 0;
|
||||
fifo->empty = 1;
|
||||
fifo->ready = 0;
|
||||
fifo->d_full = fifo->d_overrun = 0;
|
||||
fifo->d_empty = fifo->d_ready = 0;
|
||||
|
||||
if (fifo->d_full_evt != NULL)
|
||||
fifo->d_full_evt(fifo->priv);
|
||||
|
||||
if (fifo->d_overrun_evt != NULL)
|
||||
fifo->d_overrun_evt(fifo->priv);
|
||||
|
||||
if (fifo->d_empty_evt != NULL)
|
||||
fifo->d_empty_evt(fifo->priv);
|
||||
|
||||
if (fifo->d_ready_evt != NULL)
|
||||
fifo->d_ready_evt(fifo->priv);
|
||||
}
|
||||
|
||||
void
|
||||
fifo_close(void *priv)
|
||||
{
|
||||
free(priv);
|
||||
}
|
||||
|
||||
void *
|
||||
fifo_init(int len)
|
||||
{
|
||||
void *fifo = NULL;
|
||||
|
||||
if (len == 64)
|
||||
fifo = (void *) calloc(1, sizeof(fifo64_t));
|
||||
else if (len == 16)
|
||||
fifo = (void *) calloc(1, sizeof(fifo16_t));
|
||||
else {
|
||||
fatal("FIFO : Invalid FIFO length: %i\n", len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fifo == NULL)
|
||||
fatal("FIFO%i: Failed to allocate memory for the FIFO\n", len);
|
||||
else
|
||||
((fifo_t *) fifo)->len = len;
|
||||
|
||||
return fifo;
|
||||
}
|
||||
|
||||
#ifdef FIFO_STANDALONE
|
||||
enum {
|
||||
SERIAL_INT_LSR = 1,
|
||||
SERIAL_INT_RECEIVE = 2,
|
||||
SERIAL_INT_TRANSMIT = 4,
|
||||
SERIAL_INT_MSR = 8,
|
||||
SERIAL_INT_TIMEOUT = 16
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t lsr, int_status, tsr, tsr_empty;
|
||||
|
||||
fifo16_t *rcvr_fifo, *xmit_fifo;
|
||||
} serial_t;
|
||||
|
||||
static void
|
||||
serial_receive_timer(fifo16_t *f16, uint8_t val)
|
||||
{
|
||||
fifo_write_evt(val, f16);
|
||||
|
||||
printf("Write %02X to FIFO [F: %i, E: %i, O: %i, R: %i]\n", val,
|
||||
fifo_get_full(f16), fifo_get_empty(f16),
|
||||
fifo_get_overrun(f16), fifo_get_ready(f16));
|
||||
|
||||
/*
|
||||
if (fifo_get_d_overrun(f16))
|
||||
dev->lsr = (dev->lsr & 0xfd) | (fifo_get_overrun(f16) << 1);
|
||||
*/
|
||||
if (fifo_get_d_overrun(f16)) printf(" FIFO overrun state changed: %i -> %i\n",
|
||||
!fifo_get_overrun(f16), fifo_get_overrun(f16));
|
||||
|
||||
/*
|
||||
if (fifo_get_d_empty(f16)) {
|
||||
dev->lsr = (dev->lsr & 0xfe) | !fifo_get_empty(f16);
|
||||
timer_on_auto(&dev->timeout_timer, 4.0 * dev->bits * dev->transmit_period);
|
||||
}
|
||||
*/
|
||||
if (fifo_get_d_empty(f16)) printf(" FIFO empty state changed: %i -> %i\n",
|
||||
!fifo_get_empty(f16), fifo_get_empty(f16));
|
||||
|
||||
/*
|
||||
if (fifo_get_d_ready(f16)) {
|
||||
dev->int_status = (dev->int_status & ~SERIAL_INT_RECEIVE) |
|
||||
(fifo_get_ready(f16) ? SERIAL_INT_RECEIVE : 0);
|
||||
serial_update_ints();
|
||||
}
|
||||
*/
|
||||
if (fifo_get_d_ready(f16)) printf(" FIFO ready state changed: %i -> %i\n",
|
||||
!fifo_get_ready(f16), fifo_get_ready(f16));
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
serial_read(fifo16_t *f16)
|
||||
{
|
||||
uint8_t ret;
|
||||
|
||||
ret = fifo_read_evt(f16);
|
||||
|
||||
printf("Read %02X from FIFO [F: %i, E: %i, O: %i, R: %i]\n", ret,
|
||||
fifo_get_full(f16), fifo_get_empty(f16),
|
||||
fifo_get_overrun(f16), fifo_get_ready(f16));
|
||||
|
||||
/*
|
||||
if (fifo_get_d_ready(f16)) {
|
||||
dev->int_status = (dev->int_status & ~SERIAL_INT_RECEIVE) |
|
||||
(fifo_get_ready(f16) ? SERIAL_INT_RECEIVE : 0);
|
||||
serial_update_ints();
|
||||
}
|
||||
*/
|
||||
if (fifo_get_d_ready(f16)) printf(" FIFO ready state changed: %i -> %i\n",
|
||||
!fifo_get_ready(f16), fifo_get_ready(f16));
|
||||
|
||||
/*
|
||||
if (fifo_get_d_empty(f16)) {
|
||||
dev->lsr = (dev->lsr & 0xfe) | !fifo_get_empty(f16);
|
||||
timer_on_auto(&dev->timeout_timer, 4.0 * dev->bits * dev->transmit_period);
|
||||
}
|
||||
*/
|
||||
if (fifo_get_d_empty(f16)) printf(" FIFO empty state changed: %i -> %i\n",
|
||||
!fifo_get_empty(f16), fifo_get_empty(f16));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
serial_xmit_d_empty_evt(void *priv)
|
||||
{
|
||||
serial_t *dev = (serial_t *) priv;
|
||||
|
||||
dev->lsr = (dev->lsr & 0x9f) | (fifo_get_empty(dev->xmit_fifo) << 5) |
|
||||
((dev->tsr_empty && fifo_get_empty(dev->xmit_fifo)) << 6);
|
||||
dev->int_status = (dev->int_status & ~SERIAL_INT_TRANSMIT) |
|
||||
(fifo_get_empty(dev->xmit_fifo) ? SERIAL_INT_TRANSMIT : 0);
|
||||
// serial_update_ints();
|
||||
|
||||
printf("NS16550: serial_xmit_d_empty_evt(%08X): dev->lsr = %02X\n", priv, dev->lsr);
|
||||
printf("NS16550: serial_xmit_d_empty_evt(%08X): dev->int_status = %02X\n", priv, dev->int_status);
|
||||
}
|
||||
|
||||
static void
|
||||
serial_rcvr_d_empty_evt(void *priv)
|
||||
{
|
||||
serial_t *dev = (serial_t *) priv;
|
||||
|
||||
dev->lsr = (dev->lsr & 0xfe) | !fifo_get_empty(dev->rcvr_fifo);
|
||||
// timer_on_auto(&dev->timeout_timer, 4.0 * dev->bits * dev->transmit_period);
|
||||
|
||||
printf("NS16550: serial_rcvr_d_empty_evt(%08X): dev->lsr = %02X\n", priv, dev->lsr);
|
||||
}
|
||||
|
||||
static void
|
||||
serial_rcvr_d_overrun_evt(void *priv)
|
||||
{
|
||||
serial_t *dev = (serial_t *) priv;
|
||||
|
||||
dev->lsr = (dev->lsr & 0xfd) | (fifo_get_overrun(dev->rcvr_fifo) << 1);
|
||||
|
||||
printf("NS16550: serial_rcvr_d_overrun_evt(%08X): dev->lsr = %02X\n", priv, dev->lsr);
|
||||
}
|
||||
|
||||
static void
|
||||
serial_rcvr_d_ready_evt(void *priv)
|
||||
{
|
||||
serial_t *dev = (serial_t *) priv;
|
||||
|
||||
dev->int_status = (dev->int_status & ~SERIAL_INT_RECEIVE) |
|
||||
(fifo_get_ready(dev->rcvr_fifo) ? SERIAL_INT_RECEIVE : 0);
|
||||
// serial_update_ints();
|
||||
|
||||
printf("NS16550: serial_rcvr_d_ready_evt(%08X): dev->int_status = %02X\n", priv, dev->int_status);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
uint8_t val, ret;
|
||||
|
||||
printf("Initializing serial...\n");
|
||||
serial_t *dev = (serial_t *) calloc(1, sizeof(serial_t));
|
||||
dev->tsr_empty = 1;
|
||||
|
||||
printf("Initializing dev->xmit_fifo...\n");
|
||||
dev->xmit_fifo = fifo16_init();
|
||||
fifo_set_trigger_len(dev->xmit_fifo, 255);
|
||||
|
||||
fifo_set_priv(dev->xmit_fifo, dev);
|
||||
fifo_set_d_empty_evt(dev->xmit_fifo, serial_xmit_d_empty_evt);
|
||||
|
||||
printf("\nResetting dev->xmit_fifo...\n");
|
||||
fifo_reset_evt(dev->xmit_fifo);
|
||||
|
||||
printf("\nInitializing dev->rcvr_fifo...\n");
|
||||
dev->rcvr_fifo = fifo16_init();
|
||||
fifo_set_trigger_len(dev->rcvr_fifo, 4);
|
||||
|
||||
fifo_set_priv(dev->rcvr_fifo, dev);
|
||||
fifo_set_d_empty_evt(dev->rcvr_fifo, serial_rcvr_d_empty_evt);
|
||||
fifo_set_d_overrun_evt(dev->rcvr_fifo, serial_rcvr_d_overrun_evt);
|
||||
fifo_set_d_ready_evt(dev->rcvr_fifo, serial_rcvr_d_ready_evt);
|
||||
|
||||
printf("\nResetting dev->rcvr_fifo...\n");
|
||||
fifo_reset_evt(dev->rcvr_fifo);
|
||||
|
||||
printf("\nSending/receiving data...\n");
|
||||
serial_receive_timer(dev->rcvr_fifo, '8');
|
||||
serial_receive_timer(dev->rcvr_fifo, '6');
|
||||
ret = serial_read(dev->rcvr_fifo);
|
||||
serial_receive_timer(dev->rcvr_fifo, 'B');
|
||||
ret = serial_read(dev->rcvr_fifo);
|
||||
serial_receive_timer(dev->rcvr_fifo, 'o');
|
||||
ret = serial_read(dev->rcvr_fifo);
|
||||
serial_receive_timer(dev->rcvr_fifo, 'x');
|
||||
ret = serial_read(dev->rcvr_fifo);
|
||||
ret = serial_read(dev->rcvr_fifo);
|
||||
|
||||
fifo_close(dev->rcvr_fifo);
|
||||
fifo_close(dev->xmit_fifo);
|
||||
|
||||
free(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user