xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CDaffyDuck.cpp (revision 5d9e40fe9252c8f9c5e5e41594545bfa4419fcc7)
1 // ****************************************************************************
2 //
3 //		CDaffyDuck.CPP
4 //
5 //		Implementation file for the CDaffyDuck class.
6 //		Set editor tabs to 3 for your viewing pleasure.
7 //
8 //		Copyright Echo Digital Audio Corporation (c) 1998 - 2002
9 //		All rights reserved
10 //		www.echoaudio.com
11 //
12 //		Permission is hereby granted, free of charge, to any person obtaining a
13 //		copy of this software and associated documentation files (the
14 //		"Software"), to deal with the Software without restriction, including
15 //		without limitation the rights to use, copy, modify, merge, publish,
16 //		distribute, sublicense, and/or sell copies of the Software, and to
17 //		permit persons to whom the Software is furnished to do so, subject to
18 //		the following conditions:
19 //
20 //		- Redistributions of source code must retain the above copyright
21 //		notice, this list of conditions and the following disclaimers.
22 //
23 //		- Redistributions in binary form must reproduce the above copyright
24 //		notice, this list of conditions and the following disclaimers in the
25 //		documentation and/or other materials provided with the distribution.
26 //
27 //		- Neither the name of Echo Digital Audio, nor the names of its
28 //		contributors may be used to endorse or promote products derived from
29 //		this Software without specific prior written permission.
30 //
31 //		THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 //		EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 //		MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
34 //		IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
35 //		ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
36 //		TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
37 //		SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
38 //
39 //---------------------------------------------------------------------------
40 //
41 // The head pointer tracks the next free slot in the circular buffers
42 // The tail pointer tracks the oldest mapping.
43 //
44 //****************************************************************************
45 
46 #include "CEchoGals.h"
47 
48 
49 
50 /****************************************************************************
51 
52 	Construction/destruction
53 
54  ****************************************************************************/
55 
56 //===========================================================================
57 //
58 // Overload new & delete so memory for this object is allocated
59 //	from non-paged memory.
60 //
61 //===========================================================================
62 
63 PVOID CDaffyDuck::operator new( size_t Size )
64 {
65 	PVOID 		pMemory;
66 	ECHOSTATUS 	Status;
67 
68 	Status = OsAllocateNonPaged(Size,&pMemory);
69 
70 	if ( (ECHOSTATUS_OK != Status) || (NULL == pMemory ))
71 	{
72 		ECHO_DEBUGPRINTF(("CDaffyDuck::operator new - memory allocation failed\n"));
73 
74 		pMemory = NULL;
75 	}
76 	else
77 	{
78 		memset( pMemory, 0, Size );
79 	}
80 
81 	return pMemory;
82 
83 }	// PVOID CDaffyDuck::operator new( size_t Size )
84 
85 
86 VOID  CDaffyDuck::operator delete( PVOID pVoid )
87 {
88 	if ( ECHOSTATUS_OK != OsFreeNonPaged( pVoid ) )
89 	{
90 		ECHO_DEBUGPRINTF(("CDaffyDuck::operator delete memory free failed\n"));
91 	}
92 
93 }	// VOID  CDaffyDuck::operator delete( PVOID pVoid )
94 
95 
96 //===========================================================================
97 //
98 // Constructor
99 //
100 //===========================================================================
101 
102 CDaffyDuck::CDaffyDuck
103 (
104 	PCOsSupport 	pOsSupport,
105 	CDspCommObject	*pDspCommObject,
106 	WORD				wPipeIndex
107 )
108 {
109 	//
110 	// Allocate the page for the duck entries
111 	//
112 	if ( ECHOSTATUS_OK != pOsSupport->OsPageAllocate( 1,
113 																	  (PVOID *) &m_DuckEntries,
114 																	  &m_dwDuckEntriesPhys ) )
115 	{
116 		ECHO_DEBUGPRINTF( ("CDaffyDuck: memory allocation failed\n") );
117 		return;
118 	}
119 
120 	//
121 	//	Stash stuff
122 	//
123 	m_pOsSupport = pOsSupport;
124 	m_pDspCommObject = pDspCommObject;
125 	m_wPipeIndex = wPipeIndex;
126 
127 	//
128 	// Put the physical address of the duck at the end of
129 	// the m_DuckEntries array so the DSP will wrap around
130 	// to the start of the duck.
131 	//
132 	m_DuckEntries[MAX_ENTRIES].PhysAddr = SWAP( m_dwDuckEntriesPhys );
133 
134 }	// CDaffyDuck::CDaffyDuck()
135 
136 
137 //===========================================================================
138 //
139 // Destructor
140 //
141 //===========================================================================
142 
143 CDaffyDuck::~CDaffyDuck()
144 {
145 
146 	m_pOsSupport->OsPageFree(1,m_DuckEntries,m_dwDuckEntriesPhys);
147 
148 }	// CDaffyDuck::~CDaffyDuck()
149 
150 
151 
152 
153 /****************************************************************************
154 
155 	Setup and initialization
156 
157  ****************************************************************************/
158 
159 //===========================================================================
160 //
161 // InitCheck - returns ECHOSTATUS_OK if the duck created OK
162 //
163 //===========================================================================
164 
165 ECHOSTATUS CDaffyDuck::InitCheck()
166 {
167 	if (NULL == m_DuckEntries)
168 		return ECHOSTATUS_NO_MEM;
169 
170 	return ECHOSTATUS_OK;
171 
172 }	// InitCheck
173 
174 
175 //===========================================================================
176 //
177 // Reset - resets the mapping and duck entry circular buffers
178 //
179 //===========================================================================
180 
181 void CDaffyDuck::Reset()
182 {
183 	//
184 	//	Zero everything
185 	//
186 	OsZeroMemory(m_DuckEntries,4096);
187 	OsZeroMemory(m_Mappings,sizeof(m_Mappings));
188 
189 	m_dwHead = 0;
190 	m_dwTail = 0;
191 	m_dwCount = 0;
192 	m_ullLastEndPos = 0;
193 
194 	//
195 	// Put the physical address of the duck at the end of
196 	// the m_DuckEntries array so the DSP will wrap around
197 	// to the start of the duck.
198 	//
199 	m_DuckEntries[MAX_ENTRIES].PhysAddr = SWAP( m_dwDuckEntriesPhys );
200 
201 }	// Reset
202 
203 
204 //===========================================================================
205 //
206 // ResetStartPos - Takes the current list and re-calculates the
207 // DMA end position for each mapping, starting at DMA position zero.
208 //
209 //===========================================================================
210 
211 void CDaffyDuck::ResetStartPos()
212 {
213 	DWORD dwRemaining,dwIndex,dwNumEntries;
214 
215 	m_ullLastEndPos = 0L;
216 
217 	//
218 	// See if the mapping at the buffer tail has been consumed
219 	//
220 	dwRemaining = m_dwCount;
221 	dwIndex = m_dwTail;
222 	while (0 != dwRemaining)
223 	{
224 		m_Mappings[dwIndex].ullEndPos =
225 			m_ullLastEndPos + SWAP( m_DuckEntries[dwIndex].dwSize );
226 
227 		m_ullLastEndPos = m_Mappings[dwIndex].ullEndPos;
228 
229 		dwNumEntries = m_Mappings[m_dwTail].dwNumEntries;
230 
231 		ASSERT(dwRemaining >= dwNumEntries);
232 
233 		dwIndex += dwNumEntries;
234 		if (dwIndex >= MAX_ENTRIES)
235 			dwIndex -= MAX_ENTRIES;
236 
237 		dwRemaining -= dwNumEntries;
238 	}
239 
240 }	// ResetStartPos
241 
242 
243 
244 
245 /****************************************************************************
246 
247 	Mapping management
248 
249  ****************************************************************************/
250 
251 //===========================================================================
252 //
253 // AddMapping
254 //
255 // Take a mapping and add it to the circular buffer.
256 //
257 // Note that the m_DuckEntries array is read by the DSP; entries must
258 // therefore be stored in little-endian format.
259 //
260 // The buffer pointed to by dwPhysAddr and dwBytes must be
261 // physically contiguous.
262 //
263 //===========================================================================
264 
265 ECHOSTATUS CDaffyDuck::AddMapping
266 (
267 	DWORD			dwPhysAddr,
268 	DWORD			dwBytes,
269 	DWORD			dwTag,
270 	DWORD			dwInterrupt,
271 	DWORD			&dwNumFreeEntries
272 )
273 {
274 #ifdef INTEGRITY_CHECK
275 	CheckIntegrity();
276 #endif
277 
278 	//
279 	// The caller should only be trying to do this if
280 	// there are at least two free entries
281 	//
282 	ASSERT((MAX_ENTRIES - m_dwCount) >= 2);
283 
284 	//
285 	//	At least two slots are available in the circular
286 	// buffer, so it's OK to add either a regular mapping or
287 	// a mapping with a double zero
288 	//
289 	m_DuckEntries[m_dwHead].PhysAddr = SWAP( dwPhysAddr );
290 	m_DuckEntries[m_dwHead].dwSize 	= SWAP( dwBytes );
291 
292 	m_Mappings[m_dwHead].dwTag			= dwTag;
293 	m_Mappings[m_dwHead].ullEndPos	= m_ullLastEndPos + dwBytes;
294 
295 	m_ullLastEndPos = m_Mappings[m_dwHead].ullEndPos;
296 
297 	/*
298 	ECHO_DEBUGPRINTF(("CDaffyDuck::AddMapping  m_dwCount %ld  pipe index %d  end pos %I64x\n",
299 							m_dwCount,m_wPipeIndex,m_ullLastEndPos));
300 	*/
301 
302 	//
303 	// If the caller wants an interrupt after this mapping, then
304 	// dwInterrupt will be non-zero
305 	//
306 	if (dwInterrupt)
307 	{
308 		DWORD dwNext;
309 
310 		//
311 		// Interrupt wanted - need to add an extra slot with the double zero
312 		//
313 		m_Mappings[m_dwHead].dwNumEntries = 2;
314 
315 		//
316 		// Put in the double zero so the DSP will
317 		// generate an interrupt
318 		//
319 		dwNext = m_dwHead + 1;
320 		if (dwNext >= MAX_ENTRIES)
321 			dwNext -= MAX_ENTRIES;
322 
323 		m_DuckEntries[dwNext].PhysAddr 	= 0;	// no need to swap zero!
324 		m_DuckEntries[dwNext].dwSize 		= 0;
325 
326 		m_dwCount += 2;
327 	}
328 	else
329 	{
330 		m_Mappings[m_dwHead].dwNumEntries = 1;
331 
332 		m_dwCount++;
333 	}
334 
335 	//
336 	// Move the head to point to the next empty slot
337 	//
338 	m_dwHead += m_Mappings[m_dwHead].dwNumEntries;
339 	if (m_dwHead >= MAX_ENTRIES)
340 		m_dwHead -= MAX_ENTRIES;	// circular buffer wrap
341 
342 	//
343 	// Return value to the caller
344 	//
345 	dwNumFreeEntries = MAX_ENTRIES - m_dwCount;
346 
347 	//
348 	// Tell the DSP about the new buffer if the
349 	// interrupt flag is set
350 	//
351 	if (dwInterrupt != 0)
352 	{
353 		m_pDspCommObject->AddBuffer( m_wPipeIndex );
354 	}
355 
356 
357 //#ifdef _DEBUG
358 #if 0
359 	DWORD hi,lo;
360 
361 	hi = (DWORD) (m_ullLastEndPos >> 32);
362 	lo = (DWORD) (m_ullLastEndPos & 0xffffffffL);
363 
364 	ECHO_DEBUGPRINTF(("Added tag %ld, end pos 0x%08lx%08lx (int %ld)\n",dwTag,hi,lo,dwInterrupt));
365 
366 #ifdef INTEGRITY_CHECK
367 	CheckIntegrity();
368 #endif
369 
370 	ECHO_DEBUGPRINTF(("Daffy duck count is %ld\n",m_dwCount));
371 
372 #endif
373 
374 	return ECHOSTATUS_OK;
375 
376 }	// AddMapping
377 
378 
379 //===========================================================================
380 //
381 // AddDoubleZero
382 //
383 // Adds a double zero to the circular buffer to cause the DSP to generate an
384 // IRQ.  If the buffer is full -or- the most recent entry already has a
385 // double zero, nothing happens.
386 //
387 //===========================================================================
388 
389 void CDaffyDuck::AddDoubleZero()
390 {
391 	//
392 	// Skip if the circular buffer is full or empty
393 	//
394 	if ((MAX_ENTRIES == m_dwCount) || (0 == m_dwCount))
395 		return;
396 
397 	//ECHO_DEBUGPRINTF(("CDaffyDuck::AddDoubleZero  m_dwCount %ld\n",m_dwCount));
398 
399 	//
400 	// Search back before the head and find the most recent entry
401 	//
402 	DWORD	dwEntry;
403 #ifdef _DEBUG
404 	DWORD dwCount = 0;
405 #endif
406 
407 
408 	do
409 	{
410 #ifdef _DEBUG
411 		dwCount++;
412 		if (dwCount > 2)
413 		{
414 			ECHO_DEBUGPRINTF(("CDaffyDuck::AddDoubleZero - shouldn't search back more "
415 									"than two entries!\n"));
416 			ECHO_DEBUGBREAK();
417 		}
418 #endif
419 
420 		dwEntry = m_dwHead - 1;
421 		if (dwEntry >= 0x80000000)
422 			dwEntry += MAX_ENTRIES;		// circular buffer wrap
423 
424 		if (m_Mappings[dwEntry].dwNumEntries != 0)
425 			break;
426 
427 
428 
429 	} while (dwEntry != m_dwTail);
430 
431 	//
432 	// Quit if this entry already has a double zero
433 	//
434 	ASSERT(m_Mappings[dwEntry].dwNumEntries <= 2);
435 
436 	if (2 == m_Mappings[dwEntry].dwNumEntries)
437 		return;
438 
439 	//
440 	// Add the double zero
441 	//
442 	DWORD	dwNext;
443 
444 	dwNext = dwEntry + 1;
445 	if (dwNext >= MAX_ENTRIES)
446 		dwNext -= MAX_ENTRIES;
447 
448 	m_DuckEntries[dwNext].PhysAddr 	= 0;
449 	m_DuckEntries[dwNext].dwSize 		= 0;
450 
451 	m_Mappings[dwEntry].dwNumEntries += 1;
452 
453 	m_dwCount++;
454 
455 	m_dwHead = dwEntry + m_Mappings[dwEntry].dwNumEntries;
456 	if (m_dwHead >= MAX_ENTRIES)
457 		m_dwHead -= MAX_ENTRIES;
458 
459 	//
460 	// Tell the DSP about it
461 	//
462 	m_pDspCommObject->AddBuffer( m_wPipeIndex );
463 
464 	//ECHO_DEBUGPRINTF(("Added double zero\n"));
465 
466 }	// AddDoubleZero
467 
468 
469 //===========================================================================
470 //
471 //	Wrap
472 //
473 // Put a "next PLE" pointer at the end of the duck to make the DSP
474 // wrap around to the start; this creates a circular buffer.
475 //
476 //===========================================================================
477 
478 void CDaffyDuck::Wrap()
479 {
480 	ASSERT(m_dwCount != MAX_ENTRIES);
481 
482 	//
483 	// Put in the address of the start of the duck entries
484 	// and a count of zero; a count of zero tells the DSP
485 	// "Go look again for a duck entry at this address"
486 	//
487 	m_DuckEntries[m_dwHead].PhysAddr		= SWAP( m_dwDuckEntriesPhys );
488 	m_DuckEntries[m_dwHead].dwSize		= 0;
489 
490 	m_Mappings[m_dwHead].dwNumEntries 	= 1;
491 
492 	m_dwHead++;
493 	if (m_dwHead >= MAX_ENTRIES)
494 		m_dwHead -= MAX_ENTRIES;
495 
496 	m_dwCount++;
497 
498 	m_fWrapped = TRUE;
499 
500 }	// Wrap
501 
502 
503 //===========================================================================
504 //
505 // ReleaseUsedMapping
506 //
507 // Find a mapping that's been consumed and return it
508 //
509 //===========================================================================
510 
511 ECHOSTATUS CDaffyDuck::ReleaseUsedMapping
512 (
513 	ULONGLONG 	ullDmaPos,
514 	DWORD			&dwTag
515 )
516 {
517 
518 #ifdef INTERGRITY_CHECK
519 	CheckIntegrity();
520 #endif
521 
522 	//
523 	// See if there are any mappings and if the oldest mapping has
524 	// been consumed by the DSP.
525 	//
526 	if ((0 != m_dwCount) && (ullDmaPos >= m_Mappings[m_dwTail].ullEndPos))
527 	{
528 		DWORD dwNumEntries;
529 
530 		//
531 		// Time to release the mapping at the tail
532 		//
533 		dwTag = m_Mappings[m_dwTail].dwTag;
534 
535 		//
536 		// Remove the mapping from the circular buffer,
537 		// taking dwNumEntries into consideration
538 		//
539 		dwNumEntries = m_Mappings[m_dwTail].dwNumEntries;
540 		m_Mappings[m_dwTail].dwNumEntries = 0;
541 		ASSERT(m_dwCount >= dwNumEntries);
542 
543 		m_dwTail += dwNumEntries;
544 		if (m_dwTail >= MAX_ENTRIES)
545 			m_dwTail -= MAX_ENTRIES;
546 
547 		m_dwCount -= dwNumEntries;
548 
549 #ifdef INTEGRITY_CHECK
550 		CheckIntegrity();
551 #endif
552 
553 #if 0
554 //#ifdef _DEBUG
555 		ULONGLONG ullRemaining;
556 
557 		ullRemaining = m_ullLastEndPos - ullDmaPos;
558 
559 		ECHO_DEBUGPRINTF(("Released mapping for pipe index %d - bytes remaining %I64x\n",
560 								m_wPipeIndex,ullRemaining));
561 #endif
562 
563 		return ECHOSTATUS_OK;
564 	}
565 
566 	//
567 	// No bananas today, thanks.
568 	//
569 	return ECHOSTATUS_NO_DATA;
570 
571 }	// ReleaseUsedMappings
572 
573 
574 //===========================================================================
575 //
576 // RevokeMappings
577 //
578 // Returns the number actually revoked
579 //
580 //===========================================================================
581 
582 DWORD CDaffyDuck::RevokeMappings(DWORD dwFirstTag,DWORD dwLastTag)
583 {
584 	MAPPING 		*TempMappings = NULL;
585 	DUCKENTRY	*TempEntries = NULL;
586 	DWORD			dwNumTemp;
587 	DWORD			dwOffset;
588 	DWORD			dwNumRevoked = 0;
589 
590 	//
591 	// Allocate the arrays
592 	//
593 	OsAllocateNonPaged(	sizeof(MAPPING) * MAX_ENTRIES,	// fixme make this part of the duck
594 							  	(void **) &TempMappings );
595 	OsAllocateNonPaged(	sizeof(DUCKENTRY) * MAX_ENTRIES,
596 								(void **) &TempEntries );
597 	if (	(NULL == TempMappings) ||
598 			(NULL == TempEntries))
599 	{
600 		goto exit;
601 	}
602 
603 	//
604 	// Tweak the end position so it's set OK
605 	// when the duck is rebuilt
606 	//
607 	SetLastEndPos(GetStartPos());
608 
609 
610 	//----------------------------------------------------------------------
611 	//
612 	// The tags are 32 bit counters, so it is possible that they will
613 	// wrap around (that is, dwLastTag may be less than dwFirstTag).  If the
614 	// tags have wrapped, use an offset so the compares work correctly.
615 	//
616 	//----------------------------------------------------------------------
617 
618 	dwOffset = 0;
619 	if (dwLastTag < dwFirstTag)
620 	{
621 		dwOffset = 0x80000000;
622 
623 		dwLastTag -= dwOffset;
624 		dwFirstTag -= dwOffset;
625 	}
626 
627 	//----------------------------------------------------------------------
628 	//
629 	// Empty out the duck into the temp arrays, throwing away
630 	// the revoked mappings.
631 	//
632 	//----------------------------------------------------------------------
633 
634 	ECHOSTATUS 	Status;
635 	DWORD			dwAdjustedTag;
636 
637 	dwNumTemp = 0;
638 	do
639 	{
640 		Status = GetTail(	TempEntries + dwNumTemp,
641 								TempMappings + dwNumTemp);
642 		if (ECHOSTATUS_OK == Status)
643 		{
644 			dwAdjustedTag = TempMappings[dwNumTemp].dwTag - dwOffset;
645 
646 			if ((dwFirstTag <= dwAdjustedTag) &&
647 				 (dwAdjustedTag <= dwLastTag))
648 			{
649 				//
650 				// Revoke this tag
651 				//
652 				dwNumRevoked++;
653 			}
654 			else
655 			{
656 				//
657 				// Save this tag
658 				//
659 				dwNumTemp++;
660 			}
661 
662 		}
663 	} while (ECHOSTATUS_OK == Status);
664 
665 
666 	//----------------------------------------------------------------------
667 	//
668 	// Add all the saved mappings back into this duck.
669 	//
670 	//----------------------------------------------------------------------
671 
672 	DWORD i,dwDummy;
673 
674 	//
675 	// Partially reset this duck
676 	//
677 	m_dwHead = 0;
678 	m_dwTail = 0;
679 	m_dwCount = 0;
680 
681 	//
682 	// Put all the non-revoked mappings back
683 	//
684 	for (i = 0; i < dwNumTemp; i++)
685 	{
686 		AddMapping(	TempEntries[i].PhysAddr,
687 						TempEntries[i].dwSize,
688 						TempMappings[i].dwTag,
689 						TempMappings[i].dwNumEntries - 1,
690 						dwDummy);
691 	}
692 
693 	ECHO_DEBUGPRINTF(("CDaffyDuck::RevokeMappings for pipe index %d - m_dwHead %ld  m_dwTail %ld  m_dwCount %ld\n",
694 							m_wPipeIndex,m_dwHead,m_dwTail,m_dwCount));
695 
696 exit:
697 	if (TempEntries) OsFreeNonPaged(TempEntries);
698 	if (TempMappings) OsFreeNonPaged(TempMappings);
699 
700 	return dwNumRevoked;
701 
702 }	// RevokeMappings
703 
704 
705 //===========================================================================
706 //
707 // GetTail
708 //
709 // Unconditionally returns the mapping information from the tail of the
710 // circular buffer.
711 //
712 //===========================================================================
713 
714 ECHOSTATUS CDaffyDuck::GetTail(DUCKENTRY *pDuckEntry,MAPPING *pMapping)
715 {
716 	//
717 	// Punt if there are no entries in the circular buffer
718 	//
719 	if (0 == m_dwCount)
720 		return ECHOSTATUS_NO_DATA;
721 
722 	//
723 	// Copy the data
724 	//
725 	OsCopyMemory(	pDuckEntry,
726 						m_DuckEntries + m_dwTail,
727 						sizeof(DUCKENTRY));
728 	OsCopyMemory(	pMapping,
729 						m_Mappings + m_dwTail,
730 						sizeof(MAPPING));
731 
732 	//
733 	// Buffer housekeeping
734 	//
735 	DWORD dwNumEntries = m_Mappings[m_dwTail].dwNumEntries;
736 	m_Mappings[m_dwTail].dwNumEntries = 0;
737 
738 	ASSERT(m_dwCount >= dwNumEntries);
739 
740 	m_dwTail += dwNumEntries;
741 	if (m_dwTail >= MAX_ENTRIES)
742 		m_dwTail -= MAX_ENTRIES;
743 
744 	m_dwCount -= dwNumEntries;
745 
746 #ifdef INTEGRITY_CHECK
747 	CheckIntegrity();
748 #endif
749 
750 	return ECHOSTATUS_OK;
751 
752 }	// GetTail
753 
754 
755 //===========================================================================
756 //
757 // CheckIntegrity
758 //
759 // Debug code - makes sure that the buffer count, head, and tail all agree
760 //
761 //===========================================================================
762 
763 #ifdef INTEGRITY_CHECK
764 
765 void CDaffyDuck::CheckIntegrity()
766 {
767 	DWORD dwDiff;
768 
769 	dwDiff = m_dwHead - m_dwTail;
770 	if (dwDiff > 0x80000000)
771 		dwDiff += MAX_ENTRIES;
772 
773 	if ((0 == dwDiff) && (MAX_ENTRIES == m_dwCount))
774 		return;
775 
776 	if (dwDiff != m_dwCount)
777 	{
778 		ECHO_DEBUGPRINTF(("CDaffyDuck integrity check fail!  m_dwHead %ld  m_dwTail %ld  "
779 								"m_dwCount %ld  m_Mappings[m_dwHead].dwNumEntries %ld\n",
780 								m_dwHead,m_dwTail,m_dwCount,m_Mappings[m_dwHead].dwNumEntries));
781 		ECHO_DEBUGBREAK();
782 	}
783 
784 }	// CheckIntegrity
785 
786 #endif // INTEGRITY_CHECK
787 
788 
789 // *** CDaffyDuck.cpp ***
790