/* Implementation of DMA routines on DOS - from libMikMod. Copyright (C) 1999 by Andrew Zabolotny, 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 . */ #include "dosdma.h" #include /* includes sys/version.h (djgpp >= 2.02) */ #include #include #include #include /* 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(); }