Updated 808x.c to properly handle MUL/IMUL (false detection of V20 More changes in video block. Partly applied upstream patches for Amstrad machines.
424 lines
9.5 KiB
C
424 lines
9.5 KiB
C
/*
|
|
* VARCem Virtual ARchaeological Computer EMulator.
|
|
* An emulator of (mostly) x86-based PC systems and devices,
|
|
* using the ISA,EISA,VLB,MCA and PCI system buses, roughly
|
|
* spanning the era between 1981 and 1995.
|
|
*
|
|
* This file is part of the VARCem Project.
|
|
*
|
|
* Implementation of the network module.
|
|
*
|
|
* Version: @(#)network.c 1.0.19 2019/03/06
|
|
*
|
|
* Author: Fred N. van Kempen, <decwiz@yahoo.com>
|
|
*
|
|
* Copyright 2017-2019 Fred N. van Kempen.
|
|
*
|
|
* Redistribution and use in source and binary forms, with
|
|
* or without modification, are permitted provided that the
|
|
* following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the entire
|
|
* above notice, this list of conditions and the following
|
|
* disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the
|
|
* following disclaimer in the documentation and/or other
|
|
* materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names
|
|
* of its contributors may be used to endorse or promote
|
|
* products derived from this software without specific
|
|
* prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <wchar.h>
|
|
#define HAVE_STDARG_H
|
|
#define dbglog network_log
|
|
#include "../../emu.h"
|
|
#include "../../device.h"
|
|
#include "../../ui/ui.h"
|
|
#include "../../plat.h"
|
|
#include "network.h"
|
|
|
|
|
|
typedef struct {
|
|
mutex_t *mutex;
|
|
|
|
void *priv; /* card priv data */
|
|
int (*poll)(void *); /* card poll function */
|
|
NETRXCB rx; /* card RX function */
|
|
uint8_t *mac; /* card MAC address */
|
|
|
|
volatile int poll_busy, /* polling thread data */
|
|
queue_in_use;
|
|
event_t *poll_wake,
|
|
*poll_complete,
|
|
*queue_not_in_use;
|
|
} netdata_t;
|
|
|
|
|
|
/* Global variables. */
|
|
#ifdef ENABLE_NETWORK_LOG
|
|
int network_do_log = ENABLE_NETWORK_LOG;
|
|
#endif
|
|
int network_host_ndev;
|
|
netdev_t network_host_devs[32];
|
|
|
|
|
|
static netdata_t netdata; /* operational data per card */
|
|
static const struct {
|
|
const char *internal_name;
|
|
const network_t *net;
|
|
} networks[] = {
|
|
{ "none", NULL },
|
|
|
|
{ "slirp", &network_slirp },
|
|
{ "pcap", &network_pcap },
|
|
#ifdef USE_VNS
|
|
{ "vns", &network_vns },
|
|
#endif
|
|
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
|
|
/* UI */
|
|
int
|
|
network_get_from_internal_name(const char *s)
|
|
{
|
|
int c = 0;
|
|
|
|
while (networks[c].internal_name != NULL) {
|
|
if (! strcmp(networks[c].internal_name, s))
|
|
return(c);
|
|
c++;
|
|
}
|
|
|
|
/* Not found. */
|
|
return(0);
|
|
}
|
|
|
|
|
|
/* UI */
|
|
const char *
|
|
network_get_internal_name(int net)
|
|
{
|
|
return(networks[net].internal_name);
|
|
}
|
|
|
|
|
|
/* UI */
|
|
const char *
|
|
network_getname(int net)
|
|
{
|
|
if (networks[net].net != NULL)
|
|
return(networks[net].net->name);
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
|
|
/* UI */
|
|
int
|
|
network_available(int net)
|
|
{
|
|
if (networks[net].net && networks[net].net->available)
|
|
return(networks[net].net->available());
|
|
|
|
return(1);
|
|
}
|
|
|
|
|
|
#ifdef _LOGGING
|
|
void
|
|
network_log(int level, const char *fmt, ...)
|
|
{
|
|
# ifdef ENABLE_NETWORK_LOG
|
|
va_list ap;
|
|
|
|
if (network_do_log >= level) {
|
|
va_start(ap, fmt);
|
|
pclog_ex(fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
|
|
void
|
|
network_wait(int8_t do_wait)
|
|
{
|
|
if (do_wait)
|
|
thread_wait_mutex(netdata.mutex);
|
|
else
|
|
thread_release_mutex(netdata.mutex);
|
|
}
|
|
|
|
|
|
void
|
|
network_poll(void)
|
|
{
|
|
while (netdata.poll_busy)
|
|
thread_wait_event(netdata.poll_wake, -1);
|
|
|
|
thread_reset_event(netdata.poll_wake);
|
|
}
|
|
|
|
|
|
void
|
|
network_busy(int8_t set)
|
|
{
|
|
netdata.poll_busy = !!set;
|
|
|
|
if (! set)
|
|
thread_set_event(netdata.poll_wake);
|
|
}
|
|
|
|
|
|
void
|
|
network_end(void)
|
|
{
|
|
thread_set_event(netdata.poll_complete);
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize the configured network cards.
|
|
*
|
|
* This function gets called only once, from the System
|
|
* Platform initialization code (currently in pc.c) to
|
|
* set our local stuff to a known state.
|
|
*/
|
|
void
|
|
network_init(void)
|
|
{
|
|
wchar_t temp[512];
|
|
int i, k;
|
|
|
|
/* Clear the local data. */
|
|
memset(&netdata, 0x00, sizeof(netdata_t));
|
|
|
|
/* Initialize to a known state. */
|
|
network_type = 0;
|
|
network_card = 0;
|
|
|
|
/* Create a first device entry that's always there, as needed by UI. */
|
|
strcpy(network_host_devs[0].device, "none");
|
|
strcpy(network_host_devs[0].description, "None");
|
|
network_host_ndev = 1;
|
|
|
|
/* Initialize the network provider modules, if present. */
|
|
for (i = 0; networks[i].internal_name != NULL; i++) {
|
|
if (networks[i].net == NULL) continue;
|
|
|
|
/* Try to load network provider module. */
|
|
k = networks[i].net->init(&network_host_devs[network_host_ndev]);
|
|
if ((k < 0) && (i == network_type)) {
|
|
/* Provider not available. */
|
|
swprintf(temp, sizeof_w(temp),
|
|
get_string(IDS_ERR_NOLIB),
|
|
networks[i].net->name,
|
|
network_host_devs[network_host_ndev].description);
|
|
ui_msgbox(MBX_ERROR, temp);
|
|
continue;
|
|
}
|
|
|
|
/* If they have interfaces, add them. */
|
|
if (k > 0)
|
|
network_host_ndev += k;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Attach a network card to the system.
|
|
*
|
|
* This function is called by a hardware driver ("card") after it has
|
|
* finished initializing itself, to link itself to the platform support
|
|
* modules.
|
|
*/
|
|
void
|
|
network_attach(void *dev, uint8_t *mac, NETRXCB rx)
|
|
{
|
|
wchar_t temp[256];
|
|
|
|
if (network_card == 0) return;
|
|
|
|
/* Save the card's info. */
|
|
netdata.priv = dev;
|
|
netdata.rx = rx;
|
|
netdata.mac = mac;
|
|
|
|
/* Reset the network provider module. */
|
|
if (networks[network_type].net->reset(mac) < 0) {
|
|
/* Tell user we can't do this (at the moment.) */
|
|
swprintf(temp, sizeof_w(temp),
|
|
get_string(IDS_ERR_NONET), networks[network_type].net->name);
|
|
ui_msgbox(MBX_ERROR, temp);
|
|
|
|
// FIXME: we should ask in the dialog if they want to
|
|
// reconfigure or quit, and throw them into the
|
|
// Settings dialog if yes.
|
|
|
|
/* Disable network. */
|
|
network_type = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
/* Create the network events. */
|
|
netdata.poll_wake = thread_create_event();
|
|
netdata.poll_complete = thread_create_event();
|
|
}
|
|
|
|
|
|
/* Stop any network activity. */
|
|
void
|
|
network_close(void)
|
|
{
|
|
int i = 0;
|
|
|
|
/* If already closed, do nothing. */
|
|
if (netdata.mutex == NULL) return;
|
|
|
|
/* Force-close the network provider modules. */
|
|
while (networks[i].internal_name != NULL) {
|
|
if (networks[i].net)
|
|
networks[i].net->close();
|
|
i++;
|
|
}
|
|
|
|
/* Close the network events. */
|
|
if (netdata.poll_wake != NULL) {
|
|
thread_destroy_event(netdata.poll_wake);
|
|
netdata.poll_wake = NULL;
|
|
}
|
|
if (netdata.poll_complete != NULL) {
|
|
thread_destroy_event(netdata.poll_complete);
|
|
netdata.poll_complete = NULL;
|
|
}
|
|
|
|
/* Close the network thread mutex. */
|
|
thread_close_mutex(netdata.mutex);
|
|
netdata.mutex = NULL;
|
|
|
|
INFO("NETWORK: closed.\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* Reset the network card(s).
|
|
*
|
|
* This function is called each time the system is reset,
|
|
* either a hard reset (including power-up) or a soft reset
|
|
* including C-A-D reset.) It is responsible for connecting
|
|
* everything together.
|
|
*/
|
|
void
|
|
network_reset(void)
|
|
{
|
|
const device_t *dev;
|
|
|
|
#ifdef ENABLE_NETWORK_LOG
|
|
INFO("NETWORK: reset (type=%d, card=%d) debug=%d\n",
|
|
network_type, network_card, network_do_log);
|
|
#else
|
|
INFO("NETWORK: reset (type=%d, card=%d)\n",
|
|
network_type, network_card);
|
|
#endif
|
|
ui_sb_icon_update(SB_NETWORK, 0);
|
|
|
|
/* Just in case.. */
|
|
network_close();
|
|
|
|
/* If no active card, we're done. */
|
|
if ((network_type == 0) || (network_card == 0)) return;
|
|
|
|
netdata.mutex = thread_create_mutex(L"VARCem.NetMutex");
|
|
|
|
INFO("NETWORK: set up for %s, card='%s'\n",
|
|
network_getname(network_type), network_card_getname(network_card));
|
|
|
|
/* Add the selected card to the I/O system. */
|
|
dev = network_card_getdevice(network_card);
|
|
if (dev != NULL)
|
|
device_add(dev);
|
|
}
|
|
|
|
|
|
/* Transmit a packet to one of the network providers. */
|
|
void
|
|
network_tx(uint8_t *bufp, int len)
|
|
{
|
|
ui_sb_icon_update(SB_NETWORK, 1);
|
|
|
|
#if defined(WALTJE) && defined(_DEBUG) && defined(ENABLE_NETWORK_DUMP)
|
|
{
|
|
char temp[8192];
|
|
hexdump_p(temp, bufp, len);
|
|
DBGLOG(2, "NETWORK: >> len=%d\n%s\n", len, temp);
|
|
}
|
|
#endif
|
|
|
|
networks[network_type].net->send(bufp, len);
|
|
|
|
ui_sb_icon_update(SB_NETWORK, 0);
|
|
}
|
|
|
|
|
|
/* Process a packet received from one of the network providers. */
|
|
void
|
|
network_rx(uint8_t *bufp, int len)
|
|
{
|
|
ui_sb_icon_update(SB_NETWORK, 1);
|
|
|
|
#if defined(WALTJE) && defined(_DEBUG) && defined(ENABLE_NETWORK_DUMP)
|
|
{
|
|
char temp[8192];
|
|
hexdump_p(temp, bufp, len);
|
|
DBGLOG(2, "NETWORK: << len=%d\n%s\n", len, temp);
|
|
}
|
|
#endif
|
|
|
|
if (netdata.rx && netdata.priv)
|
|
netdata.rx(netdata.priv, bufp, len);
|
|
|
|
ui_sb_icon_update(SB_NETWORK, 0);
|
|
}
|
|
|
|
|
|
/* Get name of host-based network interface. */
|
|
int
|
|
network_card_to_id(const char *devname)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < network_host_ndev; i++) {
|
|
if (! strcmp(network_host_devs[i].device, devname)) {
|
|
return(i);
|
|
}
|
|
}
|
|
|
|
/* Not found. */
|
|
return(0);
|
|
}
|