Программирование Digital sound processor звуковой карты

Тема в разделе "WASM.AUDIO", создана пользователем SOA, 25 июл 2010.

  1. SOA

    SOA New Member

    Публикаций:
    0
    Регистрация:
    31 май 2010
    Сообщения:
    67
    Приветствую всех.
    Возникла необходимость программирования DSP звуковой карты, в книге В. Несвижский "Программирование аппаратных средств в Windows" имеется некоторый материал, откуда понятно что DSP надо программировать через порты 2x6h; 2xAh; 2xCh; 2xEh.
    Меня конкретно интересуют функции порта 2xCh.
    В книге есть небольшой список функций, но их оказалось не достаточно.
    Пожалуйста помогите достать как можно более полный список функций для этого порта(2xCh).
     
  2. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    SOA
    Эм... Ну вообще то, этото порт используется для записи команд или данных.
    Но он легко гуглится :)
    Перечень команды можн найти тут: http://irlp.kc6hur.net/SoundBlaster.pdf
    Некоторое пособие по кодингу нашлось тут: http://articles.org.ru/docum/soundbl.php

    В принципе вам надо бы сказать что же вы хотите сделать, более конкретно, каких вам функций не хватило например. Тогда вам подскажут гораздо более точно.
     
  3. SOA

    SOA New Member

    Публикаций:
    0
    Регистрация:
    31 май 2010
    Сообщения:
    67
    TermoSINteZ

    У меня по google получилось найти только то что в прикрепленном файле отсюда http://www.dcee.net/Files/Programm/Sound/.

    Спасибо за перечень команд!

    Я пытаюсь написать простенький плеер с возможностью регулировки звука покачто только для wav файлов.
    Поэтому у меня возникла необходимость написания процедур воспроизведения звука как минимум с 8 битным, 16 битным и в перспективе 24 битным сэймплом.
    С написанием функции для воспроизведения 8 битного сэймпла проблем не возникло, я использовал функцию 10h и воспроизвожу звук в цикле по биту из буфера, длина которого расчитывается так чтоб его хвататало на 1 секунду.
    Но вот для воспроизведения 16 битного сэймпла я функцию пока не нашел чтоб была подобной функции 10h для 8 битного. В наличии покачто только функции с использованием DMA где размер буфера не может превышать 64Кб и воспроизводится на автомате из этого же буфера, а не как в 10h по одному байту, вследствие чего проблематично поставить в цикл данную функцию. Можно конечно использовать функции паузы, или еще как то ухищряться, но это не то что мне нужно.

    P.S. то что находится в прикрепленном файле можно посмотреть в более читабельном виде здесь
    http://homepages.cae.wisc.edu/~brodskye/sb16doc/sb16doc.html
     
  4. SOA

    SOA New Member

    Публикаций:
    0
    Регистрация:
    31 май 2010
    Сообщения:
    67
    TermoSINteZ
    Ещё раз спасибо за перечень команд, там на 55 странице кажется нашел то что меня интересовало.
    Думаю вопрос можно считать закрытым.
    8D
     
  5. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    SOA
    Ну если точно удалось, вы напишите как поступили и как решили проблему, чтобы другие новички, столкнувшись - нашли ответ.
     
  6. SOA

    SOA New Member

    Публикаций:
    0
    Регистрация:
    31 май 2010
    Сообщения:
    67
    TermoSINteZ
    OK
    Если получится то отпишу процедуру с коментами, если вы конечно это имели ввиду.
    Ну а если вы имели ввиду функцию то тут все просто ставим длину буфера для функции 16 бит и все функция обрабатывается один раз и ее смело можно помещать в цикл обработки большого буфера.
     
  7. TermoSINteZ

    TermoSINteZ Синоби даоса Команда форума

    Публикаций:
    2
    Регистрация:
    11 июн 2004
    Сообщения:
    3.546
    Адрес:
    Russia
    Да, именно это и имел ввиду. Ну или хотя бы на словах подробно, как решалась задача.
     
  8. SOA

    SOA New Member

    Публикаций:
    0
    Регистрация:
    31 май 2010
    Сообщения:
    67
    TermoSINteZ
    К сожелению при попытке использования данной функции возникли некоторые трудности. Распишу по шагам, как на странице 55 перечня команд:
    1 Установить бит прерываний для DSP
    Код (Text):
    1. Enable_16_bit_Interrupt proc    ; процедура устанавливающая прерывание DSP для 16 битного сэймпла
    2. Push edx
    3.  
    4. and bl,11110000b
    5. add bl,Fh   ; сохраняем в bx номер порта 2xFh
    6.  
    7. mov dl,82h  ; номер регистра хранящего бит прерываний для DSP
    8. call Write_DSP  ; вызываем этот регистр(функцию)
    9. in Restore_int_mode,bx  ; сохраняем предидущее значение бита прерываний для DSP
    10.  
    11. @cikl
    12. mov dl,82h  ; номер регистра хранящего бит прерываний для DSP
    13. call Write_DSP  ; вызываем этот регистр(функцию)
    14. mov dl,2h   ; устанавливаем второй бит
    15. call Write_DSP  ; записываем в порт 2xF значение 00000010b
    16. in dl,bx    ; проверяем установился ли бит
    17. cmp dl,2h
    18. jne @cikl
    19.  
    20. Pop edx
    21. ret
    22. Enable_16_bit_Interrupt ENDP
    2 разрешить прерывания (я их не запрещал)

    3 запрограммировать DMA контроллер для 16 битного DMA mode transfer (к сожелению информации в мануале не оказалось, вернее она слишком скудная для программирования контроллера динамического доступа к памяти)

    4 установить частоту дискретизации
    Код (Text):
    1. Set_DSP_Sampling_Rate proc  ; процедура устанавливающая частоту дискретизации в моем случае не важна
    2. push edx
    3.  
    4. and bl,11110000b
    5. add bl,Ch   ; сохраняем в bx номер порта 2xCh
    6. mov dl,41h  ; режим воспроизведения
    7. call Write_DSP
    8. mov dl,ACh  ; Старший байт значения частоты
    9. call Write_DSP
    10. mov dl,44h  ; младший байт значения частоты
    11. call Write_DSP  ; частота дискретизации равна 44100Гц но в моём случае не важна
    12.  
    13. pop edx
    14. ret
    15. Set_DSP_Sampling_Rate ENDP
    5 установить команду, затем тип передачи бит, число передаваемых бит
    Код (Text):
    1. Set_Transfer_mode proc  ; процедура устанавливающая тип передачи бит и размер буфера
    2. push edx
    3.  
    4. and bl,11110000b
    5. add bl,Ch   ; сохраняем в bx номер порта 2xCh
    6.  
    7. mov dl,B0h  ; 16 битное воспроизведение
    8. call Write_DSP
    9. mov dl,30h  ; 16 битное стерео
    10. call Write_DSP
    11. mov dl,1h   ; младший байт числа сэймплов, которые будут обработаны
    12. call Write_DSP
    13. mov dl,0h   ; старший байт числа сэймплов, которые будут обработаны
    14. call Write_DSP
    15.  
    16. pop edx
    17. ret
    18. Set_Transfer_mode ENDP
    6 Восстановить бит прерываний для DSP
    Код (Text):
    1. Restore_interrupt_mode proc ; процедура востанавливающая значение бита прерывания для DSP
    2. Push edx
    3.  
    4. and bl,11110000b
    5. add bl,Fh   ; сохраняем в bx номер порта 2xFh
    6.  
    7.  
    8. @cikl
    9. mov dl,82h  ; номер регистра хранящего бит прерываний для DSP
    10. call Write_DSP  ; вызываем этот регистр(функцию)
    11. mov dl,Restore_int_mode ; востанавливаем значение бита прерываний для DSP
    12. call Write_DSP  ; записываем в порт 2xFh значение 00000010b
    13. in dl,bx    ; проверяем установился ли бит
    14. cmp dl,Restore_int_mode
    15. jne @cikl
    16.  
    17. pop edx
    18. ret
    19. Restore_interrupt_mode ENDP
    Код (Text):
    1. Write_DSP proc  ; процедура записи в порт DSP 2xCh
    2.  
    3. @cikl1:     ; проверяем готовность порта 2xch к приему данных
    4. in dl,bx    ; считываем в dl значение в порте 2xch
    5. bt dl,7
    6. jc @cikl1
    7.  
    8. out bx,dl   ; записываем команду в порт 2xch
    9. ret
    10. Write_DSP ENDP
    После этих инструкций на 56 странице идет следующий текст после таблички The transfer begins here. The DSP will generate an interrupt after transferring the programmed number of samples.
    Я так понимаю что после 5 пункта DSP должен считать из памяти DMA заданное число бит, после чего будет сгенерировано прерывание. Но т.к. DMA остался незапрограммированным, то нет возможности передать через него функции информацию.
    Получается все упирается в пункт 3.
    Если я не прав поправьте меня пожалуйста. =(
     
  9. SOA

    SOA New Member

    Публикаций:
    0
    Регистрация:
    31 май 2010
    Сообщения:
    67
    Вроде бы нашел информацию по программированию DMA.

    http://wasm.ru/article.php?article=atazen02
    www.inversereality.org/files/dmaprogramming.pdf

    Не знаю поможет или нет :/.

    P.S. если у когото есть маны по программированию DMA на asm для soundblaster'а plz скажите где скачать.
     
  10. wsd

    wsd New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2007
    Сообщения:
    2.824
    SOA
    кажется в "Ассемблер Зубков С.В." было
     
  11. SOA

    SOA New Member

    Публикаций:
    0
    Регистрация:
    31 май 2010
    Сообщения:
    67
    wsd
    Спасибо!
     
  12. SOA

    SOA New Member

    Публикаций:
    0
    Регистрация:
    31 май 2010
    Сообщения:
    67
    Нашел еще немного информации по программированию DMA, которую свел в небольшой мануал(пока черновой вариант).
    По прежнему остаются вопросы по программированию DMA

    Как запрограммировать прерывания, которыми DMA сигнализирует устройству о завершении транзакции?

    Как сообщить устройству из какого канала DMA считывать информацию, хотя сам собой напрашивается ответ сообщить ему адрес в виртуальной памяти, где хранится DMA буфер и при получении прерывания считывать буфер, вот только если бы это было реализуемо, то в DMA отпала необходимость и я сообщил бы адрес большого буфера, но информации на этот счет нет.

    Но стало ясно что программа должна работать в нулевом кольце системы, т.к. при программировании DMA используется команда CLI, которая если я не ошибаюсь может использоваться только в нулевом кольце.
     
  13. SOA

    SOA New Member

    Публикаций:
    0
    Регистрация:
    31 май 2010
    Сообщения:
    67
    Все мануал считаю окончательно сформированным.
    Спасибо Jari Kaija программисту из братской финляндии, благодаря мануалу которого удалось разобраться с программированием DMA для DSP и добавить пример программирования.
     
  14. SOA

    SOA New Member

    Публикаций:
    0
    Регистрация:
    31 май 2010
    Сообщения:
    67
    Пока что удалось накодить следующее и исправить множество ошибок допущенных в предидущем коде

    Данные

    Код (Text):
    1. title PlayBuf for 32 byte
    2. .486P
    3. .MODEL FLAT, Stdcall
    4.  
    5. PUBLIC PlayByte
    6. PUBLIC PlayWord
    7. PUBLIC ResetDSP
    8.          
    9. .DATA
    10. EXTRN bufsize: DWORD        ; Перемнная хранящая количество элементов массива
    11. EXTRN numchannels: WORD     ; Переменная хранящая количество каналов fmt.numchannels
    12. EXTRN vol: BYTE         ; Переменная которая контроллирует громкость звука
    13.  
    14. adres DWORD ?           ; Переменная хранящая адрес нулевого элемента массива
    15. delay WORD ?            ; Переменная содержащая задержку в мкс
    16. Restore_int_mode byte ?     ; переменная хранящая тип прерывания DSP который был ранее
    1Установить бит прерываний для DSP

    Код (Text):
    1. Enable_16_bit_Interrupt proc    ; процедура устанавливающая бит прерывания для DSP
    2. Push eax
    3.  
    4. and dl,11110000b
    5. add dl,0Fh  ; сохраняем в bx номер порта 2xFh
    6.  
    7. mov al,82h  ; адрес регистра хранящего бит прерываний в карте регистров микшера
    8. out dx,al   ; вызываем этот регистр(функцию)
    9. in al,dx    ; сохраняем предидущее значение бита прерываний для DSP
    10. mov Restore_int_mode,al
    11.  
    12. cikl8:
    13. mov al,82h  ; номер регистра хранящего бит прерываний для DSP
    14. out dx,al   ; вызываем этот регистр(функцию)
    15. mov al,2h   ; устанавливаем второй бит (прерывание для 16 битного сэймпла)
    16. out dx,al   ; записываем в порт 2xF значение 00000010b
    17. in al,dx    ; проверяем установился ли бит
    18. cmp al,2h
    19. jne cikl8
    20.  
    21. Pop eax
    22. ret
    23. Enable_16_bit_Interrupt ENDP
    2 пропускаем разрешение прерываний так как мы их не запрещали

    3 Программируем DMA контроллер для 16 битного сэймпла без автоинициализации в моно режиме

    Код (Text):
    1. Program_DMA_chanel_5 proc   ; Программируем 5 порт DMA
    2. push eax edx
    3.  
    4. ;Отключаем 5 канал, который будем использовать для передачи
    5.  
    6. mov dx,0D4h ; адрес Single mask register для 16 битного DMA
    7. mov al,5h   ; команда отключения 5 канала
    8. out dx,al
    9.  
    10. ;Перезагружаем регистр flip-flop для DMA2
    11.  
    12. mov dx,0D8h ; адрес регистра flip-flop для 16 битного DMA
    13. mov al,0h
    14. out dx,al
    15.  
    16. ;Устанавливаем значение Mode регистра
    17.  
    18. mov dx,0D6h     ; Адрес mode регистра для DMA2
    19. mov al,01001001b    ; программируем mode регистр
    20. out dx,al
    21.  
    22. ;Передаем в Offset port значение указателя
    23.  
    24. mov dx,0C4h ; Адрес offset port для 5 канала
    25. push eax ebx ecx
    26. bt ebx,16
    27. jc next1    ; если 16 бит равен 1
    28. jmp next2   ; если не равен 1
    29. next1:
    30. mov cl,1
    31. shr ebx,cl     ; делим указатель на 2
    32. next2:
    33. mov ax,bx   ; помещаем младшую часть делимого в ax
    34. mov edx,ebx
    35. shr edx,16  ; помещаем старшую часть делимого в dx
    36.  
    37. mov bx,65535
    38. div bx
    39.  
    40. out dx,al   ; Передаем в порт младший бит часного
    41. mov al,ah
    42. out dx,al   ; Передаем в порт старший бит часного
    43.  
    44. pop ecx ebx eax
    45.  
    46. ;Передаем в Block size регистр значение 1= 2 байтам т.к. передаваемое значение всегда на меньше
    47.  
    48. mov dx,0C6h ; Адрес Block size регистра
    49. mov al,01h
    50. out dx,al
    51.  
    52. ;***************************************************************************
    53. ;Передаем в page port значение номера страницы
    54.  
    55. mov dx,8Bh  ; Адрес page port для 5 канала
    56. push eax ebx
    57.  
    58. mov ax,bx   ; помещаем младшую часть делимого в ax
    59. mov edx,ebx
    60. shr edx,16  ; помещаем старшую часть делимого в dx
    61.  
    62. mov bx,65535; получаем номер страницы
    63. div bx
    64.    
    65. out dx,al             ; передаем в page порт номер страницы
    66.  
    67.  
    68. pop ebx eax
    69. ;***************************************************************************
    70.  
    71.  
    72. ;Включаем 5 канал
    73.  
    74. mov dx,0D4h ; Адрес Single mask register для 16 битного DMA
    75. mov al,1h   ; Команда включения 5 канала
    76. out dx,al
    77.  
    78. pop edx eax
    79. ret
    80. Program_DMA_chanel_5 ENDP
    4 устанавливаем частоту дискретизации

    Код (Text):
    1. Set_DSP_Sampling_Rate proc  ; процедура устанавливающая частоту дискретизации в моем случае не важна
    2. push eax
    3.  
    4. and dl,11110000b
    5. add dl,Ch   ; сохраняем в bx номер порта 2xCh
    6. mov al,41h  ; режим воспроизведения
    7. call Write_DSP
    8. mov al,0ACh ; Старший байт значения частоты
    9. call Write_DSP
    10. mov al,44h  ; младший байт значения частоты
    11. call Write_DSP  ; частота дискретизации равна 44100Гц но в моём случае не важна
    12.  
    13. pop eax
    14. ret
    15. Set_DSP_Sampling_Rate ENDP
    5 Устанавливаем команду тип передачи и число бит

    Код (Text):
    1. Set_Transfer_mode proc  ; процедура устанавливающая тип передачи бит и размер буфера
    2. push eax
    3.  
    4. and dl,11110000b
    5. add dl,Ch   ; сохраняем в bx номер порта 2xCh
    6.  
    7. mov al,0B0h ; 16 битное воспроизведение
    8. call Write_DSP
    9. mov al,10h  ; 16 битное моно
    10. call Write_DSP
    11. mov al,1h   ; младший байт числа сэймплов, которые будут обработаны
    12. call Write_DSP
    13. mov al,0h   ; старший байт числа сэймплов, которые будут обработаны
    14. call Write_DSP
    15.  
    16. pop eax
    17. ret
    18. Set_Transfer_mode ENDP
    6 Востанавливаем бит прерываний для DSP

    Код (Text):
    1. Restore_interrupt_mode proc ; процедура востанавливающая значение бита прерывания для DSP
    2. Push eax
    3.  
    4. and dl,11110000b
    5. add dl,0Fh  ; сохраняем в bx номер порта 2xFh
    6.  
    7. cikl9:
    8. mov al,82h  ; номер регистра хранящего бит прерываний для DSP
    9. out dx,al   ; вызываем этот регистр(функцию)
    10. mov al,Restore_int_mode ; востанавливаем значение бита прерываний для DSP
    11. out dx,al   ; записываем в порт 2xFh значение 00000010b
    12. in al,dx    ; проверяем установился ли бит
    13. cmp al,Restore_int_mode
    14. jne cikl9
    15.  
    16. pop eax
    17. ret
    18. Restore_interrupt_mode ENDP
    Процедура записи в порт DSP

    Код (Text):
    1. Write_DSP proc      ; процедура записи в порт DSP 2xCh
    2. push eax
    3.  
    4. cikl3:          ; проверяем готовность порта 2xch к приему данных
    5. in al,dx        ; считываем в al значение в порте 2xch
    6. bt ax,7
    7. jc cikl3
    8.  
    9. pop eax
    10. out dx,al       ; записываем команду в порт 2xch
    11. ret
    12. Write_DSP ENDP
    Процедура воспроизведения 16 битного сэймпла из большого буфера
    Код (Text):
    1. PlayWord Proc   ; Процедура проигрывания wav файла с длинной сэймпла word
    2.     PUSH EAX EBX EDX ECX
    3.     push ebp
    4.     mov ebp,esp       ; Поместили в bp указатель на вершину стека
    5.     mov dx,[ebp+18]       ; Поместили в dx номер порта который предварительно сохранили под delphi
    6.                   ; 18 потому что eax+ebx+edx+ecx+bp=18 байт
    7.         mov ebx,[ebp+20]
    8.     mov adres,ebx         ; adres указывает на область памяти где хранится нулевой элемент массива 
    9.                   ; используем ebx потому что переменная типа DWORD
    10.     mov ax,[ebp+24]
    11.         mov delay,ax
    12.  
    13.  
    14.     POP ebp
    15.     call ResetDSP
    16.     call Enable_16_bit_Interrupt    ; Устанавливаем прерывания для DSP
    17.     call Set_DSP_Sampling_Rate  ; Устанавливаем частоту дискретизации для DSP
    18.    
    19.     mov ecx,bufsize             ; проигрываем буфер обмена
    20.     and dl,11110000b
    21.     add dl,Ch                     ; установили в bx номер порта 2xCh
    22.     cikl2:                  ; воспроизводим два бита
    23.     call Program_DMA_chanel_5   ; Программируем DMA для DSP
    24.     call Set_Transfer_mode      ; Программируем тип передачи и длинну буфера для DSP
    25.     ;Теперь мы должны что то услышать   
    26.  
    27.     push eax ecx edx
    28.     mov ah,86h  ; номер функции задержки
    29.     xor ecx,ecx ; старшее слово таймера задержки
    30.     mov dx,delay    ; младшее слово таймера задержки
    31.     int 15h     ; подождать некоторое время
    32.     pop edx ecx eax
    33.  
    34.     lea ebx,[ebx+numchannels]
    35.     lea ebx,[ebx+numchannels] ; увеличиваем указатель два раза потому что тип word
    36.     loop cikl2
    37.  
    38.     call Restore_interrupt_mode ; Востанавливаем бит прерывания для DSP
    39.  
    40.     POP ECX EDX EBX EAX
    41.     add esp,8   ; т.к. мы поместили 8 байт в стек перед выполнением процедуры
    42.             ; номер порта+adres+delay
    43. ret
    44. PlayWord ENDP
    Пока что интересует вопрос можно ли считать 32 битный указатель типа pointer из delphi линейным адресом, потому что от этого зависит работа процедуры инициализации DMA контроллера.
    Если выражаться более точно то правильность значений в порте Offset и порте Page port DMA контроллера.

    Кто в курсе пишите, ну и конечно если ошибки увидите ругайте:)

    Ниже прикрепляю asm файл для tasm.