dosgus.c

来自「这是著名的TCPMP播放器在WINDWOWS,和WINCE下编译通过的源程序.笔」· C语言 代码 · 共 1,872 行 · 第 1/4 页

C
1,872
字号
static unsigned int __gus_mem_get_free_16()
{
	__gus_mcb *cur = gus.mcb;
	unsigned int size = 0;

	if (!gus.open)
		return 0;

	while (cur) {
		if (cur->free) {
			unsigned int size16 = cur->size;
			unsigned int tmp;
			/* 16-bit samples cannot cross 256K boundaries */
			tmp = 0x40000 - (cur->addr & 0x3ffff);
			if (size16 > tmp)
				size16 = tmp;
			/* 16-bit samples should be aligned on a 32-byte boundary */
			size16 -= (32 - cur->addr) & 0x1f;

			if (size16 > size)
				size = size16;

			/* Now try vice versa: skip a portion of aligned memory */
			size16 =
			  (cur->addr + cur->size) - ((cur->addr + 0x3ffff) & ~0x3ffff);
			if ((size16 < 0x7fffffff) && (size16 > size))
				size = size16;
		}
		cur = cur->next;
	}

	return size;
}

/* Allocate a segment of GUS DRAM for a sample with given bits per sample.
 * The algorithm tries to find the smallest free block that fits requested
 * size; but if found free block is larger by some (large) delta than
 * requested block size, the largest possible block is preffered.
 */
static unsigned int __gus_mem_alloc(unsigned int size, int bits16)
{
	__gus_mcb *cur = gus.mcb;
	__gus_mcb *best_max = NULL, *best_min = NULL;
	unsigned int best_max_delta = 0, best_min_delta = 0xffffffff;
	unsigned int best_max_prefix = 0, best_min_prefix = 0;
	unsigned int memaddr, memsize;

	if (!gus.open || !size || (bits16 && size > 0x40000))
		return -1;

	/* Round block size up to nearest acceptable DMA bound */
	if (bits16)
		size = (size + 0x1f) & ~0x1f;
	else
		size = (size + 0x0f) & ~0x0f;

	while (cur) {
		if (cur->free) {
			unsigned char fits = 0;

			memsize = cur->size;
			memaddr = cur->addr;

			if (bits16) {
				/* 16-bit samples cannot cross 256K boundaries */
				unsigned int tmp = 256 * 1024 - (memaddr & 0x3ffff);
				if (memsize > tmp)
					memsize = tmp;
				/* 16-bit samples should be aligned on a 32-byte boundary */
				memsize -= (32 - memaddr) & 0x1f;
				memaddr = (memaddr + 0x1f) & ~0x1f;
			}

			/* If block fits, analyze it */
			if (size <= memsize)
				fits = 1;
			/* Look if we still can complete the request by creating a free
			   block */
			else if (size <= cur->size) {
				/* Align start address to next 256k boundary */
				unsigned int endaddr = cur->addr + cur->size;
				memaddr = (cur->addr + 0x3ffff) & ~0x3ffff;
				/* Can we split current block by inserting a free block at the
				   beginning? */
				if ((memaddr < endaddr) && (memaddr + size <= endaddr))
					fits = 1;
			}

			if (fits) {
				unsigned int size_delta = cur->size - size;
				unsigned int size_prefix = memaddr - cur->addr;
				if (size_delta < best_min_delta)
					best_min = cur, best_min_delta =
					  size_delta, best_min_prefix = size_prefix;
				if (size_delta > best_max_delta)
					best_max = cur, best_max_delta =
					  size_delta, best_max_prefix = size_prefix;
			}
		}

		cur = cur->next;
	}

	if (!best_min)
		return -1;

	/* If minimal block that fits is too large, use largest block that fits */
	/* But if using the maximal block is going to create a small hole, forget
	   it */
	if ((best_max_prefix == 0)
		|| (best_max_prefix >= DRAM_HOLE_THRESHOLD)
		|| (best_min_prefix != 0))
		if (
			((best_min_delta < DRAM_HOLE_THRESHOLD) &&
			 (best_max_delta >= DRAM_HOLE_THRESHOLD)) ||
			((best_min_prefix > 0) && (best_min_prefix < DRAM_HOLE_THRESHOLD)
			 && ((best_max_prefix == 0) ||
				 (best_max_prefix > best_min_prefix))) ||
			((best_min_prefix != 0) && (best_max_prefix == 0))) {
			best_min = best_max;
			best_min_delta = best_max_delta;
			best_min_prefix = best_max_prefix;
		}

	/* Compute the DRAM address to return */
	memaddr = best_min->addr + best_min_prefix;
	if (bits16)
		memaddr = (memaddr + 0x1f) & ~0x1f;
	else
		memaddr = (memaddr + 0x0f) & ~0x0f;

	/* If we have a considerable hole at the beginning of sample,
	   create a free node describing the hole */
	if (memaddr - best_min->addr >= DRAM_SPLIT_THRESHOLD) {
		__gus_mcb *newmcb = malloc(sizeof(__gus_mcb));
		newmcb->prev = best_min->prev;
		newmcb->next = best_min;
		newmcb->addr = best_min->addr;
		newmcb->size = memaddr - best_min->addr;
		newmcb->free = 1;
		best_min->addr = memaddr;
		best_min->size -= newmcb->size;
		best_min->prev = newmcb;
		if (newmcb->prev)
			newmcb->prev->next = newmcb;
	}

	/* Compute the size of hole at the end of block */
	memsize = (best_min->addr + best_min->size) - (memaddr + size);

	/* Split the block if the block is larger than requested amount */
	if (memsize > DRAM_SPLIT_THRESHOLD) {
		/* The next node cannot be free since free blocks are always glued
		   together */
		__gus_mcb *newmcb = malloc(sizeof(__gus_mcb));
		best_min->size -= memsize;
		newmcb->prev = best_min;
		newmcb->next = best_min->next;
		newmcb->addr = best_min->addr + best_min->size;
		newmcb->size = memsize;
		newmcb->free = 1;
		if (best_min->next)
			best_min->next->prev = newmcb;
		best_min->next = newmcb;
	}
	best_min->free = 0;

	return memaddr;
}

