Moved some files to the utils folder, that's where the CRC code is soon going to reside as well.

This commit is contained in:
OBattler
2025-02-13 10:31:12 +01:00
parent 1caa7564c2
commit 48f42af745
8 changed files with 28 additions and 7 deletions

25
src/utils/CMakeLists.txt Normal file
View File

@@ -0,0 +1,25 @@
#
# 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.
#
# CMake build script.
#
# Authors: David Hrdlička, <hrdlickadavid@outlook.com>
# Jasmine Iwanek, <jriwanek@gmail.com>
#
# Copyright 2020-2021 David Hrdlička.
# Copyright 2024 Jasmine Iwanek.
#
add_library(utils OBJECT
cJSON.c
fifo.c
fifo8.c
ini.c
log.c
random.c
)

3129
src/utils/cJSON.c Normal file

File diff suppressed because it is too large Load Diff

731
src/utils/fifo.c Normal file
View File

@@ -0,0 +1,731 @@
/*
* 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-2025 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_tagged(uint8_t tag, 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->tag[fifo->end] = tag;
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);
}
}
}
void
fifo_write_evt_tagged(uint8_t tag, 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->tag[fifo->end] = tag;
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_tagged(uint8_t *tag, void *priv)
{
fifo_t *fifo = (fifo_t *) priv;
uint8_t ret = 0x00;
int count;
if (!fifo->empty) {
ret = fifo->buf[fifo->start];
*tag = fifo->tag[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;
}
} else
*tag = 0x00;
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;
}
uint8_t
fifo_read_evt_tagged(uint8_t *tag, 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];
*tag = fifo->tag[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);
}
}
} else
*tag = 0x00;
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

179
src/utils/fifo8.c Normal file
View File

@@ -0,0 +1,179 @@
/*
* Generic FIFO component, implemented as a circular buffer.
*
* Copyright (c) 2012 Peter A. G. Crosthwaite
*
* This program 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <wchar.h>
#include <assert.h>
#include <86box/86box.h>
#include <86box/fifo8.h>
void
fifo8_reset(Fifo8 *fifo)
{
fifo->num = 0;
fifo->head = 0;
}
void
fifo8_create(Fifo8 *fifo, uint32_t capacity)
{
fifo->data = (uint8_t *) calloc(1, capacity);
fifo->capacity = capacity;
fifo8_reset(fifo);
}
void
fifo8_destroy(Fifo8 *fifo)
{
if (fifo->data) {
free(fifo->data);
fifo->data = NULL;
}
}
void
fifo8_push(Fifo8 *fifo, uint8_t data)
{
assert(fifo->num < fifo->capacity);
fifo->data[(fifo->head + fifo->num) % fifo->capacity] = data;
fifo->num++;
}
void
fifo8_push_all(Fifo8 *fifo, const uint8_t *data, uint32_t num)
{
uint32_t start;
uint32_t avail;
assert((fifo->num + num) <= fifo->capacity);
start = (fifo->head + fifo->num) % fifo->capacity;
if (start + num <= fifo->capacity) {
memcpy(&fifo->data[start], data, num);
} else {
avail = fifo->capacity - start;
memcpy(&fifo->data[start], data, avail);
memcpy(&fifo->data[0], &data[avail], num - avail);
}
fifo->num += num;
}
uint8_t
fifo8_pop(Fifo8 *fifo)
{
uint8_t ret;
assert(fifo->num > 0);
ret = fifo->data[fifo->head++];
fifo->head %= fifo->capacity;
fifo->num--;
return ret;
}
static const uint8_t
*fifo8_peekpop_buf(Fifo8 *fifo, uint32_t max, uint32_t *numptr, int do_pop)
{
uint8_t *ret;
uint32_t num;
assert((max > 0) && (max <= fifo->num));
num = MIN(fifo->capacity - fifo->head, max);
ret = &fifo->data[fifo->head];
if (do_pop) {
fifo->head += num;
fifo->head %= fifo->capacity;
fifo->num -= num;
}
if (numptr)
*numptr = num;
return ret;
}
const uint8_t
*fifo8_peek_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr)
{
return fifo8_peekpop_buf(fifo, max, numptr, 0);
}
const uint8_t
*fifo8_pop_bufptr(Fifo8 *fifo, uint32_t max, uint32_t *numptr)
{
return fifo8_peekpop_buf(fifo, max, numptr, 1);
}
uint32_t
fifo8_pop_buf(Fifo8 *fifo, uint8_t *dest, uint32_t destlen)
{
const uint8_t *buf;
uint32_t n1, n2 = 0;
uint32_t len;
if (destlen == 0)
return 0;
len = destlen;
buf = fifo8_pop_bufptr(fifo, len, &n1);
if (dest)
memcpy(dest, buf, n1);
/* Add FIFO wraparound if needed */
len -= n1;
len = MIN(len, fifo8_num_used(fifo));
if (len) {
buf = fifo8_pop_bufptr(fifo, len, &n2);
if (dest) {
memcpy(&dest[n1], buf, n2);
}
}
return n1 + n2;
}
void
fifo8_drop(Fifo8 *fifo, uint32_t len)
{
len -= fifo8_pop_buf(fifo, NULL, len);
assert(len == 0);
}
int
fifo8_is_empty(Fifo8 *fifo)
{
return (fifo->num == 0);
}
int
fifo8_is_full(Fifo8 *fifo)
{
return (fifo->num == fifo->capacity);
}
uint32_t
fifo8_num_free(Fifo8 *fifo)
{
return fifo->capacity - fifo->num;
}
uint32_t
fifo8_num_used(Fifo8 *fifo)
{
return fifo->num;
}

