⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tl_sb.c

📁 DC上的GB模拟器源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
   sb.dma = detect_dma(false);
   if ((uint8) INVALID == sb.dma)
      return -1;

   /* may or may not exist */
   sb.dma16 = detect_dma(true);

   return 0;
}

/*
** Probe for an SB
*/
static int sb_probe(void)
{
   int retval;

   retval = parse_blaster_env();

   /* if environment parse failed, try brute force autodetection */
   if (-1 == retval)
      retval = sb_detect();

   /* no blaster found */
   if (-1 == retval)
   {
      thin_printf("thinlib.sb: no sound blaster found\n");
      return -1;
   }

   if (dsp_reset())
   {
      thin_printf("thinlib.sb: could not reset SB DSP: check BLASTER= variable\n");
      return -1;
   }

   sb.dsp_version = dsp_getversion();
   return 0;
}

/*
** Interrupt handler for 8/16-bit audio 
*/

static int sb_isr(void)
{
   uint32 address, offset;

   dma.count++;

   /* NOTE: this only works with 8-bit, as one-shot mode
   ** does not seem to work with 16-bit transfers
   */
   if (false == dma.autoinit)
   {
      dsp_write(DSP_DMA_DAC_8BIT);
      dsp_write(LOW_BYTE(sb.buf_size - 1));
      dsp_write(HIGH_BYTE(sb.buf_size - 1));
   }

   /* indicate we got the interrupt */
   inportb(dma.ackport);

   /* determine the current playback position */
   address = inportb(dma.addrport);
   address |= (inportb(dma.addrport) << 8);
   address -= dos.offset;

   if (address < sb.buf_size)
      offset = sb.buf_chunk;
   else
      offset = 0;

   sb.callback(sb.user_data, sb.buffer + offset, sb.buf_size);

   /* if we haven't enabled near pointers, we've written to a double
   ** buffer, so transfer it to low DOS memory area
   */
   if (0 == thinlib_nearptr)
      dosmemput(sb.buffer + offset, sb.buf_chunk, dos.bufaddr + offset);

   /* acknowledge interrupt was taken */
   if (sb.irq > 7)
      outportb(0xA0, 0x20);
   outportb(0x20, 0x20);

   return 0;
}
THIN_LOCKED_STATIC_FUNC(sb_isr)


/* install the SB ISR */
static void sb_setisr(void)
{
   THIN_DISABLE_INTS();

   thin_int_install(SB_IRQVEC(sb.irq), sb_isr);

   /* enable IRQ */
   thin_irq_enable(sb.irq);

   THIN_ENABLE_INTS();
}


static void sb_restoreisr(void)
{
   THIN_DISABLE_INTS();

   /* restore IRQ to previous state */
   thin_irq_restore(sb.irq);

   thin_int_remove(SB_IRQVEC(sb.irq));

   THIN_ENABLE_INTS();
}

/* allocate sound buffers */
static int sb_allocate_buffers(int buf_size)
{
   int double_bufsize;

   sb.buf_size = buf_size;

//   if (sb.format & SB_FORMAT_STEREO)
//      sb.buf_size *= 2;

   if (sb.format & SB_FORMAT_16BIT)
      sb.buf_chunk = sb.buf_size * sizeof(uint16);
   else
      sb.buf_chunk = sb.buf_size * sizeof(uint8);

   double_bufsize = 2 * sb.buf_chunk;

   dos.buffer.size = (double_bufsize + 15) >> 4;
   if (_go32_dpmi_allocate_dos_memory(&dos.buffer))
      return -1;

   /* calc linear address */
   dos.bufaddr = dos.buffer.rm_segment << 4;
   if (sb.format & SB_FORMAT_16BIT)
   {
      dos.page = (dos.bufaddr >> 16) & 0xFF;
      dos.offset = (dos.bufaddr >> 1) & 0xFFFF;
   }
   else
   {
      dos.page = (dos.bufaddr >> 16) & 0xFF;
      dos.offset = dos.bufaddr & 0xFFFF;
   }

   if (thinlib_nearptr)
   {
      sb.buffer = (uint8 *) THIN_PHYSICAL_ADDR(dos.bufaddr);
   }
   else
   {
      sb.buffer = malloc(double_bufsize);
      if (NULL == sb.buffer)
         return -1;
   }

   /* clear out the buffers */
   if (sb.format & SB_FORMAT_SIGNED)
      memset(sb.buffer, SILENCE_SIGNED, double_bufsize);
   else
      memset(sb.buffer, SILENCE_UNSIGNED, double_bufsize);

   if (0 == thinlib_nearptr)
      dosmemput(sb.buffer, double_bufsize, dos.bufaddr);

   return 0;
}