static void __gus_mem_free(unsigned int addr)
{
	__gus_mcb *cur = gus.mcb;
	while (cur) {
		if (!cur->free && (cur->addr <= addr) &&
			(cur->addr + cur->size > addr)) {
			cur->free = 1;

			/* If next block is free as well, link them together */
			if (cur->next && cur->next->free) {
				__gus_mcb *next = cur->next;
				cur->size += next->size;
				cur->next = next->next;
				if (next->next)
					next->next->prev = cur;
				free(next);
			}

			/* If previous block is free, link current block with it */
			if (cur->prev && cur->prev->free) {
				cur->prev->size += cur->size;
				cur->prev->next = cur->next;
				if (cur->next)
					cur->next->prev = cur->prev;
				free(cur);
			}
			return;
		}
		cur = cur->next;
	}
}

static void __gus_mem_pack()
{
}

#ifdef MIKMOD_DEBUG

/* Debug dump of GUS DRAM heap */
void __gus_mem_dump()
{
	__gus_mcb *cur = gus.mcb;
	fprintf(stderr, "/-- Offset --+-- Prev --+-- Size --+-- Free --\\\n");
	while (cur) {
		fprintf(stderr, "|  %08X  | %08X |  %6d  |   %s    |\n",
				cur->addr, cur->prev ? cur->prev->addr : -1, cur->size,
				cur->free ? "yes" : " no");
		cur = cur->next;
	}
	fprintf(stderr, "\\------------+----------+----------+----------/\n");
}

#endif

/************************************************** Middle-level routines *****/

