1 2/*--------------------------------------------------------------------*/ 3/*--- The core dispatch loop, for jumping to a code address. ---*/ 4/*--- dispatch-ppc32-aix5.S ---*/ 5/*--------------------------------------------------------------------*/ 6 7/* 8 This file is part of Valgrind, a dynamic binary instrumentation 9 framework. 10 11 Copyright (C) 2006-2010 OpenWorks LLP 12 info@open-works.co.uk 13 14 This program is free software; you can redistribute it and/or 15 modify it under the terms of the GNU General Public License as 16 published by the Free Software Foundation; either version 2 of the 17 License, or (at your option) any later version. 18 19 This program is distributed in the hope that it will be useful, but 20 WITHOUT ANY WARRANTY; without even the implied warranty of 21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 General Public License for more details. 23 24 You should have received a copy of the GNU General Public License 25 along with this program; if not, write to the Free Software 26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 27 02111-1307, USA. 28 29 The GNU General Public License is contained in the file COPYING. 30 31 Neither the names of the U.S. Department of Energy nor the 32 University of California nor the names of its contributors may be 33 used to endorse or promote products derived from this software 34 without prior written permission. 35*/ 36 37#if defined(VGP_ppc32_aix5) 38 39#include "pub_core_basics_asm.h" 40#include "pub_core_dispatch_asm.h" 41#include "pub_core_transtab_asm.h" 42#include "libvex_guest_offsets.h" /* for OFFSET_ppc32_CIA */ 43 44 45/*------------------------------------------------------------*/ 46/*--- ---*/ 47/*--- The dispatch loop. VG_(run_innerloop) is used to ---*/ 48/*--- run all translations except no-redir ones. ---*/ 49/*--- ---*/ 50/*------------------------------------------------------------*/ 51 52/*----------------------------------------------------*/ 53/*--- Incomprehensible TOC mumbo-jumbo nonsense. ---*/ 54/*----------------------------------------------------*/ 55 56/* No, I don't have a clue either. I just compiled a bit of 57 C with gcc and copied the assembly code it produced. */ 58 59/* Basically "lwz rd, tocent__foo(2)" gets &foo into rd. */ 60 61 .file "dispatch-ppc32-aix5.S" 62 .machine "any" 63 .toc 64 .csect .text[PR] 65 .toc 66tocent__vgPlain_dispatch_ctr: 67 .tc vgPlain_dispatch_ctr[TC],vgPlain_dispatch_ctr[RW] 68tocent__vgPlain_machine_ppc32_has_VMX: 69 .tc vgPlain_machine_ppc32_has_VMX[TC],vgPlain_machine_ppc32_has_VMX[RW] 70tocent__vgPlain_machine_ppc32_has_FP: 71 .tc vgPlain_machine_ppc32_has_FP[TC],vgPlain_machine_ppc32_has_FP[RW] 72tocent__vgPlain_tt_fast: 73 .tc vgPlain_tt_fast[TC],vgPlain_tt_fast[RW] 74tocent__vgPlain_tt_fastN: 75 .tc vgPlain_tt_fast[TC],vgPlain_tt_fastN[RW] 76 .csect .text[PR] 77 .align 2 78 .globl vgPlain_run_innerloop 79 .globl .vgPlain_run_innerloop 80 .csect vgPlain_run_innerloop[DS] 81vgPlain_run_innerloop: 82 .long .vgPlain_run_innerloop, TOC[tc0], 0 83 .csect .text[PR] 84 85/*----------------------------------------------------*/ 86/*--- Preamble (set everything up) ---*/ 87/*----------------------------------------------------*/ 88 89/* signature: 90UWord VG_(run_innerloop) ( void* guest_state, UWord do_profiling ); 91*/ 92.vgPlain_run_innerloop: 93 /* r3 holds guest_state */ 94 /* r4 holds do_profiling */ 95 /* Rather than attempt to make sense of the AIX ABI, just 96 drop r1 by 256 (to get away from the caller's frame), then 97 512 (to give ourselves a 512-byte save area), and then 98 another 256 (to clear our save area). In all, drop r1 by 1024 99 and dump stuff on the stack at 256(1)..768(1). */ 100 101 /* ----- entry point to VG_(run_innerloop) ----- */ 102 /* For AIX/ppc32 we do: LR-> +8(parent_sp), CR-> +4(parent_sp) */ 103 104 /* Save lr and cr*/ 105 mflr 0 106 stw 0,8(1) 107 mfcr 0 108 stw 0,4(1) 109 110 /* New stack frame */ 111 stwu 1,-1024(1) /* sp should maintain 16-byte alignment */ 112 113 /* Save callee-saved registers... */ 114 /* r3, r4 are live here, so use r5 */ 115 lwz 5,tocent__vgPlain_machine_ppc32_has_FP(2) 116 lwz 5,0(5) 117 cmplwi 5,0 118 beq LafterFP1 119 120 /* Floating-point reg save area : 144 bytes at r1[256..399] */ 121 stfd 31,392(1) 122 stfd 30,384(1) 123 stfd 29,376(1) 124 stfd 28,368(1) 125 stfd 27,360(1) 126 stfd 26,352(1) 127 stfd 25,344(1) 128 stfd 24,336(1) 129 stfd 23,328(1) 130 stfd 22,320(1) 131 stfd 21,312(1) 132 stfd 20,304(1) 133 stfd 19,296(1) 134 stfd 18,288(1) 135 stfd 17,280(1) 136 stfd 16,272(1) 137 stfd 15,264(1) 138 stfd 14,256(1) 139LafterFP1: 140 141 /* General reg save area : 76 bytes at r1[400 .. 475] */ 142 stw 31,472(1) 143 stw 30,468(1) 144 stw 29,464(1) 145 stw 28,460(1) 146 stw 27,456(1) 147 stw 26,452(1) 148 stw 25,448(1) 149 stw 24,444(1) 150 stw 23,440(1) 151 stw 22,436(1) 152 stw 21,432(1) 153 stw 20,428(1) 154 stw 19,424(1) 155 stw 18,420(1) 156 stw 17,416(1) 157 stw 16,412(1) 158 stw 15,408(1) 159 stw 14,404(1) 160 /* Probably not necessary to save r13 (thread-specific ptr), 161 as VEX stays clear of it... but what the hell. */ 162 stw 13,400(1) 163 164 /* It's necessary to save/restore VRSAVE in the AIX / Darwin ABI. 165 The Linux kernel might not actually use VRSAVE for its intended 166 purpose, but it should be harmless to preserve anyway. */ 167 /* r3, r4 are live here, so use r5 */ 168 lwz 5,tocent__vgPlain_machine_ppc32_has_VMX(2) 169 lwz 5,0(5) 170 cmplwi 5,0 171 beq LafterVMX1 172 173// Sigh. AIX 5.2 has no idea that Altivec exists. 174// /* VRSAVE save word : 4 bytes at r1[476 .. 479] */ 175// mfspr 5,256 /* vrsave reg is spr number 256 */ 176// stw 5,476(1) 177// 178// /* Vector reg save area (quadword aligned): 179// 192 bytes at r1[480 .. 671] */ 180// li 5,656 181// stvx 31,5,1 182// li 5,640 183// stvx 30,5,1 184// li 5,624 185// stvx 29,5,1 186// li 5,608 187// stvx 28,5,1 188// li 5,592 189// stvx 27,5,1 190// li 5,576 191// stvx 26,5,1 192// li 5,560 193// stvx 25,5,1 194// li 5,544 195// stvx 25,5,1 196// li 5,528 197// stvx 23,5,1 198// li 5,512 199// stvx 22,5,1 200// li 5,496 201// stvx 21,5,1 202// li 5,480 203// stvx 20,5,1 204LafterVMX1: 205 206 /* Local variable space... */ 207 /* Put the original guest state pointer at r1[128]. We 208 will need to refer to it each time round the dispatch loop. 209 Apart from that, we can use r1[0 .. 128] and r1[132 .. 255] 210 as scratch space. */ 211 212 /* r3 holds guest_state */ 213 /* r4 holds do_profiling */ 214 mr 31,3 /* r31 (generated code gsp) = r3 */ 215 stw 3,128(1) /* stash orig guest_state ptr */ 216 217 /* hold dispatch_ctr in r29 */ 218 lwz 5,tocent__vgPlain_dispatch_ctr(2) 219 lwz 29,0(5) 220 221 /* set host FPU control word to the default mode expected 222 by VEX-generated code. See comments in libvex.h for 223 more info. */ 224 lwz 5,tocent__vgPlain_machine_ppc32_has_FP(2) 225 lwz 5,0(5) 226 cmplwi 5,0 227 beq LafterFP2 228 229 /* get zero into f3 (tedious) */ 230 /* note: fsub 3,3,3 is not a reliable way to do this, 231 since if f3 holds a NaN or similar then we don't necessarily 232 wind up with zero. */ 233 li 5,0 234 stw 5,64(1) /* r1[64] is scratch */ 235 lfs 3,64(1) 236 mtfsf 0xFF,3 /* fpscr = f3 */ 237LafterFP2: 238 239 /* set host AltiVec control word to the default mode expected 240 by VEX-generated code. */ 241 lwz 5,tocent__vgPlain_machine_ppc32_has_VMX(2) 242 lwz 5,0(5) 243 cmplwi 5,0 244 beq LafterVMX2 245 246// Sigh. AIX 5.2 has no idea that Altivec exists. 247// vspltisw 3,0x0 /* generate zero */ 248// mtvscr 3 249LafterVMX2: 250 251 /* fetch %CIA into r3 */ 252 lwz 3,OFFSET_ppc32_CIA(31) 253 254 /* fall into main loop (the right one) */ 255 /* r4 = do_profiling. It's probably trashed after here, 256 but that's OK: we don't need it after here. */ 257 cmplwi 4,0 258 beq VG_(run_innerloop__dispatch_unprofiled) 259 b VG_(run_innerloop__dispatch_profiled) 260 /*NOTREACHED*/ 261 262/*----------------------------------------------------*/ 263/*--- NO-PROFILING (standard) dispatcher ---*/ 264/*----------------------------------------------------*/ 265 266.globl VG_(run_innerloop__dispatch_unprofiled) 267VG_(run_innerloop__dispatch_unprofiled): 268 /* At entry: Live regs: 269 r1 (=sp) 270 r3 (=CIA = next guest address) 271 r29 (=dispatch_ctr) 272 r31 (=guest_state) 273 Stack state: 274 128(r1) (=orig guest_state) 275 */ 276 277 /* Has the guest state pointer been messed with? If yes, exit. 278 Also set up & VG_(tt_fast) early in an attempt at better 279 scheduling. */ 280 lwz 5,128(1) /* original guest_state ptr */ 281 cmpw 5,31 282 lwz 5,tocent__vgPlain_tt_fast(2) /* r5 = &tt_fast */ 283 bne gsp_changed 284 285 /* save the jump address in the guest state */ 286 stw 3,OFFSET_ppc32_CIA(31) 287 288 /* Are we out of timeslice? If yes, defer to scheduler. */ 289 addi 29,29,-1 290 cmplwi 29,0 291 beq counter_is_zero 292 293 /* try a fast lookup in the translation cache */ 294 /* r4 = VG_TT_FAST_HASH(addr) * sizeof(FastCacheEntry) 295 = ((r3 >>u 2) & VG_TT_FAST_MASK) << 3 */ 296 rlwinm 4,3,1, 29-VG_TT_FAST_BITS, 28 /* entry# * 8 */ 297 add 5,5,4 /* & VG_(tt_fast)[entry#] */ 298 lwz 6,0(5) /* .guest */ 299 lwz 7,4(5) /* .host */ 300 cmpw 3,6 301 bne fast_lookup_failed 302 303 /* Found a match. Call .host. */ 304 mtctr 7 305 bctrl 306 307 /* On return from guest code: 308 r3 holds destination (original) address. 309 r31 may be unchanged (guest_state), or may indicate further 310 details of the control transfer requested to *r3. 311 */ 312 /* start over */ 313 b VG_(run_innerloop__dispatch_unprofiled) 314 /*NOTREACHED*/ 315 316/*----------------------------------------------------*/ 317/*--- PROFILING dispatcher (can be much slower) ---*/ 318/*----------------------------------------------------*/ 319 320.globl VG_(run_innerloop__dispatch_profiled) 321VG_(run_innerloop__dispatch_profiled): 322 /* At entry: Live regs: 323 r1 (=sp) 324 r3 (=CIA = next guest address) 325 r29 (=dispatch_ctr) 326 r31 (=guest_state) 327 Stack state: 328 128(r1) (=orig guest_state) 329 */ 330 /* Has the guest state pointer been messed with? If yes, exit. 331 Also set up & VG_(tt_fast) early in an attempt at better 332 scheduling. */ 333 lwz 5,128(1) /* original guest_state ptr */ 334 cmpw 5,31 335 lwz 5,tocent__vgPlain_tt_fast(2) /* r5 = &tt_fast */ 336 bne gsp_changed 337 338 /* save the jump address in the guest state */ 339 stw 3,OFFSET_ppc32_CIA(31) 340 341 /* Are we out of timeslice? If yes, defer to scheduler. */ 342 addi 29,29,-1 343 cmplwi 29,0 344 beq counter_is_zero 345 346 /* try a fast lookup in the translation cache */ 347 /* r4 = VG_TT_FAST_HASH(addr) * sizeof(FastCacheEntry) 348 = ((r3 >>u 2) & VG_TT_FAST_MASK) << 3 */ 349 rlwinm 4,3,1, 29-VG_TT_FAST_BITS, 28 /* entry# * 8 */ 350 add 5,5,4 /* & VG_(tt_fast)[entry#] */ 351 lwz 6,0(5) /* .guest */ 352 lwz 7,4(5) /* .host */ 353 cmpw 3,6 354 bne fast_lookup_failed 355 356 /* increment bb profile counter */ 357 srwi 4,4,1 /* entry# * sizeof(UInt*) */ 358 lwz 9,tocent__vgPlain_tt_fastN(2) /* r9 = &tt_fastN */ 359 lwzx 8,9,4 /* r7 = tt_fastN[r4] */ 360 lwz 10,0(8) 361 addi 10,10,1 362 stw 10,0(8) 363 364 /* Found a match. Call .host. */ 365 mtctr 7 366 bctrl 367 368 /* On return from guest code: 369 r3 holds destination (original) address. 370 r31 may be unchanged (guest_state), or may indicate further 371 details of the control transfer requested to *r3. 372 */ 373 /* start over */ 374 b VG_(run_innerloop__dispatch_unprofiled) 375 /*NOTREACHED*/ 376 377 378/*----------------------------------------------------*/ 379/*--- exit points ---*/ 380/*----------------------------------------------------*/ 381 382gsp_changed: 383 /* Someone messed with the gsp (in r31). Have to 384 defer to scheduler to resolve this. dispatch ctr 385 is not yet decremented, so no need to increment. */ 386 /* %CIA is NOT up to date here. First, need to write 387 %r3 back to %CIA, but without trashing %r31 since 388 that holds the value we want to return to the scheduler. 389 Hence use %r5 transiently for the guest state pointer. */ 390 lwz 5,128(1) /* original guest_state ptr */ 391 stw 3,OFFSET_ppc32_CIA(5) 392 mr 3,31 /* r3 = new gsp value */ 393 b run_innerloop_exit 394 /*NOTREACHED*/ 395 396counter_is_zero: 397 /* %CIA is up to date */ 398 /* back out decrement of the dispatch counter */ 399 addi 29,29,1 400 li 3,VG_TRC_INNER_COUNTERZERO 401 b run_innerloop_exit 402 403fast_lookup_failed: 404 /* %CIA is up to date */ 405 /* back out decrement of the dispatch counter */ 406 addi 29,29,1 407 li 3,VG_TRC_INNER_FASTMISS 408 b run_innerloop_exit 409 410 411 412/* All exits from the dispatcher go through here. 413 r3 holds the return value. 414*/ 415run_innerloop_exit: 416 /* We're leaving. Check that nobody messed with 417 VSCR or FPSCR. */ 418 419 /* Using r10 - value used again further on, so don't trash! */ 420 lwz 10,tocent__vgPlain_machine_ppc32_has_FP(2) 421 lwz 10,0(10) 422 cmplwi 10,0 423 beq LafterFP8 424 425 /* Set fpscr back to a known state, since vex-generated code 426 may have messed with fpscr[rm]. */ 427 li 5,0 428 stw 5,64(1) /* r1[64] is scratch */ 429 lfs 3,64(1) 430 mtfsf 0xFF,3 /* fpscr = f3 */ 431LafterFP8: 432 433 /* Using r11 - value used again further on, so don't trash! */ 434 lwz 11,tocent__vgPlain_machine_ppc32_has_VMX(2) 435 lwz 11,0(11) 436 cmplwi 11,0 437 beq LafterVMX8 438 439// Sigh. AIX 5.2 has no idea that Altivec exists. 440// /* Check VSCR[NJ] == 1 */ 441// /* first generate 4x 0x00010000 */ 442// vspltisw 4,0x1 /* 4x 0x00000001 */ 443// vspltisw 5,0x0 /* zero */ 444// vsldoi 6,4,5,0x2 /* <<2*8 => 4x 0x00010000 */ 445// /* retrieve VSCR and mask wanted bits */ 446// mfvscr 7 447// vand 7,7,6 /* gives NJ flag */ 448// vspltw 7,7,0x3 /* flags-word to all lanes */ 449// vcmpequw. 8,6,7 /* CR[24] = 1 if v6 == v7 */ 450// bt 24,invariant_violation /* branch if all_equal */ 451LafterVMX8: 452 453 /* otherwise we're OK */ 454 b run_innerloop_exit_REALLY 455 456 457invariant_violation: 458 li 3,VG_TRC_INVARIANT_FAILED 459 b run_innerloop_exit_REALLY 460 461run_innerloop_exit_REALLY: 462 /* r3 holds VG_TRC_* value to return */ 463 464 /* Write ctr to VG(dispatch_ctr) */ 465 lwz 5,tocent__vgPlain_dispatch_ctr(2) 466 stw 29,0(5) 467 468 /* Restore callee-saved registers... */ 469 470 /* r10 already holds VG_(machine_ppc32_has_FP) value */ 471 cmplwi 10,0 472 beq LafterFP9 473 474 /* Floating-point regs */ 475 lfd 31,392(1) 476 lfd 30,384(1) 477 lfd 29,376(1) 478 lfd 28,368(1) 479 lfd 27,360(1) 480 lfd 26,352(1) 481 lfd 25,344(1) 482 lfd 24,336(1) 483 lfd 23,328(1) 484 lfd 22,320(1) 485 lfd 21,312(1) 486 lfd 20,304(1) 487 lfd 19,296(1) 488 lfd 18,288(1) 489 lfd 17,280(1) 490 lfd 16,272(1) 491 lfd 15,264(1) 492 lfd 14,256(1) 493LafterFP9: 494 495 /* General regs */ 496 lwz 31,472(1) 497 lwz 30,468(1) 498 lwz 29,464(1) 499 lwz 28,460(1) 500 lwz 27,456(1) 501 lwz 26,452(1) 502 lwz 25,448(1) 503 lwz 24,444(1) 504 lwz 23,440(1) 505 lwz 22,436(1) 506 lwz 21,432(1) 507 lwz 20,428(1) 508 lwz 19,424(1) 509 lwz 18,420(1) 510 lwz 17,416(1) 511 lwz 16,412(1) 512 lwz 15,408(1) 513 lwz 14,404(1) 514 lwz 13,400(1) 515 516 /* r11 already holds VG_(machine_ppc32_has_VMX) value */ 517 cmplwi 11,0 518 beq LafterVMX9 519 520// Sigh. AIX 5.2 has no idea that Altivec exists. 521// /* VRSAVE */ 522// lwz 4,476(1) 523// mtspr 4,256 /* VRSAVE reg is spr number 256 */ 524// 525// /* Vector regs */ 526// li 4,656 527// lvx 31,4,1 528// li 4,640 529// lvx 30,4,1 530// li 4,624 531// lvx 29,4,1 532// li 4,608 533// lvx 28,4,1 534// li 4,592 535// lvx 27,4,1 536// li 4,576 537// lvx 26,4,1 538// li 4,560 539// lvx 25,4,1 540// li 4,544 541// lvx 24,4,1 542// li 4,528 543// lvx 23,4,1 544// li 4,512 545// lvx 22,4,1 546// li 4,496 547// lvx 21,4,1 548// li 4,480 549// lvx 20,4,1 550LafterVMX9: 551 552 /* r3 is live here; don't trash it */ 553 /* restore lr,cr,sp */ 554 addi 4,1,1024 /* r4 = old SP */ 555 lwz 0,8(4) 556 mtlr 0 557 lwz 0,4(4) 558 mtcr 0 559 mr 1,4 560 blr 561 562LT..vgPlain_run_innerloop: 563 .long 0 564 .byte 0,0,32,64,0,0,2,0 565 .long 0 566 .long LT..vgPlain_run_innerloop-.vgPlain_run_innerloop 567 .short 13 568 .byte "vgPlain_run_innerloop" 569 .align 2 570_section_.text: 571 .csect .data[RW],3 572 .long _section_.text 573 574/*------------------------------------------------------------*/ 575/*--- ---*/ 576/*--- A special dispatcher, for running no-redir ---*/ 577/*--- translations. Just runs the given translation once. ---*/ 578/*--- ---*/ 579/*------------------------------------------------------------*/ 580 581/* signature: 582void VG_(run_a_noredir_translation) ( UWord* argblock ); 583*/ 584 585/* Run a no-redir translation. argblock points to 4 UWords, 2 to carry args 586 and 2 to carry results: 587 0: input: ptr to translation 588 1: input: ptr to guest state 589 2: output: next guest PC 590 3: output: guest state pointer afterwards (== thread return code) 591*/ 592.csect .text[PR] 593.align 2 594.globl .VG_(run_a_noredir_translation) 595.VG_(run_a_noredir_translation): 596 597 /* Rather than attempt to make sense of the AIX ABI, just 598 drop r1 by 256 (to get away from the caller's frame), then 599 512 (to give ourselves a 512-byte save area), and then 600 another 256 (to clear our save area). In all, drop r1 by 1024 601 and dump stuff on the stack at 256(1)..768(1). */ 602 /* At entry, r3 points to argblock */ 603 604 /* ----- entry point to VG_(run_innerloop) ----- */ 605 /* For AIX/ppc32 we do: LR-> +8(parent_sp), CR-> +4(parent_sp) */ 606 607 /* Save lr and cr*/ 608 mflr 0 609 stw 0,8(1) 610 mfcr 0 611 stw 0,4(1) 612 613 /* New stack frame */ 614 stwu 1,-1024(1) /* sp should maintain 16-byte alignment */ 615 616 /* General reg save area : 76 bytes at r1[400 .. 475] */ 617 stw 31,472(1) 618 stw 30,468(1) 619 stw 29,464(1) 620 stw 28,460(1) 621 stw 27,456(1) 622 stw 26,452(1) 623 stw 25,448(1) 624 stw 24,444(1) 625 stw 23,440(1) 626 stw 22,436(1) 627 stw 21,432(1) 628 stw 20,428(1) 629 stw 19,424(1) 630 stw 18,420(1) 631 stw 17,416(1) 632 stw 16,412(1) 633 stw 15,408(1) 634 stw 14,404(1) 635 stw 13,400(1) 636 stw 3,396(1) /* will need it later */ 637 638 lwz 31,4(3) /* rd argblock[1] */ 639 lwz 30,0(3) /* rd argblock[0] */ 640 mtlr 30 /* run translation */ 641 blrl 642 643 lwz 4,396(1) /* &argblock */ 644 stw 3, 8(4) /* wr argblock[2] */ 645 stw 31,12(4) /* wr argblock[3] */ 646 647 /* General regs */ 648 lwz 31,472(1) 649 lwz 30,468(1) 650 lwz 29,464(1) 651 lwz 28,460(1) 652 lwz 27,456(1) 653 lwz 26,452(1) 654 lwz 25,448(1) 655 lwz 24,444(1) 656 lwz 23,440(1) 657 lwz 22,436(1) 658 lwz 21,432(1) 659 lwz 20,428(1) 660 lwz 19,424(1) 661 lwz 18,420(1) 662 lwz 17,416(1) 663 lwz 16,412(1) 664 lwz 15,408(1) 665 lwz 14,404(1) 666 lwz 13,400(1) 667 668 /* restore lr,cr,sp */ 669 addi 4,1,1024 /* r4 = old SP */ 670 lwz 0,8(4) 671 mtlr 0 672 lwz 0,4(4) 673 mtcr 0 674 mr 1,4 675 blr 676 677#endif // defined(VGP_ppc32_aix5) 678 679/*--------------------------------------------------------------------*/ 680/*--- end ---*/ 681/*--------------------------------------------------------------------*/ 682