1/* m68hc11-dis.c -- Motorola 68HC11 & 68HC12 disassembly 2 Copyright 1999, 2000, 2001, 2002, 2003, 2006 3 Free Software Foundation, Inc. 4 Written by Stephane Carrez (stcarrez@nerim.fr) 5 6This program is free software; you can redistribute it and/or modify 7it under the terms of the GNU General Public License as published by 8the Free Software Foundation; either version 2 of the License, or 9(at your option) any later version. 10 11This program is distributed in the hope that it will be useful, 12but WITHOUT ANY WARRANTY; without even the implied warranty of 13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14GNU General Public License for more details. 15 16You should have received a copy of the GNU General Public License 17along with this program; if not, write to the Free Software 18Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ 19 20#include <stdio.h> 21 22#include "ansidecl.h" 23#include "opcode/m68hc11.h" 24#include "dis-asm.h" 25 26#define PC_REGNUM 3 27 28static const char *const reg_name[] = { 29 "X", "Y", "SP", "PC" 30}; 31 32static const char *const reg_src_table[] = { 33 "A", "B", "CCR", "TMP3", "D", "X", "Y", "SP" 34}; 35 36static const char *const reg_dst_table[] = { 37 "A", "B", "CCR", "TMP2", "D", "X", "Y", "SP" 38}; 39 40#define OP_PAGE_MASK (M6811_OP_PAGE2|M6811_OP_PAGE3|M6811_OP_PAGE4) 41 42/* Prototypes for local functions. */ 43static int read_memory (bfd_vma, bfd_byte *, int, struct disassemble_info *); 44static int print_indexed_operand (bfd_vma, struct disassemble_info *, 45 int*, int, int, bfd_vma); 46static int print_insn (bfd_vma, struct disassemble_info *, int); 47 48static int 49read_memory (bfd_vma memaddr, bfd_byte* buffer, int size, 50 struct disassemble_info* info) 51{ 52 int status; 53 54 /* Get first byte. Only one at a time because we don't know the 55 size of the insn. */ 56 status = (*info->read_memory_func) (memaddr, buffer, size, info); 57 if (status != 0) 58 { 59 (*info->memory_error_func) (status, memaddr, info); 60 return -1; 61 } 62 return 0; 63} 64 65 66/* Read the 68HC12 indexed operand byte and print the corresponding mode. 67 Returns the number of bytes read or -1 if failure. */ 68static int 69print_indexed_operand (bfd_vma memaddr, struct disassemble_info* info, 70 int* indirect, int mov_insn, int pc_offset, 71 bfd_vma endaddr) 72{ 73 bfd_byte buffer[4]; 74 int reg; 75 int status; 76 short sval; 77 int pos = 1; 78 79 if (indirect) 80 *indirect = 0; 81 82 status = read_memory (memaddr, &buffer[0], 1, info); 83 if (status != 0) 84 { 85 return status; 86 } 87 88 /* n,r with 5-bits signed constant. */ 89 if ((buffer[0] & 0x20) == 0) 90 { 91 reg = (buffer[0] >> 6) & 3; 92 sval = (buffer[0] & 0x1f); 93 if (sval & 0x10) 94 sval |= 0xfff0; 95 /* 68HC12 requires an adjustment for movb/movw pc relative modes. */ 96 if (reg == PC_REGNUM && info->mach == bfd_mach_m6812 && mov_insn) 97 sval += pc_offset; 98 (*info->fprintf_func) (info->stream, "%d,%s", 99 (int) sval, reg_name[reg]); 100 101 if (reg == PC_REGNUM) 102 { 103 (* info->fprintf_func) (info->stream, " {"); 104 (* info->print_address_func) (endaddr + sval, info); 105 (* info->fprintf_func) (info->stream, "}"); 106 } 107 } 108 109 /* Auto pre/post increment/decrement. */ 110 else if ((buffer[0] & 0xc0) != 0xc0) 111 { 112 const char *mode; 113 114 reg = (buffer[0] >> 6) & 3; 115 sval = (buffer[0] & 0x0f); 116 if (sval & 0x8) 117 { 118 sval |= 0xfff0; 119 sval = -sval; 120 mode = "-"; 121 } 122 else 123 { 124 sval = sval + 1; 125 mode = "+"; 126 } 127 (*info->fprintf_func) (info->stream, "%d,%s%s%s", 128 (int) sval, 129 (buffer[0] & 0x10 ? "" : mode), 130 reg_name[reg], (buffer[0] & 0x10 ? mode : "")); 131 } 132 133 /* [n,r] 16-bits offset indexed indirect. */ 134 else if ((buffer[0] & 0x07) == 3) 135 { 136 if (mov_insn) 137 { 138 (*info->fprintf_func) (info->stream, "<invalid op: 0x%x>", 139 buffer[0] & 0x0ff); 140 return 0; 141 } 142 reg = (buffer[0] >> 3) & 0x03; 143 status = read_memory (memaddr + pos, &buffer[0], 2, info); 144 if (status != 0) 145 { 146 return status; 147 } 148 149 pos += 2; 150 sval = ((buffer[0] << 8) | (buffer[1] & 0x0FF)); 151 (*info->fprintf_func) (info->stream, "[%u,%s]", 152 sval & 0x0ffff, reg_name[reg]); 153 if (indirect) 154 *indirect = 1; 155 } 156 157 /* n,r with 9 and 16 bit signed constant. */ 158 else if ((buffer[0] & 0x4) == 0) 159 { 160 if (mov_insn) 161 { 162 (*info->fprintf_func) (info->stream, "<invalid op: 0x%x>", 163 buffer[0] & 0x0ff); 164 return 0; 165 } 166 reg = (buffer[0] >> 3) & 0x03; 167 status = read_memory (memaddr + pos, 168 &buffer[1], (buffer[0] & 0x2 ? 2 : 1), info); 169 if (status != 0) 170 { 171 return status; 172 } 173 if (buffer[0] & 2) 174 { 175 sval = ((buffer[1] << 8) | (buffer[2] & 0x0FF)); 176 sval &= 0x0FFFF; 177 pos += 2; 178 endaddr += 2; 179 } 180 else 181 { 182 sval = buffer[1] & 0x00ff; 183 if (buffer[0] & 0x01) 184 sval |= 0xff00; 185 pos++; 186 endaddr++; 187 } 188 (*info->fprintf_func) (info->stream, "%d,%s", 189 (int) sval, reg_name[reg]); 190 if (reg == PC_REGNUM) 191 { 192 (* info->fprintf_func) (info->stream, " {"); 193 (* info->print_address_func) (endaddr + sval, info); 194 (* info->fprintf_func) (info->stream, "}"); 195 } 196 } 197 else 198 { 199 reg = (buffer[0] >> 3) & 0x03; 200 switch (buffer[0] & 3) 201 { 202 case 0: 203 (*info->fprintf_func) (info->stream, "A,%s", reg_name[reg]); 204 break; 205 case 1: 206 (*info->fprintf_func) (info->stream, "B,%s", reg_name[reg]); 207 break; 208 case 2: 209 (*info->fprintf_func) (info->stream, "D,%s", reg_name[reg]); 210 break; 211 case 3: 212 default: 213 (*info->fprintf_func) (info->stream, "[D,%s]", reg_name[reg]); 214 if (indirect) 215 *indirect = 1; 216 break; 217 } 218 } 219 220 return pos; 221} 222 223/* Disassemble one instruction at address 'memaddr'. Returns the number 224 of bytes used by that instruction. */ 225static int 226print_insn (bfd_vma memaddr, struct disassemble_info* info, int arch) 227{ 228 int status; 229 bfd_byte buffer[4]; 230 unsigned char code; 231 long format, pos, i; 232 short sval; 233 const struct m68hc11_opcode *opcode; 234 235 /* Get first byte. Only one at a time because we don't know the 236 size of the insn. */ 237 status = read_memory (memaddr, buffer, 1, info); 238 if (status != 0) 239 { 240 return status; 241 } 242 243 format = 0; 244 code = buffer[0]; 245 pos = 0; 246 247 /* Look for page2,3,4 opcodes. */ 248 if (code == M6811_OPCODE_PAGE2) 249 { 250 pos++; 251 format = M6811_OP_PAGE2; 252 } 253 else if (code == M6811_OPCODE_PAGE3 && arch == cpu6811) 254 { 255 pos++; 256 format = M6811_OP_PAGE3; 257 } 258 else if (code == M6811_OPCODE_PAGE4 && arch == cpu6811) 259 { 260 pos++; 261 format = M6811_OP_PAGE4; 262 } 263 264 /* We are in page2,3,4; get the real opcode. */ 265 if (pos == 1) 266 { 267 status = read_memory (memaddr + pos, &buffer[1], 1, info); 268 if (status != 0) 269 { 270 return status; 271 } 272 code = buffer[1]; 273 } 274 275 276 /* Look first for a 68HC12 alias. All of them are 2-bytes long and 277 in page 1. There is no operand to print. We read the second byte 278 only when we have a possible match. */ 279 if ((arch & cpu6812) && format == 0) 280 { 281 int must_read = 1; 282 283 /* Walk the alias table to find a code1+code2 match. */ 284 for (i = 0; i < m68hc12_num_alias; i++) 285 { 286 if (m68hc12_alias[i].code1 == code) 287 { 288 if (must_read) 289 { 290 status = read_memory (memaddr + pos + 1, 291 &buffer[1], 1, info); 292 if (status != 0) 293 break; 294 295 must_read = 1; 296 } 297 if (m68hc12_alias[i].code2 == (unsigned char) buffer[1]) 298 { 299 (*info->fprintf_func) (info->stream, "%s", 300 m68hc12_alias[i].name); 301 return 2; 302 } 303 } 304 } 305 } 306 307 pos++; 308 309 /* Scan the opcode table until we find the opcode 310 with the corresponding page. */ 311 opcode = m68hc11_opcodes; 312 for (i = 0; i < m68hc11_num_opcodes; i++, opcode++) 313 { 314 int offset; 315 int pc_src_offset; 316 int pc_dst_offset = 0; 317 318 if ((opcode->arch & arch) == 0) 319 continue; 320 if (opcode->opcode != code) 321 continue; 322 if ((opcode->format & OP_PAGE_MASK) != format) 323 continue; 324 325 if (opcode->format & M6812_OP_REG) 326 { 327 int j; 328 int is_jump; 329 330 if (opcode->format & M6811_OP_JUMP_REL) 331 is_jump = 1; 332 else 333 is_jump = 0; 334 335 status = read_memory (memaddr + pos, &buffer[0], 1, info); 336 if (status != 0) 337 { 338 return status; 339 } 340 for (j = 0; i + j < m68hc11_num_opcodes; j++) 341 { 342 if ((opcode[j].arch & arch) == 0) 343 continue; 344 if (opcode[j].opcode != code) 345 continue; 346 if (is_jump) 347 { 348 if (!(opcode[j].format & M6811_OP_JUMP_REL)) 349 continue; 350 351 if ((opcode[j].format & M6812_OP_IBCC_MARKER) 352 && (buffer[0] & 0xc0) != 0x80) 353 continue; 354 if ((opcode[j].format & M6812_OP_TBCC_MARKER) 355 && (buffer[0] & 0xc0) != 0x40) 356 continue; 357 if ((opcode[j].format & M6812_OP_DBCC_MARKER) 358 && (buffer[0] & 0xc0) != 0) 359 continue; 360 if ((opcode[j].format & M6812_OP_EQ_MARKER) 361 && (buffer[0] & 0x20) == 0) 362 break; 363 if (!(opcode[j].format & M6812_OP_EQ_MARKER) 364 && (buffer[0] & 0x20) != 0) 365 break; 366 continue; 367 } 368 if (opcode[j].format & M6812_OP_EXG_MARKER && buffer[0] & 0x80) 369 break; 370 if ((opcode[j].format & M6812_OP_SEX_MARKER) 371 && (((buffer[0] & 0x07) >= 3 && (buffer[0] & 7) <= 7)) 372 && ((buffer[0] & 0x0f0) <= 0x20)) 373 break; 374 if (opcode[j].format & M6812_OP_TFR_MARKER 375 && !(buffer[0] & 0x80)) 376 break; 377 } 378 if (i + j < m68hc11_num_opcodes) 379 opcode = &opcode[j]; 380 } 381 382 /* We have found the opcode. Extract the operand and print it. */ 383 (*info->fprintf_func) (info->stream, "%s", opcode->name); 384 385 format = opcode->format; 386 if (format & (M6811_OP_MASK | M6811_OP_BITMASK 387 | M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16)) 388 { 389 (*info->fprintf_func) (info->stream, "\t"); 390 } 391 392 /* The movb and movw must be handled in a special way... 393 The source constant 'ii' is not always at the same place. 394 This is the same for the destination for the post-indexed byte. 395 The 'offset' is used to do the appropriate correction. 396 397 offset offset 398 for constant for destination 399 movb 18 OB ii hh ll 0 0 400 18 08 xb ii 1 -1 401 18 0C hh ll hh ll 0 0 402 18 09 xb hh ll 1 -1 403 18 0D xb hh ll 0 0 404 18 0A xb xb 0 0 405 406 movw 18 03 jj kk hh ll 0 0 407 18 00 xb jj kk 1 -1 408 18 04 hh ll hh ll 0 0 409 18 01 xb hh ll 1 -1 410 18 05 xb hh ll 0 0 411 18 02 xb xb 0 0 412 413 After the source operand is read, the position 'pos' is incremented 414 this explains the negative offset for destination. 415 416 movb/movw above are the only instructions with this matching 417 format. */ 418 offset = ((format & M6812_OP_IDX_P2) 419 && (format & (M6811_OP_IMM8 | M6811_OP_IMM16 | 420 M6811_OP_IND16))); 421 422 /* Operand with one more byte: - immediate, offset, 423 direct-low address. */ 424 if (format & 425 (M6811_OP_IMM8 | M6811_OP_IX | M6811_OP_IY | M6811_OP_DIRECT)) 426 { 427 status = read_memory (memaddr + pos + offset, &buffer[0], 1, info); 428 if (status != 0) 429 { 430 return status; 431 } 432 433 pos++; 434 435 /* This movb/movw is special (see above). */ 436 offset = -offset; 437 438 pc_dst_offset = 2; 439 if (format & M6811_OP_IMM8) 440 { 441 (*info->fprintf_func) (info->stream, "#%d", (int) buffer[0]); 442 format &= ~M6811_OP_IMM8; 443 /* Set PC destination offset. */ 444 pc_dst_offset = 1; 445 } 446 else if (format & M6811_OP_IX) 447 { 448 /* Offsets are in range 0..255, print them unsigned. */ 449 (*info->fprintf_func) (info->stream, "%u,x", buffer[0] & 0x0FF); 450 format &= ~M6811_OP_IX; 451 } 452 else if (format & M6811_OP_IY) 453 { 454 (*info->fprintf_func) (info->stream, "%u,y", buffer[0] & 0x0FF); 455 format &= ~M6811_OP_IY; 456 } 457 else if (format & M6811_OP_DIRECT) 458 { 459 (*info->fprintf_func) (info->stream, "*"); 460 (*info->print_address_func) (buffer[0] & 0x0FF, info); 461 format &= ~M6811_OP_DIRECT; 462 } 463 } 464 465#define M6812_DST_MOVE (M6812_OP_IND16_P2 | M6812_OP_IDX_P2) 466#define M6812_INDEXED_FLAGS (M6812_OP_IDX|M6812_OP_IDX_1|M6812_OP_IDX_2) 467 /* Analyze the 68HC12 indexed byte. */ 468 if (format & M6812_INDEXED_FLAGS) 469 { 470 int indirect; 471 bfd_vma endaddr; 472 473 endaddr = memaddr + pos + 1; 474 if (format & M6811_OP_IND16) 475 endaddr += 2; 476 pc_src_offset = -1; 477 pc_dst_offset = 1; 478 status = print_indexed_operand (memaddr + pos, info, &indirect, 479 (format & M6812_DST_MOVE), 480 pc_src_offset, endaddr); 481 if (status < 0) 482 { 483 return status; 484 } 485 pos += status; 486 487 /* The indirect addressing mode of the call instruction does 488 not need the page code. */ 489 if ((format & M6812_OP_PAGE) && indirect) 490 format &= ~M6812_OP_PAGE; 491 } 492 493 /* 68HC12 dbcc/ibcc/tbcc operands. */ 494 if ((format & M6812_OP_REG) && (format & M6811_OP_JUMP_REL)) 495 { 496 status = read_memory (memaddr + pos, &buffer[0], 2, info); 497 if (status != 0) 498 { 499 return status; 500 } 501 (*info->fprintf_func) (info->stream, "%s,", 502 reg_src_table[buffer[0] & 0x07]); 503 sval = buffer[1] & 0x0ff; 504 if (buffer[0] & 0x10) 505 sval |= 0xff00; 506 507 pos += 2; 508 (*info->print_address_func) (memaddr + pos + sval, info); 509 format &= ~(M6812_OP_REG | M6811_OP_JUMP_REL); 510 } 511 else if (format & (M6812_OP_REG | M6812_OP_REG_2)) 512 { 513 status = read_memory (memaddr + pos, &buffer[0], 1, info); 514 if (status != 0) 515 { 516 return status; 517 } 518 519 pos++; 520 (*info->fprintf_func) (info->stream, "%s,%s", 521 reg_src_table[(buffer[0] >> 4) & 7], 522 reg_dst_table[(buffer[0] & 7)]); 523 } 524 525 if (format & (M6811_OP_IMM16 | M6811_OP_IND16)) 526 { 527 int val; 528 bfd_vma addr; 529 unsigned page = 0; 530 531 status = read_memory (memaddr + pos + offset, &buffer[0], 2, info); 532 if (status != 0) 533 { 534 return status; 535 } 536 if (format & M6812_OP_IDX_P2) 537 offset = -2; 538 else 539 offset = 0; 540 pos += 2; 541 542 val = ((buffer[0] << 8) | (buffer[1] & 0x0FF)); 543 val &= 0x0FFFF; 544 addr = val; 545 pc_dst_offset = 2; 546 if (format & M6812_OP_PAGE) 547 { 548 status = read_memory (memaddr + pos + offset, buffer, 1, info); 549 if (status != 0) 550 return status; 551 552 page = (unsigned) buffer[0]; 553 if (addr >= M68HC12_BANK_BASE && addr < 0x0c000) 554 addr = ((val - M68HC12_BANK_BASE) 555 | (page << M68HC12_BANK_SHIFT)) 556 + M68HC12_BANK_VIRT; 557 } 558 else if ((arch & cpu6812) 559 && addr >= M68HC12_BANK_BASE && addr < 0x0c000) 560 { 561 int cur_page; 562 bfd_vma vaddr; 563 564 if (memaddr >= M68HC12_BANK_VIRT) 565 cur_page = ((memaddr - M68HC12_BANK_VIRT) 566 >> M68HC12_BANK_SHIFT); 567 else 568 cur_page = 0; 569 570 vaddr = ((addr - M68HC12_BANK_BASE) 571 + (cur_page << M68HC12_BANK_SHIFT)) 572 + M68HC12_BANK_VIRT; 573 if (!info->symbol_at_address_func (addr, info) 574 && info->symbol_at_address_func (vaddr, info)) 575 addr = vaddr; 576 } 577 if (format & M6811_OP_IMM16) 578 { 579 format &= ~M6811_OP_IMM16; 580 (*info->fprintf_func) (info->stream, "#"); 581 } 582 else 583 format &= ~M6811_OP_IND16; 584 585 (*info->print_address_func) (addr, info); 586 if (format & M6812_OP_PAGE) 587 { 588 (* info->fprintf_func) (info->stream, " {"); 589 (* info->print_address_func) (val, info); 590 (* info->fprintf_func) (info->stream, ", %d}", page); 591 format &= ~M6812_OP_PAGE; 592 pos += 1; 593 } 594 } 595 596 if (format & M6812_OP_IDX_P2) 597 { 598 (*info->fprintf_func) (info->stream, ", "); 599 status = print_indexed_operand (memaddr + pos + offset, info, 600 0, 1, pc_dst_offset, 601 memaddr + pos + offset + 1); 602 if (status < 0) 603 return status; 604 pos += status; 605 } 606 607 if (format & M6812_OP_IND16_P2) 608 { 609 int val; 610 611 (*info->fprintf_func) (info->stream, ", "); 612 613 status = read_memory (memaddr + pos + offset, &buffer[0], 2, info); 614 if (status != 0) 615 { 616 return status; 617 } 618 pos += 2; 619 620 val = ((buffer[0] << 8) | (buffer[1] & 0x0FF)); 621 val &= 0x0FFFF; 622 (*info->print_address_func) (val, info); 623 } 624 625 /* M6811_OP_BITMASK and M6811_OP_JUMP_REL must be treated separately 626 and in that order. The brset/brclr insn have a bitmask and then 627 a relative branch offset. */ 628 if (format & M6811_OP_BITMASK) 629 { 630 status = read_memory (memaddr + pos, &buffer[0], 1, info); 631 if (status != 0) 632 { 633 return status; 634 } 635 pos++; 636 (*info->fprintf_func) (info->stream, " #$%02x%s", 637 buffer[0] & 0x0FF, 638 (format & M6811_OP_JUMP_REL ? " " : "")); 639 format &= ~M6811_OP_BITMASK; 640 } 641 if (format & M6811_OP_JUMP_REL) 642 { 643 int val; 644 645 status = read_memory (memaddr + pos, &buffer[0], 1, info); 646 if (status != 0) 647 { 648 return status; 649 } 650 651 pos++; 652 val = (buffer[0] & 0x80) ? buffer[0] | 0xFFFFFF00 : buffer[0]; 653 (*info->print_address_func) (memaddr + pos + val, info); 654 format &= ~M6811_OP_JUMP_REL; 655 } 656 else if (format & M6812_OP_JUMP_REL16) 657 { 658 int val; 659 660 status = read_memory (memaddr + pos, &buffer[0], 2, info); 661 if (status != 0) 662 { 663 return status; 664 } 665 666 pos += 2; 667 val = ((buffer[0] << 8) | (buffer[1] & 0x0FF)); 668 if (val & 0x8000) 669 val |= 0xffff0000; 670 671 (*info->print_address_func) (memaddr + pos + val, info); 672 format &= ~M6812_OP_JUMP_REL16; 673 } 674 675 if (format & M6812_OP_PAGE) 676 { 677 int val; 678 679 status = read_memory (memaddr + pos + offset, &buffer[0], 1, info); 680 if (status != 0) 681 { 682 return status; 683 } 684 pos += 1; 685 686 val = buffer[0] & 0x0ff; 687 (*info->fprintf_func) (info->stream, ", %d", val); 688 } 689 690#ifdef DEBUG 691 /* Consistency check. 'format' must be 0, so that we have handled 692 all formats; and the computed size of the insn must match the 693 opcode table content. */ 694 if (format & ~(M6811_OP_PAGE4 | M6811_OP_PAGE3 | M6811_OP_PAGE2)) 695 { 696 (*info->fprintf_func) (info->stream, "; Error, format: %lx", format); 697 } 698 if (pos != opcode->size) 699 { 700 (*info->fprintf_func) (info->stream, "; Error, size: %ld expect %d", 701 pos, opcode->size); 702 } 703#endif 704 return pos; 705 } 706 707 /* Opcode not recognized. */ 708 if (format == M6811_OP_PAGE2 && arch & cpu6812 709 && ((code >= 0x30 && code <= 0x39) || (code >= 0x40))) 710 (*info->fprintf_func) (info->stream, "trap\t#%d", code & 0x0ff); 711 712 else if (format == M6811_OP_PAGE2) 713 (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x", 714 M6811_OPCODE_PAGE2, code); 715 else if (format == M6811_OP_PAGE3) 716 (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x", 717 M6811_OPCODE_PAGE3, code); 718 else if (format == M6811_OP_PAGE4) 719 (*info->fprintf_func) (info->stream, ".byte\t0x%02x, 0x%02x", 720 M6811_OPCODE_PAGE4, code); 721 else 722 (*info->fprintf_func) (info->stream, ".byte\t0x%02x", code); 723 724 return pos; 725} 726 727/* Disassemble one instruction at address 'memaddr'. Returns the number 728 of bytes used by that instruction. */ 729int 730print_insn_m68hc11 (bfd_vma memaddr, struct disassemble_info* info) 731{ 732 return print_insn (memaddr, info, cpu6811); 733} 734 735int 736print_insn_m68hc12 (bfd_vma memaddr, struct disassemble_info* info) 737{ 738 return print_insn (memaddr, info, cpu6812); 739} 740