DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 #ifndef DOSBOX_DMA_H 00021 #define DOSBOX_DMA_H 00022 #include <fstream> 00023 00024 enum DMAEvent { 00025 DMA_REACHED_TC, 00026 DMA_MASKED, 00027 DMA_UNMASKED, 00028 DMA_TRANSFEREND 00029 }; 00030 00031 enum DMATransfer { 00032 DMAT_VERIFY=0, 00033 DMAT_WRITE=1, 00034 DMAT_READ=2 00035 }; 00036 00037 class DmaChannel; 00038 typedef void (* DMA_CallBack)(DmaChannel * chan,DMAEvent event); 00039 00040 class DmaChannel { 00041 public: 00042 Bit32u pagebase; 00043 Bit16u baseaddr; 00044 Bit32u curraddr; 00045 Bit16u basecnt; 00046 Bit16u currcnt; 00047 Bit8u channum; 00048 Bit8u pagenum; 00049 Bit8u DMA16_PAGESHIFT; 00050 Bit32u DMA16_ADDRMASK; 00051 Bit8u DMA16; 00052 Bit8u transfer_mode; 00053 bool increment; 00054 bool autoinit; 00055 bool masked; 00056 bool tcount; 00057 bool request; 00058 DMA_CallBack callback; 00059 00060 // additional PC-98 proprietary feature: 00061 // auto "bank" increment on DMA wraparound. 00062 // 00063 // I/O port 29h: 00064 // bits [7:4] = 0 00065 // bits [3:2] = increment mode 0=64KB wraparound (no incr) 1=1MB boundary wrap 2=invalid 3=16MB boundary wrap 00066 // bits [1:0] = which DMA channel to set 00067 // 00068 // This value is set by: 00069 // 0 = 0x00 00070 // 1 = 0x0F 00071 // 2 = 0xF0 (probably why it's invalid) 00072 // 3 = 0xFF 00073 // 00074 // TODO: Does this setting stick or does it reset after normal legacy programming? 00075 // TODO: When the bank auto increments does it increment the actual register or just 00076 // an internal copy? 00077 Bit8u page_bank_increment_wraparound = 0u; 00078 00079 void page_bank_increment(void) { // to be called on DMA wraparound 00080 if (page_bank_increment_wraparound != 0u) { 00081 // FIXME: Improve this. 00082 // Currently this code assumes that the auto increment in PC-98 modifies the 00083 // register value (and therefore visible to the guest). Change this code if 00084 // that model is wrong. 00085 const Bit8u add = 00086 increment ? 0x01u : 0xFFu; 00087 const Bit8u nv = 00088 ( pagenum & (~page_bank_increment_wraparound)) + 00089 ((pagenum + add) & ( page_bank_increment_wraparound)); 00090 SetPage(nv); 00091 } 00092 } 00093 00094 DmaChannel(Bit8u num, bool dma16); 00095 void DoCallBack(DMAEvent event) { 00096 if (callback) (*callback)(this,event); 00097 } 00098 void SetMask(bool _mask) { 00099 masked=_mask; 00100 DoCallBack(masked ? DMA_MASKED : DMA_UNMASKED); 00101 } 00102 void Set128KMode(bool en) { 00103 // 128KB mode (legacy ISA) (en=true): 00104 // page shift = 1 (discard bit 0 of page register) 00105 // addr mask = 0x1FFFF (all bits 0-15 become bits 1-16, bit 15 of addr takes the place of page register bit 0) 00106 // 64KB mode (modern PCI including Intel chipsets) (en=false): 00107 // page shift = 0 (all 8 bits of page register are used) 00108 // addr mask = 0xFFFF (discard bit 15, bits 0-14 become bits 1-15 on ISA bus) 00109 DMA16_PAGESHIFT = (en && DMA16) ? 0x1 : 0x0; // nonzero if we're to discard bit 0 of page register 00110 DMA16_ADDRMASK = (1UL << ((en && DMA16) ? 17UL : 16UL)) - 1UL; // nonzero if (addrreg << 1) to cover 128KB, zero if (addrreg << 1) to discard MSB, limit to 64KB 00111 } 00112 void Register_Callback(DMA_CallBack _cb) { 00113 callback = _cb; 00114 SetMask(masked); 00115 if (callback) Raise_Request(); 00116 else Clear_Request(); 00117 } 00118 void ReachedTC(void) { 00119 tcount=true; 00120 DoCallBack(DMA_REACHED_TC); 00121 } 00122 void SetPage(Bit8u val) { 00123 pagenum=val; 00124 pagebase=(Bit32u)(pagenum >> DMA16_PAGESHIFT) << (Bit32u)((Bit8u)16u + DMA16_PAGESHIFT); 00125 } 00126 void Raise_Request(void) { 00127 request=true; 00128 } 00129 void Clear_Request(void) { 00130 request=false; 00131 } 00132 Bitu Read(Bitu want, Bit8u * buffer); 00133 Bitu Write(Bitu want, Bit8u * buffer); 00134 00135 void SaveState( std::ostream& stream ); 00136 void LoadState( std::istream& stream ); 00137 }; 00138 00139 class DmaController { 00140 private: 00141 Bit8u ctrlnum; 00142 bool flipflop; 00143 DmaChannel* DmaChannels[4] = {}; 00144 public: 00145 IO_ReadHandleObject DMA_ReadHandler[0x15]; 00146 IO_WriteHandleObject DMA_WriteHandler[0x15]; 00147 DmaController(Bit8u num) { 00148 flipflop = false; 00149 ctrlnum = num; /* first or second DMA controller */ 00150 for(Bit8u i=0;i<4;i++) { 00151 DmaChannels[i] = new DmaChannel(i+ctrlnum*4,ctrlnum==1); 00152 } 00153 } 00154 ~DmaController(void) { 00155 for(Bit8u i=0;i<4;i++) { 00156 delete DmaChannels[i]; 00157 } 00158 } 00159 DmaChannel * GetChannel(Bit8u chan) { 00160 if (chan<4) return DmaChannels[chan]; 00161 else return NULL; 00162 } 00163 void WriteControllerReg(Bitu reg,Bitu val,Bitu len); 00164 Bitu ReadControllerReg(Bitu reg,Bitu len); 00165 00166 void SaveState( std::ostream& stream ); 00167 void LoadState( std::istream& stream ); 00168 }; 00169 00170 DmaChannel * GetDMAChannel(Bit8u chan); 00171 00172 void CloseSecondDMAController(void); 00173 bool SecondDMAControllerAvailable(void); 00174 00175 void DMA_SetWrapping(Bit32u wrap); 00176 00177 #endif