home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / audiopdd.zip / dma.c < prev    next >
C/C++ Source or Header  |  1999-02-28  |  10KB  |  289 lines

  1. //
  2. // dma.c
  3. // 25-Jan-99
  4. //
  5. // ULONG  dmaQueryDelta(AUDIOBUFFER *audioBufferPtr);
  6. // VOID   dmaWaitForChannel(USHORT count, AUDIOBUFFER *audioBufferPtr);
  7. // VOID   dmaStart(AUDIOBUFFER *audioBufferPtr);
  8. // VOID   dmaStop(AUDIOBUFFER *audioBufferPtr);
  9. // USHORT dmaSetModeType(AUDIOBUFFER *audioBufferPtr, USHORT mode);
  10. // USHORT dmaInit(USHORT dmaChannel, USHORT dmaType, AUDIOBUFFER *audioBufferPtr);
  11. // USHORT dmaDeinit(AUDIOBUFFER *audioBufferPtr);
  12.  
  13. #include "cs40.h"
  14.  
  15. //typedef struct _DMACHANNEL_INFO {
  16. // USHORT portAddr;       //  0
  17. // USHORT portCount;      //  2
  18. // USHORT portPage;       //  4
  19. // USHORT portMask;       //  6
  20. // USHORT portMode;       //  8
  21. // USHORT portFlipFlop;   // 10
  22. // USHORT portStatus;     // 12
  23. // UCHAR  maskStatus;     // 14
  24. // UCHAR  maskEnable;     // 15
  25. // UCHAR  maskDisable;    // 16
  26. // UCHAR  typeFdma;       // 17
  27. // USHORT rsv18;          // 18
  28. //} DMACHANNEL_INFO;      // 20 bytes
  29. //
  30. //typedef struct _DMACHANNEL {
  31. // USHORT status;         //  0
  32. // USHORT ch;             //  2 0-3, 5-7
  33. // USHORT type;           //  4 DMA_TYPE_*
  34. // UCHAR  mode;           //  6 data for DMA mode register
  35. // UCHAR  page;           //  7
  36. // UCHAR  addrHi;         //  8
  37. // UCHAR  addrLo;         //  9
  38. // UCHAR  countHi;        // 10
  39. // UCHAR  countLo;        // 11
  40. // DMACHANNEL_INFO chInfo;// 12 setup data (20 bytes)
  41. // ULONG  audioBufferSize;// 32
  42. // ULONG  lastPosition;   // 36
  43. //} DMACHANNEL;           // 40 bytes
  44.  
  45. static DMACHANNEL_INFO dmaInfo[8] = {
  46.  {0x00,0x01,0x87,0x0A,0x0B,0x0C,0x08, 0x10,0x00,0x04,0,0},
  47.  {0x02,0x03,0x83,0x0A,0x0B,0x0C,0x08, 0x20,0x01,0x05,0,0},
  48.  {0x04,0x05,0x81,0x0A,0x0B,0x0C,0x08, 0x40,0x02,0x06,0,0},
  49.  {0x06,0x07,0x82,0x0A,0x0B,0x0C,0x08, 0x80,0x03,0x07,0,0},
  50.  {0xC0,0xC2,0x8F,0xD4,0xD6,0xD8,0xD0, 0x10,0x00,0x04,0,0},
  51.  {0xC4,0xC6,0x8B,0xD4,0xD6,0xD8,0xD0, 0x20,0x01,0x05,0,0},
  52.  {0xC8,0xCA,0x89,0xD4,0xD6,0xD8,0xD0, 0x40,0x02,0x06,0,0},
  53.  {0xCC,0xCE,0x8A,0xD4,0xD6,0xD8,0xD0, 0x80,0x03,0x07,0,0},
  54. };
  55.  
  56.  
  57. // type-F DMA has bits7/6 = 0, so instead of 0x5x for dma???Mode[ch], use 0x1x
  58. // selectively modified at runtime
  59.  
  60. static UCHAR dmaPlayMode[]   = {0x58,0x59,0x5A,0x5B,0x58,0x59,0x5A,0x5B};
  61. static UCHAR dmaCaptureMode[]= {0x54,0x55,0x56,0x57,0x54,0x55,0x56,0x57};
  62.  
  63.  
  64. //-------------------
  65. // in: audioBufferPtr
  66. //out: change in data produced/consumed
  67. //nts: .lastPosition is set to 0 at start
  68.  
  69. ULONG dmaQueryDelta(AUDIOBUFFER *audioBufferPtr) {
  70.  
  71.  ULONG delta, lastPos, pos;
  72.  USHORT count;
  73.  USHORT countLo, countHi;  // use USHORT rather than UCHAR so can << 8
  74.  
  75.  lastPos = audioBufferPtr->dmaCh.lastPosition;
  76.  
  77.  //NOTE: if autoinit mode then count register will NEVER read FFFF since it gets
  78.  //      reloaded with the base count -- that means count reaches 0 as "lowest"
  79.  //      -- here's a sample frequency distribution over 8192 trips through a 32KB
  80.  //      dma buffer, where for x=n, x is the count value and n is the freq of that count
  81.  //      (so, count reg=0 came up 60 times out of 8192...interesting that 7FFE is 0...
  82.  //      ...which is probably caused by IRQ handler being active, see second freqdist)
  83.  //
  84.  //      irq on:  0=60, 1=72, 2=45, 3=259, 7FFE=0,  7FFF=45, 8000=0, FFFF=0
  85.  //      irq off: 0=62, 1=72, 2=46, 3=259, 7FFE=55, 7FFF=248, 8000=0, FFFF=0
  86.  //
  87.  //      if it were a random event (it's not!), expect count for each
  88.  //      would be 0.25 (8192 div 32768) -- see bottom of this file for count test tracker
  89.  //
  90.  // this gets the current position of the channel, from 0 to size of dma buffer-1
  91.  // the port count register is decremented after each xfer
  92.  // it goes from 0 to FFFF at the last byte to xfer (that's why it's setup with count-1)
  93.  
  94.  _cli_();
  95.  outp(audioBufferPtr->dmaCh.chInfo.portFlipFlop, 0);  // set flipflop
  96.  countLo = inp(audioBufferPtr->dmaCh.chInfo.portCount);
  97.  countHi = inp(audioBufferPtr->dmaCh.chInfo.portCount);
  98.  _sti_();
  99.  
  100.  count = (countHi << 8) + countLo;
  101.  
  102.  if (audioBufferPtr->dmaCh.ch > 3) count = count + count;  // count as words if 16-bit channel
  103.  
  104.  pos = audioBufferPtr->dmaCh.audioBufferSize - (ULONG)count;
  105.  
  106.  if (pos >= lastPos) {
  107.     delta = pos - lastPos;
  108.  }
  109.  else {
  110.     delta = (audioBufferPtr->dmaCh.audioBufferSize - lastPos) + pos;
  111.  }
  112.  
  113.  audioBufferPtr->dmaCh.lastPosition = pos;
  114.  
  115.  return delta;
  116. }
  117.  
  118.  
  119. // ----------------------------------------
  120. // in: wait = uSecs to wait  (micro seconds)
  121. //     audioBufferPtr
  122. //out: n/a
  123. //nts: wait for the DRQ bit to turn off (de-assert) on this DMA channel
  124. //     since some DMA devices won't de-assert their DRQ line if they are stopped while active
  125. //
  126. //     might want to count number of loops spent here...?  but won't solve anything
  127.  
  128. VOID dmaWaitForChannel(USHORT count, AUDIOBUFFER *audioBufferPtr) {
  129.  
  130.  USHORT tPort = audioBufferPtr->dmaCh.chInfo.portStatus;
  131.  UCHAR tMask = audioBufferPtr->dmaCh.chInfo.maskStatus;
  132.  UCHAR tByte = inp(tPort);
  133.  
  134.  while ((tByte & tMask) && count) {
  135.     count--;
  136.     iodelay(WAIT_1US);  // was DMA_IO_WAIT (which is the same thing: 2)
  137.     tByte = inp(tPort);
  138.  }
  139.  
  140.  return;
  141. }
  142.  
  143.  
  144. // ----------------------------------------
  145. // in: audioBufferPtr
  146. //out: n/a
  147. //nts: starts the DMA channel (info setup in InitDMA())
  148.  
  149. VOID dmaStart(AUDIOBUFFER *audioBufferPtr) {
  150.  
  151.  UCHAR mode;
  152.  
  153.  audioBufferPtr->dmaCh.lastPosition = 0;
  154.  
  155.  mode = audioBufferPtr->dmaCh.mode;
  156.  if (audioBufferPtr->dmaCh.chInfo.typeFdma) mode = mode & 0x3F;  // use demand mode if typeF DMA
  157.  
  158.  _cli_();
  159.  outp(audioBufferPtr->dmaCh.chInfo.portMask, audioBufferPtr->dmaCh.chInfo.maskDisable); // disable channel
  160.  outp(audioBufferPtr->dmaCh.chInfo.portMode, mode);  // set mode
  161.  outp(audioBufferPtr->dmaCh.chInfo.portPage, audioBufferPtr->dmaCh.page); // set page
  162.  outp(audioBufferPtr->dmaCh.chInfo.portFlipFlop, 0); // set flip-flop
  163.  outp(audioBufferPtr->dmaCh.chInfo.portAddr, audioBufferPtr->dmaCh.addrLo); // set low address
  164.  outp(audioBufferPtr->dmaCh.chInfo.portAddr, audioBufferPtr->dmaCh.addrHi); // set high
  165.  outp(audioBufferPtr->dmaCh.chInfo.portFlipFlop, 0); // set flip-flop
  166.  outp(audioBufferPtr->dmaCh.chInfo.portCount, audioBufferPtr->dmaCh.countLo); // set low count
  167.  outp(audioBufferPtr->dmaCh.chInfo.portCount, audioBufferPtr->dmaCh.countHi); // set high
  168.  outp(audioBufferPtr->dmaCh.chInfo.portMask, audioBufferPtr->dmaCh.chInfo.maskEnable); // enable channel
  169.  _sti_();
  170.  
  171.  return;
  172. }
  173.  
  174.  
  175. // ----------------------------------------
  176. // in: audioBufferPtr
  177. //out: n/a
  178. //nts: stops the DMA channel
  179.  
  180. VOID dmaStop(AUDIOBUFFER *audioBufferPtr) {
  181.  
  182.  outp(audioBufferPtr->dmaCh.chInfo.portMask, audioBufferPtr->dmaCh.chInfo.maskDisable); // disable channel
  183.  
  184.  return;
  185. }
  186.  
  187.  
  188. // ----------------------------------------
  189. // in: audioBufferPtr
  190. //     mode  0=single, 1=demand
  191. //out: last mode type
  192. //nts: sets DMA mode type (demand mode or single mode)
  193.  
  194. USHORT dmaSetModeType(AUDIOBUFFER *audioBufferPtr, USHORT mode) {
  195.  
  196.  USHORT lastType = audioBufferPtr->dmaCh.chInfo.typeFdma;
  197.  
  198.  if (mode) {
  199.     audioBufferPtr->dmaCh.chInfo.typeFdma = 1;
  200.  }
  201.  else {
  202.     audioBufferPtr->dmaCh.chInfo.typeFdma = 0;
  203.  }
  204.  
  205.  return lastType;
  206. }
  207.  
  208.  
  209. // ----------------------------------------
  210. // in: channel = 0-3,5-7
  211. //     dmaType = see cs40.h (also comes in with special flag here: bit8=1 to use type F dma)
  212. //     audioBufferPtr
  213. //out:
  214. //nts: dma constructor
  215. //     must be sure to always have dmaType piggy-backed with DMA_TYPE_FTYPE bit flag set as needed
  216. //     called more than just from init
  217.  
  218. USHORT dmaInit(USHORT dmaChannel, USHORT dmaType, AUDIOBUFFER *audioBufferPtr) {
  219.  
  220.  ULONG bufferPhysAddr, count;
  221.  USHORT typeFdma;
  222.  
  223.  typeFdma = dmaType & DMA_TYPE_FTYPE;   // bit8=1
  224.  dmaType = dmaType & ~DMA_TYPE_FTYPE;
  225.  
  226.  if ((dmaType & DMA_TYPE_ISA) == 0) return 87;  // whatever
  227.  if (dmaChannel > 7 || dmaChannel == 2 || dmaChannel == 4) return 87;
  228.  
  229.  audioBufferPtr->dmaCh.ch = dmaChannel;
  230.  audioBufferPtr->dmaCh.type = dmaType;
  231.  
  232.  audioBufferPtr->dmaCh.chInfo = dmaInfo[dmaChannel]; // whoa! structure copy
  233.  
  234.  if ((dmaType & DMA_TYPE_DIRECTION) == DMA_TYPE_CAPTURE) {
  235.     audioBufferPtr->dmaCh.mode = dmaCaptureMode[dmaChannel];
  236.  }
  237.  else {
  238.     audioBufferPtr->dmaCh.mode = dmaPlayMode[dmaChannel];
  239.  }
  240.  
  241.  // doing demand mode DMA instead of single improves performance a lot:
  242.  //    55% CPU use to 28% on 486
  243.  // this picked up each time dma is started, so can be changed at runtime
  244.  // just by chaning abPtr->dmaCh.chInfo.typeFdma (via dmaSetModeType())
  245.  // may also want to look at chipsetSetDTM(USHORT dtmFlag)
  246.  
  247.  dmaSetModeType(audioBufferPtr, typeFdma);
  248.  
  249.  // while it seems redundant to have both dmaCh.audioBufferSize and ab.bufferSize (and ws.bufferSize)
  250.  // it serves its purpose since dmaCh.audioBufferSize is only set here and so
  251.  // I can call here to re-size logical buffer size
  252.  
  253.  count = audioBufferPtr->bufferSize;
  254.  audioBufferPtr->dmaCh.audioBufferSize = count;
  255.  
  256.  if (dmaChannel > 3) {
  257.     count = count >> 1;  // word count (apparently don't need to -1 as do with 8 bit DMA channel)
  258.  }
  259.  else {
  260.     count = count - 1;
  261.  }
  262.  
  263.  audioBufferPtr->dmaCh.countHi = (UCHAR)(count >> 8);
  264.  audioBufferPtr->dmaCh.countLo = (UCHAR)count;
  265.  
  266.  bufferPhysAddr = audioBufferPtr->bufferPhysAddr;
  267.  
  268.  audioBufferPtr->dmaCh.page = (UCHAR)(bufferPhysAddr >> 16);
  269.  audioBufferPtr->dmaCh.addrHi = (UCHAR)(bufferPhysAddr >> 8);
  270.  audioBufferPtr->dmaCh.addrLo = (UCHAR)bufferPhysAddr;
  271.  
  272.  return 0;
  273. }
  274.  
  275.  
  276. // ----------------------------------------
  277. // in: audioBufferPtr
  278. //out:
  279. //nts: dma destructor
  280. //     assumes is running...probably doesn't matter if it isn't
  281.  
  282. USHORT dmaDeinit(AUDIOBUFFER *audioBufferPtr) {
  283.  
  284.  dmaStop(audioBufferPtr);
  285.  
  286.  return 0;
  287. }
  288.  
  289.