📄 dosgus.c
字号:
__gus_outregb(GF1R_DRAM_HIGH, address >> 16);
if (flags & GUS_WAVE_INVERT)
if (flags & GUS_WAVE_16BIT)
while (size64k-- && size64k--) {
__gus_outregw(GF1R_DRAM_LOW, address++);
outportb(GF1_DRAM, *source++);
__gus_outregw(GF1R_DRAM_LOW, address++);
outportb(GF1_DRAM, (*source++) ^ 0x80);
} else
while (size64k--) {
__gus_outregw(GF1R_DRAM_LOW, address++);
outportb(GF1_DRAM, (*source++) ^ 0x80);
} else
while (size64k--) {
__gus_outregw(GF1R_DRAM_LOW, address++);
outportb(GF1_DRAM, *source++);
}
}
}
/* Wait for DMA transfer to finish between 8-9 1/18sec timer ticks */
static int __gus_wait_dma()
{
unsigned long timer;
_farsetsel(_dos_ds);
timer = _farnspeekl(0x46c);
while (gus.dma_active)
if (_farnspeekl(0x46c) - timer > 8) {
/* Force DMA abort since something went wrong */
__gus_reset(0);
return -1;
}
return 0;
}
/* Transfer a block of data into GUS DRAM through DMA controller */
static void __gus_transfer_dma(unsigned long address, unsigned char *source,
unsigned long size, int flags)
{
unsigned char dma_control;
unsigned long bytes_left;
unsigned long cur_size;
unsigned long dest_addr;
if ((gus.dma[0] > 3) || (flags & GUS_WAVE_16BIT))
size = (size + 1) & ~1;
bytes_left = size;
while (bytes_left) {
__gus_wait_dma();
cur_size = gus.dma_buff->size;
if (cur_size > bytes_left)
cur_size = bytes_left;
bytes_left -= cur_size;
dest_addr = address;
if (gus.dma_buff->linear != source)
memmove(gus.dma_buff->linear, source, cur_size);
source += cur_size;
address += cur_size;
/* Disable GUS -> DMA tie */
__gus_outregb(GF1R_DMA_CONTROL, 0);
__gus_delay();
/* Set up the DMA */
dma_start(gus.dma_buff, cur_size, DMA_MODE_WRITE);
gus.dma_active = 1;
/* Reset the DMA IRQ pending bit if set */
__gus_inregb(GF1R_DMA_CONTROL);
/* The 16-bit DMA channels needs a slightly different approach */
dma_control = GF1M_DMAR_ENABLE | GF1M_DMAR_IRQ_ENABLE | gus.dma_rate;
if (gus.dma[0] > 3) {
dest_addr = __gus_convert_addr16(dest_addr);
dma_control |= GF1M_DMAR_CHAN16;
}
__gus_outregw(GF1R_DMA_ADDRESS, dest_addr >> 4);
if (flags & GUS_WAVE_16BIT)
dma_control |= GF1M_DMAR_DATA16;
if (flags & GUS_WAVE_INVERT)
dma_control |= GF1M_DMAR_TOGGLE_SIGN;
/* Tell GUS to start transfer */
__gus_outregb(GF1R_DMA_CONTROL, dma_control);
}
}
static void __gus_detect_version()
{
unsigned char tmp;
switch (gus.version = inportb(GF1_REVISION)) {
case 5:
gus.version = GUS_CARD_VERSION_CLASSIC_ICS;
gus.ics = 1;
gus.ics_flipped = 1;
break;
case 6:
case 7:
case 8:
case 9:
gus.version = GUS_CARD_VERSION_CLASSIC_ICS;
gus.ics = 1;
break;
case 10:
gus.version = GUS_CARD_VERSION_MAX;
gus.codec = 1;
break;
case 11:
gus.version = GUS_CARD_VERSION_MAX1;
gus.codec = 1;
break;
case 0x30:
gus.version = GUS_CARD_VERSION_ACE;
break;
case 0x50:
gus.version = GUS_CARD_VERSION_EXTREME;
break;
case 0xff:
/* Pre-3.7 board */
outportb(GF1_REG_CTRL, 0x20);
tmp = inportb(GF1_REG_CTRL);
if ((tmp != 0xff) && (tmp & 0x06))
gus.version = GUS_CARD_VERSION_CLASSIC1;
else
gus.version = GUS_CARD_VERSION_CLASSIC;
break;
default:
/* Hmm... unknown revision. Assume a safe Classic model */
#ifdef MIKMOD_DEBUG
fprintf(stderr, "libgus: Unknown board revision (%02x)\n",
gus.version);
#endif
gus.version = GUS_CARD_VERSION_CLASSIC;
break;
}
}
static void __gus_detect_transfer()
{
unsigned char *outbuff, *inbuff;
unsigned int i, j, seed = 0x13243546;
__gus_transfer_func func;
#define TRANSFER_SIZE 0x4000
outbuff = malloc(TRANSFER_SIZE);
inbuff = malloc(TRANSFER_SIZE);
/* Suppose we have an malfunctioning GUS */
gus.transfer = NULL;
for (i = (gus.dma_buff ? 0 : 4); i <= 4; i++) {
switch (i) {
case 0:
gus.dma_rate = GF1M_DMAR_RATE0;
func = __gus_transfer_dma;
break;
case 1:
gus.dma_rate = GF1M_DMAR_RATE1;
func = __gus_transfer_dma;
break;
case 2:
gus.dma_rate = GF1M_DMAR_RATE2;
func = __gus_transfer_dma;
break;
case 3:
gus.dma_rate = GF1M_DMAR_RATE3;
func = __gus_transfer_dma;
break;
case 4:
func = __gus_transfer_io;
break;
}
/* Fill data array each time with pseudo-random values */
for (j = 0; j < TRANSFER_SIZE; j++)
outbuff[j] = seed, seed =
((seed + 358979323) ^ (seed >> 16)) * 314159265;
/* Transfer the random array to GUS */
/* Poke a security fence around dest block */
__gus_poke(0x100 - 1, 0xAA);
__gus_poke(0x100 - 2, 0x55);
__gus_poke(0x100 + TRANSFER_SIZE + 0, 0xAA);
__gus_poke(0x100 + TRANSFER_SIZE + 1, 0x55);
func(0x100, outbuff, TRANSFER_SIZE, 0);
if (__gus_wait_dma() == 0) {
/* Check if the security fence was not damaged */
if ((__gus_peek(0x100 - 1) != 0xAA)
|| (__gus_peek(0x100 - 2) != 0x55)
|| (__gus_peek(0x100 + TRANSFER_SIZE + 0) != 0xAA)
|| (__gus_peek(0x100 + TRANSFER_SIZE + 1) != 0x55))
continue;
/* Now check if GUS DRAM really data that we expects to be transferred */
__gus_transfer_io_in(0x100, inbuff, TRANSFER_SIZE);
if (memcmp(outbuff, inbuff, TRANSFER_SIZE) == 0) {
gus.transfer = func;
break;
}
}
}
#undef TRANSFER_SIZE
free(inbuff);
free(outbuff);
}
static void __gus_detect_memory()
{
unsigned int size;
for (size = 0; size < 1024; size += 256) {
__gus_poke(size * 1024, 0xaa);
if (__gus_peek(size * 1024) != 0xaa)
break;
__gus_poke(size * 1024, 0x55);
if (__gus_peek(size * 1024) != 0x55)
break;
}
gus.ram = size;
}
static void __gus_init()
{
char *gusenv = getenv("ULTRASND");
memset((void *)&gus, 0, sizeof(gus));
gus.cmd_voice = -1;
if (!gusenv)
return;
sscanf(gusenv, "%x,%d,%d,%d,%d", &gus.port, &gus.dma[0], &gus.dma[1],
&gus.irq[0], &gus.irq[1]);
/* A relaxed sanity check */
if ((gus.port < 0x100) || (gus.port > 0x1000)
|| (gus.irq[0] < 2) || (gus.irq[0] > 15)
|| (gus.irq[1] < 2) || (gus.irq[1] > 15)
|| (gus.dma[0] < 0) || (gus.dma[0] > 7)
|| (gus.dma[1] < 0) || (gus.dma[1] > 7))
return;
gus.voices = 32;
gus.timer_ctl = GF1M_MASK_TIMER1 | GF1M_MASK_TIMER2;
/* Detect if the card is really there */
if (__gus_detect() == 0)
return;
/* Detect the version of Gravis Ultrasound */
__gus_detect_version();
/* Reset the card */
__gus_reset(1);
/* Detect the amount of on-board memory */
__gus_detect_memory();
gus.ok = 1;
}
static void __gus_kick(gus_wave_t * wave, unsigned int wave_offset)
{
unsigned char vc;
vc = GF1VC_IRQ;
if (wave->format & GUS_WAVE_16BIT)
vc |= GF1VC_DATA16;
if (wave->format & GUS_WAVE_BACKWARD)
vc |= GF1VC_BACKWARD;
if (wave->format & GUS_WAVE_LOOP) {
vc |= GF1VC_LOOP_ENABLE;
if (wave->format & GUS_WAVE_BIDIR)
vc |= GF1VC_BI_LOOP;
}
__gus_set_loop_start(vc, (wave->begin.memory << 4) + wave->loop_start);
if (wave->format & GUS_WAVE_LOOP)
__gus_set_loop_end(vc, (wave->begin.memory << 4) + wave->loop_end);
else
__gus_set_loop_end(vc, (wave->begin.memory + wave->size) << 4);
__gus_set_current(vc, (wave->begin.memory << 4) + wave_offset + 100);
__gus_outregb_slow(GF1R_VOICE_CONTROL, vc);
}
/* Timer 1 callback function (updates voices) */
static void __gus_timer_update()
{
gus_wave_t *wave;
unsigned long wave_offset;
unsigned char *src, *top;
unsigned int vmask = (1 << gus.cur_voice);
if (!gus.cmd_pool_ready)
return;
__gus_select_voice(gus.cur_voice);
wave_offset = 0;
src = gus.cmd_pool;
top = gus.cmd_pool + gus.cmd_pool_top;
#define GET_B *src
#define GET_W *((unsigned short *)src)
#define GET_L *((unsigned long *)src)
while (src < top) {
__gus_delay();
switch (GET_B++) {
case PCMD_VOICE:
__gus_select_voice(gus.cur_voice = GET_B++);
vmask = (1 << gus.cur_voice);
break;
case PCMD_FREQ:
__gus_outregw(GF1R_FREQUENCY, GET_W++);
break;
case PCMD_PAN:
__gus_outregb(GF1R_BALANCE, GET_B++);
break;
case PCMD_VOLUME:
__gus_volume_ramp_to(gus.cur_vol[gus.cur_voice] =
GET_W++, GUS_VOLCHANGE_RAMP, GF1VL_IRQ);
break;
case PCMD_VOLUME_PREPARE:
gus.cur_vol[gus.cur_voice] = GET_W++;
break;
case PCMD_OFFSET:
wave_offset = GET_L++;
break;
case PCMD_START:
wave = (gus_wave_t *) GET_L++;
gus.cur_wave[gus.cur_voice] = wave;
gus.kick_offs[gus.cur_voice] = wave_offset;
if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ)) {
__gus_kick(wave, wave_offset);
__gus_volume_ramp_to(gus.cur_vol[gus.cur_voice],
GUS_VOLCHANGE_RAMP, GF1VL_IRQ);
} else
gus.voice_kick[gus.cur_voice] = 1;
wave_offset = 0;
gus.eow_ignore |= vmask;
break;
case PCMD_STOP:
/* If volume is close to nothing, abort immediately instead of
ramping */
gus.cur_vol[gus.cur_voice] = 0;
gus.cur_wave[gus.cur_voice] = NULL;
if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ))
__gus_stop_voice();
break;
case PCMD_STOP_LOOP:
__gus_outregb_slow(GF1R_VOICE_CONTROL,
(__gus_inregb(GF1R_VOICE_CONTROL) | GF1VC_IRQ)
& ~GF1VC_LOOP_ENABLE);
__gus_outregb_slow(GF1R_VOLUME_CONTROL,
__gus_inregb(GF1R_VOLUME_CONTROL) &
~GF1VL_ROLLOVER);
break;
default:
/* Alarm! Break out immediately */
src = top;
break;
}
}
#undef GET_B
#undef GET_W
#undef GET_L
gus.cmd_pool_ready = 0;
gus.cmd_pool_top = 0;
}
static void __gus_wavetable_update(unsigned int voice, unsigned int voice_ctl,
unsigned int volume_ctl)
{
gus_wave_t *wave = gus.cur_wave[voice];
if (!wave || !(wave->format & GUS_WAVE_LOOP)) {
__gus_stop_voice();
gus.cur_wave[voice] = NULL;
gus.cur_vol[voice] = 0;
if (__gus_volume_ramp_to(0, GUS_VOLCHANGE_RAMP, GF1VL_IRQ))
__gus_stop_voice();
}
}
static void __gus_volume_update(unsigned int voice, unsigned int voice_ctl,
unsigned int volume_ctl)
{
__gus_volume_ramp_to(gus.cur_vol[voice], GUS_VOLCHANGE_RAMP, GF1VL_IRQ);
if (!gus.cur_wave[voice])
__gus_stop_voice();
else if (gus.voice_kick[voice])
__gus_kick(gus.cur_wave[voice], gus.kick_offs[voice]);
gus.voice_kick[voice] = 0;
}
/***************************************************** GUS memory manager *****/
/* Mark all GUS memory as available */
static void __gus_mem_clear()
{
__gus_mcb *cur = gus.mcb;
while (cur) {
__gus_mcb *next = cur->next;
if (cur != gus.mcb)
free(cur);
cur = next;
}
if (!gus.mcb)
gus.mcb = malloc(sizeof(__gus_mcb));
gus.mcb->next = gus.mcb->prev = NULL;
gus.mcb->addr = 0;
gus.mcb->size = gus.ram * 1024;
gus.mcb->free = 1;
}
/* Return amount of free memory */
static unsigned int __gus_mem_get_free()
{
__gus_mcb *cur = gus.mcb;
unsigned int size = 0;
if (!gus.open)
return gus.ram * 1024;
while (cur) {
if (cur->free)
size += cur->size;
cur = cur->next;
}
return size;
}
/* Return largest size for a 8-bit sample */
static unsigned int __gus_mem_get_free_8()
{
__gus_mcb *cur = gus.mcb;
unsigned int size = 0;
if (!gus.open)
return 0;
while (cur) {
if (cur->free && (cur->size > size))
size = cur->size;
cur = cur->next;
}
return size;
}
/* Return largest size for a 16-bit sample */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -