#include"../x86/kernel/hwio.h"
#include"dma.h"
const byte _DMA_ADDR_REG[8] = { 0, 2, 4, 6, 0xC0, 0xC4, 0xC8, 0xCC };
const byte _DMA_EXT_ADDR_REG[8] = { 0x87, 0x83, 0x81, 0x82, 0x8F, 0x89, 0x87, 0x88 };
const byte _DMA_COUNT_REG[8] = { 1, 3, 5, 7, 0xC2, 0xC6, 0xCA, 0xCE };
#define DMA_SLAVE_SET_MODE 0x0B
#define DMA_MASTER_SET_MODE 0xD6
#define DMA_MODE_MASK_SEL 3
#define DMA_MODE_MASK_TRA 0xc
#define DMA_MODE_SELF_TEST 0
#define DMA_MODE_READ_TRANSFER 4
#define DMA_MODE_WRITE_TRANSFER 8
#define DMA_MODE_MASK_AUTO 0x10
#define DMA_MODE_MASK_IDEC 0x20
#define DMA_MODE_MASK 0xc0
#define DMA_MODE_TRANSFER_ON_DEMAND 0
#define DMA_MODE_TRANSFER_SINGLE 0x40
#define DMA_MODE_TRANSFER_BLOCK 0x80
#define DMA_MODE_TRANSFER_CASCADE 0xC0
#define DMA_CMD_MASK_MEMTOMEM 1
#define DMA_CMD_MASK_CHAN0ADDRHOLD 2
#define DMA_CMD_MASK_ENABLE 4
#define DMA_CMD_MASK_TIMING 8
#define DMA_CMD_MASK_PRIORITY 0x10
#define DMA_CMD_MASK_WRITESEL 0x20
#define DMA_CMD_MASK_DREQ 0x40
#define DMA_CMD_MASK_DACK 0x80
#define DMA_SLAVE_INTER_REG 0x0D
#define DMA_MASTER_INTER_REG 0xDA
#define DMA_SLAVE_CHMASK 0x0A
#define DMA_MASTER_CHMASK 0xD4
#define DMA_SLAVE_CLEARBYTE_FLIPFLOP_REG 0x0C
#define DMA_MASTER_CLEARBYTE_FLIPFLOP_REG 0xD8
inline void dmaReset(enum DMAC n)
{
	switch (n)
	{
	case Master_DMA:
		outb(DMA_MASTER_INTER_REG, 0xFF); break;
	case Slave_DMA:
		outb(DMA_SLAVE_INTER_REG, 0xFF); break;
	default:;
	}
}
inline void dmaAddr(byte channel, void* addr)
{
	/*
		Value from the DMA Extended Page Register can be used as DWORD PTR 16-24 bits,
		while the address register can be used as first 16 bits in such.
	*/
	byte page = (((dword)addr) & 0xFF0000) >> 16;
	outb(_DMA_EXT_ADDR_REG[channel], page);
	outw(_DMA_ADDR_REG[channel], (word)(addr));
}
inline void dmaCount(byte channel, word n)
{
	outw(_DMA_COUNT_REG[channel], n);
}
inline void dmaSetMask(byte channel, bool state)
{
	if (channel > 7) return;
	else if (channel > 3)
		outb(DMA_MASTER_CHMASK, (channel-4)|(state<<2));
	else
		outb(DMA_SLAVE_CHMASK, (channel)|(state<<2));
}
void dmaMode(byte channel, byte mode)
{
	byte port=DMA_SLAVE_SET_MODE;
	if (channel > 3)
	{
		//Master DMA
		channel -= 4;
		port += (DMA_MASTER_SET_MODE - DMA_SLAVE_SET_MODE);
	}
	dmaSetMask(channel,true);
	outb(port, mode | channel);
	dmaSetMask(channel,false);
}
inline void dmaRead(byte channel)
{
	dmaMode(channel, DMA_MODE_READ_TRANSFER | DMA_MODE_TRANSFER_SINGLE);
}
inline void dmaWrite(byte channel)
{
	dmaMode(channel, DMA_MODE_WRITE_TRANSFER | DMA_MODE_TRANSFER_SINGLE);
}
inline void dmaClearFlipFlop(enum DMAC n)
{
	switch (n)
	{
	case Master_DMA:
		outb(DMA_MASTER_CLEARBYTE_FLIPFLOP_REG, 0xFF); break;
	case Slave_DMA:
		outb(DMA_SLAVE_CLEARBYTE_FLIPFLOP_REG, 0xFF); break;
	default:;
	}
}