/* free buffers */
static void sb_free_buffers(void)
{
   sb.callback = NULL;

   _go32_dpmi_free_dos_memory(&dos.buffer);

   if (0 == thinlib_nearptr)
   {
      free(sb.buffer);
      sb.buffer = NULL;
   }

   sb.buffer = NULL;
}

/* get rid of all things SB */
void thin_sb_shutdown(void)
{
   if (true == sb.initialized)
   {
      sb.initialized = false;

      dsp_reset();

      sb_restoreisr();
      
      sb_free_buffers();
   }
}

/* initialize sound bastard */
int thin_sb_init(int *sample_rate, int *buf_size, int *format)
{
#define  CLAMP_RATE(in_rate, min_rate, max_rate) \
                    (in_rate < min_rate ? min_rate : \
                    (in_rate > max_rate ? max_rate : in_rate))
   
   /* don't init twice! */
   if (true == sb.initialized)
      return 0;

   /* lock variables, routines */
   THIN_LOCK_VAR(dma);
   THIN_LOCK_VAR(dos);
   THIN_LOCK_VAR(sb);
   THIN_LOCK_FUNC(sb_isr);

   memset(&sb, 0, sizeof(sb));
   
   if (sb_probe())
      return -1;

   /* try autoinit DMA first */
   dma.autoinit = true;
   sb.format = (uint8) *format;

   /* determine which SB model we have, and act accordingly */
   if (sb.dsp_version < DSP_VERSION_SB_15)
   {
      /* SB 1.0 */
      sb.sample_rate = CLAMP_RATE(*sample_rate, 4000, 22050);
      sb.format &= ~(SB_FORMAT_16BIT | SB_FORMAT_STEREO);
      dma.autoinit = false;
   }
   else if (sb.dsp_version < DSP_VERSION_SB_20)
   {
      /* SB 1.5 */
      sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 22050);
      sb.format &= ~(SB_FORMAT_16BIT | SB_FORMAT_STEREO);
   }
   else if (sb.dsp_version < DSP_VERSION_SB_PRO)
   {
      /* SB 2.0 */
      sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 44100);
      sb.format &= ~(SB_FORMAT_16BIT | SB_FORMAT_STEREO);
   }
   else if (sb.dsp_version < DSP_VERSION_SB16)
   {
      /* SB Pro */
      if (sb.format & SB_FORMAT_STEREO)
         sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 22050);
      else
         sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 44100);
      sb.format &= ~SB_FORMAT_16BIT;
   }
   else
   {
      /* SB 16 */
      sb.sample_rate = CLAMP_RATE(*sample_rate, 5000, 44100);
   }

   /* sanity check for 16-bit */
   if ((sb.format & SB_FORMAT_16BIT) && ((uint8) INVALID == sb.dma16))
   {
      sb.format &= ~SB_FORMAT_16BIT;
      thin_printf("thinlib.sb: 16-bit DMA channel not available, dropping to 8-bit\n");
   }

   /* clamp buffer size to something sane */
   if ((uint16) *buf_size > sb.sample_rate)
   {
      *buf_size = sb.sample_rate;
      thin_printf("thinlib.sb: buffer size too big, dropping to %d bytes\n", *buf_size);
   }

   /* allocate buffer / DOS memory */
   if (sb_allocate_buffers(*buf_size))
   {
      thin_printf("thinlib.sb: failed allocating sound buffers\n");
      return -1;
   }

   /* set the new IRQ vector! */
   sb_setisr();

   sb.initialized = true;

   /* return the actual values */
   *sample_rate = sb.sample_rate;
   *buf_size = sb.buf_size;
   *format = sb.format;

   return 0;
}

