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.

882 lines
18 KiB

  1. ;
  2. ; search.asm
  3. ;
  4. ; 08/16/96 jforbes ASM implementation of binary_search_findmatch()
  5. ;
  6. ; There is a fair amount of optimisation towards instruction-scheduling.
  7. ;
  8. ; About 58% of the time is spent in the binary_search_findmatch()
  9. ; routine. Around 31% is spent in the optimal parser.
  10. ;
  11. TITLE SEARCH.ASM
  12. .386P
  13. .model FLAT
  14. PUBLIC _binary_search_findmatch
  15. _TEXT SEGMENT
  16. INCLUDE offsets.i
  17. $match_length EQU 0
  18. $small_len EQU 4
  19. $small_ptr EQU 8
  20. $big_ptr EQU 12
  21. $end_pos EQU 16
  22. $clen EQU 20
  23. $left EQU 24
  24. $right EQU 28
  25. $mem_window EQU 32
  26. $matchpos_table EQU 36
  27. $context EQU 40
  28. $best_repeat EQU 44
  29. LOCAL_STACK EQU 48
  30. MIN_MATCH EQU 2
  31. MAX_MATCH EQU 257
  32. BREAK_LENGTH EQU 50
  33. ;
  34. ; binary_search_findmatch(t_encoder_context *context, long BufPos)
  35. ;
  36. _binary_search_findmatch PROC NEAR
  37. push ebx
  38. push ecx
  39. push edx
  40. push esi
  41. push edi
  42. push ebp
  43. mov ebp, [esp + 28] ; context
  44. mov esi, [esp + 32] ; bufpos
  45. ; tree_to_use = *((ushort *) &enc_MemWindow[BufPos])
  46. mov edi, [ebp + OFF_MEM_WINDOW] ; edi = _enc_MemWindow
  47. xor eax, eax
  48. mov ax, WORD PTR [edi + esi] ; eax = tree_to_use
  49. sub esp, LOCAL_STACK ; allocate space for stack vars
  50. mov [esp + $mem_window], edi
  51. mov [esp + $context], ebp
  52. lea ecx, [ebp + OFF_MATCHPOS_TABLE]
  53. mov [esp + $matchpos_table], ecx
  54. mov ecx, [ebp + OFF_TREE_ROOT]
  55. mov ebx, [ecx + eax*4] ; ebx = tree_root[tree_to_use]
  56. mov [ecx + eax*4], esi ; tree_root[tree_to_use] = bufpos
  57. lea edx, [esi + 4] ; edx = BufPos+4
  58. sub edx, [ebp + OFF_WINDOW_SIZE] ; endpos = BufPos-(ws-4)
  59. mov [esp + $end_pos], edx
  60. ; if (ptr <= endpos)
  61. ; have a short "stub" jump so that the jump is paired
  62. cmp ebx, edx
  63. jle SHORT close_ptr_le_endpos
  64. ;
  65. ; for main loop:
  66. ;
  67. ; eax = scratch
  68. ; ebx = ptr
  69. ; ecx = same
  70. ; edx = scratch
  71. ; esi = BufPos
  72. ; edi = scratch
  73. ; ebp = big_len
  74. ;
  75. ;
  76. ; The following instructions have been carefully
  77. ; interleaved for simultaneous execution on a Pentium's
  78. ; U and V pipelines.
  79. ;
  80. mov edi, 2 ; commonly used constant here
  81. mov edx, [ebp + OFF_LEFT]
  82. mov [esp + $left], edx
  83. mov [esp + $clen], edi ; clen = 2
  84. lea edx, [edx + esi*4] ; edx = &Left[BufPos]
  85. lea eax, [esi + edi] ; eax = BufPos+2
  86. mov [esp + $small_ptr], edx ; smallptr=&Left[BufPos]
  87. mov [esp + $match_length], edi ; match_length = 2
  88. mov edx, [ebp + OFF_RIGHT]
  89. mov [esp + $right], edx
  90. sub eax, ebx ; eax = BufPos-ptr+2
  91. lea edx, [edx + esi*4] ; edx = &Right[BufPos]
  92. mov [esp + $small_len], edi ; small_len = 2
  93. mov [esp + $big_ptr], edx ; bigptr=&Right[BufPos]
  94. mov ecx, edi ; same = 2 (first iter)
  95. ; enc_matchpos_table[2] = BufPos - ptr + 2
  96. mov edi, [esp + $mem_window]
  97. mov [ebp + OFF_MATCHPOS_TABLE + 8], eax
  98. add edi, ecx ; u edi = &enc_MemWindow[clen]
  99. mov ebp, 2 ; v big_len = 2
  100. mov eax, [edi + esi] ; u *(DWORD*) enc_MemWindow[b] (bufpos+clen)
  101. jmp SHORT main_loop ; v
  102. close_ptr_le_endpos:
  103. jmp ptr_le_endpos
  104. ;
  105. ; same <= big_len
  106. ;
  107. ; this code is actually replicated much later in this file,
  108. ; but it's too far away for a SHORT jump, which will cause
  109. ; pipeline stalls.
  110. ;
  111. close_same_le_biglen:
  112. mov edx, [esp + $left] ; u
  113. mov eax, [esp + $big_ptr] ; v
  114. lea edi, [edx + ebx*4] ; u edi=&Left[ptr]
  115. mov [eax], ebx ; v *big_ptr=ptr
  116. mov [esp + $big_ptr], edi ; u big_ptr=&left[ptr]
  117. mov ecx, DWORD PTR [esp + $clen] ; v clen (next iter.)
  118. mov ebx, [edi] ; u ptr = *big_ptr
  119. mov edi, [esp + $mem_window] ; v (next iter.)
  120. ; bottom of main loop
  121. add edi, ecx ; u edi = &enc_MemWindow[clen]
  122. cmp ebx, [esp + $end_pos] ; v
  123. ; for next iteration
  124. mov eax, [edi + esi] ; u *(DWORD*) enc_MemWindow[b] (bufpos+clen)
  125. ja SHORT main_loop ; v
  126. ; fall through
  127. close_exit_main_loop:
  128. jmp exit_main_loop
  129. ;
  130. ; same <= small_len
  131. ;
  132. ; ditto - see above
  133. ;
  134. close_same_le_smalllen:
  135. mov edx, [esp + $right]
  136. mov eax, [esp + $small_ptr]
  137. lea edi, [edx + ebx*4] ; u edi = &Right[ptr]
  138. mov [eax], ebx ; v *small_ptr = ptr
  139. mov [esp + $small_ptr], edi ; u small_ptr = &right[ptr]
  140. mov ecx, [esp + $clen] ; v for next iteration
  141. mov ebx, [edi] ; u ptr = *small_ptr
  142. mov edi, [esp + $mem_window] ; v (next iter.)
  143. ; bottom of main loop
  144. add edi, ecx ; u (next iter.)
  145. cmp ebx, [esp + $end_pos] ; v
  146. mov eax, [edi + esi] ; u (next iter.)
  147. jna SHORT close_exit_main_loop ; v
  148. ; fall through to main loop
  149. ;
  150. ; at the bottom of the main loop, we goto here
  151. ;
  152. main_loop:
  153. ;
  154. ; If the first characters don't match, then we know for
  155. ; certain that we have not exceeded small_len or big_len,
  156. ; and therefore clen won't change either. We can therefore
  157. ; skip some of the checks.
  158. ;
  159. ; This is the most common case.
  160. ;
  161. ; These jumps must be SHORT to be paired.
  162. ;
  163. cmp [edi + ebx], al ; u
  164. ja SHORT close_same_le_smalllen ; v
  165. jb SHORT close_same_le_biglen ; u
  166. shr eax, 8 ; u
  167. inc ecx ; same++ ; v
  168. ;
  169. ; second and further iterations
  170. ;
  171. ; we only check same (ecx) against MAX_MATCH
  172. ; every 4 characters
  173. ;
  174. ; operations paired for U and V pipeline
  175. ; simultaneous execution
  176. ;
  177. ; notes:
  178. ; SHR must be on the U pipeline
  179. ;
  180. unrolled_loop:
  181. ; 1
  182. cmp [edi + ebx + 1], al ; u
  183. jne SHORT not_eq ; v
  184. shr eax, 8 ; u
  185. inc ecx ; v
  186. ; 2
  187. cmp [edi + ebx + 2], al
  188. jne SHORT not_eq
  189. shr eax, 8
  190. inc ecx
  191. ; 3
  192. cmp [edi + ebx + 3], al
  193. jne SHORT not_eq
  194. mov eax, [edi + esi + 4] ; u
  195. inc ecx ; v
  196. mov dl, [edi + ebx + 4] ; u
  197. add edi, 4 ; v
  198. ; 4
  199. cmp dl, al
  200. jne SHORT not_eq
  201. shr eax, 8
  202. inc ecx
  203. cmp ecx, MAX_MATCH
  204. jl SHORT unrolled_loop
  205. ;
  206. ; clen >= MAX_MATCH
  207. ;
  208. ; ecx could be larger than MAX_MATCH right now,
  209. ; so correct it
  210. ;
  211. mov edx, [esp + $match_length]
  212. mov ecx, MAX_MATCH
  213. jmp SHORT long_match
  214. same1_ge_break_length:
  215. same2_ge_break_length:
  216. ; can trash clen (ecx)
  217. ; ecx = left
  218. mov ecx, [esp + $left]
  219. ; eax = small_ptr
  220. mov eax, [esp + $small_ptr]
  221. ; ecx = Left[ptr]
  222. mov ecx, [ecx + ebx*4]
  223. ; edx = Right
  224. mov edx, [esp + $right]
  225. ; *small_ptr = left[ptr]
  226. mov [eax], ecx
  227. ; *big_ptr = right[ptr]
  228. mov edx, [edx + ebx*4]
  229. ; *big_ptr = right[ptr]
  230. mov eax, [esp + $big_ptr]
  231. mov [eax], edx
  232. ; goto end_bsearch
  233. jmp end_bsearch
  234. ;
  235. ; warning, "same" (ecx) could be larger than
  236. ; MAX_MATCH, so we will have to correct it
  237. ;
  238. not_eq:
  239. ja val_greater_than_0
  240. ;
  241. ; -----------------------------------------
  242. ; VAL < 0
  243. ; -----------------------------------------
  244. ;
  245. val_less_than_0:
  246. ; if (same > big_len)
  247. cmp ecx, ebp
  248. jle SHORT same_le_biglen
  249. ; if (same > match_length)
  250. cmp ecx, [esp + $match_length]
  251. jle SHORT same1_le_ml
  252. ; here's where we truncate ecx to MAX_MATCH if it
  253. ; was too large
  254. cmp ecx, MAX_MATCH
  255. jg SHORT trunc_same1
  256. back_from_trunc1:
  257. long_match:
  258. mov edi, [esp + $matchpos_table]
  259. lea eax, [esi + 2]
  260. ; eax = BufPos-ptr+2
  261. mov edx, [esp + $match_length]
  262. sub eax, ebx
  263. ; do
  264. ; {
  265. ; enc_matchpos_table[++match_length] = BufPos-ptr+2
  266. ; } while (match_length < same);
  267. ; store match_length
  268. mov [esp + $match_length], ecx
  269. loop1:
  270. ; match_length++
  271. inc edx
  272. ; enc_matchpos_table[match_length] = BufPos-ptr+2
  273. mov [edi + edx*4], eax
  274. ; while (match_length < same)
  275. cmp edx, ecx
  276. jl SHORT loop1
  277. ; if (same >= BREAK_LENGTH)
  278. cmp ecx, BREAK_LENGTH
  279. jge SHORT same1_ge_break_length
  280. ; same <= match_length
  281. same1_le_ml:
  282. ; clen = min(small_len, big_len=same)
  283. cmp [esp + $small_len], ecx
  284. ; big_len = same
  285. mov ebp, ecx
  286. ; small_len >= same?
  287. jge SHORT over1
  288. ; no, small_len < same
  289. ; therefore clen := small_len
  290. ; (otherwise clen stays at big_len which ==same)
  291. mov ecx, [esp + $small_len]
  292. over1:
  293. mov [esp + $clen], ecx
  294. ;
  295. ; same <= big_len
  296. ;
  297. same_le_biglen:
  298. mov edx, [esp + $left] ; u
  299. mov eax, [esp + $big_ptr] ; v
  300. lea edi, [edx + ebx*4] ; u edi=&Left[ptr]
  301. mov [eax], ebx ; v *big_ptr=ptr
  302. mov [esp + $big_ptr], edi ; u big_ptr=&left[ptr]
  303. mov ecx, DWORD PTR [esp + $clen] ; v clen (next iter.)
  304. mov ebx, [edi] ; u ptr = *big_ptr
  305. mov edi, [esp + $mem_window] ; v (next iter.)
  306. ; bottom of main loop
  307. add edi, ecx ; u edi = &enc_MemWindow[clen]
  308. cmp ebx, [esp + $end_pos] ; v
  309. ; for next iteration
  310. mov eax, [edi + esi] ; u *(DWORD*) enc_MemWindow[b] (bufpos+clen)
  311. ja main_loop ; v
  312. jmp exit_main_loop
  313. trunc_same1:
  314. mov ecx, MAX_MATCH
  315. jmp SHORT back_from_trunc1
  316. trunc_same2:
  317. mov ecx, MAX_MATCH
  318. jmp SHORT back_from_trunc2
  319. ; -----------------------------------------
  320. ; VAL > 0
  321. ; -----------------------------------------
  322. val_greater_than_0:
  323. ; if (same > small_len)
  324. cmp ecx, [esp + $small_len]
  325. jle SHORT same_le_smalllen
  326. ; if (same > match_length)
  327. cmp ecx, [esp + $match_length]
  328. jle SHORT same2_le_ml
  329. ; here's where we truncate ecx to MAX_MATCH if it
  330. ; was too large
  331. cmp ecx, MAX_MATCH
  332. jg SHORT trunc_same2
  333. ; can trash clen
  334. ; ecx = BufPos-ptr+2
  335. back_from_trunc2:
  336. mov edi, [esp + $matchpos_table]
  337. lea eax, [esi + 2]
  338. mov edx, [esp + $match_length]
  339. sub eax, ebx
  340. mov [esp + $match_length], ecx
  341. ; do
  342. ; {
  343. ; enc_matchpos_table[++match_length] = BufPos-ptr+2
  344. ; } while (match_length < same);
  345. loop2:
  346. inc edx ; match_length++
  347. ; enc_matchpos_table[match_length] = BufPos-ptr+2
  348. mov [edi + edx*4], eax
  349. cmp edx, ecx
  350. jl SHORT loop2
  351. ; if (same >= BREAK_LENGTH)
  352. cmp ecx, BREAK_LENGTH
  353. jge same2_ge_break_length
  354. same2_le_ml:
  355. mov edx, [esp + $small_len]
  356. ; clen = min(small_len=ecx, big_len)
  357. cmp ebp, ecx
  358. ; small_len = same
  359. mov [esp + $small_len], ecx
  360. jge SHORT over2
  361. ; same = big_len
  362. mov ecx, ebp
  363. over2:
  364. mov [esp + $clen], ecx
  365. same_le_smalllen:
  366. mov edx, [esp + $right]
  367. mov eax, [esp + $small_ptr]
  368. lea edi, [edx + ebx*4] ; u edi = &Right[ptr]
  369. mov [eax], ebx ; v *small_ptr = ptr
  370. mov [esp + $small_ptr], edi ; u small_ptr = &right[ptr]
  371. mov ecx, [esp + $clen] ; v for next iteration
  372. mov ebx, [edi] ; u ptr = *small_ptr
  373. mov edi, [esp + $mem_window] ; v (next iter.)
  374. ; bottom of main loop
  375. add edi, ecx ; u (next iter.)
  376. cmp ebx, [esp + $end_pos] ; v
  377. mov eax, [edi + esi] ; u (next iter.)
  378. ja main_loop
  379. exit_main_loop:
  380. mov eax, [esp + $small_ptr]
  381. mov edx, [esp + $big_ptr]
  382. ; *small_ptr = 0
  383. mov DWORD PTR [eax], 0
  384. ; *big_ptr = 0
  385. mov DWORD PTR [edx], 0
  386. end_bsearch:
  387. ;
  388. ; now check for repeated offsets
  389. ;
  390. ;
  391. ; FIRST REPEATED OFFSET
  392. ;
  393. mov eax, [esp + $match_length]
  394. ; for (i = 0; i < match_length; i++)
  395. ; compare bufpos+i vs. bufpos+i-enc_last_matchpos_offset[0]
  396. mov edi, [esp + $mem_window]
  397. ; ebx = bufpos
  398. mov ebx, esi
  399. ; repeated offset zero
  400. ; ebx = bufpos - repeated_offset[0]
  401. mov ecx, [esp + $context]
  402. sub ebx, [ecx + OFF_LAST_MATCHPOS_OFFSET]
  403. ; i = 0
  404. xor ecx, ecx
  405. rp1_loop:
  406. mov dl, [edi + esi]
  407. cmp dl, [edi + ebx]
  408. jne SHORT rp1_mismatch
  409. ; i++
  410. inc ecx
  411. ; inc window pointer
  412. inc edi
  413. ; i < match_length?
  414. cmp ecx, eax
  415. jl SHORT rp1_loop
  416. ;
  417. ; i == match_length
  418. ;
  419. ; therefore force ourselves to take rp1
  420. ;
  421. ; (this code is not in the C source, since it is
  422. ; messy to do)
  423. ;
  424. mov ebx, [esp + $matchpos_table]
  425. force_rp1_copy:
  426. mov DWORD PTR [ebx + ecx*4], 0
  427. dec ecx
  428. cmp ecx, MIN_MATCH
  429. jge SHORT force_rp1_copy
  430. jmp boundary_check
  431. ;
  432. ; i < match_length
  433. ;
  434. rp1_mismatch:
  435. ; best_repeated_offset = i
  436. mov [esp + $best_repeat], ecx
  437. ; if (i >= MIN_MATCH)
  438. cmp ecx, MIN_MATCH
  439. jl SHORT try_rp2
  440. ; for (; i >= MIN_MATCH; i--)
  441. ; enc_matchpos_table[i] = 0
  442. mov ebx, [esp + $matchpos_table]
  443. rp1_copy:
  444. mov DWORD PTR [ebx + ecx*4], 0
  445. dec ecx
  446. cmp ecx, MIN_MATCH
  447. jge SHORT rp1_copy
  448. ; quick check
  449. cmp DWORD PTR [esp + $best_repeat], BREAK_LENGTH
  450. jg boundary_check
  451. ;
  452. ; SECOND REPEATED OFFSET
  453. ;
  454. try_rp2:
  455. ; for (i = 0; i < match_length; i++)
  456. ; compare bufpos+i vs. bufpos+i-enc_last_matchpos_offset[1]
  457. mov edi, [esp + $mem_window]
  458. ; ebx = bufpos
  459. mov ebx, esi
  460. ; repeated offset zero
  461. ; ebx = bufpos - repeated_offset[1]
  462. mov ecx, [esp + $context]
  463. sub ebx, [ecx + OFF_LAST_MATCHPOS_OFFSET + 4]
  464. ; i = 0
  465. xor ecx, ecx
  466. rp2_loop:
  467. mov dl, [edi + esi]
  468. cmp dl, [edi + ebx]
  469. jne SHORT rp2_mismatch
  470. ; i++
  471. inc ecx
  472. ; inc window pointer
  473. inc edi
  474. ; i < match_length?
  475. cmp ecx, eax
  476. jl SHORT rp2_loop
  477. ;
  478. ; i == match_length
  479. ;
  480. ; therefore force ourselves to take rp2
  481. ;
  482. ; (this code is not in the C source, since it is
  483. ; messy to do)
  484. ;
  485. mov ebx, [esp + $matchpos_table]
  486. force_rp2_copy:
  487. mov DWORD PTR [ebx + ecx*4], 1
  488. dec ecx
  489. cmp ecx, MIN_MATCH
  490. jge SHORT force_rp2_copy
  491. jmp SHORT boundary_check
  492. rp2_mismatch:
  493. ; if (i > best_repeated_offset)
  494. cmp ecx, [esp + $best_repeat]
  495. jle SHORT try_rp3
  496. ; do
  497. ; enc_matchpos_table[++best_repeated_offset] = 1
  498. ; while (best_repeated_offset < i)
  499. mov edi, [esp + $best_repeat]
  500. mov ebx, [esp + $matchpos_table]
  501. rp2_copy:
  502. inc edi ; ++best_repeated_offset
  503. mov DWORD PTR [ebx + edi*4], 1
  504. cmp edi, ecx ; best_repeated_offset < i ?
  505. jl SHORT rp2_copy
  506. ; best_repeat = i
  507. mov [esp + $best_repeat], ecx
  508. ;
  509. ; THIRD REPEATED OFFSET
  510. ;
  511. try_rp3:
  512. ; for (i = 0; i < match_length; i++)
  513. ; compare bufpos+i vs. bufpos+i-enc_last_matchpos_offset[2]
  514. mov edi, [esp + $mem_window]
  515. ; ebx = bufpos
  516. mov ebx, esi
  517. ; repeated offset zero
  518. ; ebx = bufpos - repeated_offset[2]
  519. mov ecx, [esp + $context]
  520. sub ebx, [ecx + OFF_LAST_MATCHPOS_OFFSET + 8]
  521. ; i = 0
  522. xor ecx, ecx
  523. rp3_loop:
  524. mov dl, [edi + esi]
  525. cmp dl, [edi + ebx]
  526. jne SHORT rp3_mismatch
  527. ; i++
  528. inc ecx
  529. ; inc window pointer
  530. inc edi
  531. ; i < match_length?
  532. cmp ecx, eax
  533. jl SHORT rp3_loop
  534. ;
  535. ; i == match_length
  536. ;
  537. ; therefore force ourselves to take rp3
  538. ;
  539. ; (this code is not in the C source, since it is
  540. ; messy to do)
  541. ;
  542. mov ebx, [esp + $matchpos_table]
  543. force_rp3_copy:
  544. mov DWORD PTR [ebx + ecx*4], 2
  545. dec ecx
  546. cmp ecx, MIN_MATCH
  547. jge SHORT force_rp3_copy
  548. jmp SHORT boundary_check
  549. rp3_mismatch:
  550. ; if (i > best_repeated_offset)
  551. cmp ecx, [esp + $best_repeat]
  552. jle SHORT boundary_check
  553. ; do
  554. ; enc_matchpos_table[++best_repeated_offset] = 2
  555. ; while (best_repeated_offset < i)
  556. mov edi, [esp + $best_repeat]
  557. mov ebx, [esp + $matchpos_table]
  558. rp3_copy:
  559. inc edi ; ++best_repeated_offset
  560. mov DWORD PTR [ebx + edi*4], 2
  561. cmp edi, ecx ; best_repeated_offset < i ?
  562. jl SHORT rp3_copy
  563. ;
  564. ; Check that our match length does not cause us
  565. ; to cross a 32K boundary, and truncate if necessary.
  566. ;
  567. ; bytes_to_boundary = 32767 - (BufPos & 32767)
  568. boundary_check:
  569. mov edx, 32767
  570. and esi, 32767
  571. mov eax, [esp + $match_length]
  572. sub edx, esi ; edx = 32767 - (BufPos & 32767)
  573. ;
  574. ; if (matchlength <= bytes_to_boundary)
  575. ; then we're ok
  576. ;
  577. cmp eax, edx
  578. jle SHORT does_not_cross
  579. ;
  580. ; otherwise we have to truncate the match
  581. ;
  582. mov eax, edx
  583. ;
  584. ; if we truncate the match, does it become
  585. ; smaller than MIN_MATCH?
  586. ;
  587. cmp edx, MIN_MATCH
  588. jge SHORT ge_min_match
  589. ;
  590. ; yes, so we return that no matches at all
  591. ; were found
  592. ;
  593. xor eax, eax
  594. ge_min_match:
  595. does_not_cross:
  596. ;
  597. ; return our match length in eax
  598. ;
  599. cleanup:
  600. add esp, LOCAL_STACK
  601. pop ebp
  602. pop edi
  603. pop esi
  604. pop edx
  605. pop ecx
  606. pop ebx
  607. ret 0
  608. ;
  609. ; ptr <= endpos
  610. ;
  611. ptr_le_endpos:
  612. ;
  613. ; left[BufPos] = right[BufPos] = 0
  614. ;
  615. xor eax, eax ; return match length zero
  616. mov ecx, [ebp + OFF_LEFT]
  617. mov edx, [ebp + OFF_RIGHT]
  618. mov [ecx + esi*4], eax
  619. mov [edx + esi*4], eax
  620. ; cleanup
  621. add esp, LOCAL_STACK
  622. pop ebp
  623. pop edi
  624. pop esi
  625. pop edx
  626. pop ecx
  627. pop ebx
  628. ret 0
  629. _binary_search_findmatch ENDP
  630. _TEXT ENDS
  631. END