xref: /haiku/src/add-ons/kernel/drivers/audio/echo/generic/CDaffyDuck.cpp (revision fef6144999c2fa611f59ee6ffe6dd7999501385c)
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 // This file is part of Echo Digital Audio's generic driver library.
9 // Copyright Echo Digital Audio Corporation (c) 1998 - 2005
10 // All rights reserved
11 // www.echoaudio.com
12 //
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2.1 of the License, or (at your option) any later version.
17 //
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 // Lesser General Public License for more details.
22 //
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26 //
27 //---------------------------------------------------------------------------
28 //
29 // The head pointer tracks the next free slot in the circular buffers
30 // The tail pointer tracks the oldest mapping.
31 //
32 // Fixme add integrity checks for all functions
33 //
34 //****************************************************************************
35 
36 #include "CEchoGals.h"
37 
38 
39 
40 /****************************************************************************
41 
42 	Construction/destruction
43 
44  ****************************************************************************/
45 
46 //===========================================================================
47 //
48 // Overload new & delete so memory for this object is allocated
49 //	from non-paged memory.
50 //
51 //===========================================================================
52 
53 PVOID CDaffyDuck::operator new( size_t Size )
54 {
55 	PVOID 		pMemory;
56 	ECHOSTATUS 	Status;
57 
58 	Status = OsAllocateNonPaged(Size,&pMemory);
59 
60 	if ( (ECHOSTATUS_OK != Status) || (NULL == pMemory ))
61 	{
62 		ECHO_DEBUGPRINTF(("CDaffyDuck::operator new - memory allocation failed\n"));
63 
64 		pMemory = NULL;
65 	}
66 	else
67 	{
68 		memset( pMemory, 0, Size );
69 	}
70 
71 	return pMemory;
72 
73 }	// PVOID CDaffyDuck::operator new( size_t Size )
74 
75 
76 VOID  CDaffyDuck::operator delete( PVOID pVoid )
77 {
78 
79 	if ( ECHOSTATUS_OK != OsFreeNonPaged( pVoid ) )
80 	{
81 		ECHO_DEBUGPRINTF(("CDaffyDuck::operator delete memory free failed\n"));
82 	}
83 
84 }	// VOID  CDaffyDuck::operator delete( PVOID pVoid )
85 
86 
87 //===========================================================================
88 //
89 // Constructor
90 //
91 //===========================================================================
92 
93 CDaffyDuck::CDaffyDuck
94 (
95 	PCOsSupport 	pOsSupport
96 )
97 {
98 	//
99 	// Allocate the page for the duck entries
100 	//
101 	ECHOSTATUS Status;
102 	DWORD dwSegmentSize;
103 
104 
105 	Status = pOsSupport->AllocPhysPageBlock( PAGE_SIZE, m_pDuckPage);
106 	if (ECHOSTATUS_OK != Status)
107 	{
108 		ECHO_DEBUGPRINTF(("CDaffyDuck::CDaffyDuck - duck entry malloc failed\n"));
109 		return;
110 	}
111 
112 	m_DuckEntries = (DUCKENTRY *) pOsSupport->GetPageBlockVirtAddress( m_pDuckPage );
113 	pOsSupport->GetPageBlockPhysSegment(m_pDuckPage,
114 													0,
115 													m_dwDuckEntriesPhys,
116 													dwSegmentSize);
117 	//
118 	//	Stash stuff
119 	//
120 	m_pOsSupport = pOsSupport;
121 
122 	//
123 	// Other init
124 	//
125 	Reset();
126 
127 }	// CDaffyDuck::CDaffyDuck()
128 
129 
130 //===========================================================================
131 //
132 // Destructor
133 //
134 //===========================================================================
135 
136 CDaffyDuck::~CDaffyDuck()
137 {
138 
139 	m_pOsSupport->FreePhysPageBlock( PAGE_SIZE, m_pDuckPage);
140 
141 }	// CDaffyDuck::~CDaffyDuck()
142 
143 
144 
145 
146 /****************************************************************************
147 
148 	Setup and initialization
149 
150  ****************************************************************************/
151 
152 //===========================================================================
153 //
154 // InitCheck - returns ECHOSTATUS_OK if the duck created OK
155 //
156 //===========================================================================
157 
158 ECHOSTATUS CDaffyDuck::InitCheck()
159 {
160 	if (NULL == m_DuckEntries)
161 		return ECHOSTATUS_NO_MEM;
162 
163 	return ECHOSTATUS_OK;
164 
165 }	// InitCheck
166 
167 
168 //===========================================================================
169 //
170 // Reset - resets the mapping and duck entry circular buffers
171 //
172 //===========================================================================
173 
174 void CDaffyDuck::Reset()
175 {
176 	//
177 	//	Zero stuff
178 	//
179 	OsZeroMemory(m_Mappings,sizeof(m_Mappings));
180 
181 	m_dwHead = 0;
182 	m_dwTail = 0;
183 	m_dwCount = 0;
184 	m_ullLastEndPos = 0;
185 
186 	//
187 	// Set all duck entries to "end of list" except for the last one
188 	//
189 	DWORD i;
190 
191 	for (i = 0; i < MAX_ENTRIES; i++)
192 	{
193 		m_DuckEntries[i].PhysAddr = 0;
194 		m_DuckEntries[i].dwSize = 0xffffffff;
195 	}
196 
197 	//
198 	// Put the physical address of the duck at the end of
199 	// the m_DuckEntries array so the DSP will wrap around
200 	// to the start of the duck.
201 	//
202 
203 	m_DuckEntries[MAX_ENTRIES].PhysAddr = SWAP( m_dwDuckEntriesPhys );
204 	m_DuckEntries[MAX_ENTRIES].dwSize = 0;
205 
206 }	// Reset
207 
208 
209 //===========================================================================
210 //
211 // ResetStartPos - Takes the current list and re-calculates the
212 // DMA end position for each mapping, starting at DMA position zero.
213 //
214 //===========================================================================
215 
216 void CDaffyDuck::ResetStartPos()
217 {
218 	DWORD dwRemaining,dwIndex;
219 
220 	m_ullLastEndPos = 0L;
221 
222 	//
223 	// Re-calculate the end positions
224 	//
225 	dwRemaining = m_dwCount;
226 	dwIndex = m_dwTail;
227 	while (0 != dwRemaining)
228 	{
229 		if (	( 0 != m_DuckEntries[ dwIndex ].dwSize) &&
230 				( 0 != m_DuckEntries[ dwIndex ].PhysAddr ) )
231 		{
232 			m_Mappings[dwIndex].ullEndPos =
233 				m_ullLastEndPos + SWAP( m_DuckEntries[ dwIndex ].dwSize );
234 
235 			m_ullLastEndPos = m_Mappings[ dwIndex ].ullEndPos;
236 		}
237 		else
238 		{
239 			m_Mappings[dwIndex].ullEndPos = m_ullLastEndPos;
240 		}
241 
242 		dwIndex++;
243 		dwIndex &= ENTRY_INDEX_MASK;
244 
245 		dwRemaining--;
246 	}
247 
248 }	// ResetStartPos
249 
250 
251 
252 /****************************************************************************
253 
254 	Mapping management
255 
256  ****************************************************************************/
257 
258 //===========================================================================
259 //
260 // AddMapping
261 //
262 // Take a mapping and add it to the circular buffer.
263 //
264 // Note that the m_DuckEntries array is read by the DSP; entries must
265 // therefore be stored in little-endian format.
266 //
267 // The buffer pointed to by dwPhysAddr and dwBytes must be
268 // physically contiguous.
269 //
270 //===========================================================================
271 
272 ECHOSTATUS CDaffyDuck::AddMapping
273 (
274 	DWORD			dwPhysAddr,
275 	DWORD			dwBytes,
276 	NUINT 		Tag,
277 	DWORD			dwInterrupt,
278 	DWORD			&dwNumFreeEntries
279 )
280 {
281 #ifdef INTEGRITY_CHECK
282 	CheckIntegrity();
283 #endif
284 
285 	//
286 	// There must always be at least one free entry for the "end of list"
287 	// entry.  dwInterrupt may be non-zero, so make sure that there's enough
288 	// room for two more entries
289 	//
290 	if ((MAX_ENTRIES - m_dwCount) < 3)
291 	{
292 		ECHO_DEBUGPRINTF(("AddMapping - duck is full\n"));
293 		return ECHOSTATUS_DUCK_FULL;
294 	}
295 
296 	//
297 	//	At least two slots are available in the circular
298 	// buffer, so it's OK to add either a regular mapping or
299 	// a mapping with a double zero
300 	//
301 	m_DuckEntries[m_dwHead].PhysAddr = SWAP( dwPhysAddr );
302 	m_DuckEntries[m_dwHead].dwSize 	= SWAP( dwBytes );
303 
304 	m_Mappings[m_dwHead].Tag			= Tag;
305 	m_Mappings[m_dwHead].ullEndPos	= m_ullLastEndPos + dwBytes;
306 
307 	m_ullLastEndPos = m_Mappings[m_dwHead].ullEndPos;
308 
309 	//
310 	// If the caller wants an interrupt after this mapping, then
311 	// dwInterrupt will be non-zero
312 	//
313 	if (dwInterrupt)
314 	{
315 		DWORD dwNext;
316 
317 		//
318 		// Put in the double zero so the DSP will
319 		// generate an interrupt
320 		//
321 		dwNext = m_dwHead + 1;
322 		dwNext &= ENTRY_INDEX_MASK;
323 
324 		m_DuckEntries[dwNext].PhysAddr 	= 0;	// no need to swap zero!
325 		m_DuckEntries[dwNext].dwSize 		= 0;
326 
327 		m_Mappings[dwNext].ullEndPos = m_ullLastEndPos;
328 
329 		m_dwHead += 2;
330 		m_dwCount += 2;
331 	}
332 	else
333 	{
334 		m_dwHead++;
335 		m_dwCount++;
336 	}
337 
338 	//
339 	// Wrap the head index
340 	//
341 	m_dwHead &=	ENTRY_INDEX_MASK;
342 
343 	//
344 	// Return value to the caller
345 	//
346 	dwNumFreeEntries = MAX_ENTRIES - m_dwCount;
347 
348 //#ifdef _DEBUG
349 #if 0
350 	DWORD hi,lo;
351 
352 	hi = (DWORD) (m_ullLastEndPos >> 32);
353 	lo = (DWORD) (m_ullLastEndPos & 0xffffffffL);
354 
355 	ECHO_DEBUGPRINTF(("Added tag %ld, end pos 0x%08lx%08lx (int %ld)\n",Tag,hi,lo,dwInterrupt));
356 
357 #ifdef INTEGRITY_CHECK
358 	CheckIntegrity();
359 #endif
360 
361 	ECHO_DEBUGPRINTF(("Daffy duck count is %ld\n",m_dwCount));
362 
363 #endif
364 
365 	return ECHOSTATUS_OK;
366 
367 }	// AddMapping
368 
369 
370 //===========================================================================
371 //
372 // AddDoubleZero
373 //
374 // Adds a double zero to the circular buffer to cause the DSP to generate an
375 // IRQ.
376 //
377 //===========================================================================
378 
379 ECHOSTATUS CDaffyDuck::AddDoubleZero()
380 {
381 	//
382 	// There must always be at least one free entry for the "end of list"
383 	// entry.
384 	//
385 	if ((MAX_ENTRIES - m_dwCount) < 2)
386 	{
387 		ECHO_DEBUGPRINTF(("AddDoubleZero - duck is full\n"));
388 		return ECHOSTATUS_DUCK_FULL;
389 	}
390 
391 	//ECHO_DEBUGPRINTF(("CDaffyDuck::AddDoubleZero  m_dwCount %ld\n",m_dwCount));
392 
393 	//
394 	// Add the double zero
395 	//
396 	m_DuckEntries[m_dwHead].PhysAddr 	= 0;
397 	m_DuckEntries[m_dwHead].dwSize 		= 0;
398 	m_Mappings[m_dwHead].ullEndPos		= m_ullLastEndPos;
399 
400 	//
401 	// Housekeeping
402 	//
403 	m_dwHead++;
404 	m_dwHead &=	ENTRY_INDEX_MASK;
405 
406 	m_dwCount++;
407 
408 	return ECHOSTATUS_OK;
409 
410 }	// AddDoubleZero
411 
412 
413 //===========================================================================
414 //
415 //	Wrap
416 //
417 // Put a "next PLE" pointer at the end of the duck to make the DSP
418 // wrap around to the start; this creates a circular buffer.
419 //
420 //===========================================================================
421 
422 void CDaffyDuck::Wrap()
423 {
424 	ASSERT(m_dwCount != MAX_ENTRIES);
425 
426 	//
427 	// Put in the address of the start of the duck entries
428 	// and a count of zero; a count of zero tells the DSP
429 	// "Go look again for a duck entry at this address"
430 	//
431 	m_DuckEntries[m_dwHead].PhysAddr		= SWAP( m_dwDuckEntriesPhys );
432 	m_DuckEntries[m_dwHead].dwSize		= 0;
433 
434 	m_dwHead++;
435 	m_dwHead &= ENTRY_INDEX_MASK;
436 
437 	m_dwCount++;
438 
439 	m_fWrapped = TRUE;
440 
441 }	// Wrap
442 
443 
444 
445 //===========================================================================
446 //
447 // ReleaseUsedMappings
448 //
449 // Find all the mapping that've been consumed and return a list of tags
450 //
451 // Return value is the number of tags written to the array
452 //
453 //===========================================================================
454 
455 DWORD CDaffyDuck::ReleaseUsedMappings
456 (
457 	ULONGLONG 	ullDmaPos,
458 	NUINT 		*Tags,		// an array of tags
459 	DWORD			dwMaxTags	// the max number of tags in the array
460 )
461 {
462 	DWORD dwTempAddr,dwTempSize;
463 	NUINT Tag;
464 	DWORD dwTagsFree;
465 
466 	dwTagsFree = dwMaxTags;
467 	while ( (0 != m_dwCount) && (0 != dwTagsFree) )
468 	{
469 		//
470 		// Get the data from the tail
471 		//
472 		Tag = m_Mappings[m_dwTail].Tag;
473 		dwTempAddr = SWAP( m_DuckEntries[m_dwTail].PhysAddr );
474 		dwTempSize = SWAP( m_DuckEntries[m_dwTail].dwSize );
475 
476 		//
477 		// Is this an audio data mapping?
478 		//
479 		if ( (0 != dwTempAddr) && (0 != dwTempSize) )
480 		{
481 			//
482 			// Is this audio data mapping done?
483 			//
484 			if ( ullDmaPos < m_Mappings[m_dwTail].ullEndPos )
485 				break;
486 
487 			//
488 			// This one's done
489 			//
490 			*Tags = Tag;
491 			Tags++;
492 			dwTagsFree--;
493 
494 			EjectTail();
495 		}
496 		else
497 		{
498 			//
499 			// Is this non-audio data mapping done?
500 			//
501 			if ( ullDmaPos <= m_Mappings[m_dwTail].ullEndPos )
502 				break;
503 
504 			//
505 			// Pop it
506 			//
507 			EjectTail();
508 		}
509 	}
510 
511 	//
512 	// Return the number written
513 	//
514 	return dwMaxTags - dwTagsFree;
515 
516 }	// ReleaseUsedMappings
517 
518 
519 //===========================================================================
520 //
521 // RevokeMappings
522 //
523 // Returns the number actually revoked
524 //
525 //===========================================================================
526 
527 DWORD CDaffyDuck::RevokeMappings(NUINT FirstTag,NUINT LastTag)
528 {
529 	NUINT	Offset;
530 	DWORD	dwNumRevoked;
531 
532 	dwNumRevoked = 0;
533 
534 
535 	//----------------------------------------------------------------------
536 	//
537 	// The tags may be 32 bit counters, so it is possible that they will
538 	// wrap around (that is, dwLastTag may be less than dwFirstTag).  If the
539 	// tags have wrapped, use an offset so the compares work correctly.
540 	//
541 	//----------------------------------------------------------------------
542 
543 	Offset = 0;
544 	if (LastTag < FirstTag)
545 	{
546 		Offset = LastTag;
547 
548 		LastTag -= Offset;
549 		FirstTag -= Offset;
550 	}
551 
552 
553 	//----------------------------------------------------------------------
554 	//
555 	// Go through the list and revoke stuff
556 	//
557 	//----------------------------------------------------------------------
558 
559 	DWORD dwCount;
560 	DWORD dwCurrentIndex;
561 	DWORD dwNextIndex;
562 	NUINT AdjustedTag;
563 
564 	dwCurrentIndex = m_dwTail;
565 	dwCount = m_dwCount;
566 	while (dwCount != 0)
567 	{
568 		//
569 		// Get info for this mapping
570 		//
571 		AdjustedTag = m_Mappings[dwCurrentIndex].Tag - Offset;
572 
573 		//
574 		// Only check this mapping if it contains audio data
575 		//
576 		if (	(0 != m_DuckEntries[dwCurrentIndex].PhysAddr) &&
577 				(0 != m_DuckEntries[dwCurrentIndex].dwSize) )
578 		{
579 			//
580 			// See if the current mapping needs to be revoked
581 			//
582 			if ((FirstTag <= AdjustedTag) &&
583 				 (AdjustedTag <= LastTag))
584 			{
585 				//
586 				// Revoke this tag
587 				//
588 				dwNumRevoked++;
589 
590 				//
591 				// Change this duck into a duck entry pointer; the DSP
592 				// will see that the size is zero and re-fetch the duck entry
593 				// from the address specified in PhysAddr.
594 				//
595 				dwNextIndex = dwCurrentIndex + 1;
596 				dwNextIndex &= ENTRY_INDEX_MASK;
597 
598 				m_DuckEntries[dwCurrentIndex].PhysAddr =
599 					m_dwDuckEntriesPhys + (dwNextIndex * sizeof(DUCKENTRY) );
600 				m_DuckEntries[dwCurrentIndex].dwSize = 0;
601 
602 			}
603 		}
604 
605 		dwCurrentIndex++;
606 		dwCurrentIndex &= ENTRY_INDEX_MASK;
607 
608 		dwCount--;
609 	}
610 
611 
612 	//----------------------------------------------------------------------
613 	//
614 	// If any mappings were revoked, do various housekeeping tasks
615 	//
616 	//----------------------------------------------------------------------
617 
618 	if (0 != dwNumRevoked)
619 	{
620 		CleanUpTail();
621 		ResetStartPos();
622 	}
623 
624 	return dwNumRevoked;
625 
626 }	// RevokeMappings
627 
628 
629 
630 //===========================================================================
631 //
632 // CleanUpTail
633 //
634 // Removes any non-audio mappings from the tail of the list; stops
635 // removing if it finds an audio mapping
636 //
637 //===========================================================================
638 
639 void CDaffyDuck::CleanUpTail()
640 {
641 	while (0 != m_dwCount)
642 	{
643 		//
644 		// Quit the loop at the first audio data entry
645 		//
646 		if (	(0 != m_DuckEntries[ m_dwTail ].PhysAddr) &&
647 				(0 != m_DuckEntries[ m_dwTail ].dwSize) )
648 			break;
649 
650 		//
651 		// Pop goes the weasel
652 		//
653 		EjectTail();
654 	}
655 
656 }	// CleanUpTail
657 
658 
659 
660 
661 //===========================================================================
662 //
663 // EjectTail
664 //
665 // Removes a single mapping from the tail of the list
666 //
667 //===========================================================================
668 
669 void CDaffyDuck::EjectTail()
670 {
671 #ifdef _DEBUG
672 	if (0 == m_dwCount)
673 	{
674 		ECHO_DEBUGPRINTF(("EjectTail called with zero count!\n"));
675 		ECHO_DEBUGBREAK();
676 		return;
677 	}
678 #endif
679 
680 	//
681 	//	Mark this entry with the "end of list" values
682 	//
683 	m_DuckEntries[ m_dwTail ].PhysAddr = 0;
684 	m_DuckEntries[ m_dwTail ].dwSize = 0xffffffff;
685 
686 	//
687 	// Move the tail forward and decrement the count
688 	//
689 	m_dwTail++;
690 	m_dwTail &= ENTRY_INDEX_MASK;
691 
692 	m_dwCount--;
693 
694 } // EjectTail
695 
696 
697 
698 //===========================================================================
699 //
700 // Adjusts the duck so that DMA will start from a given position; useful
701 // when resuming from pause
702 //
703 //===========================================================================
704 
705 void CDaffyDuck::AdjustStartPos(ULONGLONG ullPos)
706 {
707 	DWORD dwCount,dwIndex;
708 	ULONGLONG ullMapStartPos;
709 	DWORD dwPhysAddr;
710 	DWORD dwSize;
711 
712 
713 	dwCount = m_dwCount;
714 	dwIndex = m_dwTail;
715 	while (0 != dwCount)
716 	{
717 		//
718 		// Check DMA pos
719 		//
720 		if (ullPos >= m_Mappings[dwIndex].ullEndPos)
721 			break;
722 
723 		dwSize = SWAP(m_DuckEntries[dwIndex].dwSize);
724 		ullMapStartPos = m_Mappings[dwIndex].ullEndPos - dwSize;
725 		if (ullPos >= ullMapStartPos)
726 		{
727 			dwPhysAddr = SWAP(m_DuckEntries[dwIndex].PhysAddr);
728 			if ( (0 != dwPhysAddr) && (0 != dwSize) )
729 			{
730 				DWORD dwDelta;
731 
732 				dwDelta = (DWORD) (m_Mappings[dwIndex].ullEndPos - ullPos);
733 				dwPhysAddr += dwDelta;
734 				dwSize -= dwDelta;
735 
736 				m_DuckEntries[dwIndex].PhysAddr = SWAP(dwPhysAddr);
737 				m_DuckEntries[dwIndex].dwSize = SWAP(dwSize);
738 				break;
739 			}
740 		}
741 
742 		dwCount--;
743 		dwIndex++;
744 		dwIndex &= ENTRY_INDEX_MASK;
745 	}
746 
747 }
748 
749 
750 //===========================================================================
751 //
752 // GetPhysStartAddr
753 //
754 // This returns the physical address of the start of the scatter-gather
755 // list; used to tell the DSP where to start looking for duck entries.
756 //
757 //===========================================================================
758 
759 DWORD CDaffyDuck::GetPhysStartAddr()
760 {
761 	return m_dwDuckEntriesPhys + (m_dwTail * sizeof(DUCKENTRY));
762 }
763 
764 
765 //===========================================================================
766 //
767 // CheckIntegrity
768 //
769 // Debug code - makes sure that the buffer count, head, and tail all agree
770 //
771 //===========================================================================
772 
773 #ifdef INTEGRITY_CHECK
774 
775 void CDaffyDuck::CheckIntegrity()
776 {
777 	DWORD dwDiff,dwCount,dwTemp,dwSum;
778 
779 	dwDiff = m_dwHead - m_dwTail;
780 	if (dwDiff > 0x80000000)
781 		dwDiff += MAX_ENTRIES;
782 
783 	if ((0 == dwDiff) && (MAX_ENTRIES == m_dwCount))
784 		return;
785 
786 	if (dwDiff != m_dwCount)
787 	{
788 		ECHO_DEBUGPRINTF(("CDaffyDuck integrity check fail!  m_dwHead %ld  m_dwTail %ld  "
789 								"m_dwCount %ld  m_Mappings[m_dwHead].dwNumEntries %ld\n",
790 								m_dwHead,m_dwTail,m_dwCount,m_Mappings[m_dwHead].dwNumEntries));
791 		ECHO_DEBUGBREAK();
792 	}
793 
794 	dwTemp = m_dwTail;
795 	dwCount = m_dwCount;
796 	dwSum = 0;
797 	while (dwCount)
798 	{
799 		dwSum += m_Mappings[dwTemp].dwNumEntries;
800 
801 		dwCount--;
802 		dwTemp++;
803 		dwTemp &= ENTRY_INDEX_MASK;
804 	}
805 
806 	if (dwSum != m_dwCount)
807 	{
808 		ECHO_DEBUGPRINTF(("CDaffyDuck integrity check fail!  dwSum %ld  m_dwCount %ld\n",
809 								dwSum,m_dwCount));
810 		ECHO_DEBUGBREAK();
811 	}
812 
813 }	// CheckIntegrity
814 
815 #endif // INTEGRITY_CHECK
816 
817 
818 // *** CDaffyDuck.cpp ***
819 
820