Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
6.4 KiB

  1. // A Somewhat Useful Sample Real Time Client.
  2. // Author: Joseph Ballantyne
  3. // Date: 3/12/99
  4. // This is the first real time client that actually does something
  5. // useful. This is a midi MPU401 sequencer. It is simple, but it
  6. // works and uses less CPU than our current 98 and NT WDM sequencer.
  7. // Of course since this is a sample and I am not in the business of
  8. // writing MIDI sequencers, it has some limitations.
  9. // The data to be sequenced is passed in in one big block.
  10. // It is passed in already formatted and timestamped appropriately.
  11. // This code simply processes the buffer it was passed, and when
  12. // it has sequenced everything that was passed in, it returns -
  13. // which kills the real time thread.
  14. #include "common.h"
  15. #include "rt.h"
  16. #include "sequence.h"
  17. // This code only supports the MPU401 at a fixed IO location of 0x330.
  18. // MPU401 defines
  19. #define MPU401BASEADDRESS 0x330
  20. #define MPU401_REG_DATA 0x00 // Data in/out register offset from base address
  21. #define MPU401_REG_COMMAND 0x01 // Command register offset from base address
  22. #define MPU401_REG_STATUS 0x01 // Status register offset from base addess
  23. #define MPU401_DRR 0x40 // Output ready (for command or data)
  24. #define MPU401_DSR 0x80 // Input ready (for data)
  25. #define MPU401_CMD_RESET 0xFF // Reset command
  26. #define MPU401_CMD_UART 0x3F // Switch to UART mod
  27. #define MPU401_ACK 0xFE // Ack from MPU401 after successful command.
  28. #define MidiWriteOK(status) ((status & MPU401_DRR) == 0)
  29. #define MidiReadOK(status) ((status & MPU401_DSR) == 0)
  30. // Here is the format of the data we process.
  31. #pragma pack(push,1)
  32. typedef struct MidiChunk{
  33. struct MidiChunk *next;
  34. struct MidiChunk *previous;
  35. ULONGLONG timestamp;
  36. ULONG numbytes;
  37. UCHAR data[3];
  38. } MidiMessage, *PMidiMessage;
  39. #pragma pack(pop)
  40. // Remember, everything we touch HAS to be locked down.
  41. #pragma LOCKED_CODE
  42. #pragma LOCKED_DATA
  43. MidiMessage testmidi[2]={
  44. {&testmidi[1],&testmidi[1],0,3,0x99,0x25,0x7f},
  45. {&testmidi[0],&testmidi[0],240,3,0x99,0x25,0}
  46. };
  47. #pragma warning ( disable : 4035 )
  48. #define rdtsc __asm _emit 0x0f __asm _emit 0x31
  49. LONGLONG __inline ReadCycleCounter(VOID)
  50. {
  51. __asm {
  52. rdtsc
  53. }
  54. }
  55. #pragma warning ( default : 4035 )
  56. VOID OutB(ULONG address, ULONG data)
  57. {
  58. __asm {
  59. mov edx,address
  60. mov eax,data
  61. out dx,al
  62. }
  63. }
  64. #pragma warning( disable : 4035 )
  65. ULONG InB(ULONG address)
  66. {
  67. __asm {
  68. mov edx,address
  69. xor eax,eax
  70. in al,dx
  71. }
  72. }
  73. #pragma warning( default : 4035 )
  74. // This routine sends a command to the MPU401 and waits for an acknowledge from
  75. // the MPU that the command succeeded. If no ack is recieved in 200ms then
  76. // it returns FALSE, otherwise it returns TRUE.
  77. BOOL SendMpuCommand(ULONG MidiBaseAddress, UCHAR command)
  78. {
  79. LONG count=0;
  80. // Wait until OK to write a command.
  81. while (!MidiWriteOK(InB(MidiBaseAddress+MPU401_REG_STATUS))) {
  82. RtYield(0, 0);
  83. }
  84. // Send command to the MPU401.
  85. OutB(MidiBaseAddress+MPU401_REG_COMMAND, command);
  86. // Wait for the MPU acknowlege. If we don't get the correct response
  87. // in 200ms or less, then punt.
  88. while (!MidiReadOK(InB(MidiBaseAddress+MPU401_REG_STATUS))) {
  89. count++;
  90. if (count<=200) {
  91. RtYield(0, 0);
  92. }
  93. else {
  94. // We did not get any response, perhaps we were in UART mode.
  95. #if 0
  96. return FALSE;
  97. #else
  98. break;
  99. #endif
  100. }
  101. }
  102. // At this point we received something from the MPU, check if it is an ack.
  103. if (InB(MidiBaseAddress+MPU401_REG_DATA)!=0xfe) {
  104. // Not a command acknowledge.
  105. Trap();
  106. return FALSE;
  107. }
  108. return TRUE;
  109. }
  110. VOID PlayMidi(PVOID Context, ThreadStats *Statistics)
  111. {
  112. PMidiMessage RealTimeMidiData;
  113. ULONG MidiBaseAddress;
  114. ULONG MidiStatus;
  115. ULONG count;
  116. LONGLONG starttime;
  117. //Trap();
  118. RealTimeMidiData=(PMidiMessage)Context;
  119. MidiBaseAddress=MPU401BASEADDRESS;
  120. count=0;
  121. // First wait until the MPU401 comes on line. We need to do this because
  122. // many MPU401s are plug and play devices and do not appear until
  123. // the hardware is setup. Note that on most machines, when there is
  124. // no device on the bus at the I/O port address, InB will return
  125. // 0xff.
  126. while (InB(MidiBaseAddress+MPU401_REG_STATUS)==0xff) {
  127. RtYield(0, 0);
  128. }
  129. //Trap();
  130. // At this point, there is detected hardware at 331.
  131. // Now wait an extra 10 seconds.
  132. starttime=Statistics->ThisPeriodStartTime;
  133. while((Statistics->ThisPeriodStartTime-starttime)/SEC<10) {
  134. RtYield(0, 0);
  135. }
  136. //Trap();
  137. // Now read the MPU401 data register until it is empty.
  138. while (MidiReadOK(InB(MidiBaseAddress+MPU401_REG_STATUS))) {
  139. InB(MidiBaseAddress+MPU401_REG_DATA);
  140. RtYield(0, 0);
  141. }
  142. //Trap();
  143. // Now reset the MPU401. If this succeeds, we know we have a real
  144. // MPU at our base address. Note that the MPU may be in UART
  145. // mode. If so, then we will NOT get an acknowlege back when we reset it.
  146. // To handle this case, if there is no acknowlege on the first
  147. // reset, we will retry once and see if we get the acknowlege the second
  148. // time. If not, then we punt.
  149. if (!SendMpuCommand(MidiBaseAddress, MPU401_CMD_RESET)) {
  150. if (!SendMpuCommand(MidiBaseAddress, MPU401_CMD_RESET)) {
  151. Trap();
  152. return;
  153. }
  154. }
  155. // Now put the MPU into UART mode.
  156. if (!SendMpuCommand(MidiBaseAddress, MPU401_CMD_UART)) {
  157. // For now we disable the trap and the thread exit.
  158. // We do this because if we run this code on current WDM driven devices,
  159. // the acknowlege code will be handled by the read interrupt service
  160. // routine of the driver and so we will not see it.
  161. Trap();
  162. return;
  163. }
  164. // Now reset the starttime.
  165. starttime=ReadCycleCounter();
  166. // Run until we are out of data to send.
  167. while (RealTimeMidiData!=NULL) {
  168. // Wait until we need to send the next chunk of data.
  169. // if (Statistics->totalperiods*MSEC<RealTimeMidiData->timestamp) {
  170. if ((ULONGLONG)(ReadCycleCounter()-starttime)/200000<RealTimeMidiData->timestamp) {
  171. RtYield(0, 0);
  172. continue;
  173. }
  174. // Read the status of the device.
  175. MidiStatus=InB(MidiBaseAddress+MPU401_REG_STATUS);
  176. // Make sure its OK to write to the device. We should ALWAYS
  177. // be able to, since we syncronize with the hardware. If not,
  178. // then update the syncronization, and slip to our next time slice.
  179. if (!MidiWriteOK(MidiStatus)) {
  180. RtYield(0, 0);
  181. continue;
  182. }
  183. // Now write the next byte out to the MPU.
  184. OutB(MidiBaseAddress+MPU401_REG_DATA, RealTimeMidiData->data[count]);
  185. // Update our state.
  186. count++;
  187. if (count>=RealTimeMidiData->numbytes) {
  188. RealTimeMidiData->timestamp+=250;
  189. RealTimeMidiData=RealTimeMidiData->next;
  190. count=0;
  191. }
  192. // And yeild - we are done until its time for the next byte.
  193. RtYield(0, 0);
  194. }
  195. }