static int __gus_instrument_free(gus_instrument_t * instrument)
{
	gus_instrument_t **cur_instr;
	gus_layer_t *cur_layer;
	gus_wave_t *cur_wave, *wave_head;

	/* Remove the instrument from the list of registered instruments */
	cur_instr = (gus_instrument_t **) & gus.instr;
	while (*cur_instr) {
		if (*cur_instr == instrument) {
			*cur_instr = instrument->next;
			goto instr_loaded;
		}
		cur_instr = &(*cur_instr)->next;
	}
	return -1;

instr_loaded:
	wave_head = NULL;
	for (cur_layer = instrument->info.layer; cur_layer;
		 cur_layer = cur_layer->next)
		/* Free all waves */
		for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next) {
			if (!wave_head)
				wave_head = cur_wave;
			if (cur_wave->begin.memory != (unsigned int)-1)
				__gus_mem_free(cur_wave->begin.memory);
		}
	if (wave_head)
		free(wave_head);

	free(instrument->info.layer);
	if (instrument->name)
		free(instrument->name);
	free(instrument);
	return 0;
}

static gus_instrument_t *__gus_instrument_get(int program)
{
	gus_instrument_t *cur_instr = (gus_instrument_t *) gus.instr;
	while (cur_instr) {
		if (cur_instr->number.instrument == program)
			return cur_instr;
		cur_instr = cur_instr->next;
	}
	return NULL;
}

static gus_instrument_t *__gus_instrument_copy(gus_instrument_t * instrument)
{
	gus_instrument_t **cur_instr, *instr;
	gus_layer_t *cur_layer, *dest_layer;
	gus_wave_t *cur_wave, *dest_wave;
	unsigned int waves, layers;

	if (!instrument || !instrument->info.layer || !gus.open)
		return NULL;

	if (__gus_instrument_get(instrument->number.instrument))
		return NULL;

	instr = malloc(sizeof(gus_instrument_t));
	*instr = *instrument;

	if (instrument->name)
		instr->name = strdup(instrument->name);

	/* Make a copy of all layers at once */
	for (layers = 0, cur_layer = instrument->info.layer; cur_layer; layers++)
		cur_layer = cur_layer->next;

	if (!(dest_layer = instr->info.layer = malloc(sizeof(gus_layer_t) * layers))) {
		if (instr->name)
			free(instr->name);
		free(instr);
		return NULL;
	}
	for (waves = 0, cur_layer = instrument->info.layer; cur_layer;
		 cur_layer = cur_layer->next) {
		*dest_layer = *cur_layer;
		dest_layer->wave = NULL;
		/* Count the total number of waves */
		for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next)
			waves++;
		if (cur_layer->next)
			dest_layer->next = dest_layer + 1;
		else
			dest_layer->next = NULL;
		dest_layer++;
	}

	/* Allocate memory for waves */
	if (!(dest_wave = malloc(sizeof(gus_wave_t) * waves))) {
		free(instr->info.layer);
		if (instr->name)
			free(instr->name);
		free(instr);
		return NULL;
	}
	for (cur_layer = instrument->info.layer, dest_layer = instr->info.layer;
	     cur_layer; cur_layer = cur_layer->next, dest_layer = dest_layer->next)
		/* Copy all waves */
		for (cur_wave = cur_layer->wave; cur_wave; cur_wave = cur_wave->next) {
			if (!dest_layer->wave)
				dest_layer->wave = dest_wave;

			*dest_wave = *cur_wave;
			/* Mark DRAM address as unallocated */
			dest_wave->begin.memory = -1;

			if (cur_wave->next)
				dest_wave->next = (dest_wave + 1);
			else
				dest_wave->next = NULL;
			dest_wave++;
		}

	/* Insert the instrument into list of registered instruments */
	cur_instr = (gus_instrument_t **) & gus.instr;
	while (*cur_instr)
		cur_instr = &(*cur_instr)->next;
	*cur_instr = instr;

	return instr;
}

