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.

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