📄 dosdma.c
字号:
/*
Implementation of DMA routines on DOS
Copyright (C) 1999 by Andrew Zabolotny, <bit@eltech.ru>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "dosdma.h"
#include <dos.h>
#include <dpmi.h>
#include <sys/nearptr.h>
#include <malloc.h>
__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;
/* BUG WARNING: there is an error in dpmi.h DJGPP 2.01 library:
the order of "selector" and "alloc size" should be
reversed */
if (__dpmi_resize_dos_memory(allocsize, selector, &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 = malloc(sizeof(dma_buffer));
buffer->linear = (void *)(__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();
}
/* ex:set ts=4: */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -