| /* |
| * (C) Copyright 2002 |
| * Daniel Engström, Omicron Ceti AB <daniel@omicron.se>. |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| * MA 02111-1307 USA |
| */ |
| |
| /* This file is largely based on code obtned from AMD. AMD's original |
| * copyright is included below |
| */ |
| |
| /* TITLE SIZER - Aspen DRAM Sizing Routine. |
| * ============================================================================= |
| * |
| * Copyright 1999 Advanced Micro Devices, Inc. |
| * You may redistribute this program and/or modify this program under the terms |
| * of the GNU General Public License as published by the Free Software Foundation; |
| * either version 2 of the License, or (at your option) any later version. |
| * |
| * This program is distributed WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED |
| * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * this program; if not, write to the Free Software Foundation, Inc., |
| * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
| * |
| * THE MATERIALS ARE PROVIDED "AS IS" WITHOUT ANY EXPRESS OR IMPLIED WARRANTY |
| * OF ANY KIND INCLUDING WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT OF |
| * THIRD-PARTY INTELLECTUAL PROPERTY, OR FITNESS FOR ANY PARTICULAR PURPOSE. |
| * IN NO EVENT SHALL AMD OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER |
| * (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, BUSINESS |
| * INTERRUPTION, LOSS OF INFORMATION) ARISING OUT OF THE USE OF OR INABILITY |
| * TO USE THE MATERIALS, EVEN IF AMD HAS BEEN ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGES. BECAUSE SOME JURSIDICTIONS PROHIBIT THE EXCLUSION OR |
| * LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE |
| * LIMITATION MAY NOT APPLY TO YOU. |
| * |
| * AMD does not assume any responsibility for any errors that may appear in |
| * the Materials nor any responsibility to support or update the Materials. |
| * AMD retains the right to make changes to its test specifications at any |
| * time, without notice. |
| * ============================================================================== |
| */ |
| |
| /* |
| ****************************************************************************** |
| * |
| * FILE : sizer.asm - SDRAM DIMM Sizing Algorithm |
| * |
| * |
| * |
| * FUNCTIONS : sizemem() - jumped to, not called. To be executed after |
| * reset to determine the size of the SDRAM DIMMs. Initializes |
| * the memory subsystem. |
| * |
| * |
| * AUTHOR : Buddy Fey - Original. |
| * |
| * |
| * DESCRIPTION : Performs sizing on SDRAM DIMMs on ASPEN processor. |
| * NOTE: This is a small memory model version |
| * |
| * |
| * INPUTS : BP contains return address offset |
| * CACHE is assumed to be disabled. |
| * The FS segment limit has already been set to big real mode |
| * (full 32-bit addressing capability) |
| * |
| * |
| * OUTPUTS : None |
| * |
| * |
| * REG USE : ax,bx,cx,dx,di,si,bp, fs |
| * |
| * |
| * REVISION : See PVCS info below |
| * |
| * |
| * TEST PLAN CROSS REFERENCE: |
| * |
| * |
| * $Workfile: $ |
| * $Revision: 1.2 $ |
| * $Date: 1999/09/22 12:49:33 $ |
| * $Author: chipf $ |
| * $Log: sizer.asm $ |
| * Revision 1.2 1999/09/22 12:49:33 chipf |
| * Add legal header |
| * |
| ******************************************************************************* |
| */ |
| |
| |
| /******************************************************************************* |
| * FUNCTIONAL DESCRIPTION: |
| * This routine is called to autodetect the geometry of the DRAM. |
| * |
| * This routine is called to determine the number of column bits for the DRAM |
| * devices in this external bank. This routine assumes that the external bank |
| * has been configured for an 11-bit column and for 4 internal banks. This gives |
| * us the maximum address reach in memory. By writing a test value to the max |
| * address and locating where it aliases to, we can determine the number of valid |
| * column bits. |
| * |
| * This routine is called to determine the number of internal banks each DRAM |
| * device has. The external bank (under test) is configured for maximum reach |
| * with 11-bit columns and 4 internal banks. This routine will write to a max |
| * address (BA1 and BA0 = 1) and then read from an address with BA1=0 to see if |
| * that column is a "don't care". If BA1 does not affect write/read of data, |
| * then this device has only 2 internal banks. |
| * |
| * This routine is called to determine the ending address for this external |
| * bank of SDRAM. We write to a max address with a data value and then disable |
| * row address bits looking for "don't care" locations. Each "don't care" bit |
| * represents a dividing of the maximum density (128M) by 2. By dividing the |
| * maximum of 32 4M chunks in an external bank down by all the "don't care" bits |
| * determined during sizing, we set the proper density. |
| * |
| * WARNINGS. |
| * bp must be preserved because it is used for return linkage. |
| * |
| * EXIT |
| * nothing returned - but the memory subsystem is enabled |
| ******************************************************************************* |
| */ |
| |
| #include <config.h> |
| |
| .section .text |
| .equ DRCCTL, 0x0fffef010 /* DRAM control register */ |
| .equ DRCTMCTL, 0x0fffef012 /* DRAM timing control register */ |
| .equ DRCCFG, 0x0fffef014 /* DRAM bank configuration register */ |
| .equ DRCBENDADR, 0x0fffef018 /* DRAM bank ending address register */ |
| .equ ECCCTL, 0x0fffef020 /* DRAM ECC control register */ |
| .equ ECCINT, 0x0fffefd18 /* DRAM ECC nmi-INT mapping */ |
| .equ DBCTL, 0x0fffef040 /* DRAM buffer control register */ |
| |
| .equ CACHELINESZ, 0x00000010 /* size of our cache line (read buffer) */ |
| .equ COL11_ADR, 0x0e001e00 /* 11 col addrs */ |
| .equ COL10_ADR, 0x0e000e00 /* 10 col addrs */ |
| .equ COL09_ADR, 0x0e000600 /* 9 col addrs */ |
| .equ COL08_ADR, 0x0e000200 /* 8 col addrs */ |
| .equ ROW14_ADR, 0x0f000000 /* 14 row addrs */ |
| .equ ROW13_ADR, 0x07000000 /* 13 row addrs */ |
| .equ ROW12_ADR, 0x03000000 /* 12 row addrs */ |
| .equ ROW11_ADR, 0x01000000 /* 11 row addrs/also bank switch */ |
| .equ ROW10_ADR, 0x00000000 /* 10 row addrs/also bank switch */ |
| .equ COL11_DATA, 0x0b0b0b0b /* 11 col addrs */ |
| .equ COL10_DATA, 0x0a0a0a0a /* 10 col data */ |
| .equ COL09_DATA, 0x09090909 /* 9 col data */ |
| .equ COL08_DATA, 0x08080808 /* 8 col data */ |
| .equ ROW14_DATA, 0x3f3f3f3f /* 14 row data (MASK) */ |
| .equ ROW13_DATA, 0x1f1f1f1f /* 13 row data (MASK) */ |
| .equ ROW12_DATA, 0x0f0f0f0f /* 12 row data (MASK) */ |
| .equ ROW11_DATA, 0x07070707 /* 11 row data/also bank switch (MASK) */ |
| .equ ROW10_DATA, 0xaaaaaaaa /* 10 row data/also bank switch (MASK) */ |
| |
| |
| /* |
| * initialize dram controller registers |
| */ |
| .globl mem_init |
| mem_init: |
| xorw %ax,%ax |
| movl $DBCTL, %edi |
| movb %al, (%edi) /* disable write buffer */ |
| |
| movl $ECCCTL, %edi |
| movb %al, (%edi) /* disable ECC */ |
| |
| movl $DRCTMCTL, %edi |
| movb $0x1E,%al /* Set SDRAM timing for slowest */ |
| movb %al, (%edi) |
| |
| /* |
| * setup loop to do 4 external banks starting with bank 3 |
| */ |
| movl $0xff000000,%eax /* enable last bank and setup */ |
| movl $DRCBENDADR, %edi /* ending address register */ |
| movl %eax, (%edi) |
| |
| movl $DRCCFG, %edi /* setup */ |
| movw $0xbbbb,%ax /* dram config register for */ |
| movw %ax, (%edi) |
| |
| /* |
| * issue a NOP to all DRAMs |
| */ |
| movl $DRCCTL, %edi /* setup DRAM control register with */ |
| movb $0x1,%al /* Disable refresh,disable write buffer */ |
| movb %al, (%edi) |
| movl $CACHELINESZ, %esi /* just a dummy address to write for */ |
| movw %ax, (%esi) |
| /* |
| * delay for 100 usec? 200? |
| * ******this is a cludge for now ************* |
| */ |
| movw $100,%cx |
| sizdelay: |
| loop sizdelay /* we need 100 usec here */ |
| /***********************************************/ |
| |
| /* |
| * issue all banks precharge |
| */ |
| movb $0x2,%al /* All banks precharge */ |
| movb %al, (%edi) |
| movw %ax, (%esi) |
| |
| /* |
| * issue 2 auto refreshes to all banks |
| */ |
| movb $0x4,%al /* Auto refresh cmd */ |
| movb %al, (%edi) |
| movw $2,%cx |
| refresh1: |
| movw %ax, (%esi) |
| loop refresh1 |
| |
| /* |
| * issue LOAD MODE REGISTER command |
| */ |
| movb $0x3,%al /* Load mode register cmd */ |
| movb %al, (%edi) |
| movw %ax, (%esi) |
| |
| /* |
| * issue 8 more auto refreshes to all banks |
| */ |
| movb $0x4,%al /* Auto refresh cmd */ |
| movb %al, (%edi) |
| movw $8,%cx |
| refresh2: |
| movw %ax, (%esi) |
| loop refresh2 |
| |
| /* |
| * set control register to NORMAL mode |
| */ |
| movb $0x0,%al /* Normal mode value */ |
| movb %al, (%edi) |
| |
| /* |
| * size dram starting with external bank 3 moving to external bank 0 |
| */ |
| movl $0x3,%ecx /* start with external bank 3 */ |
| |
| nextbank: |
| |
| /* |
| * write col 11 wrap adr |
| */ |
| movl $COL11_ADR, %esi /* set address to max col (11) wrap addr */ |
| movl $COL11_DATA, %eax /* pattern for max supported columns(11) */ |
| movl %eax, (%esi) /* write max col pattern at max col adr */ |
| movl (%esi), %ebx /* optional read */ |
| cmpl %ebx,%eax /* to verify write */ |
| jnz bad_ram /* this ram is bad */ |
| /* |
| * write col 10 wrap adr |
| */ |
| |
| movl $COL10_ADR, %esi /* set address to 10 col wrap address */ |
| movl $COL10_DATA, %eax /* pattern for 10 col wrap */ |
| movl %eax, (%esi) /* write 10 col pattern @ 10 col wrap adr */ |
| movl (%esi), %ebx /* optional read */ |
| cmpl %ebx,%eax /* to verify write */ |
| jnz bad_ram /* this ram is bad */ |
| /* |
| * write col 9 wrap adr |
| */ |
| movl $COL09_ADR, %esi /* set address to 9 col wrap address */ |
| movl $COL09_DATA, %eax /* pattern for 9 col wrap */ |
| movl %eax, (%esi) /* write 9 col pattern @ 9 col wrap adr */ |
| movl (%esi), %ebx /* optional read */ |
| cmpl %ebx,%eax /* to verify write */ |
| jnz bad_ram /* this ram is bad */ |
| /* |
| * write col 8 wrap adr |
| */ |
| movl $COL08_ADR, %esi /* set address to min(8) col wrap address */ |
| movl $COL08_DATA, %eax /* pattern for min (8) col wrap */ |
| movl %eax, (%esi) /* write min col pattern @ min col adr */ |
| movl (%esi), %ebx /* optional read */ |
| cmpl %ebx,%eax /* to verify write */ |
| jnz bad_ram /* this ram is bad */ |
| /* |
| * write row 14 wrap adr |
| */ |
| movl $ROW14_ADR, %esi /* set address to max row (14) wrap addr */ |
| movl $ROW14_DATA, %eax /* pattern for max supported rows(14) */ |
| movl %eax, (%esi) /* write max row pattern at max row adr */ |
| movl (%esi), %ebx /* optional read */ |
| cmpl %ebx,%eax /* to verify write */ |
| jnz bad_ram /* this ram is bad */ |
| /* |
| * write row 13 wrap adr |
| */ |
| movl $ROW13_ADR, %esi /* set address to 13 row wrap address */ |
| movl $ROW13_DATA, %eax /* pattern for 13 row wrap */ |
| movl %eax, (%esi) /* write 13 row pattern @ 13 row wrap adr */ |
| movl (%esi), %ebx /* optional read */ |
| cmpl %ebx,%eax /* to verify write */ |
| jnz bad_ram /* this ram is bad */ |
| /* |
| * write row 12 wrap adr |
| */ |
| movl $ROW12_ADR, %esi /* set address to 12 row wrap address */ |
| movl $ROW12_DATA, %eax /* pattern for 12 row wrap */ |
| movl %eax, (%esi) /* write 12 row pattern @ 12 row wrap adr */ |
| movl (%esi), %ebx /* optional read */ |
| cmpl %ebx,%eax /* to verify write */ |
| jnz bad_ram /* this ram is bad */ |
| /* |
| * write row 11 wrap adr |
| */ |
| movl $ROW11_ADR, %edi /* set address to 11 row wrap address */ |
| movl $ROW11_DATA, %eax /* pattern for 11 row wrap */ |
| movl %eax, (%edi) /* write 11 row pattern @ 11 row wrap adr */ |
| movl (%edi), %ebx /* optional read */ |
| cmpl %ebx,%eax /* to verify write */ |
| jnz bad_ram /* this ram is bad */ |
| /* |
| * write row 10 wrap adr --- this write is really to determine number of banks |
| */ |
| movl $ROW10_ADR, %edi /* set address to 10 row wrap address */ |
| movl $ROW10_DATA, %eax /* pattern for 10 row wrap (AA) */ |
| movl %eax, (%edi) /* write 10 row pattern @ 10 row wrap adr */ |
| movl (%edi), %ebx /* optional read */ |
| cmpl %ebx,%eax /* to verify write */ |
| jnz bad_ram /* this ram is bad */ |
| /* |
| * read data @ row 12 wrap adr to determine * banks, |
| * and read data @ row 14 wrap adr to determine * rows. |
| * if data @ row 12 wrap adr is not AA, 11 or 12 we have bad RAM. |
| * if data @ row 12 wrap == AA, we only have 2 banks, NOT 4 |
| * if data @ row 12 wrap == 11 or 12, we have 4 banks, |
| */ |
| xorw %di,%di /* value for 2 banks in DI */ |
| movl (%esi), %ebx /* read from 12 row wrap to check banks |
| * (esi is setup from the write to row 12 wrap) */ |
| cmpl %ebx,%eax /* check for AA pattern (eax holds the aa pattern) */ |
| jz only2 /* if pattern == AA, we only have 2 banks */ |
| |
| /* 4 banks */ |
| |
| movw $8,%di /* value for 4 banks in DI (BNK_CNT bit) */ |
| cmpl $ROW11_DATA, %ebx /* only other legitimate values are 11 */ |
| jz only2 |
| cmpl $ROW12_DATA, %ebx /* and 12 */ |
| jnz bad_ram /* its bad if not 11 or 12! */ |
| |
| /* fall through */ |
| only2: |
| /* |
| * validate row mask |
| */ |
| movl $ROW14_ADR, %esi /* set address back to max row wrap addr */ |
| movl (%esi), %eax /* read actual number of rows @ row14 adr */ |
| |
| cmpl $ROW11_DATA, %eax /* row must be greater than 11 pattern */ |
| jb bad_ram |
| |
| cmpl $ROW14_DATA, %eax /* and row must be less than 14 pattern */ |
| ja bad_ram |
| |
| cmpb %ah,%al /* verify all 4 bytes of dword same */ |
| jnz bad_ram |
| movl %eax,%ebx |
| shrl $16,%ebx |
| cmpw %bx,%ax |
| jnz bad_ram |
| /* |
| * read col 11 wrap adr for real column data value |
| */ |
| movl $COL11_ADR, %esi /* set address to max col (11) wrap addr */ |
| movl (%esi), %eax /* read real col number at max col adr */ |
| /* |
| * validate column data |
| */ |
| cmpl $COL08_DATA, %eax /* col must be greater than 8 pattern */ |
| jb bad_ram |
| |
| cmpl $COL11_DATA, %eax /* and row must be less than 11 pattern */ |
| ja bad_ram |
| |
| subl $COL08_DATA, %eax /* normalize column data to zero */ |
| jc bad_ram |
| cmpb %ah,%al /* verify all 4 bytes of dword equal */ |
| jnz bad_ram |
| movl %eax,%edx |
| shrl $16,%edx |
| cmpw %dx,%ax |
| jnz bad_ram |
| /* |
| * merge bank and col data together |
| */ |
| addw %di,%dx /* merge of bank and col info in dl */ |
| /* |
| * fix ending addr mask based upon col info |
| */ |
| movb $3,%al |
| subb %dh,%al /* dh contains the overflow from the bank/col merge */ |
| movb %bl,%dh /* bl contains the row mask (aa, 07, 0f, 1f or 3f) */ |
| xchgw %cx,%ax /* cx = ax = 3 or 2 depending on 2 or 4 bank device */ |
| shrb %cl,%dh /* */ |
| incb %dh /* ending addr is 1 greater than real end */ |
| xchgw %cx,%ax /* cx is bank number again */ |
| /* |
| * issue all banks precharge |
| */ |
| bad_reint: |
| movl $DRCCTL, %esi /* setup DRAM control register with */ |
| movb $0x2,%al /* All banks precharge */ |
| movb %al, (%esi) |
| movl $CACHELINESZ, %esi /* address to init read buffer */ |
| movw %ax, (%esi) |
| |
| /* |
| * update ENDING ADDRESS REGISTER |
| */ |
| movl $DRCBENDADR, %edi /* DRAM ending address register */ |
| movl %ecx,%ebx |
| addl %ebx, %edi |
| movb %dh, (%edi) |
| /* |
| * update CONFIG REGISTER |
| */ |
| xorb %dh,%dh |
| movw $0x00f,%bx |
| movw %cx,%ax |
| shlw $2,%ax |
| xchgw %cx,%ax |
| shlw %cl,%dx |
| shlw %cl,%bx |
| notw %bx |
| xchgw %cx,%ax |
| movl $DRCCFG, %edi |
| mov (%edi), %ax |
| andw %bx,%ax |
| orw %dx,%ax |
| movw %ax, (%edi) |
| jcxz cleanup |
| |
| decw %cx |
| movl %ecx,%ebx |
| movl $DRCBENDADR, %edi /* DRAM ending address register */ |
| movb $0xff,%al |
| addl %ebx, %edi |
| movb %al, (%edi) |
| /* |
| * set control register to NORMAL mode |
| */ |
| movl $DRCCTL, %esi /* setup DRAM control register with */ |
| movb $0x0,%al /* Normal mode value */ |
| movb %al, (%esi) |
| movl $CACHELINESZ, %esi /* address to init read buffer */ |
| movw %ax, (%esi) |
| jmp nextbank |
| |
| cleanup: |
| movl $DRCBENDADR, %edi /* DRAM ending address register */ |
| movw $4,%cx |
| xorw %ax,%ax |
| cleanuplp: |
| movb (%edi), %al |
| orb %al,%al |
| jz emptybank |
| |
| addb %ah,%al |
| jns nottoomuch |
| |
| movb $0x7f,%al |
| nottoomuch: |
| movb %al,%ah |
| orb $0x80,%al |
| movb %al, (%edi) |
| emptybank: |
| incl %edi |
| loop cleanuplp |
| |
| #if defined CONFIG_SYS_SDRAM_DRCTMCTL |
| /* just have your hardware desinger _GIVE_ you what you need here! */ |
| movl $DRCTMCTL, %edi |
| movb $CONFIG_SYS_SDRAM_DRCTMCTL,%al |
| movb (%edi), %al |
| #else |
| #if defined(CONFIG_SYS_SDRAM_CAS_LATENCY_2T) || defined(CONFIG_SYS_SDRAM_CAS_LATENCY_3T) |
| /* set the CAS latency now since it is hard to do |
| * when we run from the RAM */ |
| movl $DRCTMCTL, %edi /* DRAM timing register */ |
| movb (%edi), %al |
| #ifdef CONFIG_SYS_SDRAM_CAS_LATENCY_2T |
| andb $0xef, %al |
| #endif |
| #ifdef CONFIG_SYS_SDRAM_CAS_LATENCY_3T |
| orb $0x10, %al |
| #endif |
| movb %al, (%edi) |
| #endif |
| #endif |
| movl $DRCCTL, %edi /* DRAM Control register */ |
| movb $0x3,%al /* Load mode register cmd */ |
| movb %al, (%edi) |
| movw %ax, (%esi) |
| |
| |
| movl $DRCCTL, %edi /* DRAM Control register */ |
| movb $0x18,%al /* Enable refresh and NORMAL mode */ |
| movb %al, (%edi) |
| |
| jmp dram_done |
| |
| bad_ram: |
| xorl %edx,%edx |
| xorl %edi,%edi |
| jmp bad_reint |
| |
| dram_done: |
| |
| #if CONFIG_SYS_SDRAM_ECC_ENABLE |
| /* |
| * We are in the middle of an existing 'call' - Need to store the |
| * existing return address before making another 'call' |
| */ |
| movl %ebp, %ebx |
| |
| /* Get the memory size */ |
| movl $init_ecc, %ebp |
| jmpl get_mem_size |
| |
| init_ecc: |
| /* Restore the orignal return address */ |
| movl %ebx, %ebp |
| |
| /* A nominal memory test: just a byte at each address line */ |
| movl %eax, %ecx |
| shrl $0x1, %ecx |
| movl $0x1, %edi |
| memtest0: |
| movb $0xa5, (%edi) |
| cmpb $0xa5, (%edi) |
| jne out |
| shrl $1, %ecx |
| andl %ecx,%ecx |
| jz set_ecc |
| shll $1, %edi |
| jmp memtest0 |
| |
| set_ecc: |
| /* clear all ram with a memset */ |
| movl %eax, %ecx |
| xorl %esi, %esi |
| xorl %edi, %edi |
| xorl %eax, %eax |
| shrl $2, %ecx |
| cld |
| rep stosl |
| /* enable read, write buffers */ |
| movb $0x11, %al |
| movl $DBCTL, %edi |
| movb %al, (%edi) |
| /* enable NMI mapping for ECC */ |
| movl $ECCINT, %edi |
| mov $0x10, %al |
| movb %al, (%edi) |
| /* Turn on ECC */ |
| movl $ECCCTL, %edi |
| mov $0x05, %al |
| movb %al, (%edi) |
| #endif |
| |
| out: |
| jmp *%ebp |
| |
| /* |
| * Read and decode the sc520 DRCBENDADR MMCR and return the number of |
| * available ram bytes in %eax |
| */ |
| .globl get_mem_size |
| get_mem_size: |
| movl $DRCBENDADR, %edi /* DRAM ending address register */ |
| |
| bank0: movl (%edi), %eax |
| movl %eax, %ecx |
| andl $0x00000080, %ecx |
| jz bank1 |
| andl $0x0000007f, %eax |
| shll $22, %eax |
| movl %eax, %ebx |
| |
| bank1: movl (%edi), %eax |
| movl %eax, %ecx |
| andl $0x00008000, %ecx |
| jz bank2 |
| andl $0x00007f00, %eax |
| shll $14, %eax |
| movl %eax, %ebx |
| |
| bank2: movl (%edi), %eax |
| movl %eax, %ecx |
| andl $0x00800000, %ecx |
| jz bank3 |
| andl $0x007f0000, %eax |
| shll $6, %eax |
| movl %eax, %ebx |
| |
| bank3: movl (%edi), %eax |
| movl %eax, %ecx |
| andl $0x80000000, %ecx |
| jz done |
| andl $0x7f000000, %eax |
| shrl $2, %eax |
| movl %eax, %ebx |
| |
| done: |
| movl %ebx, %eax |
| jmp *%ebp |