uboot/arch/powerpc/cpu/mpc8xx/start.S
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 1998  Dan Malek <dmalek@jlc.net>
   3 *  Copyright (C) 1999  Magnus Damm <kieraypc01.p.y.kie.era.ericsson.se>
   4 *  Copyright (C) 2000,2001,2002 Wolfgang Denk <wd@denx.de>
   5 *
   6 * See file CREDITS for list of people who contributed to this
   7 * project.
   8 *
   9 * This program is free software; you can redistribute it and/or
  10 * modify it under the terms of the GNU General Public License as
  11 * published by the Free Software Foundation; either version 2 of
  12 * the License, or (at your option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software
  21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  22 * MA 02111-1307 USA
  23 */
  24
  25/*  U-Boot - Startup Code for PowerPC based Embedded Boards
  26 *
  27 *
  28 *  The processor starts at 0x00000100 and the code is executed
  29 *  from flash. The code is organized to be at an other address
  30 *  in memory, but as long we don't jump around before relocating,
  31 *  board_init lies at a quite high address and when the cpu has
  32 *  jumped there, everything is ok.
  33 *  This works because the cpu gives the FLASH (CS0) the whole
  34 *  address space at startup, and board_init lies as a echo of
  35 *  the flash somewhere up there in the memory map.
  36 *
  37 *  board_init will change CS0 to be positioned at the correct
  38 *  address and (s)dram will be positioned at address 0
  39 */
  40#include <asm-offsets.h>
  41#include <config.h>
  42#include <mpc8xx.h>
  43#include <version.h>
  44
  45#define CONFIG_8xx 1            /* needed for Linux kernel header files */
  46#define _LINUX_CONFIG_H 1       /* avoid reading Linux autoconf.h file  */
  47
  48#include <ppc_asm.tmpl>
  49#include <ppc_defs.h>
  50
  51#include <asm/cache.h>
  52#include <asm/mmu.h>
  53#include <asm/u-boot.h>
  54
  55/* We don't want the  MMU yet.
  56*/
  57#undef  MSR_KERNEL
  58#define MSR_KERNEL ( MSR_ME | MSR_RI )  /* Machine Check and Recoverable Interr. */
  59
  60/*
  61 * Set up GOT: Global Offset Table
  62 *
  63 * Use r12 to access the GOT
  64 */
  65        START_GOT
  66        GOT_ENTRY(_GOT2_TABLE_)
  67        GOT_ENTRY(_FIXUP_TABLE_)
  68
  69        GOT_ENTRY(_start)
  70        GOT_ENTRY(_start_of_vectors)
  71        GOT_ENTRY(_end_of_vectors)
  72        GOT_ENTRY(transfer_to_handler)
  73
  74        GOT_ENTRY(__init_end)
  75        GOT_ENTRY(__bss_end__)
  76        GOT_ENTRY(__bss_start)
  77        END_GOT
  78
  79/*
  80 * r3 - 1st arg to board_init(): IMMP pointer
  81 * r4 - 2nd arg to board_init(): boot flag
  82 */
  83        .text
  84        .long   0x27051956              /* U-Boot Magic Number                  */
  85        .globl  version_string
  86version_string:
  87        .ascii U_BOOT_VERSION_STRING, "\0"
  88
  89        . = EXC_OFF_SYS_RESET
  90        .globl  _start
  91_start:
  92        lis     r3, CONFIG_SYS_IMMR@h           /* position IMMR */
  93        mtspr   638, r3
  94
  95        /* Initialize machine status; enable machine check interrupt            */
  96        /*----------------------------------------------------------------------*/
  97        li      r3, MSR_KERNEL          /* Set ME, RI flags */
  98        mtmsr   r3
  99        mtspr   SRR1, r3                /* Make SRR1 match MSR */
 100
 101        mfspr   r3, ICR                 /* clear Interrupt Cause Register */
 102
 103        /* Initialize debug port registers                                      */
 104        /*----------------------------------------------------------------------*/
 105        xor     r0, r0, r0              /* Clear R0 */
 106        mtspr   LCTRL1, r0              /* Initialize debug port regs */
 107        mtspr   LCTRL2, r0
 108        mtspr   COUNTA, r0
 109        mtspr   COUNTB, r0
 110
 111        /* Reset the caches                                                     */
 112        /*----------------------------------------------------------------------*/
 113
 114        mfspr   r3, IC_CST              /* Clear error bits */
 115        mfspr   r3, DC_CST
 116
 117        lis     r3, IDC_UNALL@h         /* Unlock all */
 118        mtspr   IC_CST, r3
 119        mtspr   DC_CST, r3
 120
 121        lis     r3, IDC_INVALL@h        /* Invalidate all */
 122        mtspr   IC_CST, r3
 123        mtspr   DC_CST, r3
 124
 125        lis     r3, IDC_DISABLE@h       /* Disable data cache */
 126        mtspr   DC_CST, r3
 127
 128#if !defined(CONFIG_SYS_DELAYED_ICACHE)
 129                                        /* On IP860 and PCU E,
 130                                         * we cannot enable IC yet
 131                                         */
 132        lis     r3, IDC_ENABLE@h        /* Enable instruction cache */
 133#endif
 134        mtspr   IC_CST, r3
 135
 136        /* invalidate all tlb's                                                 */
 137        /*----------------------------------------------------------------------*/
 138
 139        tlbia
 140        isync
 141
 142        /*
 143         * Calculate absolute address in FLASH and jump there
 144         *----------------------------------------------------------------------*/
 145
 146        lis     r3, CONFIG_SYS_MONITOR_BASE@h
 147        ori     r3, r3, CONFIG_SYS_MONITOR_BASE@l
 148        addi    r3, r3, in_flash - _start + EXC_OFF_SYS_RESET
 149        mtlr    r3
 150        blr
 151
 152in_flash:
 153
 154        /* initialize some SPRs that are hard to access from C                  */
 155        /*----------------------------------------------------------------------*/
 156
 157        lis     r3, CONFIG_SYS_IMMR@h           /* pass IMMR as arg1 to C routine */
 158        ori     r1, r3, CONFIG_SYS_INIT_SP_OFFSET /* set up the stack in internal DPRAM */
 159        /* Note: R0 is still 0 here */
 160        stwu    r0, -4(r1)              /* clear final stack frame so that      */
 161        stwu    r0, -4(r1)              /* stack backtraces terminate cleanly   */
 162
 163        /*
 164         * Disable serialized ifetch and show cycles
 165         * (i.e. set processor to normal mode).
 166         * This is also a silicon bug workaround, see errata
 167         */
 168
 169        li      r2, 0x0007
 170        mtspr   ICTRL, r2
 171
 172        /* Set up debug mode entry */
 173
 174        lis     r2, CONFIG_SYS_DER@h
 175        ori     r2, r2, CONFIG_SYS_DER@l
 176        mtspr   DER, r2
 177
 178        /* let the C-code set up the rest                                       */
 179        /*                                                                      */
 180        /* Be careful to keep code relocatable !                                */
 181        /*----------------------------------------------------------------------*/
 182
 183        GET_GOT                 /* initialize GOT access                        */
 184
 185        /* r3: IMMR */
 186        bl      cpu_init_f      /* run low-level CPU init code     (from Flash) */
 187
 188        bl      board_init_f    /* run 1st part of board init code (from Flash) */
 189
 190        /* NOTREACHED - board_init_f() does not return */
 191
 192
 193        .globl  _start_of_vectors
 194_start_of_vectors:
 195
 196/* Machine check */
 197        STD_EXCEPTION(0x200, MachineCheck, MachineCheckException)
 198
 199/* Data Storage exception.  "Never" generated on the 860. */
 200        STD_EXCEPTION(0x300, DataStorage, UnknownException)
 201
 202/* Instruction Storage exception.  "Never" generated on the 860. */
 203        STD_EXCEPTION(0x400, InstStorage, UnknownException)
 204
 205/* External Interrupt exception. */
 206        STD_EXCEPTION(0x500, ExtInterrupt, external_interrupt)
 207
 208/* Alignment exception. */
 209        . = 0x600
 210Alignment:
 211        EXCEPTION_PROLOG(SRR0, SRR1)
 212        mfspr   r4,DAR
 213        stw     r4,_DAR(r21)
 214        mfspr   r5,DSISR
 215        stw     r5,_DSISR(r21)
 216        addi    r3,r1,STACK_FRAME_OVERHEAD
 217        EXC_XFER_TEMPLATE(Alignment, AlignmentException, MSR_KERNEL, COPY_EE)
 218
 219/* Program check exception */
 220        . = 0x700
 221ProgramCheck:
 222        EXCEPTION_PROLOG(SRR0, SRR1)
 223        addi    r3,r1,STACK_FRAME_OVERHEAD
 224        EXC_XFER_TEMPLATE(ProgramCheck, ProgramCheckException,
 225                MSR_KERNEL, COPY_EE)
 226
 227        /* No FPU on MPC8xx.  This exception is not supposed to happen.
 228        */
 229        STD_EXCEPTION(0x800, FPUnavailable, UnknownException)
 230
 231        /* I guess we could implement decrementer, and may have
 232         * to someday for timekeeping.
 233         */
 234        STD_EXCEPTION(0x900, Decrementer, timer_interrupt)
 235        STD_EXCEPTION(0xa00, Trap_0a, UnknownException)
 236        STD_EXCEPTION(0xb00, Trap_0b, UnknownException)
 237        STD_EXCEPTION(0xc00, SystemCall, UnknownException)
 238        STD_EXCEPTION(0xd00, SingleStep, UnknownException)
 239
 240        STD_EXCEPTION(0xe00, Trap_0e, UnknownException)
 241        STD_EXCEPTION(0xf00, Trap_0f, UnknownException)
 242
 243        /* On the MPC8xx, this is a software emulation interrupt.  It occurs
 244         * for all unimplemented and illegal instructions.
 245         */
 246        STD_EXCEPTION(0x1000, SoftEmu, SoftEmuException)
 247
 248        STD_EXCEPTION(0x1100, InstructionTLBMiss, UnknownException)
 249        STD_EXCEPTION(0x1200, DataTLBMiss, UnknownException)
 250        STD_EXCEPTION(0x1300, InstructionTLBError, UnknownException)
 251        STD_EXCEPTION(0x1400, DataTLBError, UnknownException)
 252
 253        STD_EXCEPTION(0x1500, Reserved5, UnknownException)
 254        STD_EXCEPTION(0x1600, Reserved6, UnknownException)
 255        STD_EXCEPTION(0x1700, Reserved7, UnknownException)
 256        STD_EXCEPTION(0x1800, Reserved8, UnknownException)
 257        STD_EXCEPTION(0x1900, Reserved9, UnknownException)
 258        STD_EXCEPTION(0x1a00, ReservedA, UnknownException)
 259        STD_EXCEPTION(0x1b00, ReservedB, UnknownException)
 260
 261        STD_EXCEPTION(0x1c00, DataBreakpoint, UnknownException)
 262        STD_EXCEPTION(0x1d00, InstructionBreakpoint, DebugException)
 263        STD_EXCEPTION(0x1e00, PeripheralBreakpoint, UnknownException)
 264        STD_EXCEPTION(0x1f00, DevPortBreakpoint, UnknownException)
 265
 266
 267        .globl  _end_of_vectors
 268_end_of_vectors:
 269
 270
 271        . = 0x2000
 272
 273/*
 274 * This code finishes saving the registers to the exception frame
 275 * and jumps to the appropriate handler for the exception.
 276 * Register r21 is pointer into trap frame, r1 has new stack pointer.
 277 */
 278        .globl  transfer_to_handler
 279transfer_to_handler:
 280        stw     r22,_NIP(r21)
 281        lis     r22,MSR_POW@h
 282        andc    r23,r23,r22
 283        stw     r23,_MSR(r21)
 284        SAVE_GPR(7, r21)
 285        SAVE_4GPRS(8, r21)
 286        SAVE_8GPRS(12, r21)
 287        SAVE_8GPRS(24, r21)
 288        mflr    r23
 289        andi.   r24,r23,0x3f00          /* get vector offset */
 290        stw     r24,TRAP(r21)
 291        li      r22,0
 292        stw     r22,RESULT(r21)
 293        mtspr   SPRG2,r22               /* r1 is now kernel sp */
 294        lwz     r24,0(r23)              /* virtual address of handler */
 295        lwz     r23,4(r23)              /* where to go when done */
 296        mtspr   SRR0,r24
 297        mtspr   SRR1,r20
 298        mtlr    r23
 299        SYNC
 300        rfi                             /* jump to handler, enable MMU */
 301
 302int_return:
 303        mfmsr   r28                     /* Disable interrupts */
 304        li      r4,0
 305        ori     r4,r4,MSR_EE
 306        andc    r28,r28,r4
 307        SYNC                            /* Some chip revs need this... */
 308        mtmsr   r28
 309        SYNC
 310        lwz     r2,_CTR(r1)
 311        lwz     r0,_LINK(r1)
 312        mtctr   r2
 313        mtlr    r0
 314        lwz     r2,_XER(r1)
 315        lwz     r0,_CCR(r1)
 316        mtspr   XER,r2
 317        mtcrf   0xFF,r0
 318        REST_10GPRS(3, r1)
 319        REST_10GPRS(13, r1)
 320        REST_8GPRS(23, r1)
 321        REST_GPR(31, r1)
 322        lwz     r2,_NIP(r1)             /* Restore environment */
 323        lwz     r0,_MSR(r1)
 324        mtspr   SRR0,r2
 325        mtspr   SRR1,r0
 326        lwz     r0,GPR0(r1)
 327        lwz     r2,GPR2(r1)
 328        lwz     r1,GPR1(r1)
 329        SYNC
 330        rfi
 331
 332/* Cache functions.
 333*/
 334        .globl  icache_enable
 335icache_enable:
 336        SYNC
 337        lis     r3, IDC_INVALL@h
 338        mtspr   IC_CST, r3
 339        lis     r3, IDC_ENABLE@h
 340        mtspr   IC_CST, r3
 341        blr
 342
 343        .globl  icache_disable
 344icache_disable:
 345        SYNC
 346        lis     r3, IDC_DISABLE@h
 347        mtspr   IC_CST, r3
 348        blr
 349
 350        .globl  icache_status
 351icache_status:
 352        mfspr   r3, IC_CST
 353        srwi    r3, r3, 31      /* >>31 => select bit 0 */
 354        blr
 355
 356        .globl  dcache_enable
 357dcache_enable:
 358#if 0
 359        SYNC
 360#endif
 361#if 1
 362        lis     r3, 0x0400              /* Set cache mode with MMU off */
 363        mtspr   MD_CTR, r3
 364#endif
 365
 366        lis     r3, IDC_INVALL@h
 367        mtspr   DC_CST, r3
 368#if 0
 369        lis     r3, DC_SFWT@h
 370        mtspr   DC_CST, r3
 371#endif
 372        lis     r3, IDC_ENABLE@h
 373        mtspr   DC_CST, r3
 374        blr
 375
 376        .globl  dcache_disable
 377dcache_disable:
 378        SYNC
 379        lis     r3, IDC_DISABLE@h
 380        mtspr   DC_CST, r3
 381        lis     r3, IDC_INVALL@h
 382        mtspr   DC_CST, r3
 383        blr
 384
 385        .globl  dcache_status
 386dcache_status:
 387        mfspr   r3, DC_CST
 388        srwi    r3, r3, 31      /* >>31 => select bit 0 */
 389        blr
 390
 391        .globl  dc_read
 392dc_read:
 393        mtspr   DC_ADR, r3
 394        mfspr   r3, DC_DAT
 395        blr
 396
 397/*
 398 * unsigned int get_immr (unsigned int mask)
 399 *
 400 * return (mask ? (IMMR & mask) : IMMR);
 401 */
 402        .globl  get_immr
 403get_immr:
 404        mr      r4,r3           /* save mask */
 405        mfspr   r3, IMMR        /* IMMR */
 406        cmpwi   0,r4,0          /* mask != 0 ? */
 407        beq     4f
 408        and     r3,r3,r4        /* IMMR & mask */
 4094:
 410        blr
 411
 412        .globl get_pvr
 413get_pvr:
 414        mfspr   r3, PVR
 415        blr
 416
 417
 418        .globl wr_ic_cst
 419wr_ic_cst:
 420        mtspr   IC_CST, r3
 421        blr
 422
 423        .globl rd_ic_cst
 424rd_ic_cst:
 425        mfspr   r3, IC_CST
 426        blr
 427
 428        .globl wr_ic_adr
 429wr_ic_adr:
 430        mtspr   IC_ADR, r3
 431        blr
 432
 433
 434        .globl wr_dc_cst
 435wr_dc_cst:
 436        mtspr   DC_CST, r3
 437        blr
 438
 439        .globl rd_dc_cst
 440rd_dc_cst:
 441        mfspr   r3, DC_CST
 442        blr
 443
 444        .globl wr_dc_adr
 445wr_dc_adr:
 446        mtspr   DC_ADR, r3
 447        blr
 448
 449/*------------------------------------------------------------------------------*/
 450
 451/*
 452 * void relocate_code (addr_sp, gd, addr_moni)
 453 *
 454 * This "function" does not return, instead it continues in RAM
 455 * after relocating the monitor code.
 456 *
 457 * r3 = dest
 458 * r4 = src
 459 * r5 = length in bytes
 460 * r6 = cachelinesize
 461 */
 462        .globl  relocate_code
 463relocate_code:
 464        mr      r1,  r3         /* Set new stack pointer                */
 465        mr      r9,  r4         /* Save copy of Global Data pointer     */
 466        mr      r10, r5         /* Save copy of Destination Address     */
 467
 468        GET_GOT
 469        mr      r3,  r5                         /* Destination Address  */
 470        lis     r4, CONFIG_SYS_MONITOR_BASE@h           /* Source      Address  */
 471        ori     r4, r4, CONFIG_SYS_MONITOR_BASE@l
 472        lwz     r5, GOT(__init_end)
 473        sub     r5, r5, r4
 474        li      r6, CONFIG_SYS_CACHELINE_SIZE           /* Cache Line Size      */
 475
 476        /*
 477         * Fix GOT pointer:
 478         *
 479         * New GOT-PTR = (old GOT-PTR - CONFIG_SYS_MONITOR_BASE) + Destination Address
 480         *
 481         * Offset:
 482         */
 483        sub     r15, r10, r4
 484
 485        /* First our own GOT */
 486        add     r12, r12, r15
 487        /* then the one used by the C code */
 488        add     r30, r30, r15
 489
 490        /*
 491         * Now relocate code
 492         */
 493
 494        cmplw   cr1,r3,r4
 495        addi    r0,r5,3
 496        srwi.   r0,r0,2
 497        beq     cr1,4f          /* In place copy is not necessary       */
 498        beq     7f              /* Protect against 0 count              */
 499        mtctr   r0
 500        bge     cr1,2f
 501
 502        la      r8,-4(r4)
 503        la      r7,-4(r3)
 5041:      lwzu    r0,4(r8)
 505        stwu    r0,4(r7)
 506        bdnz    1b
 507        b       4f
 508
 5092:      slwi    r0,r0,2
 510        add     r8,r4,r0
 511        add     r7,r3,r0
 5123:      lwzu    r0,-4(r8)
 513        stwu    r0,-4(r7)
 514        bdnz    3b
 515
 516/*
 517 * Now flush the cache: note that we must start from a cache aligned
 518 * address. Otherwise we might miss one cache line.
 519 */
 5204:      cmpwi   r6,0
 521        add     r5,r3,r5
 522        beq     7f              /* Always flush prefetch queue in any case */
 523        subi    r0,r6,1
 524        andc    r3,r3,r0
 525        mr      r4,r3
 5265:      dcbst   0,r4
 527        add     r4,r4,r6
 528        cmplw   r4,r5
 529        blt     5b
 530        sync                    /* Wait for all dcbst to complete on bus */
 531        mr      r4,r3
 5326:      icbi    0,r4
 533        add     r4,r4,r6
 534        cmplw   r4,r5
 535        blt     6b
 5367:      sync                    /* Wait for all icbi to complete on bus */
 537        isync
 538
 539/*
 540 * We are done. Do not return, instead branch to second part of board
 541 * initialization, now running from RAM.
 542 */
 543
 544        addi    r0, r10, in_ram - _start + EXC_OFF_SYS_RESET
 545        mtlr    r0
 546        blr
 547
 548in_ram:
 549
 550        /*
 551         * Relocation Function, r12 point to got2+0x8000
 552         *
 553         * Adjust got2 pointers, no need to check for 0, this code
 554         * already puts a few entries in the table.
 555         */
 556        li      r0,__got2_entries@sectoff@l
 557        la      r3,GOT(_GOT2_TABLE_)
 558        lwz     r11,GOT(_GOT2_TABLE_)
 559        mtctr   r0
 560        sub     r11,r3,r11
 561        addi    r3,r3,-4
 5621:      lwzu    r0,4(r3)
 563        cmpwi   r0,0
 564        beq-    2f
 565        add     r0,r0,r11
 566        stw     r0,0(r3)
 5672:      bdnz    1b
 568
 569        /*
 570         * Now adjust the fixups and the pointers to the fixups
 571         * in case we need to move ourselves again.
 572         */
 573        li      r0,__fixup_entries@sectoff@l
 574        lwz     r3,GOT(_FIXUP_TABLE_)
 575        cmpwi   r0,0
 576        mtctr   r0
 577        addi    r3,r3,-4
 578        beq     4f
 5793:      lwzu    r4,4(r3)
 580        lwzux   r0,r4,r11
 581        cmpwi   r0,0
 582        add     r0,r0,r11
 583        stw     r4,0(r3)
 584        beq-    5f
 585        stw     r0,0(r4)
 5865:      bdnz    3b
 5874:
 588clear_bss:
 589        /*
 590         * Now clear BSS segment
 591         */
 592        lwz     r3,GOT(__bss_start)
 593        lwz     r4,GOT(__bss_end__)
 594
 595        cmplw   0, r3, r4
 596        beq     6f
 597
 598        li      r0, 0
 5995:
 600        stw     r0, 0(r3)
 601        addi    r3, r3, 4
 602        cmplw   0, r3, r4
 603        bne     5b
 6046:
 605
 606        mr      r3, r9          /* Global Data pointer          */
 607        mr      r4, r10         /* Destination Address          */
 608        bl      board_init_r
 609
 610        /*
 611         * Copy exception vector code to low memory
 612         *
 613         * r3: dest_addr
 614         * r7: source address, r8: end address, r9: target address
 615         */
 616        .globl  trap_init
 617trap_init:
 618        mflr    r4                      /* save link register           */
 619        GET_GOT
 620        lwz     r7, GOT(_start)
 621        lwz     r8, GOT(_end_of_vectors)
 622
 623        li      r9, 0x100               /* reset vector always at 0x100 */
 624
 625        cmplw   0, r7, r8
 626        bgelr                           /* return if r7>=r8 - just in case */
 6271:
 628        lwz     r0, 0(r7)
 629        stw     r0, 0(r9)
 630        addi    r7, r7, 4
 631        addi    r9, r9, 4
 632        cmplw   0, r7, r8
 633        bne     1b
 634
 635        /*
 636         * relocate `hdlr' and `int_return' entries
 637         */
 638        li      r7, .L_MachineCheck - _start + EXC_OFF_SYS_RESET
 639        li      r8, Alignment - _start + EXC_OFF_SYS_RESET
 6402:
 641        bl      trap_reloc
 642        addi    r7, r7, 0x100           /* next exception vector        */
 643        cmplw   0, r7, r8
 644        blt     2b
 645
 646        li      r7, .L_Alignment - _start + EXC_OFF_SYS_RESET
 647        bl      trap_reloc
 648
 649        li      r7, .L_ProgramCheck - _start + EXC_OFF_SYS_RESET
 650        bl      trap_reloc
 651
 652        li      r7, .L_FPUnavailable - _start + EXC_OFF_SYS_RESET
 653        li      r8, SystemCall - _start + EXC_OFF_SYS_RESET
 6543:
 655        bl      trap_reloc
 656        addi    r7, r7, 0x100           /* next exception vector        */
 657        cmplw   0, r7, r8
 658        blt     3b
 659
 660        li      r7, .L_SingleStep - _start + EXC_OFF_SYS_RESET
 661        li      r8, _end_of_vectors - _start + EXC_OFF_SYS_RESET
 6624:
 663        bl      trap_reloc
 664        addi    r7, r7, 0x100           /* next exception vector        */
 665        cmplw   0, r7, r8
 666        blt     4b
 667
 668        mtlr    r4                      /* restore link register        */
 669        blr
 670