void thin_sb_stop(void)
{
   if (true == sb.initialized)
   {
      if (sb.format & SB_FORMAT_16BIT)
      {
         dsp_write(DSP_DMA_PAUSE_16BIT);  /* pause 16-bit DMA */
         dsp_write(DSP_DMA_STOP_8BIT);
         dsp_write(DSP_DMA_PAUSE_16BIT);
      }
      else
      {
         dsp_write(DSP_DMA_PAUSE_8BIT);  /* pause 8-bit DMA */
         dsp_write(DSP_SPEAKER_OFF);
      }
   }
}

/* return time constant for older sound bastards */
static uint8 get_time_constant(int rate)
{
   return ((65536 - (256000000L / rate)) / 256);
}

static void init_samplerate(int rate)
{
   if ((sb.format & SB_FORMAT_16BIT) || sb.dsp_version >= DSP_VERSION_SB16)
   {
      dsp_write(DSP_DMA_DAC_RATE);
      dsp_write(HIGH_BYTE(rate));
      dsp_write(LOW_BYTE(rate));
   }
   else
   {
      dsp_write(DSP_DMA_TIME_CONST);
      dsp_write(get_time_constant(rate));
   }
}

/* set the sample rate */
void thin_sb_setrate(int rate)
{
   if (sb.format & SB_FORMAT_16BIT)
   {
      dsp_write(DSP_DMA_PAUSE_16BIT);  /* pause 16-bit DMA */
      init_samplerate(rate);
      dsp_write(DSP_DMA_CONT_16BIT);   /* continue 16-bit DMA */
   }
   else
   {
      dsp_write(DSP_DMA_PAUSE_8BIT);   /* pause 8-bit DMA */
      init_samplerate(rate);
      dsp_write(DSP_DMA_CONT_8BIT);    /* continue 8-bit DMA */
   }

   sb.sample_rate = rate;
}

