here begins the big question: what do motherboards do with the address

register and page register when carrying out 16-bit DMA? this change
updates the code to allow either one method that allows 128KB boundaries
for 16-bit DMA (which DOSBox has already been doing anyway) and the
other method that limits 16-bit DMA to 64KB boundaries but preserves all
bits of the page register (which is said to be the way PCI-based
motherboard do it). You can choose which one from dosbox.conf now, or
let DOSBox-X decide based on your hardware configuration (which is
currently based on whether or not the VM has PCI emulation enabled).
This commit is contained in:
Jonathan Campbell 2016-10-18 17:44:11 -07:00
parent 014b5f9930
commit 82cd1b54ad
4 changed files with 59 additions and 14 deletions

View File

@ -40,6 +40,8 @@ public:
Bit16u currcnt;
Bit8u channum;
Bit8u pagenum;
Bit8u DMA16_PAGESHIFT;
Bit32u DMA16_ADDRMASK;
Bit8u DMA16;
bool increment;
bool autoinit;
@ -57,6 +59,16 @@ public:
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);
@ -69,7 +81,7 @@ public:
}
void SetPage(Bit8u val) {
pagenum=val;
pagebase=(pagenum >> DMA16) << (16+DMA16);
pagebase=(pagenum >> DMA16_PAGESHIFT) << (16+DMA16_PAGESHIFT);
}
void Raise_Request(void) {
request=true;

View File

@ -843,6 +843,7 @@ void DOSBOX_SetupConfigSections(void) {
const char *qualityno[] = { "0", "1", "2", "3", 0 };
const char* tandys[] = { "auto", "on", "off", 0};
const char* ps1opt[] = { "on", "off", 0};
const char* truefalseautoopt[] = { "true", "false", "1", "0", "auto", 0};
const char* irqssbhack[] = {
"none", "cs_equ_ds", 0
@ -1157,6 +1158,12 @@ void DOSBOX_SetupConfigSections(void) {
Pbool->Set_help("If set, allow increment & decrement modes as specified in the 8237 datasheet.\n"
"If clear, always increment the address (as if to emulate clone 8237 implementations that skipped the inc/dec bit).");
Pstring = secprop->Add_string("enable 128k capable 16-bit dma", Property::Changeable::OnlyAtStart,"auto");
Pstring->Set_values(truefalseautoopt);
Pstring->Set_help("If true, DMA controller emulation models ISA hardware that permits 16-bit DMA to span 128KB.\n"
"If false, DMA controller emulation models PCI hardware that limits 16-bit DMA to 64KB boundaries.\n"
"If auto, the choice is made according to other factors in hardware emulation");
Pbool = secprop->Add_bool("enable dma extra page registers",Property::Changeable::WhenIdle,true);
Pbool->Set_help("If set, emulate the extra page registers (I/O ports 0x80, 0x84-0x86, 0x88, 0x8C-0x8E), like actual hardware.\n"
"Note that mainline DOSBox behavior is to NOT emulate these registers.");

View File

@ -27,6 +27,8 @@
#include "setup.h"
#include "control.h"
bool has_pcibus_enable(void);
DmaController *DmaControllers[2]={NULL};
unsigned char dma_extra_page_registers[16]={0}; /* 0x80-0x8F */
bool enable_dma_extra_page_registers = true;
@ -40,6 +42,7 @@ static Bit32u dma_wrapping = 0xffff;
bool enable_1st_dma = true;
bool enable_2nd_dma = true;
bool allow_decrement_mode = true;
int isadma128k = -1;
static void UpdateEMSMapping(void) {
/* if EMS is not present, this will result in a 1:1 mapping */
@ -50,12 +53,12 @@ static void UpdateEMSMapping(void) {
}
/* read a block from physical memory */
static void DMA_BlockRead(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u dma16) {
static void DMA_BlockRead(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u dma16,const Bit32u DMA16_ADDRMASK) {
Bit8u * write=(Bit8u *) data;
Bitu highpart_addr_page = spage>>12;
size <<= dma16;
offset <<= dma16;
Bit32u dma_wrap = ((0xffff<<dma16)+dma16) | dma_wrapping;
Bit32u dma_wrap = (((0xffff<<dma16)+dma16)&DMA16_ADDRMASK) | dma_wrapping;
for ( ; size ; size--, offset++) {
offset &= dma_wrap;
Bitu page = highpart_addr_page+(offset >> 12);
@ -73,13 +76,13 @@ static void DMA_BlockRead(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u
* NTS: Don't forget, from 8237 datasheet: The DMA chip transfers a byte (or word if 16-bit) of data,
* and THEN increments or decrements the address. So in decrement mode, "address" is still the
* first byte before decrementing. */
static void DMA_BlockReadBackwards(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u dma16) {
static void DMA_BlockReadBackwards(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u dma16,const Bit32u DMA16_ADDRMASK) {
Bit8u * write=(Bit8u *) data;
Bitu highpart_addr_page = spage>>12;
size <<= dma16;
offset <<= dma16;
Bit32u dma_wrap = ((0xffff<<dma16)+dma16) | dma_wrapping;
Bit32u dma_wrap = (((0xffff<<dma16)+dma16)&DMA16_ADDRMASK) | dma_wrapping;
if (dma16) {
/* I'm going to assume by how ISA DMA works that you can't just copy bytes backwards,
@ -114,12 +117,12 @@ static void DMA_BlockReadBackwards(PhysPt spage,PhysPt offset,void * data,Bitu s
}
/* write a block into physical memory */
static void DMA_BlockWrite(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u dma16) {
static void DMA_BlockWrite(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u dma16,const Bit32u DMA16_ADDRMASK) {
Bit8u * read=(Bit8u *) data;
Bitu highpart_addr_page = spage>>12;
size <<= dma16;
offset <<= dma16;
Bit32u dma_wrap = ((0xffff<<dma16)+dma16) | dma_wrapping;
Bit32u dma_wrap = (((0xffff<<dma16)+dma16)&DMA16_ADDRMASK) | dma_wrapping;
for ( ; size ; size--, offset++) {
if (offset>(dma_wrapping<<dma16)) {
LOG_MSG("DMA segbound wrapping (write): %x:%x size %x [%x] wrap %x",(int)spage,(int)offset,(int)size,dma16,(int)dma_wrapping);
@ -342,7 +345,15 @@ DmaChannel::DmaChannel(Bit8u num, bool dma16) {
if(num == 4) return;
channum = num;
DMA16 = dma16 ? 0x1 : 0x0;
pagenum = 0;
if (isadma128k >= 0)
Set128KMode(isadma128k > 0); // user's choice
else
Set128KMode(has_pcibus_enable()); // auto, based on whether PCI bus is present
LOG(LOG_DMACONTROL,LOG_DEBUG)("DMA channel %u. DMA16_PAGESHIFT=%u DMA16_ADDRMASK=0x%lx",
(unsigned int)channum,(unsigned int)DMA16_PAGESHIFT,(unsigned long)DMA16_ADDRMASK);
pagenum = 0;
pagebase = 0;
baseaddr = 0;
curraddr = 0;
@ -368,11 +379,11 @@ again:
Bitu left=(currcnt+1);
if (want<left) {
if (increment) {
DMA_BlockRead(pagebase,curraddr,buffer,want,DMA16);
DMA_BlockRead(pagebase,curraddr,buffer,want,DMA16,DMA16_ADDRMASK);
curraddr+=want;
}
else {
DMA_BlockReadBackwards(pagebase,curraddr,buffer,want,DMA16);
DMA_BlockReadBackwards(pagebase,curraddr,buffer,want,DMA16,DMA16_ADDRMASK);
curraddr-=want;
}
@ -380,9 +391,9 @@ again:
done+=want;
} else {
if (increment)
DMA_BlockRead(pagebase,curraddr,buffer,want,DMA16);
DMA_BlockRead(pagebase,curraddr,buffer,want,DMA16,DMA16_ADDRMASK);
else
DMA_BlockReadBackwards(pagebase,curraddr,buffer,want,DMA16);
DMA_BlockReadBackwards(pagebase,curraddr,buffer,want,DMA16,DMA16_ADDRMASK);
buffer+=left << DMA16;
want-=left;
@ -425,12 +436,12 @@ Bitu DmaChannel::Write(Bitu want, Bit8u * buffer) {
again:
Bitu left=(currcnt+1);
if (want<left) {
DMA_BlockWrite(pagebase,curraddr,buffer,want,DMA16);
DMA_BlockWrite(pagebase,curraddr,buffer,want,DMA16,DMA16_ADDRMASK);
done+=want;
curraddr+=want;
currcnt-=want;
} else {
DMA_BlockWrite(pagebase,curraddr,buffer,left,DMA16);
DMA_BlockWrite(pagebase,curraddr,buffer,left,DMA16,DMA16_ADDRMASK);
buffer+=left << DMA16;
want-=left;
done+=left;
@ -491,6 +502,17 @@ void DMA_Reset(Section* /*sec*/) {
dma_page_register_writeonly = section->Get_bool("dma page registers write-only");
allow_decrement_mode = section->Get_bool("allow dma address decrement");
{
std::string s = section->Get_string("enable 128k capable 16-bit dma");
if (s == "true" || s == "1")
isadma128k = 1;
else if (s == "false" || s == "0")
isadma128k = 0;
else
isadma128k = -1;
}
if (enable_1st_dma)
DmaControllers[0] = new DmaController(0);
else

View File

@ -34,6 +34,10 @@
bool pcibus_enable = false;
bool log_pci = false;
bool has_pcibus_enable(void) {
return pcibus_enable;
}
static Bit32u pci_caddress=0; // current PCI addressing
static PCI_Device* pci_devices[PCI_MAX_PCIBUSSES][PCI_MAX_PCIDEVICES]={{NULL}}; // registered PCI devices