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

1

7

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.

Leakage answered 14/11, 2018 at 18:34 Comment(8)
Try running a known-good sound-producing program or demo in your QEMU, to rule out that problem. e.g. boot DOS in your QEMU and run something that makes sound. Or boot Linux in your QEMU. re: SB16 info: wiki.osdev.org/Sound_Blaster_16 has some stuff, including links to more detailed guides.Gouge
I booted up DOS and ran DOOM inside of Qemu. The sound works correctly in that configurationLeakage
Well that proves that your QEMU is configured to produce sound through the Windows host OS, and that DMA sound output works. I assume QEMU's SB16 emulation correctly handles non-DMA programmed-I/O sound output as well. I don't have any experience with writing SB16 drivers, so IDK if your code has any obvious errors. You don't show the definitions for your inb or outb functions, and errors in using GNU C inline asm can lead to weirdness, like if it's not volatile or you clobber a register without telling the compiler.Gouge
I should also mention that I haven't changed anything in the mixer or registers. Maybe that has something to do with it? But I don't know what registers to change or setLeakage
I know for a fact the inb and outb functions work correctly as they are used elsewhere in the OS and they work without any troubleLeakage
Probably your inb / outb are fine, but wrong constraints to inline asm can happen to work depending on the surrounding code. Working in other contexts does not prove that your definition is safe.Gouge
Are you sure you don't need to set a mode or something? Does it default to unsigned 8-bit PCM? Have you tried 0 / 127 in case it's signed PCM and you were playing a really quiet 0 / -1? Or does it maybe need outw to write a 16-bit integer? But yeah, I have no idea what the mixer defaults would be, whether it maybe defaults to muted or low volume.Gouge
A complete answer would be great, because this is an interesting topic.Versicular
L
4

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)

Leakage answered 14/11, 2018 at 21:32 Comment(1)
Interesting, I guess I was wrong to assume that working DMA output would imply complete SB16 emulation. Did you check BOCHS or DOSBOX to see if they have a more complete virtual SB16?Gouge

© 2022 - 2024 — McMap. All rights reserved.