This repository has been archived on 2025-05-24. You can view files and clone it, but cannot push or open issues or pull requests.
Files
VARCem/src/io.c

471 lines
9.1 KiB
C
Raw Normal View History

2018-02-20 21:44:51 -05:00
/*
2018-03-08 15:58:46 -05:00
* VARCem Virtual ARchaeological Computer EMulator.
2018-02-20 21:44:51 -05:00
* 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.
*
* Implement I/O ports and their operations.
*
* Version: @(#)io.c 1.0.6 2019/05/17
2018-02-20 21:44:51 -05:00
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* Sarah Walker, <tommowalker@tommowalker.co.uk>
*
* Copyright 2017-2019 Fred N. van Kempen.
2018-02-20 21:44:51 -05:00
* Copyright 2016-2018 Miran Grca.
* Copyright 2008-2018 Sarah Walker.
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include "emu.h"
#include "io.h"
#include "cpu/cpu.h"
#define NPORTS 65536 /* PC/AT supports 64K ports */
typedef struct _io_ {
uint8_t (*inb)(uint16_t, priv_t);
void (*outb)(uint16_t, uint8_t, priv_t);
2018-02-20 21:44:51 -05:00
uint16_t (*inw)(uint16_t, priv_t);
void (*outw)(uint16_t, uint16_t, priv_t);
uint32_t (*inl)(uint16_t, priv_t);
void (*outl)(uint16_t, uint32_t, priv_t);
2018-02-20 21:44:51 -05:00
priv_t priv;
2018-02-20 21:44:51 -05:00
struct _io_ *prev, *next;
} io_t;
static io_t **io = NULL,
**io_last = NULL;
2018-02-20 21:44:51 -05:00
/* Add an I/O handler to the chain. */
static void
io_insert(int c, io_t *q)
{
io_t *p;
p = io_last[c];
if (p != NULL) {
p->next = q;
q->prev = p;
} else {
io[c] = q;
q->prev = NULL;
}
io_last[c] = q;
}
/* Remove I/O handler from the chain. */
static void
io_unlink(int c)
{
io_t *p = io[c];
if (p->prev != NULL)
p->prev->next = p->next;
else
io[c] = p->next;
if (p->next != NULL)
p->next->prev = p->prev;
else
io_last[c] = p->prev;
free(p);
}
2018-02-20 21:44:51 -05:00
#ifdef IO_CATCH
static uint8_t
catch_inb(uint16_t addr, priv_t p)
{
DEBUG("IO: inb(%04x)\n", addr);
return(0xff);
}
static uint16_t
catch_inw(uint16_t addr, priv_t p)
{
DEBUG("IO: inw(%04x)\n", addr);
return(0xffff);
}
static uint32_t
catch_inl(uint16_t addr, priv_t p)
{
DEBUG("IO: inl(%04x)\n", addr);
return(0xffffffff);
}
static void
catch_outb(uint16_t addr, uint8_t val, priv_t p)
{
DEBUG("IO: outb(%04x, %02x)\n", addr, val);
}
static void
catch_outw(uint16_t addr, uint16_t val, priv_t p)
{
DEBUG("IO: outw(%04x, %04x)\n", addr, val);
}
static void
catch_outl(uint16_t addr, uint32_t val, priv_t p)
{
DEBUG("IO: outl(%04x, %08lx)\n", addr, val);
}
/* Add a catch handler to a port. */
static void
catch_add(int port)
{
io_t *p;
/* Create new handler. */
p = (io_t *)mem_alloc(sizeof(io_t));
memset(p, 0x00, sizeof(io_t));
p->inb = catch_inb;
p->outb = catch_outb;
p->inw = catch_inw;
p->outw = catch_outw;
p->inl = catch_inl;
p->outl = catch_outl;
/* Add to chain. */
io_insert(port, p);
}
/* If the only handler is the catcher, remove it. */
static void
catch_del(int port)
{
if ((io[port] != NULL) && (io[port]->inb == catch_inb))
io_unlink(port);
}
2018-02-20 21:44:51 -05:00
#endif
void
io_reset(void)
2018-02-20 21:44:51 -05:00
{
io_t *p, *q;
int c;
2018-10-06 18:20:09 -04:00
INFO("IO: initializing\n");
if (io == NULL) {
/* Allocate the arrays, one-time only. */
c = sizeof(io_t **) * NPORTS;
io = (io_t **)mem_alloc(c);
memset(io, 0x00, c);
io_last = (io_t **)mem_alloc(c);
memset(io_last, 0x00, c);
2018-02-20 21:44:51 -05:00
}
/* Clear both arrays. */
2018-10-06 18:20:09 -04:00
for (c = 0; c < NPORTS; c++) {
if (io_last[c] != NULL) {
/* At least one handler, free all handlers. */
2018-02-20 21:44:51 -05:00
p = io_last[c];
while (p != NULL) {
q = p->prev;
free(p);
p = q;
}
}
/* Reset handler. */
2018-02-20 21:44:51 -05:00
io[c] = io_last[c] = NULL;
#ifdef IO_CATCH
/* Add a default (catch) handler. */
catch_add(c);
2018-02-20 21:44:51 -05:00
#endif
}
}
void
io_sethandler(uint16_t base, int size,
uint8_t (*f_inb)(uint16_t addr, priv_t priv),
uint16_t (*f_inw)(uint16_t addr, priv_t priv),
uint32_t (*f_inl)(uint16_t addr, priv_t priv),
void (*f_outb)(uint16_t addr, uint8_t val, priv_t priv),
void (*f_outw)(uint16_t addr, uint16_t val, priv_t priv),
void (*f_outl)(uint16_t addr, uint32_t val, priv_t priv),
priv_t priv)
2018-02-20 21:44:51 -05:00
{
io_t *p;
2018-02-20 21:44:51 -05:00
int c;
2018-10-06 18:20:09 -04:00
for (c = 0; c < size; c++) {
/* Create entry for the new handler. */
p = (io_t *)mem_alloc(sizeof(io_t));
memset(p, 0x00, sizeof(io_t));
p->inb = f_inb; p->inw = f_inw; p->inl = f_inl;
p->outb = f_outb; p->outw = f_outw; p->outl = f_outl;
p->priv = priv;
2018-02-20 21:44:51 -05:00
#ifdef IO_CATCH
/* Unlink the catcher if that is the only handler. */
if (log_level < LOG_DETAIL)
catch_del(base + c);
#endif
2018-02-20 21:44:51 -05:00
/* Insert this new handler. */
io_insert(base + c, p);
2018-02-20 21:44:51 -05:00
}
}
void
io_removehandler(uint16_t base, int size,
uint8_t (*f_inb)(uint16_t addr, priv_t priv),
uint16_t (*f_inw)(uint16_t addr, priv_t priv),
uint32_t (*f_inl)(uint16_t addr, priv_t priv),
void (*f_outb)(uint16_t addr, uint8_t val, priv_t priv),
void (*f_outw)(uint16_t addr, uint16_t val, priv_t priv),
void (*f_outl)(uint16_t addr, uint32_t val, priv_t priv),
priv_t priv)
2018-02-20 21:44:51 -05:00
{
io_t *p;
int c;
2018-10-06 18:20:09 -04:00
for (c = 0; c < size; c++) {
2018-02-20 21:44:51 -05:00
p = io[base + c];
if (p == NULL)
continue;
for (; p != NULL; p = p->next) {
if ((p->inb == f_inb) && (p->inw == f_inw) &&
(p->inl == f_inl) && (p->outb == f_outb) &&
(p->outw == f_outw) && (p->outl == f_outl) &&
2018-02-20 21:44:51 -05:00
(p->priv == priv)) {
io_unlink(base + c);
2018-02-20 21:44:51 -05:00
p = NULL;
break;
}
}
}
}
#ifdef PC98
void
io_sethandler_interleaved(uint16_t base, int size,
uint8_t (*f_inb)(uint16_t addr, priv_t priv),
uint16_t (*f_inw)(uint16_t addr, priv_t priv),
uint32_t (*f_inl)(uint16_t addr, priv_t priv),
void (*f_outb)(uint16_t addr, uint8_t val, priv_t priv),
void (*f_outw)(uint16_t addr, uint16_t val, priv_t priv),
void (*f_outl)(uint16_t addr, uint32_t val, priv_t priv),
priv_t priv)
2018-02-20 21:44:51 -05:00
{
io_t *p, *q;
int c;
size <<= 2;
2018-10-06 18:20:09 -04:00
for (c = 0; c < size; c += 2) {
2018-02-20 21:44:51 -05:00
p = last_handler(base + c);
2018-10-06 18:20:09 -04:00
q = (io_t *)mem_alloc(sizeof(io_t));
2018-02-20 21:44:51 -05:00
memset(q, 0x00, sizeof(io_t));
if (p != NULL) {
p->next = q;
q->prev = p;
} else {
io[base + c] = q;
q->prev = NULL;
}
q->inb = f_inb; q->inw = f_inw; q->inl = f_inl;
2018-02-20 21:44:51 -05:00
q->outb = f_outb; q->outw = f_outw; q->outl = f_outl;
2018-02-20 21:44:51 -05:00
q->priv = priv;
}
}
void
io_removehandler_interleaved(uint16_t base, int size,
uint8_t (*f_inb)(uint16_t addr, priv_t priv),
uint16_t (*f_inw)(uint16_t addr, priv_t priv),
uint32_t (*f_inl)(uint16_t addr, priv_t priv),
void (*f_outb)(uint16_t addr, uint8_t val, priv_t priv),
void (*f_outw)(uint16_t addr, uint16_t val, priv_t priv),
void (*f_outl)(uint16_t addr, uint32_t val, priv_t priv),
priv_t priv)
2018-02-20 21:44:51 -05:00
{
io_t *p;
int c;
size <<= 2;
for (c = 0; c < size; c += 2) {
p = io[base + c];
if (p == NULL)
return;
while (p != NULL) {
if ((p->inb == f_inb) && (p->inw == f_inw) &&
(p->inl == f_inl) && (p->outb == f_outb) &&
(p->outw == f_outw) && (p->outl == f_outl) &&
2018-02-20 21:44:51 -05:00
(p->priv == priv)) {
if (p->prev != NULL)
p->prev->next = p->next;
if (p->next != NULL)
p->next->prev = p->prev;
free(p);
break;
}
p = p->next;
}
}
}
#endif
uint8_t
inb(uint16_t port)
{
uint8_t r = 0xff;
io_t *p;
p = io[port];
while(p != NULL) {
if (p->inb != NULL)
r &= p->inb(port, p->priv);
p = p->next;
}
#ifdef IO_TRACE
if (CS == IO_TRACE)
DEBUG("IOTRACE(%04x): inb(%04x)=%02x\n", IO_TRACE, port, r);
2018-02-20 21:44:51 -05:00
#endif
return(r);
}
void
outb(uint16_t port, uint8_t val)
{
io_t *p;
if (io[port] != NULL) {
p = io[port];
while (p != NULL) {
if (p->outb != NULL)
p->outb(port, val, p->priv);
p = p->next;
}
}
#ifdef IO_TRACE
if (CS == IO_TRACE)
2018-10-06 18:20:09 -04:00
DEBUG("IOTRACE(%04X): outb(%04x,%02x)\n", IO_TRACE, port, val);
2018-02-20 21:44:51 -05:00
#endif
}
uint16_t
inw(uint16_t port)
{
io_t *p;
p = io[port];
while(p != NULL) {
if (p->inw != NULL)
return(p->inw(port, p->priv));
p = p->next;
}
return(inb(port) | (inb(port + 1) << 8));
}
void
outw(uint16_t port, uint16_t val)
{
io_t *p;
p = io[port];
while(p != NULL) {
if (p->outw != NULL) {
p->outw(port, val, p->priv);
return;
}
p = p->next;
}
2018-03-24 23:55:21 +01:00
outb(port,val & 0xff);
2018-02-20 21:44:51 -05:00
outb(port+1,val>>8);
}
uint32_t
inl(uint16_t port)
{
io_t *p;
p = io[port];
while(p != NULL) {
if (p->inl != NULL)
return(p->inl(port, p->priv));
p = p->next;
}
return(inw(port) | (inw(port + 2) << 16));
}
void
outl(uint16_t port, uint32_t val)
{
io_t *p;
p = io[port];
while(p != NULL) {
if (p->outl != NULL) {
p->outl(port, val, p->priv);
return;
}
p = p->next;
}
outw(port, val);
outw(port + 2, val >> 16);
}