DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
include/dma.h
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