/* * Copyright (C) 2002-2021 The DOSBox Team * * 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef DOSBOX_INOUT_H #define DOSBOX_INOUT_H #include #include #define IO_MAX (64*1024+3) #define IO_MB 0x1 #define IO_MW 0x2 #define IO_MD 0x4 #define IO_MA (IO_MB | IO_MW | IO_MD ) class IO_CalloutObject; typedef Bitu IO_ReadHandler(Bitu port,Bitu iolen); typedef void IO_WriteHandler(Bitu port,Bitu val,Bitu iolen); typedef IO_ReadHandler* (IO_ReadCalloutHandler)(IO_CalloutObject &co,Bitu port,Bitu iolen); typedef IO_WriteHandler* (IO_WriteCalloutHandler)(IO_CalloutObject &co,Bitu port,Bitu iolen); extern IO_WriteHandler * io_writehandlers[3][IO_MAX]; extern IO_ReadHandler * io_readhandlers[3][IO_MAX]; void IO_RegisterReadHandler(Bitu port,IO_ReadHandler * handler,Bitu mask,Bitu range=1); void IO_RegisterWriteHandler(Bitu port,IO_WriteHandler * handler,Bitu mask,Bitu range=1); void IO_FreeReadHandler(Bitu port,Bitu mask,Bitu range=1); void IO_FreeWriteHandler(Bitu port,Bitu mask,Bitu range=1); void IO_InvalidateCachedHandler(Bitu port,Bitu range=1); void IO_WriteB(Bitu port,uint8_t val); void IO_WriteW(Bitu port,uint16_t val); void IO_WriteD(Bitu port,uint32_t val); uint8_t IO_ReadB(Bitu port); uint16_t IO_ReadW(Bitu port); uint32_t IO_ReadD(Bitu port); static const Bitu IOMASK_ISA_10BIT = 0x3FFU; /* ISA 10-bit decode */ static const Bitu IOMASK_ISA_12BIT = 0xFFFU; /* ISA 12-bit decode */ static const Bitu IOMASK_FULL = 0xFFFFU; /* full 16-bit decode */ /* WARNING: Will only produce a correct result if 'x' is a nonzero power of two. * For use with IOMASK_Combine. * * A device with 16 I/O ports would produce a range mask of: * * ~(16 - 1) = ~15 = ~0xF = 0xFFFFFFF0 * * or * * ~(0x10 - 1) = ~0xF = 0xFFFFFFF0 * */ static inline constexpr Bitu IOMASK_Range(const Bitu x) { return ~((Bitu)x - (Bitu)1U); } /* combine range mask with IOMASK value. * * Example: Sound Blaster 10-bit decode with 16 I/O ports: * * IOMASK_Combine(IOMASK_ISA_10BIT,IOMASK_Range(16)); * */ static inline constexpr Bitu IOMASK_Combine(const Bitu a,const Bitu b) { return a & b; } /* Classes to manage the IO objects created by the various devices. * The io objects will remove itself on destruction.*/ class IO_Base{ protected: bool installed; Bitu m_port, m_mask/*IO_MB, etc.*/, m_range/*number of ports*/; public: IO_Base() : installed(false), m_port(0), m_mask(0), m_range(0) {}; }; /* NTS: To explain the Install() method, the caller not only provides the IOMASK_.. value, but ANDs * the least significant bits to define the range of I/O ports to respond to. An ISA Sound Blaster * for example would set portmask = (IOMASK_ISA_10BIT & (~0xF)) in order to respond to 220h-22Fh, * 240h-24Fh, etc. At I/O callout time, the callout object is tested * if (cpu_ioport & io_mask) == (m_port & io_mask) * * This does not prevent emulation of devices that start on non-aligned ports or strange port ranges, * because the callout handler is free to decline the I/O request, leading the callout process to * move on to the next device or mark the I/O port as empty. */ class IO_CalloutObject: private IO_Base { public: IO_CalloutObject() : IO_Base(), io_mask(0xFFFFU), range_mask(0U), alias_mask(0xFFFFU), getcounter(0), m_r_handler(NULL), m_w_handler(NULL), alloc(false) {}; void InvalidateCachedHandlers(void); void Install(Bitu port,Bitu portmask/*IOMASK_ISA_10BIT, etc.*/,IO_ReadCalloutHandler *r_handler,IO_WriteCalloutHandler *w_handler); void Uninstall(); public: uint16_t io_mask; uint16_t range_mask; uint16_t alias_mask; unsigned int getcounter; IO_ReadCalloutHandler *m_r_handler; IO_WriteCalloutHandler *m_w_handler; bool alloc; public: inline bool MatchPort(const uint16_t p) { /* (p & io_mask) == (m_port & io_mask) but this also works. * apparently modern x86 processors are faster at addition/subtraction than bitmasking. * for this to work, m_port must be a multiple of the I/O range. For example, if the I/O * range is 16 ports, then m_port must be a multiple of 16. */ return ((p - m_port) & io_mask) == 0; } inline bool isInstalled(void) { return installed; } }; class IO_ReadHandleObject: private IO_Base { public: IO_ReadHandleObject() : IO_Base() {}; void Install(Bitu port,IO_ReadHandler * handler,Bitu mask,Bitu range=1); void Uninstall(); ~IO_ReadHandleObject(); }; class IO_WriteHandleObject: private IO_Base{ public: IO_WriteHandleObject() : IO_Base() {}; void Install(Bitu port,IO_WriteHandler * handler,Bitu mask,Bitu range=1); void Uninstall(); ~IO_WriteHandleObject(); }; static INLINE void IO_Write(Bitu port,uint8_t val) { IO_WriteB(port,val); } static INLINE uint8_t IO_Read(Bitu port){ return IO_ReadB(port); } enum IO_Type_t { IO_TYPE_NONE=0, IO_TYPE_MIN=1, IO_TYPE_ISA=1, IO_TYPE_PCI, IO_TYPE_MB, IO_TYPE_MAX }; void IO_InitCallouts(void); typedef uint32_t IO_Callout_t; static inline constexpr uint32_t IO_Callout_t_comb(const enum IO_Type_t t,const uint32_t idx) { return ((uint32_t)t << (uint32_t)28) + idx; } static inline constexpr enum IO_Type_t IO_Callout_t_type(const IO_Callout_t t) { return (enum IO_Type_t)(t >> 28); } static inline constexpr uint32_t IO_Callout_t_index(const IO_Callout_t t) { return t & (((uint32_t)1 << (uint32_t)28) - (uint32_t)1); } static const IO_Callout_t IO_Callout_t_none = (IO_Callout_t)0; IO_Callout_t IO_AllocateCallout(IO_Type_t t); void IO_FreeCallout(IO_Callout_t c); IO_CalloutObject *IO_GetCallout(IO_Callout_t c); void IO_PutCallout(IO_CalloutObject *obj); #endif