Files
86Box/src/fifo.c

597 lines
14 KiB
C

/*
* 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)
{
const 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) % fifo->len;
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) % fifo->len;
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) % fifo->len;
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) % fifo->len;
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)
{
const 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)
{
const 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)
{
const 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)
{
const 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)
{
const 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 = calloc(1, sizeof(fifo64_t));
else if (len == 16)
fifo = 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 serial_t {
uint8_t lsr;
uint8_t int_status;
uint8_t tsr;
uint8_t tsr_empty;
fifo16_t *rcvr_fifo;
fifo16_t *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 0
if (fifo_get_d_overrun(f16))
dev->lsr = (dev->lsr & 0xfd) | (fifo_get_overrun(f16) << 1);
#endif
if (fifo_get_d_overrun(f16)) printf(" FIFO overrun state changed: %i -> %i\n",
!fifo_get_overrun(f16), fifo_get_overrun(f16));
#if 0
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);
}
#endif
if (fifo_get_d_empty(f16))
printf(" FIFO empty state changed: %i -> %i\n",
!fifo_get_empty(f16), fifo_get_empty(f16));
#if 0
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();
}
#endif
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 0
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();
}
#endif
if (fifo_get_d_ready(f16))
printf(" FIFO ready state changed: %i -> %i\n",
!fifo_get_ready(f16), fifo_get_ready(f16));
#if 0
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);
}
#endif
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;
uint8_t 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