/* start SB DMA transfer */
static void start_transfer(void)
{
   uint8 dma_mode, start_command, mode_command;
   int dma_length;

   /* reset DMA count */
   dma.count = 0;

   dma_length = sb.buf_size * 2;

   if (true == dma.autoinit)
   {
      start_command = DSP_DMA_DAC_MODE;   /* autoinit DMA */
      dma_mode = DMA_AUTOINIT_MODE;
   }
   else
   {
      start_command = 0;
      dma_mode = DMA_ONESHOT_MODE;
   }

   /* things get a little bit nasty here, look out */
   if (sb.format & SB_FORMAT_16BIT)
   {
      uint8 dma_base = sb.dma16 - 4;

      dma_mode |= dma_base; 
      start_command |= DSP_DMA_START_16BIT;

      outportb(DMA_MASKPORT_16BIT, DMA_STOPMASK_BASE | dma_base);
      outportb(DMA_MODEPORT_16BIT, dma_mode);
      outportb(DMA_CLRPTRPORT_16BIT, 0x00);
      outportb(DMA_ADDRBASE_16BIT + (4 * dma_base), LOW_BYTE(dos.offset));
      outportb(DMA_ADDRBASE_16BIT + (4 * dma_base), HIGH_BYTE(dos.offset));
      outportb(DMA_COUNTBASE_16BIT + (4 * dma_base), LOW_BYTE(dma_length - 1));
      outportb(DMA_COUNTBASE_16BIT + (4 * dma_base), HIGH_BYTE(dma_length - 1));
      outportb(dma16_ports[dma_base], dos.page);
      outportb(DMA_MASKPORT_16BIT, DMA_STARTMASK_BASE | dma_base);

      dma.ackport = sb.baseio + DSP_DMA_ACK_16BIT;
      dma.addrport = DMA_ADDRBASE_16BIT + (4 * (sb.dma16 - 4));
   }
   else
   {
      dma_mode |= sb.dma;
      start_command |= DSP_DMA_START_8BIT;

      outportb(DMA_MASKPORT_8BIT, DMA_STOPMASK_BASE + sb.dma);
      outportb(DMA_MODEPORT_8BIT, dma_mode);
      outportb(DMA_CLRPTRPORT_8BIT, 0x00);
      outportb(DMA_ADDRBASE_8BIT + (2 * sb.dma), LOW_BYTE(dos.offset));
      outportb(DMA_ADDRBASE_8BIT + (2 * sb.dma), HIGH_BYTE(dos.offset));
      outportb(DMA_COUNTBASE_8BIT + (2 * sb.dma), LOW_BYTE(dma_length - 1));
      outportb(DMA_COUNTBASE_8BIT + (2 * sb.dma), HIGH_BYTE(dma_length - 1));
      outportb(dma8_ports[sb.dma], dos.page);
      outportb(DMA_MASKPORT_8BIT, DMA_STARTMASK_BASE + sb.dma);

      dma.ackport = sb.baseio + DSP_DMA_ACK_8BIT;
      dma.addrport = DMA_ADDRBASE_8BIT + (2 * sb.dma);
   }

   /* check signed/unsigned */
   if (sb.format & SB_FORMAT_SIGNED)
      mode_command = DSP_DMA_SIGNED;
   else
      mode_command = DSP_DMA_UNSIGNED;

   /* check stereo */
   if (sb.format & SB_FORMAT_STEREO)
      mode_command |= DSP_DMA_STEREO;
   else
      mode_command |= DSP_DMA_MONO;

   init_samplerate(sb.sample_rate);

   /* start things going */
   if ((sb.format & SB_FORMAT_16BIT) || sb.dsp_version >= DSP_VERSION_SB16)
   {
      dsp_write(start_command);
      dsp_write(mode_command);
      dsp_write(LOW_BYTE(sb.buf_size - 1));
      dsp_write(HIGH_BYTE(sb.buf_size - 1));
   }
   else
   {
      /* turn on speaker */
      dsp_write(DSP_SPEAKER_ON);

      if (true == dma.autoinit)
      {
         dsp_write(DSP_DMA_BLOCK_SIZE);  /* set buffer size */
         dsp_write(LOW_BYTE(sb.buf_size - 1));
         dsp_write(HIGH_BYTE(sb.buf_size - 1));

         if (sb.dsp_version < DSP_VERSION_SB_20)
            dsp_write(DSP_DMA_DAC_AI_8BIT); /* low speed autoinit */
         else
            dsp_write(DSP_DMA_DAC_HS_8BIT);
      }
      else
      {
         dsp_write(DSP_DMA_DAC_8BIT);
         dsp_write(LOW_BYTE(sb.buf_size - 1));
         dsp_write(HIGH_BYTE(sb.buf_size - 1));
      }
   }
}

/* TODO: this gets totally wacked when we change the timer rate!!! */
/* start playing the output buffer */
int thin_sb_start(sbmix_t fillbuf, void *user_data)
{
   clock_t count;
   int projected_dmacount;

   /* make sure we really should be here... */
   if (false == sb.initialized || NULL == fillbuf)
      return -1;

   /* stop any current processing */
   thin_sb_stop();

   /* set the callback routine */
   sb.callback = fillbuf;
   sb.user_data = user_data;

   /* calculate how many DMAs we should have in one second 
   ** and scale it down just a tad
   */
   projected_dmacount = (int) ((0.8 * sb.sample_rate) / sb.buf_size);
   if (projected_dmacount < 1)
      projected_dmacount = 1;

   /* get the transfer going, so we can ensure interrupts are firing */
   start_transfer();
   count = clock();
   while ((clock() - count) < CLOCKS_PER_SEC && dma.count < projected_dmacount)
      ; /* spin */

   if (dma.count < projected_dmacount)
   {
      if (true == dma.autoinit)
      {
         thin_printf("thinlib.sb: Autoinit DMA failed, trying one-shot mode.\n");
         dsp_reset();
         dma.autoinit = false;
         dma.count = 0;
         return (thin_sb_start(fillbuf, user_data));
      }
      else
      {
         thin_printf("thinlib.sb: One-shot DMA mode failed, sound will not be heard.\n");
         thin_printf("thinlib.sb: DSP version: %d.%d baseio: %X IRQ: %d DMA: %d High: %d\n",
                     sb.dsp_version >> 8, sb.dsp_version & 0xFF,
                     sb.baseio, sb.irq, sb.dma, sb.dma16);
         return -1;
      }
   }

   return 0;
}

/*
** $Log: $
*/

⌨️ 快捷键说明

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