/* * 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. * * Implement I/O ports and their operations. * * Version: @(#)io.c 1.0.6 2019/05/17 * * Authors: Fred N. van Kempen, * Miran Grca, * Sarah Walker, * * Copyright 2017-2019 Fred N. van Kempen. * 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 #include #include #include #include #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); 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); priv_t priv; struct _io_ *prev, *next; } io_t; static io_t **io = NULL, **io_last = NULL; /* 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); } #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); } #endif void io_reset(void) { io_t *p, *q; int c; 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); } /* Clear both arrays. */ for (c = 0; c < NPORTS; c++) { if (io_last[c] != NULL) { /* At least one handler, free all handlers. */ p = io_last[c]; while (p != NULL) { q = p->prev; free(p); p = q; } } /* Reset handler. */ io[c] = io_last[c] = NULL; #ifdef IO_CATCH /* Add a default (catch) handler. */ catch_add(c); #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) { io_t *p; int c; 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; #ifdef IO_CATCH /* Unlink the catcher if that is the only handler. */ if (log_level < LOG_DETAIL) catch_del(base + c); #endif /* Insert this new handler. */ io_insert(base + c, p); } } 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) { io_t *p; int c; for (c = 0; c < size; c++) { 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) && (p->priv == priv)) { io_unlink(base + c); 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) { io_t *p, *q; int c; size <<= 2; for (c = 0; c < size; c += 2) { p = last_handler(base + c); q = (io_t *)mem_alloc(sizeof(io_t)); 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; q->outb = f_outb; q->outw = f_outw; q->outl = f_outl; 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) { 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) && (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); #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) DEBUG("IOTRACE(%04X): outb(%04x,%02x)\n", IO_TRACE, port, val); #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; } outb(port,val & 0xff); 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); }