916
src/utils/ini.c Normal file
View File

@@ -0,0 +1,916 @@
/*
* 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.
*
* Configuration file handler.
*
*
*
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
* Overdoze,
* David Hrdlička, <hrdlickadavid@outlook.com>
*
* Copyright 2008-2019 Sarah Walker.
* Copyright 2016-2019 Miran Grca.
* Copyright 2017-2019 Fred N. van Kempen.
* Copyright 2018-2019 David Hrdlička.
*
* NOTE: Forcing config files to be in Unicode encoding breaks
* it on Windows XP, and possibly also Vista. Use the
* -DANSI_CFG for use on these systems.
*/
#include <inttypes.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/ini.h>
#include <86box/plat.h>
typedef struct _list_ {
struct _list_ *next;
} list_t;
typedef struct section_t {
list_t list;
char name[128];
list_t entry_head;
} section_t;
typedef struct entry_t {
list_t list;
char name[128];
char data[512];
wchar_t wdata[512];
} entry_t;
#define list_add(new, head) \
{ \
list_t *next = head; \
\
while (next->next != NULL) \
next = next->next; \
\
(next)->next = new; \
(new)->next = NULL; \
}
#define list_delete(old, head) \
{ \
list_t *next = head; \
\
while ((next)->next != old) { \
next = (next)->next; \
} \
\
(next)->next = (old)->next; \
if ((next) == (head)) \
(head)->next = (old)->next; \
}
#ifdef ENABLE_INI_LOG
int ini_do_log = ENABLE_INI_LOG;
static void
ini_log(const char *fmt, ...)
{
va_list ap;
if (ini_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define ini_log(fmt, ...)
#endif
static section_t *
find_section(list_t *head, const char *name)
{
section_t *sec = (section_t *) head->next;
const char blank[] = "";
if (name == NULL)
name = blank;
while (sec != NULL) {
if (!strncmp(sec->name, name, sizeof(sec->name)))
return sec;
sec = (section_t *) sec->list.next;
}
return NULL;
}
ini_section_t
ini_find_section(ini_t ini, const char *name)
{
if (ini == NULL)
return NULL;
return (ini_section_t) find_section((list_t *) ini, name);
}
void
ini_rename_section(ini_section_t section, const char *name)
{
section_t *sec = (section_t *) section;
if (sec == NULL)
return;
memset(sec->name, 0x00, sizeof(sec->name));
memcpy(sec->name, name, MIN(128, strlen(name) + 1));
}
static entry_t *
find_entry(section_t *section, const char *name)
{
entry_t *ent;
ent = (entry_t *) section->entry_head.next;
while (ent != NULL) {
if (!strncmp(ent->name, name, sizeof(ent->name)))
return ent;
ent = (entry_t *) ent->list.next;
}
return (NULL);
}
static int
entries_num(section_t *section)
{
entry_t *ent;
int i = 0;
ent = (entry_t *) section->entry_head.next;
while (ent != NULL) {
if (strlen(ent->name) > 0)
i++;
ent = (entry_t *) ent->list.next;
}
return i;
}
static void
delete_section_if_empty(list_t *head, section_t *section)
{
if (section == NULL)
return;
int n = entries_num(section);
if (n > 0) {
int i = 0;
entry_t *i_ent = (entry_t *) section->entry_head.next;
while (i_ent != NULL) {
int i_nlen = strlen(i_ent->name);
entry_t* i_next = (entry_t *) i_ent->list.next;
if (i_nlen > 0) {
int j = 0;
entry_t *j_ent = (entry_t *) section->entry_head.next;
while (j_ent != NULL) {
int j_nlen = strlen(j_ent->name);
entry_t* j_next = (entry_t *) j_ent->list.next;
if (j_nlen > 0) {
if ((j != i) && (strcmp(j_ent->name, i_ent->name) > 0)) {
entry_t t_ent = { 0 };
memcpy(&t_ent, j_ent, sizeof(entry_t));
/* J: Contents of I, list of J */
memcpy(j_ent->name, i_ent->name, sizeof(entry_t) - sizeof(i_ent->list));
/* I: Contents of J, list of I */
memcpy(i_ent->name, t_ent.name, sizeof(entry_t) - sizeof(i_ent->list));
}
j++;
}
j_ent = (entry_t *) j_next;
}
i++;
}
i_ent = (entry_t *) i_next;
}
} else {
list_delete(&section->list, head);
free(section);
}
}
void
ini_delete_section_if_empty(ini_t ini, ini_section_t section)
{
if (ini == NULL || section == NULL)
return;
delete_section_if_empty((list_t *) ini, (section_t *) section);
}
static section_t *
create_section(list_t *head, const char *name)
{
section_t *ns = malloc(sizeof(section_t));
memset(ns, 0x00, sizeof(section_t));
memcpy(ns->name, name, strlen(name) + 1);
list_add(&ns->list, head);
return ns;
}
ini_section_t
ini_find_or_create_section(ini_t ini, const char *name)
{
if (ini == NULL)
return NULL;
section_t *section = find_section((list_t *) ini, name);
if (section == NULL)
section = create_section((list_t *) ini, name);
return (ini_section_t) section;
}
static entry_t *
create_entry(section_t *section, const char *name)
{
entry_t *ne = malloc(sizeof(entry_t));
memset(ne, 0x00, sizeof(entry_t));
memcpy(ne->name, name, strlen(name) + 1);
list_add(&ne->list, &section->entry_head);
return ne;
}
void
ini_close(ini_t ini)
{
section_t *sec;
section_t *ns;
entry_t *ent;
list_t *list = (list_t *) ini;
if (list == NULL)
return;
sec = (section_t *) list->next;
while (sec != NULL) {
ns = (section_t *) sec->list.next;
ent = (entry_t *) sec->entry_head.next;
while (ent != NULL) {
entry_t *nent = (entry_t *) ent->list.next;
free(ent);
ent = nent;
}
free(sec);
sec = ns;
}
free(list);
}
static int
ini_detect_bom(const char *fn)
{
FILE *fp;
unsigned char bom[4] = { 0, 0, 0, 0 };
#if defined(ANSI_CFG) || !defined(_WIN32)
fp = plat_fopen(fn, "rt");
#else
fp = plat_fopen(fn, "rt, ccs=UTF-8");
#endif
if (fp == NULL)
return 0;
(void) !fread(bom, 1, 3, fp);
if (bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF) {
fclose(fp);
return 1;
}
fclose(fp);
return 0;
}
#ifdef __HAIKU__
/* Local version of fgetws to avoid a crash */
static wchar_t *
ini_fgetws(wchar_t *str, int count, FILE *stream)
{
int i = 0;
if (feof(stream))
return NULL;
for (i = 0; i < count; i++) {
wint_t curChar = fgetwc(stream);
if (curChar == WEOF) {
if (i + 1 < count)
str[i + 1] = 0;
return feof(stream) ? str : NULL;
}
str[i] = curChar;
if (curChar == '\n')
break;
}
if (i + 1 < count)
str[i + 1] = 0;
return str;
}
#endif
/* Read and parse the configuration file into memory. */
ini_t
ini_read(const char *fn)
{
char sname[128];
char ename[128];
wchar_t buff[1024];
section_t *sec;
section_t *ns;
entry_t *ne;
int c;
int d;
int bom;
FILE *fp;
list_t *head;
bom = ini_detect_bom(fn);
#if defined(ANSI_CFG) || !defined(_WIN32)
fp = plat_fopen(fn, "rt");
#else
fp = plat_fopen(fn, "rt, ccs=UTF-8");
#endif
if (fp == NULL)
return NULL;
head = malloc(sizeof(list_t));
memset(head, 0x00, sizeof(list_t));
sec = malloc(sizeof(section_t));
memset(sec, 0x00, sizeof(section_t));
list_add(&sec->list, head);
if (bom)
fseek(fp, 3, SEEK_SET);
while (1) {
memset(buff, 0x00, sizeof(buff));
#ifdef __HAIKU__
ini_fgetws(buff, sizeof_w(buff), fp);
#else
(void) !fgetws(buff, sizeof_w(buff), fp);
#endif
if (feof(fp))
break;
/* Make sure there are no stray newlines or hard-returns in there. */
if (wcslen(buff) > 0)
if (buff[wcslen(buff) - 1] == L'\n')
buff[wcslen(buff) - 1] = L'\0';
if (wcslen(buff) > 0)
if (buff[wcslen(buff) - 1] == L'\r')
buff[wcslen(buff) - 1] = L'\0';
/* Skip any leading whitespace. */
c = 0;
while ((buff[c] == L' ') || (buff[c] == L'\t'))
c++;
/* Skip empty lines. */
if (buff[c] == L'\0')
continue;
/* Skip lines that (only) have a comment. */
if ((buff[c] == L'#') || (buff[c] == L';'))
continue;
if (buff[c] == L'[') { /*Section*/
c++;
d = 0;
while (buff[c] != L']' && buff[c])
(void) !wctomb(&(sname[d++]), buff[c++]);
sname[d] = L'\0';
/* Is the section name properly terminated? */
if (buff[c] != L']')
continue;
/* Create a new section and insert it. */
ns = malloc(sizeof(section_t));
memset(ns, 0x00, sizeof(section_t));
memcpy(ns->name, sname, 128);
list_add(&ns->list, head);
/* New section is now the current one. */
sec = ns;
continue;
}
/* Get the variable name. */
d = 0;
while ((buff[c] != L'=') && (buff[c] != L' ') && buff[c])
(void) !wctomb(&(ename[d++]), buff[c++]);
ename[d] = L'\0';
/* Skip incomplete lines. */
if (buff[c] == L'\0')
continue;
/* Look for =, skip whitespace. */
while ((buff[c] == L'=' || buff[c] == L' ') && buff[c])
c++;
/* Skip incomplete lines. */
if (buff[c] == L'\0')
continue;
/* This is where the value part starts. */
d = c;
/* Allocate a new variable entry.. */
ne = malloc(sizeof(entry_t));
memset(ne, 0x00, sizeof(entry_t));
memcpy(ne->name, ename, 128);
wcsncpy(ne->wdata, &buff[d], sizeof_w(ne->wdata) - 1);
ne->wdata[sizeof_w(ne->wdata) - 1] = L'\0';
#ifdef _WIN32 /* Make sure the string is converted to UTF-8 rather than a legacy codepage */
c16stombs(ne->data, ne->wdata, sizeof(ne->data));
#else
wcstombs(ne->data, ne->wdata, sizeof(ne->data));
#endif
ne->data[sizeof(ne->data) - 1] = '\0';
/* .. and insert it. */
list_add(&ne->list, &sec->entry_head);
}
(void) fclose(fp);
return (ini_t) head;
}
/* Write the in-memory configuration to disk. */
void
ini_write(ini_t ini, const char *fn)
{
wchar_t wtemp[512];
list_t *list = (list_t *) ini;
section_t *sec;
FILE *fp;
int fl = 0;
if (list == NULL)
return;
sec = (section_t *) list->next;
#if defined(ANSI_CFG) || !defined(_WIN32)
fp = plat_fopen(fn, "wt");
#else
fp = plat_fopen(fn, "wt, ccs=UTF-8");
#endif
if (fp == NULL)
return;
while (sec != NULL) {
entry_t *ent;
if (sec->name[0]) {
mbstowcs(wtemp, sec->name, strlen(sec->name) + 1);
if (fl)
fwprintf(fp, L"\n[%ls]\n", wtemp);
else
fwprintf(fp, L"[%ls]\n", wtemp);
fl++;
}
ent = (entry_t *) sec->entry_head.next;
while (ent != NULL) {
if (ent->name[0] != '\0') {
mbstowcs(wtemp, ent->name, 128);
if (ent->wdata[0] == L'\0')
fwprintf(fp, L"%ls = \n", wtemp);
else
fwprintf(fp, L"%ls = %ls\n", wtemp, ent->wdata);
fl++;
}
ent = (entry_t *) ent->list.next;
}
sec = (section_t *) sec->list.next;
}
(void) fclose(fp);
}
ini_t
ini_new(void)
{
ini_t ini = malloc(sizeof(list_t));
memset(ini, 0, sizeof(list_t));
return ini;
}
void
ini_dump(ini_t ini)
{
section_t *sec = (section_t *) ini;
while (sec != NULL) {
entry_t *ent;
if (sec->name[0])
ini_log("[%s]\n", sec->name);
ent = (entry_t *) sec->entry_head.next;
while (ent != NULL) {
ini_log("%s = %s\n", ent->name, ent->data);
ent = (entry_t *) ent->list.next;
}
sec = (section_t *) sec->list.next;
}
}
void
ini_section_delete_var(ini_section_t self, const char *name)
{
section_t *section = (section_t *) self;
entry_t *entry;
if (section == NULL)
return;
entry = find_entry(section, name);
if (entry != NULL) {
list_delete(&entry->list, &section->entry_head);
free(entry);
}
}
int
ini_section_get_int(ini_section_t self, const char *name, int def)
{
section_t *section = (section_t *) self;
const entry_t *entry;
int value = 0;
if (section == NULL)
return def;
entry = find_entry(section, name);
if (entry == NULL)
return def;
sscanf(entry->data, "%i", &value);
return value;
}
uint32_t
ini_section_get_uint(ini_section_t self, const char *name, uint32_t def)
{
section_t *section = (section_t *) self;
const entry_t *entry;
uint32_t value = 0;
if (section == NULL)
return def;
entry = find_entry(section, name);
if (entry == NULL)
return def;
sscanf(entry->data, "%u", &value);
return value;
}
#if 0
float
ini_section_get_float(ini_section_t self, const char *name, float def)
{
section_t *section = (section_t *) self;
const entry_t *entry;
float value = 0;
if (section == NULL)
return def;
entry = find_entry(section, name);
if (entry == NULL)
return def;
sscanf(entry->data, "%g", &value);
return value;
}
#endif
double
ini_section_get_double(ini_section_t self, const char *name, double def)
{
section_t *section = (section_t *) self;
const entry_t *entry;
double value = 0;
if (section == NULL)
return def;
entry = find_entry(section, name);
if (entry == NULL)
return def;
sscanf(entry->data, "%lg", &value);
return value;
}
int
ini_section_get_hex16(ini_section_t self, const char *name, int def)
{
section_t *section = (section_t *) self;
const entry_t *entry;
unsigned int value = 0;
if (section == NULL)
return def;
entry = find_entry(section, name);
if (entry == NULL)
return def;
sscanf(entry->data, "%04X", &value);
return value;
}
int
ini_section_get_hex20(ini_section_t self, const char *name, int def)
{
section_t *section = (section_t *) self;
const entry_t *entry;
unsigned int value = 0;
if (section == NULL)
return def;
entry = find_entry(section, name);
if (entry == NULL)
return def;
sscanf(entry->data, "%05X", &value);
return value;
}
int
ini_section_get_mac(ini_section_t self, const char *name, int def)
{
section_t *section = (section_t *) self;
const entry_t *entry;
unsigned int val0 = 0;
unsigned int val1 = 0;
unsigned int val2 = 0;
if (section == NULL)
return def;
entry = find_entry(section, name);
if (entry == NULL)
return def;
sscanf(entry->data, "%02x:%02x:%02x", &val0, &val1, &val2);
return ((val0 << 16) + (val1 << 8) + val2);
}
char *
ini_section_get_string(ini_section_t self, const char *name, char *def)
{
section_t *section = (section_t *) self;
entry_t *entry;
if (section == NULL)
return def;
entry = find_entry(section, name);
if (entry == NULL)
return def;
return (entry->data);
}
wchar_t *
ini_section_get_wstring(ini_section_t self, const char *name, wchar_t *def)
{
section_t *section = (section_t *) self;
entry_t *entry;
if (section == NULL)
return def;
entry = find_entry(section, name);
if (entry == NULL)
return def;
return (entry->wdata);
}
void
ini_section_set_int(ini_section_t self, const char *name, int val)
{
section_t *section = (section_t *) self;
entry_t *ent;
if (section == NULL)
return;
ent = find_entry(section, name);
if (ent == NULL)
ent = create_entry(section, name);
sprintf(ent->data, "%i", val);
mbstowcs(ent->wdata, ent->data, 512);
}
void
ini_section_set_uint(ini_section_t self, const char *name, uint32_t val)
{
section_t *section = (section_t *) self;
entry_t *ent;
if (section == NULL)
return;
ent = find_entry(section, name);
if (ent == NULL)
ent = create_entry(section, name);
sprintf(ent->data, "%i", val);
mbstowcs(ent->wdata, ent->data, 512);
}
#if 0
void
ini_section_set_float(ini_section_t self, const char *name, float val)
{
section_t *section = (section_t *) self;
entry_t *ent;
if (section == NULL)
return;
ent = find_entry(section, name);
if (ent == NULL)
ent = create_entry(section, name);
sprintf(ent->data, "%g", val);
mbstowcs(ent->wdata, ent->data, 512);
}
#endif
void
ini_section_set_double(ini_section_t self, const char *name, double val)
{
section_t *section = (section_t *) self;
entry_t *ent;
if (section == NULL)
return;
ent = find_entry(section, name);
if (ent == NULL)
ent = create_entry(section, name);
sprintf(ent->data, "%lg", val);
mbstowcs(ent->wdata, ent->data, 512);
}
void
ini_section_set_hex16(ini_section_t self, const char *name, int val)
{
section_t *section = (section_t *) self;
entry_t *ent;
if (section == NULL)
return;
ent = find_entry(section, name);
if (ent == NULL)
ent = create_entry(section, name);
sprintf(ent->data, "%04X", val);
mbstowcs(ent->wdata, ent->data, sizeof_w(ent->wdata));
}
void
ini_section_set_hex20(ini_section_t self, const char *name, int val)
{
section_t *section = (section_t *) self;
entry_t *ent;
if (section == NULL)
return;
ent = find_entry(section, name);
if (ent == NULL)
ent = create_entry(section, name);
sprintf(ent->data, "%05X", val);
mbstowcs(ent->wdata, ent->data, sizeof_w(ent->wdata));
}
void
ini_section_set_mac(ini_section_t self, const char *name, int val)
{
section_t *section = (section_t *) self;
entry_t *ent;
if (section == NULL)
return;
ent = find_entry(section, name);
if (ent == NULL)
ent = create_entry(section, name);
sprintf(ent->data, "%02x:%02x:%02x",
(val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff);
mbstowcs(ent->wdata, ent->data, 512);
}
void
ini_section_set_string(ini_section_t self, const char *name, const char *val)
{
section_t *section = (section_t *) self;
entry_t *ent;
if (section == NULL)
return;
ent = find_entry(section, name);
if (ent == NULL)
ent = create_entry(section, name);
if ((strlen(val) + 1) <= sizeof(ent->data))
memcpy(ent->data, val, strlen(val) + 1);
else
memcpy(ent->data, val, sizeof(ent->data));
#ifdef _WIN32 /* Make sure the string is converted from UTF-8 rather than a legacy codepage */
mbstoc16s(ent->wdata, ent->data, sizeof_w(ent->wdata));
#else
mbstowcs(ent->wdata, ent->data, sizeof_w(ent->wdata));
#endif
}
void
ini_section_set_wstring(ini_section_t self, const char *name, wchar_t *val)
{
section_t *section = (section_t *) self;
entry_t *ent;
if (section == NULL)
return;
ent = find_entry(section, name);
if (ent == NULL)
ent = create_entry(section, name);
memcpy(ent->wdata, val, sizeof_w(ent->wdata));
#ifdef _WIN32 /* Make sure the string is converted to UTF-8 rather than a legacy codepage */
c16stombs(ent->data, ent->wdata, sizeof(ent->data));
#else
wcstombs(ent->data, ent->wdata, sizeof(ent->data));
#endif
}

339
src/utils/log.c Normal file
View File

@@ -0,0 +1,339 @@
/*
* 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.
*
* New logging system handler.
*
* Authors: Miran Grca, <mgrca8@gmail.com>
* Fred N. van Kempen, <decwiz@yahoo.com>
* Connor Hyde, <mario64crashed@gmail.com, nomorestarfrost@gmail.com>
*
* Copyright 2021-25 Miran Grca.
* Copyright 2021-25 Fred N. van Kempen.
* Copyright 2025 Connor Hyde.
*/
#include <inttypes.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/config.h>
#include <86box/mem.h>
#include "cpu.h"
#include <86box/plat.h>
#include <86box/version.h>
#include <86box/log.h>
typedef struct log_t {
char buff[1024];
char dev_name[1024];
int seen;
int suppr_seen;
/* Cyclical log buffer. */
char **cyclic_buff;
int32_t cyclic_last_line;
int32_t log_cycles;
} log_t;
/* File to log output to. */
extern FILE *stdlog;
/* Functions only used in this translation unit. */
void log_ensure_stdlog_open(void);
void
log_set_dev_name(void *priv, char *dev_name)
{
log_t *log = (log_t *) priv;
memcpy(log->dev_name, dev_name, strlen(dev_name) + 1);
}
static void
log_copy(log_t *log, char *dest, const char *src, size_t dest_size)
{
memset(dest, 0x00, dest_size * sizeof(char));
if ((log != NULL) && strcmp(log->dev_name, "")) {
strcat(dest, log->dev_name);
strcat(dest, ": ");
}
strcat(dest, src);
}
#ifndef RELEASE_BUILD
void
log_ensure_stdlog_open(void)
{
if (stdlog == NULL) {
if (log_path[0] != '\0') {
stdlog = plat_fopen(log_path, "w");
if (stdlog == NULL)
stdlog = stdout;
} else
stdlog = stdout;
}
}
void
log_set_suppr_seen(void *priv, int suppr_seen)
{
log_t *log = (log_t *) priv;
log->suppr_seen = suppr_seen;
}
/*
Log something to the logfile or stdout.
To avoid excessively-large logfiles because some
module repeatedly logs, we keep track of what is
being logged, and catch repeating entries.
*/
void
log_out(void *priv, const char *fmt, va_list ap)
{
log_t *log = (log_t *) priv;
char temp[1024];
char fmt2[1024];
if (log == NULL)
pclog("WARNING: Logging called with a NULL log pointer\n");
else if (fmt == NULL)
pclog("WARNING: Logging called with a NULL format pointer\n");
else if (fmt[0] != '\0') {
log_ensure_stdlog_open();
vsprintf(temp, fmt, ap);
if (log->suppr_seen && !strcmp(log->buff, temp))
log->seen++;
else {
if (log->suppr_seen && log->seen) {
log_copy(log, fmt2, "*** %d repeats ***\n", 1024);
fprintf(stdlog, fmt2, log->seen);
}
log->seen = 0;
strcpy(log->buff, temp);
log_copy(log, fmt2, temp, 1024);
fprintf(stdlog, fmt2, ap);
}
fflush(stdlog);
}
}
/*
Starfrost, 7-8 January 2025:
For RIVA 128 emulation I needed a way to suppress logging if a repeated
pattern of the same set of lines were found.
Implements a version of the Rabin-Karp algorithm:
https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm .
*/
void
log_out_cyclic(void* priv, const char* fmt, va_list ap)
{
/* Get our new logging system instance. */
log_t* log = (log_t*) priv;
/* Does the log actually exist? */
if (log == NULL)
pclog("WARNING: Cyclical logging called with a NULL log pointer\n");
else if (log->cyclic_buff == NULL)
pclog("WARNING: Cyclical logging called with a non-cyclic log\n");
else if (fmt == NULL)
pclog("WARNING: Cyclical logging called with a NULL format pointer\n");
/* Is the string empty? */
else if (fmt[0] != '\0') {
/* Ensure stdlog is open. */
log_ensure_stdlog_open();
char temp[LOG_SIZE_BUFFER] = {0};
log->cyclic_last_line %= LOG_SIZE_BUFFER_CYCLIC_LINES;
vsprintf(temp, fmt, ap);
log_copy(log, log->cyclic_buff[log->cyclic_last_line], temp,
LOG_SIZE_BUFFER);
uint32_t hashes[LOG_SIZE_BUFFER_CYCLIC_LINES] = {0};
/* Random numbers. */
uint32_t base = 257;
uint32_t mod = 1000000007;
uint32_t repeat_order = 0;
bool is_cycle = false;
/* Compute the set of hashes for the current log buffer. */
for (int32_t log_line = 0; log_line < LOG_SIZE_BUFFER_CYCLIC_LINES;
log_line++) {
if (log->cyclic_buff[log_line][0] == '\0')
continue; /* Skip. */
for (int32_t log_line_char = 0; log_line_char < LOG_SIZE_BUFFER;
log_line_char++)
hashes[log_line] = hashes[log_line] * base +
log->cyclic_buff[log_line][log_line_char] % mod;
}
/*
Now see if there are real cycles.
We implement a minimum repeat size.
*/
for (int32_t check_size = LOG_MINIMUM_REPEAT_ORDER;
check_size < LOG_SIZE_BUFFER_CYCLIC_LINES / 2; check_size++) {
/*
TODO: Log what we need for cycle 1.
TODO: Command line option that lets us turn off this behaviour.
*/
for (int32_t log_line_to_check = 0; log_line_to_check < check_size;
log_line_to_check++) {
if (hashes[log_line_to_check] ==
hashes[(log_line_to_check + check_size) %
LOG_SIZE_BUFFER_CYCLIC_LINES]) {
repeat_order = check_size;
break;
}
}
is_cycle = (repeat_order != 0);
/* If there still is a cycle, break. */
if (is_cycle)
break;
}
if (is_cycle) {
if (log->cyclic_last_line % repeat_order == 0) {
log->log_cycles++;
if (log->log_cycles == 1) {
/*
'Replay' the last few log entries so they actually
show up.
TODO: Is this right?
*/
for (uint32_t index = log->cyclic_last_line - 1;
index > (log->cyclic_last_line - repeat_order);
index--) {
/* *Very important* to prevent out of bounds index. */
uint32_t real_index = index %
LOG_SIZE_BUFFER_CYCLIC_LINES;
log_copy(log, temp, log->cyclic_buff[real_index],
LOG_SIZE_BUFFER);
fprintf(stdlog, "%s", log->cyclic_buff[real_index]);
}
/* Restore the original line. */
log_copy(log, temp,
log->cyclic_buff[log->cyclic_last_line],
LOG_SIZE_BUFFER);
/* Allow normal logging. */
fprintf(stdlog, "%s", temp);
}
if (log->log_cycles > 1 && log->log_cycles < 100)
fprintf(stdlog, "***** Cyclical Log Repeat of Order %d "
"#%d *****\n", repeat_order, log->log_cycles);
else if (log->log_cycles == 100)
fprintf(stdlog, "Logged the same cycle 100 times... "
"Silence until something interesting happens\n");
}
} else {
log->log_cycles = 0;
fprintf(stdlog, "%s", temp);
}
log->cyclic_last_line++;
}
}
#endif
void
log_fatal(void *priv, const char *fmt, ...)
{
log_t *log = (log_t *) priv;
char temp[1024];
char fmt2[1024];
va_list ap;
if (log == NULL)
return;
if (log->cyclic_buff != NULL) {
for (int i = 0; i < LOG_SIZE_BUFFER_CYCLIC_LINES; i++)
if (log->cyclic_buff[i] != NULL)
free(log->cyclic_buff[i]);
free(log->cyclic_buff);
}
va_start(ap, fmt);
log_copy(log, fmt2, fmt, 1024);
vsprintf(temp, fmt2, ap);
fatal_ex(fmt2, ap);
va_end(ap);
exit(-1);
}
static void *
log_open_common(const char *dev_name, const int cyclic)
{
log_t *log = calloc(1, sizeof(log_t));
memcpy(log->dev_name, dev_name, strlen(dev_name) + 1);
log->suppr_seen = 1;
log->cyclic_last_line = 0;
log->log_cycles = 0;
if (cyclic) {
log->cyclic_buff = calloc(LOG_SIZE_BUFFER_CYCLIC_LINES,
sizeof(char *));
for (int i = 0; i < LOG_SIZE_BUFFER_CYCLIC_LINES; i++)
log->cyclic_buff[i] = calloc(LOG_SIZE_BUFFER, sizeof(char));
}
return (void *) log;
}
void *
log_open(const char *dev_name)
{
return log_open_common(dev_name, 0);
}
/*
This is so that not all logs get the 32k cyclical buffer
they may not need.
*/
void *
log_open_cyclic(const char *dev_name)
{
return log_open_common(dev_name, 1);
}
void
log_close(void *priv)
{
log_t *log = (log_t *) priv;
free(log);
}

102
src/utils/random.c Normal file
View File

@@ -0,0 +1,102 @@
/*
* 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.
*
* A better random number generation, used for floppy weak bits
* and network MAC address generation.
*
*
*
* Authors: Miran Grca, <mgrca8@gmail.com>
*
* Copyright 2016-2018 Miran Grca.
*/
#include <stdint.h>
#include <stdlib.h>
#include <86box/random.h>
#if !(defined(__i386__) || defined(__x86_64__))
# include <time.h>
#endif
uint32_t preconst = 0x6ED9EBA1;
static __inline uint32_t
rotl32c(uint32_t x, uint32_t n)
{
#if 0
assert (n<32);
#endif
return (x << n) | (x >> (-n & 31));
}
static __inline uint32_t
rotr32c(uint32_t x, uint32_t n)
{
#if 0
assert (n<32);
#endif
return (x >> n) | (x << (-n & 31));
}
#define ROTATE_LEFT rotl32c
#define ROTATE_RIGHT rotr32c
static __inline unsigned long long
rdtsc(void)
{
#if defined(__i386__) || defined(__x86_64__)
unsigned int hi;
unsigned int lo;
# ifdef _MSC_VER
__asm {
rdtsc
mov hi, edx ; EDX:EAX is already standard return!!
mov lo, eax
}
# else
__asm__ __volatile__("rdtsc"
: "=a"(lo), "=d"(hi));
# endif
return ((unsigned long long) lo) | (((unsigned long long) hi) << 32);
#else
return time(NULL);
#endif
}
static uint32_t
RDTSC(void)
{
return (uint32_t) (rdtsc());
}
static void
random_twist(uint32_t *val)
{
*val = ROTATE_LEFT(*val, rand() % 32);
*val ^= 0x5A827999;
*val = ROTATE_RIGHT(*val, rand() % 32);
*val ^= 0x4ED32706;
}
uint8_t
random_generate(void)
{
uint16_t r = 0;
r = (RDTSC() ^ ROTATE_LEFT(preconst, rand() % 32)) % 256;
random_twist(&preconst);
return (r & 0xff);
}
void
random_init(void)
{
uint32_t seed = RDTSC();
srand(seed);
return;
}