/* * 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_DMA_H #define DOSBOX_DMA_H enum DMAEvent { DMA_REACHED_TC, DMA_MASKED, DMA_UNMASKED, // DMA_TRANSFEREND, this shouldn't really be a signal }; enum DMATransfer { DMAT_VERIFY=0, DMAT_WRITE=1, DMAT_READ=2 }; class DmaChannel; typedef void (* DMA_CallBack)(DmaChannel * chan,DMAEvent event); class DmaChannel { public: uint32_t pagebase; uint16_t baseaddr; uint32_t curraddr; uint16_t basecnt; uint16_t currcnt; uint8_t channum; uint8_t pagenum; uint8_t DMA16_PAGESHIFT; uint32_t DMA16_ADDRMASK; uint8_t DMA16; uint8_t transfer_mode; bool increment; bool autoinit; bool masked; bool tcount; bool request; DMA_CallBack callback; // additional PC-98 proprietary feature: // auto "bank" increment on DMA wraparound. // // I/O port 29h: // bits [7:4] = 0 // bits [3:2] = increment mode 0=64KB wraparound (no incr) 1=1MB boundary wrap 2=invalid 3=16MB boundary wrap // bits [1:0] = which DMA channel to set // // This value is set by: // 0 = 0x00 // 1 = 0x0F // 2 = 0xF0 (probably why it's invalid) // 3 = 0xFF // // TODO: Does this setting stick or does it reset after normal legacy programming? // TODO: When the bank auto increments does it increment the actual register or just // an internal copy? uint8_t page_bank_increment_wraparound = 0u; void page_bank_increment(void) { // to be called on DMA wraparound if (page_bank_increment_wraparound != 0u) { // FIXME: Improve this. // Currently this code assumes that the auto increment in PC-98 modifies the // register value (and therefore visible to the guest). Change this code if // that model is wrong. const uint8_t add = increment ? 0x01u : 0xFFu; const uint8_t nv = ( pagenum & (~page_bank_increment_wraparound)) + ((pagenum + add) & ( page_bank_increment_wraparound)); SetPage(nv); } } DmaChannel(uint8_t num, bool dma16); void DoCallBack(DMAEvent event) { if (callback) (*callback)(this,event); } void SetMask(bool _mask) { masked=_mask; DoCallBack(masked ? DMA_MASKED : DMA_UNMASKED); } void Set128KMode(bool en) { // 128KB mode (legacy ISA) (en=true): // page shift = 1 (discard bit 0 of page register) // addr mask = 0x1FFFF (all bits 0-15 become bits 1-16, bit 15 of addr takes the place of page register bit 0) // 64KB mode (modern PCI including Intel chipsets) (en=false): // page shift = 0 (all 8 bits of page register are used) // addr mask = 0xFFFF (discard bit 15, bits 0-14 become bits 1-15 on ISA bus) DMA16_PAGESHIFT = (en && DMA16) ? 0x1 : 0x0; // nonzero if we're to discard bit 0 of page register 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 } void Register_Callback(DMA_CallBack _cb) { callback = _cb; SetMask(masked); if (callback) Raise_Request(); else Clear_Request(); } void ReachedTC(void) { tcount=true; DoCallBack(DMA_REACHED_TC); } void SetPage(uint8_t val) { pagenum=val; pagebase=(uint32_t)(pagenum >> DMA16_PAGESHIFT) << (uint32_t)((uint8_t)16u + DMA16_PAGESHIFT); } void Raise_Request(void) { request=true; } void Clear_Request(void) { request=false; } Bitu Read(Bitu want, uint8_t * buffer); Bitu Write(Bitu want, uint8_t * buffer); void SaveState( std::ostream& stream ); void LoadState( std::istream& stream ); }; class DmaController { private: uint8_t ctrlnum; bool flipflop; DmaChannel* DmaChannels[4] = {}; public: IO_ReadHandleObject DMA_ReadHandler[0x15]; IO_WriteHandleObject DMA_WriteHandler[0x15]; DmaController(uint8_t num) { flipflop = false; ctrlnum = num; /* first or second DMA controller */ for(uint8_t i=0;i<4;i++) { DmaChannels[i] = new DmaChannel(i+ctrlnum*4,ctrlnum==1); } } ~DmaController(void) { for(uint8_t i=0;i<4;i++) { delete DmaChannels[i]; } } DmaChannel * GetChannel(uint8_t chan) { if (chan<4) return DmaChannels[chan]; else return NULL; } void WriteControllerReg(Bitu reg,Bitu val,Bitu len); Bitu ReadControllerReg(Bitu reg,Bitu len); void SaveState( std::ostream& stream ); void LoadState( std::istream& stream ); }; DmaChannel * GetDMAChannel(uint8_t chan); void CloseSecondDMAController(void); bool SecondDMAControllerAvailable(void); void DMA_SetWrapping(uint32_t wrap); #endif