static void __gus_instruments_clear()
{
	gus_instrument_t *next_instr, *cur_instr = (gus_instrument_t *) gus.instr;
	while (cur_instr) {
		next_instr = cur_instr->next;
		__gus_instrument_free(cur_instr);
		cur_instr = next_instr;
	}
}

/******************************************************* libGUS interface *****/

/* return value: number of GUS cards installed in system */
int gus_cards()
{
	if (!gus.ok)
		__gus_init();
	return gus.ok ? 1 : 0;
}

int gus_info(gus_info_t * info, int reread)
{
	if (!gus.ok)
		__gus_init();
	if (!gus.ok)
		return -1;

	strcpy(info->id, "gus0");
	info->flags = (gus.ram ? GUS_STRU_INFO_F_PCM : 0);
	info->version = gus.version;
	info->port = gus.port;
	info->irq = gus.irq[0];
	info->dma1 = gus.dma[0];
	info->dma2 = gus.dma[1];

	info->mixing_freq = gus.freq;

	info->memory_size = gus.ram * 1024;
	info->memory_free = __gus_mem_get_free();
	info->memory_block_8 = __gus_mem_get_free_8();
	info->memory_block_16 = __gus_mem_get_free_16();
	return 0;
}

int gus_open(int card, size_t queue_buffer_size, int non_block)
{
	__dpmi_meminfo struct_info, pool_info;

	if (!gus.ok)
		__gus_init();

	if (!gus.ok || gus.open || card != 0)
		return -1;

	/* Now lock the gus structure in memory */
	struct_info.address = __djgpp_base_address + (unsigned long)&gus;
	struct_info.size = sizeof(gus);
	if (__dpmi_lock_linear_region(&struct_info))
		return -1;

	/* And hook the GF1 interrupt */
	__irq_stack_count = 4;
	gus.gf1_irq =
	  irq_hook(gus.irq[0], gf1_irq, (long)gf1_irq_end - (long)gf1_irq);
	__irq_stack_count = 1;
	if (!gus.gf1_irq) {
		__dpmi_unlock_linear_region(&struct_info);
		return -1;
	}

	/* Enable the interrupt */
	irq_enable(gus.gf1_irq);
	if (gus.irq[0] > 7)
		_irq_enable(2);

	/* Allocate a DMA buffer: if we fail, we just use I/O so don't fail */
	if ((gus.transfer == NULL) || (gus.transfer == __gus_transfer_dma))
		gus.dma_buff = dma_allocate(gus.dma[0], GF1_DMA_BUFFER_SIZE);
	else
		gus.dma_buff = NULL;

	/* Detect the best available RAM -> DRAM transfer function */
	if (!gus.transfer) {
		__gus_detect_transfer();
		if (gus.transfer != __gus_transfer_dma || !gus.transfer)
			dma_free(gus.dma_buff), gus.dma_buff = NULL;

		/* If no transfer function worked, fail */
		if (!gus.transfer) {
			if (gus.dma_buff)
				dma_free(gus.dma_buff);
			__dpmi_unlock_linear_region(&struct_info);
			irq_unhook(gus.gf1_irq);
			return -1;
		}
	}

	/* Allocate and lock command pool buffer */
	if (queue_buffer_size < 64)
		queue_buffer_size = 64;
	if (queue_buffer_size > 16384)
		queue_buffer_size = 16384;
	gus.cmd_pool = malloc(queue_buffer_size);
	pool_info.address = __djgpp_base_address + (unsigned long)&gus.cmd_pool;
	pool_info.size = sizeof(queue_buffer_size);
	if (__dpmi_lock_linear_region(&pool_info)) {
		if (gus.dma_buff)
			dma_free(gus.dma_buff);
		__dpmi_unlock_linear_region(&struct_info);
		irq_unhook(gus.gf1_irq);
		return -1;
	}

	gus.open++;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?