Средства разработки приложений

Работа с DSP и DMA


unit DSP_DMA; interface procedure DspOut(val:byte);
{выводит байт на DSP} procedure SetupDMA(dmach,page,ofs,DMAcount,dmacmd:word);
{установка режима DMA} procedure SetupDSP(dspcmd, mode, DSPcount, tc:word);
{установка режима DSP} procedure SetMixer; {сброс платы и выбор источника сигнала} procedure EnableInterrupt(newvect:pointer);
{установка векторов прерываний} procedure DisableInterrupt; {восстановление векторов прерываний} implementation uses getsbinf,crt,dos; var intvecsave :pointer; { старый вектор прерывания} intrnum :integer; { номер прерывания } intrmask :integer; { маска прерывания } { структура, содержащая данные контроллера DMA } type DmaPortRec = record addr,count,page : byte; end; const DmaPorts : array[0..7]of DmaPortRec = ( (addr:$00; count:$01; page:$87), {0} (addr:$02; count:$03; page:$83), {1} (addr:$04; count:$05; page:$81), {2 не используется} (addr:$06; count:$07; page:$82), {3} (addr:$00; count:$00; page:$00), {4 не используется} (addr:$C4; count:$C6; page:$8B), {5} (addr:$C8; count:$CA; page:$89), {6} (addr:$CC; count:$CE; page:$8A));
{7} procedure DspOut(val:byte);
{выводит байт в DSP} begin while (Port[base + $0C] and $80) <>
0 do; Port[base + $0C] := val; end; function DspIn:byte;{читает байт из DSP} begin while (Port[base + $0E] and $80) = 0 do; dspin := Port[base + $0A]; end; procedure SetupDMA(dmach,page,ofs,DMAcount,dmacmd:word);
{ Программирует контроллер DMA} { для 8- или 16-разрядного канала} var mask,mode,ff : byte; begin if (dmach < 4) then begin mask := $0A; mode := $0B; ff := $0C; end else begin mask := $D4; mode := $D6; ff := $D8; ofs := (ofs shr 1) + ((page and 1) shl 15);
end; Port[mask] := 4 or dmach; { маскируем DMA} Port[FF] := 0; { сбрасываем триггер-защелку} Port[mode] := dmacmd or (dmach and 3);
{ уст.режима DMA} Port[dmaports[dmach].addr] := lo(ofs);
{ младший байт адреса} Port[dmaports[dmach].addr] := hi(ofs);
{ старший байт} Port[dmaports[dmach].page] := page; { номер страницы} Port[dmaports[dmach].count] := lo(DMAcount-1);
{ младший байт счетчика} Port[dmaports[dmach].count] := hi(DMAcount-1);
{ старший байт} Port[mask] := (dmach and 3);
{ сброс бита маски} end; procedure SetupDSP(dspcmd, mode, DSPcount, tc:word);
{ Программирует DSP звуковой платы} begin DspOut($40);
{установка константы времени} DspOut(tc);
DspOut(dspcmd);
{команда Bx/Cx} DspOut(mode);
DspOut(lo(DSPcount-1));
DspOut(hi(DSPcount-1));
end; procedure SetMixer;{сброс платы и выбор источника сигнала} var val:byte; begin Port[base + $06] := 1; {сброс DSP} delay(1);
Port[base + $06] := 0; if (dspin <>
$AA) then {проверка готовности} writeln('Sound Blaster не готов.');
Port[base + $04] := $3D; Port[base + $05] := 1; { левый канал:источник сигнала - микрофон} { Port[base + $04] := $3E; {для моно - не обязательно} { Port[base + $05] := 1; } { правый канал:источник сигнала - микрофон} Port[base + $04] := $3C; Port[base + $05] := 0; { на выходе отключаем все, что можно} end; procedure EnableInterrupt(newvect:pointer);
{установка векторов прерываний} var intrmask1:word; begin if (irq < 8) then {вычисляем номера прерывания} intrnum := irq + 8 { для IRQ 0-7 прерывания 8-15.} else intrnum := irq - 8 + $70; { для IRQ 8-15 прерывания 70H-78H.} intrmask := 1 shl irq; {маска} GetIntVec(intrnum,intvecsave);
{ сохраняем старый вектор} SetIntVec(intrnum, newvect);
{ устанавливаем новый вектор} intrmask1 := intrmask; {разрешаем прерывания} Port[$21] := Port[$21] and not intrmask1; intrmask1 := intrmask1 shr 8; Port[$A1] := Port[$A1] and not intrmask1; end; procedure DisableInterrupt; {восстановление векторов прерываний} var intrmask1:word; begin intrmask1 := intrmask; {запрещаем прерывания} Port[$21] := Port[$21] or intrmask1; intrmask1 := intrmask1 shr 8; Port[$A1] := Port[$A1] or intrmask1; SetIntVec(intrnum,intvecsave);
{восстанавливаем вектор} end; end.

После программирования DMAC то же самое проделывается и с DSP звуковой платы.
Сначала надо установить частоту дискретизации, сообщив ему константу времени t = 256 - 1 000 000 / f,
где f - частота дискретизации. Затем следует задать команду на запись/воспроизведение звука. Для Sound Blaster 16 проще всего выбрать команды Bx/Cx, состоящие из четырех байтов: Command, Mode, LenLo, LenHi. Формат первого байта Command приведен в табл. 3, а второго байта Mode - в табл. 4. Байты LenLo и LenHi - младший и старший в соответствии с длиной передаваемого блока, уменьшенной на единицу. Команды Bx/Cx позволяют задавать как знаковый, так и беззнаковый вид представления отсчетов. При знаковом отсчет представляет собой целое число со знаком, принимающее значение 0 при отсутствии входного сигнала, при беззнаковом - целое число без знака, равное 80h для 8-разрядного режима и 8000h для 16-разрядного при отсутствии входного сигнала. Стандартом де-факто является представление 8-разрядных отсчетов в беззнаковой форме, а 16-разрядных - в знаковой, однако для упрощения процедуры преобразования в приводимой программе обе величины выбраны знаковыми.

Таблица 6. Команды DSPКоманда Описание

14h8-разрядное воспроизведение через DMA без автоинициализации. Команда состоит из 3 байт, за ее кодом следует длина передаваемых данных, уменьшенная на 1
1Ch8-разрядное воспроизведение с автоинициализацией. Команда состоит из 1 байта, длина воспроизводимого блока задается командой 48h
24h8-разрядная запись, аналогичная команде 14h
2Ch8-разрядная запись с автоинициализацией, аналогичная 1Ch
40hЗадание константы времени, 2 байта: после кода команды - константа
41hЗадание частоты дискретизации вывода, 3 байта: после команды 2 байта частоты дискретизации в диапазоне 5000-45 000 Гц
42hЗадание частоты дискретизации ввода, аналогичное 41h
48hЗадание длины передаваемых данных, 3 байта, включая 2 байта данных. Определяет, по истечении какого объема переданных данных должно поступить прерывание от звуковой платы
Bxh16-разрядный ввод-вывод
Cxh8-разрядный ввод-вывод
D0hПауза 8-разрядного ввода-вывода
D1hВыключение динамика
D3hВключение динамика
D4hПродолжение 8-разрядного ввода-вывода, приостановленного командой D0h
D5hПауза 16-разрядного ввода-вывода
D6hПродолжение 16-разрядного ввода-вывода, приостановленного командой D5h
D8hПосле этой команды чтение из DSP возвращает статус динамика: 0 - выключен; FFh - включен
D9hВыход из 16-разрядного ввода-вывода с автоинициализацией
DAhВыход из 8-разрядного ввода-вывода с автоинициализацией
E1hПосле этой команды чтение 2 байт из DSP приведет к получению номера версии DSP, причем 1-й байт - старший, а 2-й - младший
После программирования микшера следует установить свои процедуры обработки прерываний от звуковой платы и только потом можно будет задавать режимы DMA и DSP.


Затем процессор свободен для выполнения любой другой работы, например с экраном, как это практикуется в компьютерных играх. В данной же программе просто происходит ожидание ввода с клавиатуры. Однако время от времени работу процессора будут приостанавливать прерывания, поступающие со звуковой платы по окончании пересылки очередной порции данных. В задачу обработчика прерываний входит определение номера канала, по которому пришло прерывание. Дело в том, что и 8-разрядный, и 16-разрядный ввод-вывод, и даже внешний MIDI-интерфейс (MPU-401) генерируют одно и то же аппаратное прерывание. Для того чтобы различать их между собой, в адресном пространстве регистров микшера имеется порт номер 82h (регистр статуса прерываний), определяющий источник прерывания (табл. 5). Обработчик прерывания должен сообщить звуковой плате, что ее прерывание принято и обработано, для чего необходимо осуществить чтение из порта 0Eh или 0Fh для 8- либо 16-разрядного режимов соответственно. После прихода прерываний от канала записи и от канала воспроизведения можно считать, что соответствующие половины буферов записи и воспроизведения уже обработаны звуковой платой и пора копировать данные из одного буфера в другой. Так как в обоих случаях была выбрана одинаковая (знаковая) форма представления данных, то их преобразование сводится лишь к переписыванию старших байтов значений двухбайтовых звуковых отсчетов из входного буфера в выходной. По завершении отработки прерывания следует осведомить об этом контроллер прерываний (с учетом каскадирования). После нажатия на программа приостанавливает и 8-, и 16-разрядные операции ввода-вывода и восстанавливает векторы прерываний. Выше приведен выборочный список команд DSP, которые применяются при записи и воспроизведении звука (табл. 6). Здесь не рассматриваются непосредственный ввод-вывод, не использующий DMAC, ввод-вывод с компрессией и MIDI-команды. ОБ АВТОРЕ Андрианов Сергей Андреевич - канд. техн. наук; e-mail: или fidonet: 2:5017/11.40.  

Содержание раздела