Newer
Older
Scratch / mobius / src / drivers / sound / dma.c
#include <stdio.h> /* printf() */
#include "sound.h" /* bool */

static const unsigned short _single_reg[8] =
{
	0x0A, 0x0A, 0x0A, 0x0A, 0xD4, 0xD4, 0xD4, 0xD4
};
static const unsigned char _disable_cmd[8] =
{
	0x04, 0x05, 0x06, 0x07, 0x04, 0x05, 0x06, 0x07,
};
static const unsigned short _mode_reg[8] =
{
	0x0B, 0x0B, 0x0B, 0x0B, 0xD6, 0xD6, 0xD6, 0xD6
};
static const unsigned short _ff_reg[8] =
{
	0x0C, 0x0C, 0x0C, 0x0C, 0xD8, 0xD8, 0xD8, 0xD8
};
static const unsigned short _adr_reg[8] =
{
	0x00, 0x02, 0x04, 0x06, 0xC0, 0xC4, 0xC8, 0xCC
};
static const unsigned short _page_reg[8] =
{
	0x87, 0x83, 0x81, 0x82, 0x8F, 0x8B, 0x89, 0x8A
};
static const unsigned short _count_reg[8] =
{
	0x01, 0x03, 0x05, 0x07, 0xC2, 0xC6, 0xCA, 0xCE
};
static const unsigned char _enable_cmd[8] =
{
	0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03,
};
/*****************************************************************************
*****************************************************************************/
int dma_from_mem(unsigned char chan, unsigned long adr, unsigned long count,
		bool auto_init)
{
	static const unsigned char write_cmd[8] =
	{
		0x48, 0x49, 0x4A, 0x4B, 0x48, 0x49, 0x4A, 0x4B
	};

/* validate chan */
	if(chan > 7)
	{
		wprintf(L"dma_from_mem: DMA channel (%u) > 7\n", chan);
		return -1;
	}
/* 16 bit data is halved */
	if(chan >= 4)
	{
		adr >>= 1;
		count >>= 1;
	}
	count--;
/* make sure transfer doesn't exceed max or cross a 64K boundary */
	if(adr + count >=  0x100000L) /* real mode; 1 meg */
//	if(adr + count >=  0x1000000L) /* pmode; 16 meg */
	{
		wprintf(L"dma_from_mem: end adr (0x%lX) exceeds max\n",
			adr + count);
		return -2;
	}
	if((adr & 0x10000L) != ((adr + count) & 0x10000L))
	{
		wprintf(L"dma_from_mem: DMA from adrs 0x%lX - 0x%lX "
			"crosses 64K boundary\n", adr, adr + count);
		return -3;
	}
/* disable channel */
	out(_single_reg[chan], _disable_cmd[chan]);
/* set mode */
	out(_mode_reg[chan],
		auto_init ? (write_cmd[chan] | 0x10) : write_cmd[chan]);
/* clear flip-flop, for LSB of address */
	out(_ff_reg[chan], 0);
/* address LSB */
	out(_adr_reg[chan], adr);
	adr >>= 8;
/* address MSB */
	out(_adr_reg[chan], adr);
	adr >>= 8;
/* page */
	out(_page_reg[chan], adr);
/* clear flip-flop, for LSB of count */
	out(_ff_reg[chan], 0);
/* count LSB */
	out(_count_reg[chan], count);
	count >>= 8;
/* count MSB */
	out(_count_reg[chan], count);
/* enable channel */
	out(_single_reg[chan], _enable_cmd[chan]);
	return 0;
}