214 lines
6 KiB
C
214 lines
6 KiB
C
|
/* Implementation of DMA routines on DOS - from libMikMod.
|
||
|
Copyright (C) 1999 by Andrew Zabolotny, <bit@eltech.ru>
|
||
|
|
||
|
This file is part of WildMIDI.
|
||
|
|
||
|
WildMIDI is free software: you can redistribute and/or modify the player
|
||
|
under the terms of the GNU General Public License and you can redistribute
|
||
|
and/or modify the library under the terms of the GNU Lesser General Public
|
||
|
License as published by the Free Software Foundation, either version 3 of
|
||
|
the licenses, or(at your option) any later version.
|
||
|
|
||
|
WildMIDI 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 and
|
||
|
the GNU Lesser General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License and the
|
||
|
GNU Lesser General Public License along with WildMIDI. If not, see
|
||
|
<http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
#include "dosdma.h"
|
||
|
|
||
|
#include <go32.h> /* includes sys/version.h (djgpp >= 2.02) */
|
||
|
#include <dos.h>
|
||
|
#include <dpmi.h>
|
||
|
#include <sys/nearptr.h>
|
||
|
#include <malloc.h>
|
||
|
|
||
|
/* BUG WARNING: there is an error in DJGPP libraries <= 2.01:
|
||
|
* src/libc/dpmi/api/d0102.s loads the selector and allocsize
|
||
|
* arguments in the wrong order. DJGPP >= 2.02 have it fixed. */
|
||
|
#if !defined(__DJGPP_MINOR__) || (__DJGPP_MINOR__-0 < 2)
|
||
|
#warning __dpmi_resize_dos_memory() from DJGPP <= 2.01 is broken!
|
||
|
#endif
|
||
|
|
||
|
__dma_regs dma[8] = {
|
||
|
/* *INDENT-OFF* */
|
||
|
{DMA_ADDR_0, DMA_PAGE_0, DMA_SIZE_0,
|
||
|
DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
|
||
|
{DMA_ADDR_1, DMA_PAGE_1, DMA_SIZE_1,
|
||
|
DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
|
||
|
|
||
|
{DMA_ADDR_2, DMA_PAGE_2, DMA_SIZE_2,
|
||
|
DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
|
||
|
{DMA_ADDR_3, DMA_PAGE_3, DMA_SIZE_3,
|
||
|
DMA1_MASK_REG, DMA1_CLEAR_FF_REG, DMA1_MODE_REG},
|
||
|
|
||
|
{DMA_ADDR_4, 0, DMA_SIZE_4,
|
||
|
DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
|
||
|
{DMA_ADDR_5, DMA_PAGE_5, DMA_SIZE_5,
|
||
|
DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
|
||
|
|
||
|
{DMA_ADDR_6, DMA_PAGE_6, DMA_SIZE_6,
|
||
|
DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG},
|
||
|
{DMA_ADDR_7, DMA_PAGE_7, DMA_SIZE_7,
|
||
|
DMA2_MASK_REG, DMA2_CLEAR_FF_REG, DMA2_MODE_REG}
|
||
|
/* *INDENT-ON* */
|
||
|
};
|
||
|
|
||
|
static int __initialized = 0;
|
||
|
static int __buffer_count = 0;
|
||
|
static __dpmi_meminfo __locked_data;
|
||
|
|
||
|
int dma_initialize()
|
||
|
{
|
||
|
if (!__djgpp_nearptr_enable())
|
||
|
return 0;
|
||
|
|
||
|
/* Trick: Avoid re-setting DS selector limit on each memory allocation
|
||
|
call */
|
||
|
__djgpp_selector_limit = 0xffffffff;
|
||
|
|
||
|
__locked_data.address = __djgpp_base_address + (unsigned long)dma;
|
||
|
__locked_data.size = sizeof(dma);
|
||
|
if (__dpmi_lock_linear_region(&__locked_data))
|
||
|
return 0;
|
||
|
|
||
|
return (__initialized = 1);
|
||
|
}
|
||
|
|
||
|
void dma_finalize()
|
||
|
{
|
||
|
if (!__initialized)
|
||
|
return;
|
||
|
__dpmi_unlock_linear_region(&__locked_data);
|
||
|
__djgpp_nearptr_disable();
|
||
|
}
|
||
|
|
||
|
dma_buffer *dma_allocate(unsigned int channel, unsigned int size)
|
||
|
{
|
||
|
int parsize = (size + 15) >> 4; /* size in paragraphs */
|
||
|
int par = 0; /* Real-mode paragraph */
|
||
|
int selector = 0; /* Protected-mode selector */
|
||
|
int mask = channel <= 3 ? 0xfff : 0x1fff; /* Alignment mask in para. */
|
||
|
int allocsize = parsize; /* Allocated size in paragraphs */
|
||
|
int count; /* Try count */
|
||
|
int bound = 0; /* Nearest bound address */
|
||
|
int maxsize; /* Maximal possible block size */
|
||
|
dma_buffer *buffer = NULL;
|
||
|
__dpmi_meminfo buff_info, struct_info;
|
||
|
|
||
|
if (!dma_initialize())
|
||
|
return NULL;
|
||
|
|
||
|
/* Loop until we'll get a properly aligned memory block */
|
||
|
for (count = 8; count; count--) {
|
||
|
int resize = (selector != 0);
|
||
|
|
||
|
/* Try first to resize (possibly previously) allocated block */
|
||
|
if (resize) {
|
||
|
maxsize = (bound + parsize) - par;
|
||
|
if (maxsize > parsize * 2)
|
||
|
maxsize = parsize * 2;
|
||
|
if (maxsize == allocsize)
|
||
|
resize = 0;
|
||
|
else {
|
||
|
allocsize = maxsize;
|
||
|
if (__dpmi_resize_dos_memory(selector, allocsize, &maxsize) !=
|
||
|
0) resize = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!resize) {
|
||
|
if (selector)
|
||
|
__dpmi_free_dos_memory(selector), selector = 0;
|
||
|
par = __dpmi_allocate_dos_memory(allocsize, &selector);
|
||
|
}
|
||
|
|
||
|
if ((par == 0) || (par == -1))
|
||
|
goto exit;
|
||
|
|
||
|
/* If memory block contains a properly aligned portion, quit loop */
|
||
|
bound = (par + mask + 1) & ~mask;
|
||
|
if (par + parsize <= bound)
|
||
|
break;
|
||
|
if (bound + parsize <= par + allocsize) {
|
||
|
par = bound;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!count) {
|
||
|
__dpmi_free_dos_memory(selector);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
buffer = (dma_buffer *) malloc(sizeof(dma_buffer));
|
||
|
buffer->linear = (unsigned char *)(__djgpp_conventional_base + bound * 16);
|
||
|
buffer->physical = bound * 16;
|
||
|
buffer->size = parsize * 16;
|
||
|
buffer->selector = selector;
|
||
|
buffer->channel = channel;
|
||
|
|
||
|
buff_info.address = buffer->physical;
|
||
|
buff_info.size = buffer->size;
|
||
|
/*
|
||
|
Don't pay attention to return code since under plain DOS it often
|
||
|
returns error (at least under HIMEM/CWSDPMI and EMM386/DPMI)
|
||
|
*/
|
||
|
__dpmi_lock_linear_region(&buff_info);
|
||
|
|
||
|
/* Lock the DMA buffer control structure as well */
|
||
|
struct_info.address = __djgpp_base_address + (unsigned long)buffer;
|
||
|
struct_info.size = sizeof(dma_buffer);
|
||
|
if (__dpmi_lock_linear_region(&struct_info)) {
|
||
|
__dpmi_unlock_linear_region(&buff_info);
|
||
|
__dpmi_free_dos_memory(selector);
|
||
|
free(buffer);
|
||
|
buffer = NULL;
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
exit:
|
||
|
if (buffer)
|
||
|
__buffer_count++;
|
||
|
else if (--__buffer_count == 0)
|
||
|
dma_finalize();
|
||
|
return buffer;
|
||
|
}
|
||
|
|
||
|
void dma_free(dma_buffer * buffer)
|
||
|
{
|
||
|
__dpmi_meminfo buff_info;
|
||
|
|
||
|
if (!buffer)
|
||
|
return;
|
||
|
|
||
|
buff_info.address = buffer->physical;
|
||
|
buff_info.size = buffer->size;
|
||
|
__dpmi_unlock_linear_region(&buff_info);
|
||
|
|
||
|
__dpmi_free_dos_memory(buffer->selector);
|
||
|
free(buffer);
|
||
|
|
||
|
if (--__buffer_count == 0)
|
||
|
dma_finalize();
|
||
|
}
|
||
|
|
||
|
void dma_start(dma_buffer * buffer, unsigned long count, unsigned char mode)
|
||
|
{
|
||
|
/* Disable interrupts */
|
||
|
int old_ints = disable();
|
||
|
dma_disable(buffer->channel);
|
||
|
dma_set_mode(buffer->channel, mode);
|
||
|
dma_clear_ff(buffer->channel);
|
||
|
dma_set_addr(buffer->channel, buffer->physical);
|
||
|
dma_clear_ff(buffer->channel);
|
||
|
dma_set_count(buffer->channel, count);
|
||
|
dma_enable(buffer->channel);
|
||
|
/* Re-enable interrupts */
|
||
|
if (old_ints)
|
||
|
enable();
|
||
|
}
|