audiox86operating-systemosdevhobby-os

How to write a simple soundblaster 16 driver using direct write mode for a hobby OS?


I am developing a 32 bit, protected mode hobby operating system. At the moment, I am looking to add simple sound support. To do this, I'm looking to use the sound blaster 16 and use the direct mode to write to the DAC (I want to avoid DMA at all costs). However, when I output a simple square wave to the DAC (using command 0x10), no sound is output from my computer's speakers. I'm looking for a solution to this problem.

I am trying to use the following algorithm to produce sound:

1. Reset DSP
2. Enable the speakers
3. Write 0x10 to 0x22C (direct mode DAC write command)
4. Write 0x00 to 0x22C (To set the speaker to low)
5. Write 0x10 to 0x22C
6. Write 0xFF to 0x22C (To set the speaker to high)
7. Jump back to step 4 and repeat.

Here is my code:

#define DSP_RESET 0x226
#define DSP_READ 0x22A
#define DSP_WRITE 0x22C
#define DSP_READ_STATUS 0x22E
#define DSP_INT_ACK 0x22F

#define REG_ADDR 0x224
#define REG_DATA 0x225

#define DIRECT_DAC 0x10
#define ENABLE_SPEAKER 0xD1

void dsp_reset(){
      uint32_t buf[4];
      *buf = 128;
      rtc_write(0, buf, 4);

      outb(1, DSP_RESET);
      rtc_read(0, 0, NULL, 0);
      outb(0, DSP_RESET);

      if(inb(DSP_READ) != 0xAA){
            print_term((uint8_t *)"Could not init sb16\n", 20);
      }

      return;
}

void play_simple_sound(){

      dsp_reset();

      while(inb(DSP_WRITE));
      print_term((uint8_t *)"Enabling speaker\n", 18);
      outb(0xD1, DSP_WRITE);

      while(inb(DSP_WRITE));
      print_term((uint8_t *)"Playing sound\n", 14);
      outb(0xF0, DSP_WRITE);

      while(1){
            while(inb(DSP_WRITE));
            outb(0x10, DSP_WRITE);
            outb(0x00, DSP_WRITE);
            rtc_read(0, 0, NULL, 0);
            while(inb(DSP_WRITE));
            outb(0x10, DSP_WRITE);
            outb(0xFF, DSP_WRITE);
            rtc_read(0, 0, NULL, 0);
      }

      return;
}

rtc_write sets the rtc frequency to a couple hundred hertz, and rct_read makes the program wait on the rtc (both these programs work correctly). The dsp_reset also works correctly, because when reading the output from the DSP, 0xAA is returned (which shows that a soundblaster 16 exists).

At the moment I am using windows 10 64 bit to run Qemu which emulates the operating system. I am running qemu with the "-soundhw all" option set. I am unsure whether I am unable to hear sound because of the code I have written or if there is something wrong with Qemu. My question is, what could the issue possibly be, and what are the steps I can take to fix this? Also, documentation and tutorials related to the sb 16 would be appreciated.


Solution

  • Under Qemu emulation, direct DAC is not possible using soundblaster 16. Direct ADC is not allowed either. Check the source code found here, look at the commands supported beginning on line 390:

    https://github.com/qemu/qemu/blob/master/hw/audio/sb16.c

    It looks like in order to output sound using the sound blaster card, you must use DMA. Going further, it looks like SB16 emulation on Qemu is pretty lacking. A good list of commands the real SB16 supports can be found here: http://the.earth.li/~tfm/oldpage/sb_dsp.html . Comparing these commands with what Qemu supports, only a small fraction are emulated.

    Edit 2: Looking into other emulators, Bochs does not support sb16 (see line 858: http://bochs.sourceforge.net/cgi-bin/lxr/source/iodev/sound/sb16.cc), but DOSBox has very impressive SB16 support, and does support direct DAC, but still doesn't support microphone direct ADC (see line 1611: http://dosbox-x.com/doxygen/html/sblaster_8cpp_source.html)