mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-05-09 03:41:10 +08:00
193 lines
6.3 KiB
C++
193 lines
6.3 KiB
C++
/*
|
|
* 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 <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
using io_port_t = uint16_t; // DOS only supports 16-bit port addresses
|
|
|
|
#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 = false;
|
|
Bitu m_port, m_mask/*IO_MB, etc.*/, m_range/*number of ports*/;
|
|
public:
|
|
IO_Base() : 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() {};
|
|
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 = 0xFFFF;
|
|
uint16_t range_mask = 0;
|
|
uint16_t alias_mask = 0xFFFF;
|
|
unsigned int getcounter = 0;
|
|
IO_ReadCalloutHandler *m_r_handler = NULL;
|
|
IO_WriteCalloutHandler *m_w_handler = NULL;
|
|
bool alloc = false;
|
|
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
|