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.

872 lines
22 KiB

  1. /*++
  2. Copyright (c) 1990, 1991 Microsoft Corporation
  3. Module Name:
  4. hwheap.c
  5. Abstract:
  6. This module goes through ROM area and tries to pick up all the ROM
  7. blocks.
  8. Author:
  9. Shie-Lin Tzong (shielint) 21-Jan-92
  10. Environment:
  11. Real mode.
  12. Revision History:
  13. --*/
  14. #include "hwdetect.h"
  15. #include "hwvbios.h"
  16. FPTEMPORARY_ROM_BLOCK BlockHead;
  17. FPTEMPORARY_ROM_BLOCK BlockPointer;
  18. BOOLEAN
  19. AddRomBlock (
  20. ULONG RomAddress,
  21. ULONG RomSize
  22. )
  23. /*++
  24. Routine Description:
  25. This routine adds a ROM/RAM block to our ROM list.
  26. Arguments:
  27. RomAddress - the starting address of the ROM/RAM block to be added.
  28. RomSize - the size of the ROM/RAM block (in byte).
  29. Return Value:
  30. A value of TRUE is returned if success. Otherwise, a value of
  31. FALSE is returned.
  32. --*/
  33. {
  34. LONG AddSize;
  35. ULONG AddAddress;
  36. FPTEMPORARY_ROM_BLOCK pCurrentBlock, pNextBlock;
  37. ULONG CurrentBlock, NextBlock, AddBlock;
  38. ULONG EndAddBlock, EndCurrentBlock, EndNextBlock;
  39. BOOLEAN fOverlap=FALSE;
  40. pCurrentBlock = NULL;
  41. pNextBlock = NULL;
  42. AddSize = RomSize;
  43. AddAddress = RomAddress;
  44. AddBlock = RomAddress;
  45. //
  46. // If there are other blocks, make sure there is no overlap with them
  47. //
  48. if (BlockHead) {
  49. pCurrentBlock = BlockHead;
  50. pNextBlock = pCurrentBlock->Next;
  51. CurrentBlock = pCurrentBlock->RomBlock.Address;
  52. EndCurrentBlock = CurrentBlock + pCurrentBlock->RomBlock.Size;
  53. EndAddBlock = RomAddress + RomSize;
  54. while (pCurrentBlock!=NULL) {
  55. //
  56. // calculate location of next block (if it's there)
  57. //
  58. if(pNextBlock) {
  59. NextBlock = pNextBlock->RomBlock.Address;
  60. EndNextBlock = NextBlock + pNextBlock->RomBlock.Size;
  61. }
  62. //
  63. // if overlapping with current block, then stop and
  64. // resolve overlap
  65. //
  66. if((RomAddress < EndCurrentBlock)&& (RomAddress >= CurrentBlock)){
  67. fOverlap = TRUE;
  68. break;
  69. }
  70. //
  71. // if add block is lower than the current one,
  72. // or there is not a next block, then no need to search further
  73. //
  74. if((EndAddBlock <= CurrentBlock) || (pNextBlock == NULL)) {
  75. break;
  76. }
  77. //
  78. // if block is lower than next one, but greater than current
  79. // one, we have found the right area
  80. //
  81. if ((EndAddBlock <= NextBlock) && (AddBlock >= EndCurrentBlock)) {
  82. break;
  83. }
  84. //
  85. // if conflicting with next block, stop searching and
  86. // resolve conflict after this loop
  87. //
  88. if((EndAddBlock > NextBlock) && (EndAddBlock <= EndNextBlock)) {
  89. fOverlap = TRUE;
  90. break;
  91. }
  92. pCurrentBlock = pNextBlock;
  93. pNextBlock = pCurrentBlock->Next;
  94. CurrentBlock = NextBlock;
  95. EndCurrentBlock = EndNextBlock;
  96. }
  97. }
  98. //
  99. // if we have reached this point, there may be a conflict
  100. // with the current block.
  101. //
  102. if(fOverlap) {
  103. if(AddBlock < EndCurrentBlock) {
  104. AddAddress = EndCurrentBlock;
  105. AddSize = EndAddBlock - EndCurrentBlock;
  106. if(AddSize <= 0) {
  107. return TRUE;
  108. }
  109. }
  110. if((pNextBlock != NULL) && (EndAddBlock > NextBlock)) {
  111. AddSize = NextBlock - AddBlock;
  112. if(AddSize <= 0) {
  113. return TRUE;
  114. }
  115. }
  116. }
  117. BlockPointer->RomBlock.Address = AddAddress;
  118. BlockPointer->RomBlock.Size = AddSize;
  119. //
  120. // Put it on the list.
  121. // if it belongs on top, put it there
  122. //
  123. if ((pCurrentBlock == NULL)||
  124. ((pCurrentBlock == BlockHead) && (CurrentBlock > AddBlock))) {
  125. BlockPointer->Next = pCurrentBlock;
  126. BlockHead = BlockPointer;
  127. } else {
  128. //
  129. // else add to middle or bottom depending on NextBlock
  130. //
  131. BlockPointer->Next = pNextBlock;
  132. pCurrentBlock->Next = BlockPointer;
  133. }
  134. BlockPointer++; // Note that this works because
  135. // we know the offset part of
  136. // the addr is always < 64k.
  137. return TRUE;
  138. }
  139. BOOLEAN
  140. ScanRomBlocks(
  141. VOID
  142. )
  143. /*++
  144. Routine Description:
  145. This routine scans the ROM IO area and checks for 55AA at every
  146. 512 bytes for valid ROM blocks.
  147. NOTES:
  148. -------------
  149. | |
  150. | |
  151. ------------------100000
  152. ^ | |
  153. | | |
  154. | -------------f0000 (ROMBIOS_START) ---
  155. | | | ^
  156. | | | |
  157. EXTROM_LEN -------------e0000 (PS2BIOS_START) --- |
  158. | | | ^ Search |
  159. | | | Search | Range |
  160. | -------------d0000 Range | on AT |
  161. | | | on PS/2| |
  162. V | | V V
  163. ------------------c0000 (EXTROM_START) --- ---
  164. ON AT:
  165. Scan through EXTROM_START-effff for ROM Blocks
  166. ON PS2
  167. Scan through EXTROM_START-dffff for ROM Blocks
  168. Arguments:
  169. None.
  170. Return Value:
  171. None.
  172. --*/
  173. {
  174. ULONG BlockSize;
  175. BOOLEAN Success = TRUE;
  176. FPUCHAR Current;
  177. ULONG RomAddr, RomEnd;
  178. //
  179. // As per the machine type restrict the search range
  180. //
  181. MAKE_FP(Current, EXTROM_START);
  182. RomAddr = EXTROM_START;
  183. if ((HwBusType == MACHINE_TYPE_MCA) ||
  184. (BiosSystemEnvironment.Model == PS2_L40) ||
  185. (BiosSystemEnvironment.Model == PS1_386) ||
  186. (BiosSystemEnvironment.Model == PS2_AT)) {
  187. RomEnd = PS2BIOS_START;
  188. } else {
  189. RomEnd = ROMBIOS_START;
  190. }
  191. while (RomAddr < RomEnd) {
  192. if (((FPROM_HEADER)Current)->Signature == ROM_HEADER_SIGNATURE) {
  193. BlockSize = (ULONG)((FPROM_HEADER)Current)->NumberBlocks * BLOCKSIZE;
  194. if ((RomAddr + BlockSize) > RomEnd) {
  195. BlockSize = RomEnd - RomAddr;
  196. }
  197. //
  198. // V7 VRAM card does not correctly report its BlockSize. Since
  199. // this is a very popular video card, we provide a workaround
  200. // for it.
  201. //
  202. if ((RomAddr == 0xC0000) && (BlockSize < 0x8000)) {
  203. BlockSize = 0x8000;
  204. }
  205. if (BlockSize != 0) {
  206. if (!AddRomBlock(RomAddr, BlockSize)) {
  207. Success = FALSE;
  208. break;
  209. }
  210. RomAddr += BlockSize;
  211. RomAddr = ALIGN_UP(RomAddr, ROM_HEADER_INCREMENT);
  212. MAKE_FP(Current, RomAddr);
  213. continue;
  214. }
  215. }
  216. RomAddr += ROM_HEADER_INCREMENT;
  217. MAKE_FP(Current, RomAddr);
  218. }
  219. //
  220. // Last but not least, add the system ROM to the list
  221. //
  222. if (Success) {
  223. RomAddr = ROMBIOS_START;
  224. BlockSize = ROMBIOS_LEN;
  225. if ((HwBusType == MACHINE_TYPE_MCA) ||
  226. (BiosSystemEnvironment.Model == PS2_L40) ||
  227. (BiosSystemEnvironment.Model == PS1_386) ||
  228. (BiosSystemEnvironment.Model == PS2_AT)) {
  229. RomAddr = PS2BIOS_START;
  230. BlockSize = PS2BIOS_LEN;
  231. }
  232. if (!AddRomBlock(RomAddr, BlockSize)) {
  233. Success = FALSE;
  234. }
  235. }
  236. return Success;
  237. }
  238. FPTEMPORARY_ROM_BLOCK
  239. MatchRomBlock (
  240. ULONG PhysicalAddr
  241. )
  242. /*++
  243. Routine Description:
  244. This routine finds the ROM block which the 'PhysicalAddr' is in.
  245. Arguments:
  246. PhysicalAddr - the physical address ...
  247. Return Value:
  248. A pointer to the detected ROM block.
  249. --*/
  250. {
  251. FPTEMPORARY_ROM_BLOCK CurrentBlock;
  252. ROM_BLOCK RomBlock;
  253. CurrentBlock = BlockHead;
  254. while (CurrentBlock) {
  255. RomBlock = CurrentBlock->RomBlock;
  256. if (RomBlock.Address <= PhysicalAddr &&
  257. RomBlock.Address + RomBlock.Size > PhysicalAddr) {
  258. break;
  259. } else {
  260. CurrentBlock = CurrentBlock->Next;
  261. }
  262. }
  263. return(CurrentBlock);
  264. }
  265. BOOLEAN
  266. IsSameRomBlock (
  267. FPTEMPORARY_ROM_BLOCK Source,
  268. FPTEMPORARY_ROM_BLOCK Destination
  269. )
  270. /*++
  271. Routine Description:
  272. This routine checks if the passed in ROM blocks contain the same
  273. information. This ususally happens when the two ROM blocks are for
  274. video ROM and shadowed video ROM.
  275. Arguments:
  276. Source - the source ROM block.
  277. Destination - the ROM block to compare with.
  278. Return Value:
  279. BOOLEAN TRUE if the two ROM blocks are the same else FALSE is returned.
  280. --*/
  281. {
  282. if (Source == NULL || Destination == NULL) {
  283. return(FALSE);
  284. }
  285. //
  286. // First make sure their sizes are the same.
  287. //
  288. if (Source->RomBlock.Size == Destination->RomBlock.Size) {
  289. if (!HwRomCompare(Source->RomBlock.Address,
  290. Destination->RomBlock.Address,
  291. Source->RomBlock.Size)){
  292. return(TRUE);
  293. }
  294. }
  295. return(FALSE);
  296. }
  297. VOID
  298. CheckVideoRom (
  299. VOID
  300. )
  301. /*++
  302. Routine Description:
  303. This routine checks if the int 10h video handler is in the video
  304. ROM block detected by us. If not, the video ROM must have been
  305. remapped/shadowed to other area (usually 0xE0000.)
  306. NOTE: In this function, I commented out the code which removes the
  307. Video ROM block if it has been shadowed. I found out
  308. machine POST code does not modify ALL the VIDEO ROM related
  309. pointers.
  310. Arguments:
  311. None.
  312. Return Value:
  313. None.
  314. --*/
  315. {
  316. ULONG Vector, Handler, VectorAddr = 0x10 * sizeof(ULONG);
  317. FPULONG pVectorAddr;
  318. FPTEMPORARY_ROM_BLOCK RomBlock, VideoRomBlock;
  319. ULONG Size;
  320. MAKE_FP(pVectorAddr, VectorAddr);
  321. Vector = *pVectorAddr;
  322. Handler = ((Vector >> 16) << 4) + (Vector & 0xffff);
  323. RomBlock = MatchRomBlock(Handler);
  324. //
  325. // Check if the int 10h handler falls in one of our ROM blocks.
  326. //
  327. if (RomBlock) {
  328. if (RomBlock->RomBlock.Address >= 0xC0000 &&
  329. RomBlock->RomBlock.Address < 0xC8000) {
  330. //
  331. // if int 10h handler is in the standard video ROM area, we simply
  332. // return. Either the video ROM is not shadowed or it
  333. // is a in-place shadow.
  334. //
  335. return;
  336. } else {
  337. //
  338. // The ROM block associated with the int 10h handler is not in
  339. // standard video bios ROM area. It must have been mapped to
  340. // the current location. We now need to make sure we have the
  341. // ROM block which contains the 40:a8 VGA parameter.
  342. //
  343. VectorAddr = VGA_PARAMETER_POINTER;
  344. MAKE_FP(pVectorAddr, VectorAddr);
  345. Vector = *pVectorAddr;
  346. Handler = ((Vector >> 16) << 4) + (Vector & 0xffff);
  347. VideoRomBlock = MatchRomBlock(Handler);
  348. if (VideoRomBlock == NULL) {
  349. //
  350. // We did not find the Video ROM associated with the
  351. // VGA parameters. Try detect it.
  352. //
  353. //
  354. // In the following memory comparison, we skip the first 16 bytes.
  355. // Because most likely the reason we did not find the standard
  356. // Video ROM is because the signature word is missing.
  357. //
  358. Handler = (Handler & 0xF0000) +
  359. (RomBlock->RomBlock.Address & 0xFFFF);
  360. if (!HwRomCompare(RomBlock->RomBlock.Address + 0x10,
  361. Handler + 0x10,
  362. RomBlock->RomBlock.Size - 0x10)) {
  363. //
  364. // Note: The old code looked like this for many years:
  365. //
  366. /*
  367. if ((Handler & 0xFFFF == 0) && (RomBlock->RomBlock.Size < 0x8000)){
  368. Size = 0x8000;
  369. } else {
  370. Size = RomBlock->RomBlock.Size;
  371. }
  372. */
  373. //
  374. // But (Handler & 0xFFFF == 0) is always false. So
  375. // Size always equals RomBlock->RomBlock.Size. Rather than
  376. // fix the comparison, which might cause machines to break,
  377. // I'm going to assume that it's fine to just make the code
  378. // do what it's always done. - JakeO 8/9/00
  379. //
  380. Size = RomBlock->RomBlock.Size;
  381. AddRomBlock(Handler, Size);
  382. }
  383. }
  384. }
  385. } else {
  386. //
  387. // There is no ROM block associated with the int 10h handler.
  388. // We can find the shadowed video ROM block if:
  389. // We detected the original video ROM in 0xC0000 - 0xC8000 range
  390. //
  391. VideoRomBlock = MatchRomBlock((Handler & 0xFFFF) + 0xC0000);
  392. if (VideoRomBlock != NULL) {
  393. //
  394. // In the following memory comparison, we skip the first 16 bytes.
  395. // Because most likely the reason we did not find the shadow rom
  396. // is the signature word is missing.
  397. //
  398. if (!HwRomCompare(VideoRomBlock->RomBlock.Address + 0x10,
  399. (Handler & 0xF0000) +
  400. (VideoRomBlock->RomBlock.Address & 0xFFFF) + 0x10,
  401. VideoRomBlock->RomBlock.Size - 0x10)) {
  402. AddRomBlock((VideoRomBlock->RomBlock.Address & 0xFFFF) +
  403. (Handler & 0xF0000),
  404. VideoRomBlock->RomBlock.Size);
  405. }
  406. }
  407. }
  408. }
  409. VOID
  410. GetRomBlocks(
  411. FPUCHAR ReservedBuffer,
  412. PUSHORT Size
  413. )
  414. /*++
  415. Routine Description:
  416. This routine scans the ROM IO area and collects all the ROM blocks.
  417. Arguments:
  418. ReservedBuffer - Supplies a far pointer to the buffer.
  419. Size - Supplies a near pointer to a variable to receive the size
  420. of the ROM block.
  421. Return Value:
  422. None.
  423. --*/
  424. {
  425. FPTEMPORARY_ROM_BLOCK Source;
  426. ULONG StartAddr, EndAddr;
  427. FPUSHORT TestAddr;
  428. FPROM_BLOCK Destination;
  429. USHORT BufferSize;
  430. ULONG EBiosAddress = 0, EBiosLength = 0;
  431. ULONG far *EBiosInformation = (ULONG far *)
  432. ((DOS_BEGIN_SEGMENT << 4) + EBIOS_INFO_OFFSET);
  433. //
  434. // First we reserve the max space needed and build our temporary rom
  435. // block list in the heap space below the space reservedand. After
  436. // the temporary list is built, we then copy it to the caller supplied
  437. // reserved space.
  438. //
  439. BlockPointer = (FPTEMPORARY_ROM_BLOCK)HwAllocateHeap(0, FALSE);
  440. BlockHead = NULL;
  441. *Size = 0;
  442. GetBiosSystemEnvironment((PUCHAR)&BiosSystemEnvironment);
  443. if (BiosSystemEnvironment.ConfigurationFlags & 0x4) {
  444. //
  445. // If extened BIOS data area is allocated, we will find out its
  446. // location and size and save in ROM blocks.
  447. //
  448. _asm {
  449. push es
  450. mov ah, 0xC1
  451. int 15h
  452. jc short Exit
  453. cmp ah, 0x86
  454. je short Exit
  455. mov bx, 0
  456. mov dx, 0
  457. mov ax, 0
  458. mov al, es:[bx]
  459. shl ax, 10
  460. mov word ptr EBiosLength, ax
  461. mov ax, es
  462. mov dx, es
  463. shl ax, 4
  464. shr dx, 12
  465. mov word ptr EBiosAddress, ax
  466. mov word ptr EBiosAddress + 2, dx
  467. Exit:
  468. pop es
  469. }
  470. }
  471. //
  472. // Save the Extended BIOS data area address and size at 700:40
  473. //
  474. if (EBiosLength) {
  475. *EBiosInformation++ = EBiosAddress;
  476. *EBiosInformation = EBiosLength;
  477. } else {
  478. *EBiosInformation++ = 0L;
  479. *EBiosInformation = 0L;
  480. }
  481. if (!ScanRomBlocks()) {
  482. return;
  483. }
  484. //
  485. // On some machines, when they shadow video ROM from 0xC0000 to
  486. // 0xE0000, they copy code only (no signature.) So, we need
  487. // special code to work around the problem.
  488. //
  489. CheckVideoRom();
  490. //
  491. // Now do our special hack for IBM. On SOME IBM PCs, they use
  492. // E0000-FFFFF for system BIOS (even on non PS/2 machines.) Since
  493. // system BIOS has no ROM header, it is very hard to know the starting
  494. // address of system ROM. So we:
  495. //
  496. // 1. Make sure there is no ROM block in E0000-EFFFF area.
  497. // 2. and E0000-EFFFF contains valid data.
  498. //
  499. // If both 1 and 2 are true, we assume E0000-EFFFF is part of system
  500. // ROM.
  501. //
  502. Source = BlockHead;
  503. while (Source) {
  504. StartAddr = Source->RomBlock.Address;
  505. EndAddr = StartAddr + Source->RomBlock.Size - 1;
  506. if ((StartAddr < 0xE0000 && EndAddr < 0xE0000) ||
  507. (StartAddr >= 0xF0000)) {
  508. Source = Source->Next;
  509. } else {
  510. break;
  511. }
  512. }
  513. if (Source == NULL) {
  514. for (StartAddr = 0xE0000; StartAddr < 0xF0000; StartAddr += 0x800) {
  515. MAKE_FP(TestAddr, StartAddr);
  516. if (*TestAddr != 0xffff) {
  517. AddRomBlock(0xE0000, 0x10000);
  518. break;
  519. }
  520. }
  521. }
  522. //
  523. // Now copy the rom block list to our reserved space and release
  524. // the extra space we reserved.
  525. //
  526. Source = BlockHead;
  527. Destination = (FPROM_BLOCK)ReservedBuffer;
  528. BufferSize = 0;
  529. while (Source) {
  530. *Destination = *((FPROM_BLOCK)&Source->RomBlock);
  531. BufferSize += sizeof(ROM_BLOCK);
  532. Source = Source->Next;
  533. Destination++;
  534. }
  535. *Size = BufferSize;
  536. }
  537. VOID
  538. HwGetBiosDate(
  539. ULONG StartingAddress,
  540. USHORT Length,
  541. PUSHORT Year,
  542. PUSHORT Month,
  543. PUSHORT Day
  544. )
  545. /*++
  546. Routine Description:
  547. Scans the specified area for the most recent date of the
  548. form xx/xx/xx.
  549. Arguments:
  550. StartingAddress - First address to scan
  551. Length - Length of area to scan
  552. Return Value:
  553. Year - If non-zero, the year of the date (1991, 1992, ...)
  554. Month - If non-zero, then month of the date found
  555. Day - If non-zero, the day of the date found
  556. --*/
  557. {
  558. FPUCHAR fp, date;
  559. USHORT y, m, d;
  560. UCHAR c;
  561. ULONG i, temp;
  562. //
  563. // Zero return values
  564. //
  565. *Year = 0;
  566. *Month = 0;
  567. *Day = 0;
  568. //
  569. // Search for date with the format MM/DD/YY or M1M1M2M2//D1D1D2D2//Y1Y1Y2Y2
  570. //
  571. MAKE_FP(fp, StartingAddress); // initialize fp pointer
  572. while (Length > 8) {
  573. c = fp[7];
  574. if ((c < '0' || c > '9') && (c != '/' && c != '-')) {
  575. // these 8 bytes are not a date, next location
  576. fp += 8;
  577. Length -= 8;
  578. continue;
  579. }
  580. date = fp; // check for date at this pointer
  581. fp += 1; // skip to next byte
  582. Length -= 1;
  583. //
  584. // Check for date of the form MM/DD/YY
  585. //
  586. y = 0;
  587. if (date[0] >= '0' && date[0] <= '9' &&
  588. date[1] >= '0' && date[1] <= '9' &&
  589. (date[2] == '/' || date[2] == '-') &&
  590. date[3] >= '0' && date[3] <= '9' &&
  591. date[4] >= '0' && date[4] <= '9' &&
  592. (date[5] == '/' || date[5] == '-') &&
  593. date[6] >= '0' && date[6] <= '9' &&
  594. date[7] >= '0' && date[7] <= '9' ) {
  595. //
  596. // A valid looking date field at date, crack it
  597. //
  598. y = (date[6] - '0') * 10 + date[7] - '0' + 1900;
  599. m = (date[0] - '0') * 10 + date[1] - '0';
  600. d = (date[3] - '0') * 10 + date[4] - '0';
  601. }
  602. //
  603. // Check for date of the form M1M1M2M2//D1D1D2D2//Y1Y1Y2Y2
  604. //
  605. if (Length >= 15 &&
  606. date[ 0] >= '0' && date[ 0] <= '9' && date[ 0] == date[ 1] &&
  607. date[ 2] >= '0' && date[ 2] <= '9' && date[ 2] == date[ 3] &&
  608. (date[ 4] == '/' || date[ 4] == '-') && date[ 4] == date[ 5] &&
  609. date[ 6] >= '0' && date[ 6] <= '9' && date[ 6] == date[ 7] &&
  610. date[ 8] >= '0' && date[ 8] <= '9' && date[ 8] == date[ 9] &&
  611. (date[10] == '/' || date[10] == '-') && date[10] == date[11] &&
  612. date[12] >= '0' && date[12] <= '9' && date[12] == date[13] &&
  613. date[14] >= '0' && date[14] <= '9' && date[14] == date[15]) {
  614. //
  615. // A valid looking date field at date, crack it
  616. //
  617. y = (date[12] - '0') * 10 + date[14] - '0' + 1900;
  618. m = (date[ 0] - '0') * 10 + date[ 2] - '0';
  619. d = (date[ 6] - '0') * 10 + date[ 8] - '0';
  620. }
  621. if (y != 0) {
  622. if (m < 1 || m > 12 || d < 1 || d > 31) {
  623. y = 0; // bad field in date, skip it
  624. } else {
  625. if (y < 1980) {
  626. //
  627. // Roll to next century.
  628. //
  629. y += 100;
  630. }
  631. }
  632. }
  633. //
  634. // Check for date of the form 19xx or 20xx
  635. //
  636. // First, check the 5th character is not a digit.
  637. //
  638. #define IS_DIGIT(x) (((x) >= '0') && ((x) <= '9'))
  639. if (!IS_DIGIT(date[4])) {
  640. for (i = 0, temp = 0; i < 4; i++) {
  641. if (!IS_DIGIT(date[i])) {
  642. temp = 0;
  643. break;
  644. }
  645. temp = (temp * 10) + date[i] - '0';
  646. }
  647. if ((temp >= 1980) && (temp < 2599)) {
  648. //
  649. // Looks like a reasonable date, use it.
  650. //
  651. y = (USHORT)temp;
  652. m = 0;
  653. d = 0;
  654. }
  655. }
  656. if (!y) {
  657. // not a date - skip it
  658. continue;
  659. }
  660. if ((y > *Year) ||
  661. (y == *Year && m > *Month) ||
  662. (y == *Year && m == *Month && d > *Day) ) {
  663. //
  664. // This date is more recent
  665. //
  666. *Year = y;
  667. *Month = m;
  668. *Day = d;
  669. }
  670. }
  671. }