| /* | |
| * DMA Emulation | |
| * | |
| * Copyright 2002 Christian Costa | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library 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 | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include "config.h" | |
| #include "windef.h" | |
| #include "dosexe.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(dma); | |
| /* Internal registers of the 2 DMA chips wich control 8 DMA channels */ | |
| static DWORD DMA_BaseAddress[8]; | |
| static WORD DMA_ByteCount[8]; | |
| static DWORD DMA_CurrentBaseAddress[8]; | |
| static WORD DMA_CurrentByteCount[8]; | |
| static BYTE DMA_Command[8]; | |
| static BYTE DMA_Mask[2]={0x0F,0x0F}; | |
| static BYTE DMA_Status[2]={0x00,0x00}; | |
| static BOOL DMA_Toggle[2]={FALSE,FALSE}; | |
| /* | |
| * DMA_Transfer : Try to perform a transfer of reqlen elements (8 or 16 bits) | |
| * on the specified channel and return the elements transferred | |
| */ | |
| int DMA_Transfer(int channel,int reqlen,void* buffer) | |
| { | |
| int i,size,ret=0; | |
| int opmode,increment,autoinit,trmode,dmachip; | |
| int regmode = DMA_Command[channel]; | |
| char *p,*dmabuf; | |
| dmabuf = buffer; | |
| dmachip = (channel<4) ? 0 : 1; | |
| TRACE("DMA_Command = %x reqlen=%d\n",regmode,reqlen); | |
| /* Exit if channel is masked */ | |
| if (DMA_Mask[dmachip]&(1<<(channel&3))) | |
| return 0; | |
| opmode = (regmode & 0xC0) >> 6; | |
| increment = !(regmode & 0x20); | |
| autoinit = regmode & 0x10; | |
| trmode = (regmode & 0x0C) >> 2; | |
| /* Transfer size : 8 bits for channels 0..3, 16 bits for channels 4..7 */ | |
| size = (channel<4) ? 1 : 2; | |
| /* Process operating mode */ | |
| switch(opmode) | |
| { | |
| case 0: | |
| /* Request mode */ | |
| FIXME("Request Mode - Not Implemented\n"); | |
| return 0; | |
| case 1: | |
| /* Single Mode */ | |
| break; | |
| case 2: | |
| /* Request mode */ | |
| FIXME("Block Mode - Not Implemented\n"); | |
| return 0; | |
| case 3: | |
| /* Cascade Mode */ | |
| ERR("Cascade Mode should not be used by regular apps\n"); | |
| return 0; | |
| } | |
| /* Perform one the 4 transfer modes */ | |
| if (trmode == 4) { | |
| /* Illegal */ | |
| ERR("DMA Transfer Type Illegal\n"); | |
| return 0; | |
| } | |
| ret = min(DMA_CurrentByteCount[channel],reqlen); | |
| /* Update DMA registers */ | |
| DMA_CurrentByteCount[channel]-=ret; | |
| if (increment) | |
| DMA_CurrentBaseAddress[channel] += ret * size; | |
| else | |
| DMA_CurrentBaseAddress[channel] -= ret * size; | |
| switch(trmode) | |
| { | |
| case 0: | |
| /* Verification (no real transfer)*/ | |
| TRACE("Verification DMA operation\n"); | |
| break; | |
| case 1: | |
| /* Write */ | |
| TRACE("Perform Write transfer of %d bytes at %lx with count %x\n",ret, | |
| DMA_CurrentBaseAddress[channel],DMA_CurrentByteCount[channel]); | |
| if (increment) | |
| memcpy((void*)DMA_CurrentBaseAddress[channel],dmabuf,ret*size); | |
| else | |
| for(i=0,p=(char*)DMA_CurrentBaseAddress[channel];i<ret*size;i++) | |
| /* FIXME: possible endianness issue for 16 bits DMA */ | |
| *(p-i) = dmabuf[i]; | |
| break; | |
| case 2: | |
| /* Read */ | |
| TRACE("Perform Read transfer of %d bytes at %lx with count %x\n",ret, | |
| DMA_CurrentBaseAddress[channel],DMA_CurrentByteCount[channel]); | |
| if (increment) | |
| memcpy(dmabuf,(void*)DMA_CurrentBaseAddress[channel],ret*size); | |
| else | |
| for(i=0,p=(char*)DMA_CurrentBaseAddress[channel];i<ret*size;i++) | |
| /* FIXME: possible endianness issue for 16 bits DMA */ | |
| dmabuf[i] = *(p-i); | |
| break; | |
| } | |
| /* Check for end of transfer */ | |
| if (DMA_CurrentByteCount[channel]==0) { | |
| TRACE("DMA buffer empty\n"); | |
| /* Update status register of the DMA chip corresponding to the channel */ | |
| DMA_Status[dmachip] |= 1 << (channel & 0x3); /* Mark transfer as finished */ | |
| DMA_Status[dmachip] &= ~(1 << ((channel & 0x3) + 4)); /* Reset soft request if any */ | |
| if (autoinit) { | |
| /* Reload Current* register to their initial values */ | |
| DMA_CurrentBaseAddress[channel] = DMA_BaseAddress[channel]; | |
| DMA_CurrentByteCount[channel] = DMA_ByteCount[channel]; | |
| } | |
| } | |
| return ret; | |
| } | |
| void DMA_ioport_out( WORD port, BYTE val ) | |
| { | |
| int channel,dmachip; | |
| switch(port) | |
| { | |
| case 0x00: | |
| case 0x02: | |
| case 0x04: | |
| case 0x06: | |
| case 0xC0: | |
| case 0xC4: | |
| case 0xC8: | |
| case 0xCC: | |
| /* Base Address*/ | |
| channel = (port&0xC0)?((port-0xC0)>>2):(port>>1); | |
| dmachip = (channel<4) ? 0 : 1; | |
| if (!DMA_Toggle[dmachip]) | |
| DMA_BaseAddress[channel]=(DMA_BaseAddress[channel] & ~0xFF)|(val & 0xFF); | |
| else { | |
| DMA_BaseAddress[channel]=(DMA_BaseAddress[channel] & (~(0xFF << 8)))|((val & 0xFF) << 8); | |
| DMA_CurrentBaseAddress[channel] = DMA_BaseAddress[channel]; | |
| TRACE("Write Base Address = %lx\n",DMA_BaseAddress[channel]); | |
| } | |
| DMA_Toggle[dmachip] = !DMA_Toggle[dmachip]; | |
| break; | |
| case 0x01: | |
| case 0x03: | |
| case 0x05: | |
| case 0x07: | |
| case 0xC2: | |
| case 0xC6: | |
| case 0xCA: | |
| case 0xCE: | |
| /* Count*/ | |
| channel = ((port-1)&0xC0)?(((port-1)-0xC0)>>2):(port>>1); | |
| dmachip = (channel<4) ? 0 : 1; | |
| if (!DMA_Toggle[dmachip]) | |
| DMA_ByteCount[channel]=(DMA_ByteCount[channel] & ~0xFF)|((val+1) & 0xFF); | |
| else { | |
| DMA_ByteCount[channel]=(DMA_ByteCount[channel] & (~(0xFF << 8)))|(((val+1) & 0xFF) << 8); | |
| DMA_CurrentByteCount[channel] = DMA_ByteCount[channel]; | |
| TRACE("Write Count = %x.\n",DMA_ByteCount[channel]); | |
| } | |
| DMA_Toggle[dmachip] = !DMA_Toggle[dmachip]; | |
| break; | |
| /* Low Page Base Address */ | |
| case 0x87: DMA_BaseAddress[0]=(DMA_BaseAddress[0] & (~0xFF << 16))|((val & 0xFF) << 16); break; | |
| case 0x83: DMA_BaseAddress[1]=(DMA_BaseAddress[1] & (~0xFF << 16))|((val & 0xFF) << 16); break; | |
| case 0x81: DMA_BaseAddress[2]=(DMA_BaseAddress[2] & (~0xFF << 16))|((val & 0xFF) << 16); break; | |
| case 0x82: DMA_BaseAddress[3]=(DMA_BaseAddress[3] & (~0xFF << 16))|((val & 0xFF) << 16); break; | |
| case 0x8B: DMA_BaseAddress[5]=(DMA_BaseAddress[5] & (~0xFF << 16))|((val & 0xFF) << 16); break; | |
| case 0x89: DMA_BaseAddress[6]=(DMA_BaseAddress[6] & (~0xFF << 16))|((val & 0xFF) << 16); break; | |
| case 0x8A: DMA_BaseAddress[7]=(DMA_BaseAddress[7] & (~0xFF << 16))|((val & 0xFF) << 16); break; | |
| /* Low Page Base Address (only 4 lower bits are significant) */ | |
| case 0x487: DMA_BaseAddress[0]=(DMA_BaseAddress[0] & (~0xFF << 24))|((val & 0x0F) << 24); break; | |
| case 0x483: DMA_BaseAddress[1]=(DMA_BaseAddress[1] & (~0xFF << 24))|((val & 0x0F) << 24); break; | |
| case 0x481: DMA_BaseAddress[2]=(DMA_BaseAddress[2] & (~0xFF << 24))|((val & 0x0F) << 24); break; | |
| case 0x482: DMA_BaseAddress[3]=(DMA_BaseAddress[3] & (~0xFF << 24))|((val & 0x0F) << 24); break; | |
| case 0x48B: DMA_BaseAddress[5]=(DMA_BaseAddress[5] & (~0xFF << 24))|((val & 0x0F) << 24); break; | |
| case 0x489: DMA_BaseAddress[6]=(DMA_BaseAddress[6] & (~0xFF << 24))|((val & 0x0F) << 24); break; | |
| case 0x48A: DMA_BaseAddress[7]=(DMA_BaseAddress[7] & (~0xFF << 24))|((val & 0x0F) << 24); break; | |
| case 0x08: | |
| case 0xD0: | |
| /* Command */ | |
| FIXME("Write Command (%x) - Not Implemented\n",val); | |
| break; | |
| case 0x0B: | |
| case 0xD6: | |
| /* Mode */ | |
| TRACE("Write Mode (%x)\n",val); | |
| DMA_Command[((port==0xD6)?4:0)+(val&0x3)]=val; | |
| switch(val>>6) | |
| { | |
| case 0: | |
| /* Request mode */ | |
| FIXME("Request Mode - Not Implemented\n"); | |
| break; | |
| case 1: | |
| /* Single Mode */ | |
| break; | |
| case 2: | |
| /* Block mode */ | |
| FIXME("Block Mode - Not Implemented\n"); | |
| break; | |
| case 3: | |
| /* Cascade Mode */ | |
| ERR("Cascade Mode should not be used by regular apps\n"); | |
| break; | |
| } | |
| break; | |
| case 0x0A: | |
| case 0xD4: | |
| /* Write Single Mask Bit */ | |
| TRACE("Write Single Mask Bit (%x)\n",val); | |
| dmachip = (port==0x0A) ? 0 : 1; | |
| if (val&4) | |
| DMA_Mask[dmachip] |= 1<<(val&3); | |
| else | |
| DMA_Mask[dmachip] &= ~(1<<(val&3)); | |
| break; | |
| case 0x0F: | |
| case 0xDE: | |
| /* Write All Mask Bits (only 4 lower bits are significant */ | |
| FIXME("Write All Mask Bits (%x)\n",val); | |
| dmachip = (port==0x0F) ? 0 : 1; | |
| DMA_Mask[dmachip] = val & 0x0F; | |
| break; | |
| case 0x09: | |
| case 0xD2: | |
| /* Software DRQx Request */ | |
| FIXME("Software DRQx Request (%x) - Not Implemented\n",val); | |
| break; | |
| case 0x0C: | |
| case 0xD8: | |
| /* Reset DMA Pointer Flip-Flop */ | |
| TRACE("Reset Flip-Flop\n"); | |
| DMA_Toggle[port==0xD8]=FALSE; | |
| break; | |
| case 0x0D: | |
| case 0xDA: | |
| /* Master Reset */ | |
| TRACE("Master Reset\n"); | |
| dmachip = (port==0x0D) ? 0 : 1; | |
| /* Reset DMA Pointer Flip-Flop */ | |
| DMA_Toggle[dmachip]=FALSE; | |
| /* Mask all channels */ | |
| DMA_Mask[dmachip] = 0x0F; | |
| break; | |
| case 0x0E: | |
| case 0xDC: | |
| /* Reset Mask Register */ | |
| FIXME("Reset Mask Register\n"); | |
| dmachip = (port==0x0E) ? 0 : 1; | |
| /* Unmask all channels */ | |
| DMA_Mask[dmachip] = 0x00; | |
| break; | |
| } | |
| } | |
| BYTE DMA_ioport_in( WORD port ) | |
| { | |
| int channel,dmachip; | |
| BYTE res = 0; | |
| switch(port) | |
| { | |
| case 0x00: | |
| case 0x02: | |
| case 0x04: | |
| case 0x06: | |
| case 0xC0: | |
| case 0xC4: | |
| case 0xC8: | |
| case 0xCC: | |
| /* Base Address*/ | |
| channel = (port&0xC0)?((port-0xC0)>>2):(port>>1); | |
| dmachip = (channel<4) ? 0 : 1; | |
| if (!DMA_Toggle[dmachip]) | |
| res = DMA_CurrentBaseAddress[channel] & 0xFF; | |
| else { | |
| res = (DMA_CurrentBaseAddress[channel] & (0xFF << 8))>>8; | |
| TRACE("Read Current Base Address = %lx\n",DMA_CurrentBaseAddress[channel]); | |
| } | |
| DMA_Toggle[dmachip] = !DMA_Toggle[dmachip]; | |
| break; | |
| case 0x01: | |
| case 0x03: | |
| case 0x05: | |
| case 0x07: | |
| case 0xC2: | |
| case 0xC6: | |
| case 0xCA: | |
| case 0xCE: | |
| /* Count*/ | |
| channel = ((port-1)&0xC0)?(((port-1)-0xC0)>>2):(port>>1); | |
| dmachip = (channel<4) ? 0 : 1; | |
| if (!DMA_Toggle[dmachip]) | |
| res = DMA_CurrentByteCount[channel] & 0xFF; | |
| else { | |
| res = (DMA_CurrentByteCount[channel] & (0xFF << 8))>>8; | |
| TRACE("Read Current Count = %x.\n",DMA_CurrentByteCount[channel]); | |
| } | |
| DMA_Toggle[dmachip] = !DMA_Toggle[dmachip]; | |
| break; | |
| /* Low Page Base Address */ | |
| case 0x87: res = (DMA_BaseAddress[0]&(0xFF<<16))>>16; break; | |
| case 0x83: res = (DMA_BaseAddress[1]&(0xFF<<16))>>16; break; | |
| case 0x81: res = (DMA_BaseAddress[2]&(0xFF<<16))>>16; break; | |
| case 0x82: res = (DMA_BaseAddress[3]&(0xFF<<16))>>16; break; | |
| case 0x8B: res = (DMA_BaseAddress[5]&(0xFF<<16))>>16; break; | |
| case 0x89: res = (DMA_BaseAddress[6]&(0xFF<<16))>>16; break; | |
| case 0x8A: res = (DMA_BaseAddress[7]&(0xFF<<16))>>16; break; | |
| /* High Page Base Address */ | |
| case 0x487: res = (DMA_BaseAddress[0]&(0xFF<<24))>>24; break; | |
| case 0x483: res = (DMA_BaseAddress[1]&(0xFF<<24))>>24; break; | |
| case 0x481: res = (DMA_BaseAddress[2]&(0xFF<<24))>>24; break; | |
| case 0x482: res = (DMA_BaseAddress[3]&(0xFF<<24))>>24; break; | |
| case 0x48B: res = (DMA_BaseAddress[5]&(0xFF<<24))>>24; break; | |
| case 0x489: res = (DMA_BaseAddress[6]&(0xFF<<24))>>24; break; | |
| case 0x48A: res = (DMA_BaseAddress[7]&(0xFF<<24))>>24; break; | |
| case 0x08: | |
| case 0xD0: | |
| /* Status */ | |
| TRACE("Status Register Read\n"); | |
| res = DMA_Status[(port==0x08)?0:1]; | |
| case 0x0D: | |
| case 0xDA: | |
| /* Temporary */ | |
| FIXME("Temporary Register Read- Not Implemented\n"); | |
| break; | |
| } | |
| return res; | |
| } |