How to extract values in bits from a register and

2020-05-07 10:45发布

问题:

I need to extract the value in bits 22 down to 4 from register $t0, compute the half of this value (assumed unsigned integer), and use the new value to replace bits 24 down to 6 in register $t1 without changing the others bits in $t1. The value isn't given but I don't think that should be an issue. There are 32 bits per register

I'm a bit new to MIPs so I'm not sure how to go about solving this problem

回答1:

I've broken down the invidual steps of the algorithm. I then created a C prototype/test program. And, finally, I created a mips asm program.

Those are the steps I would recommend for you [or anybody else], particularly when getting started.

Writing the code in C first [or at least prototyping it in C-like pseudo code], makes debugging of the algorithm easier.

From the C program, I was able to create test data for the mips program. The test data includes the solution that the C program got.

The mips program would treat this as a pass/fail diagnostic. This might seem like overkill, but in the first version of the asm program, there was a bug. I had used a wrong register in the srlv instruction's third argument and the diagnostic actually failed the tests (e.g. I used $t0 instead of $t1).


Terminology:

This is the matchup of the terms:

xval:   $t0
xhi:    22
xlo:    4

yval:   $t1
yhi:    24
ylo:    6

Extract step (1):

Given a value xval, whenever you see "extract bits xhi to xlo", the first thing to do is right shift xval by xlo:

xshf = xval >> xlo

But now, xshf still has the bits that were to the left of xhi, so we need to mask them.


Formulas:

The bit width of an "inclusive" bit range is:

wid = (hi - lo) + 1

So, the "right justified" mask is:

rmsk = (0xFFFFFFFF >> (32 - wid))

The "left justified" mask is:

lmsk = (rmsk << lo)

Note: The above equations work dynamically [as above]. Or, if we have fixed numbers, we can calculate the final values by hand to produce constants.


Extract step (2):

So, to get the isolated value for the bit range, we now apply the mask:

xshf &= xrmsk

This is the first part of the problem. We've isolated the necessary bits, so they are now in the rightmost bits of xshf (i.e. bits (xwid-1) to 0).


Extract step (3):

The above was for general bit extraction. The specific problem requires that we divide by 2:

xshf >>= 1

Merge step (1):

The second part of the problem is that we have to apply the xshf to the target value yval.

yval has a different range: yhi to ylo. We apply the formulas to get the appropriate values for yval.

We must clear out the old bits in yval:

yval &= ~ylmsk

Merge step (2):

We need to do two things to xshf:

  1. "clean up" xshf so that only the correct bits in yval get modified
  2. Shift xshf into the correct position for merging with yval

We can accomplish this in one of two ways. The following two sequences are equivalent:

xshf &= yrmsk
xshf <<= ylo

Or:

xshf <<= ylo
xshf &= ylmsk

Note: Because the bit widths in the specific problem are the same, the masking step can be eliminated (i.e. xshf had already been masked by xrmsk and it is the same as yrmsk. So, either masking step is not required).


Merge step (3):

Now, we merge in the shifted value:

yval |= xshf

That's it--we're done ...


Prototype:

Here's some C code that combines all this.

Because the bit range values are fixed, the mask values are precalculated once. If they had to vary, the mask code would have to recalculate them on each merge call.

Also note that the masks are constants because the ranges are fixed.

#include <stdio.h>
#include <stdlib.h>

int opt_v;

typedef unsigned int u32;

u32 xval;
#define XHI     22
#define XLO     4

u32 yval;
#define YHI     24
#define YLO     6

u32 xrmsk;
u32 xlmsk;
u32 yrmsk;
u32 ylmsk;

#define WID(_hi,_lo)    (((_hi) - (_lo)) + 1)
#define RMSK(_hi,_lo)   (0xFFFFFFFF >> (32 - WID(_hi,_lo)))
#define LMSK(_hi,_lo)   (RMSK(_hi,_lo) << (_lo))

#define SHOW(_sym) \
    show(_sym,#_sym)

#define SHOWX(_sym) \
    if (opt_v) \
        SHOW(_sym)

void
show(u32 val,const char *sym)
{
    u32 bitmsk;
    u32 bitval;

    printf("%8.8X ",val);

    for (bitmsk = 1u << 31;  bitmsk != 0;  bitmsk >>= 1) {
        bitval = val & bitmsk;
        printf("%d",bitval ? 1 : 0);
    }

    printf(" %s\n",sym);
}

void
merge(void)
{
    u32 xshf;

    SHOW(xval);
    SHOW(yval);

    // extract (1):
    xshf = xval >> XLO;
    SHOW(xshf);

    // extract (2):
    SHOWX(xrmsk);
    xshf &= xrmsk;
    SHOW(xshf);

    // extract (3):
    xshf >>= 1;
    SHOW(xshf);

    // merge (1):
    SHOWX(yrmsk);
    SHOWX(ylmsk);
    yval &= ~ylmsk;
    SHOW(yval);

    // merge (2):
    xshf &= yrmsk;  // belt and ...
    SHOW(xshf);
    xshf <<= YLO;
    SHOW(xshf);
    xshf &= ylmsk;  // ... suspenders
    SHOW(xshf);

    // merge (3)
    yval |= xshf;
    SHOW(yval);
}

void
test(u32 y,u32 x)
{

    printf("\n");
    yval = y;
    xval = x;
    merge();
}

// main -- main program
int
main(int argc,char **argv)
{
    char *cp;

    --argc;
    ++argv;

    for (;  argc > 0;  --argc, ++argv) {
        cp = *argv;
        if (*cp != '-')
            break;

        switch (cp[1]) {
        case 'v':
            opt_v = ! opt_v;
            break;

        default:
            break;
        }
    }

    xrmsk = RMSK(XHI,XLO);
    SHOW(xrmsk);

    xlmsk = LMSK(XHI,XLO);
    SHOW(xlmsk);

    yrmsk = RMSK(YHI,YLO);
    SHOW(yrmsk);

    ylmsk = LMSK(YHI,YLO);
    SHOW(ylmsk);

    test(0,~0);
    test(~0,0);

    for (int idx = 0;  idx <= 5;  ++idx)
        test(rand(),rand());

    return 0;
}

Output:

0007FFFF 00000000000001111111111111111111 xrmsk
007FFFF0 00000000011111111111111111110000 xlmsk
0007FFFF 00000000000001111111111111111111 yrmsk
01FFFFC0 00000001111111111111111111000000 ylmsk

FFFFFFFF 11111111111111111111111111111111 xval
00000000 00000000000000000000000000000000 yval
0FFFFFFF 00001111111111111111111111111111 xshf
0007FFFF 00000000000001111111111111111111 xshf
0003FFFF 00000000000000111111111111111111 xshf
00000000 00000000000000000000000000000000 yval
0003FFFF 00000000000000111111111111111111 xshf
00FFFFC0 00000000111111111111111111000000 xshf
00FFFFC0 00000000111111111111111111000000 xshf
00FFFFC0 00000000111111111111111111000000 yval

00000000 00000000000000000000000000000000 xval
FFFFFFFF 11111111111111111111111111111111 yval
00000000 00000000000000000000000000000000 xshf
00000000 00000000000000000000000000000000 xshf
00000000 00000000000000000000000000000000 xshf
FE00003F 11111110000000000000000000111111 yval
00000000 00000000000000000000000000000000 xshf
00000000 00000000000000000000000000000000 xshf
00000000 00000000000000000000000000000000 xshf
FE00003F 11111110000000000000000000111111 yval

6B8B4567 01101011100010110100010101100111 xval
327B23C6 00110010011110110010001111000110 yval
06B8B456 00000110101110001011010001010110 xshf
0000B456 00000000000000001011010001010110 xshf
00005A2B 00000000000000000101101000101011 xshf
32000006 00110010000000000000000000000110 yval
00005A2B 00000000000000000101101000101011 xshf
00168AC0 00000000000101101000101011000000 xshf
00168AC0 00000000000101101000101011000000 xshf
32168AC6 00110010000101101000101011000110 yval

643C9869 01100100001111001001100001101001 xval
66334873 01100110001100110100100001110011 yval
0643C986 00000110010000111100100110000110 xshf
0003C986 00000000000000111100100110000110 xshf
0001E4C3 00000000000000011110010011000011 xshf
66000033 01100110000000000000000000110011 yval
0001E4C3 00000000000000011110010011000011 xshf
007930C0 00000000011110010011000011000000 xshf
007930C0 00000000011110010011000011000000 xshf
667930F3 01100110011110010011000011110011 yval

74B0DC51 01110100101100001101110001010001 xval
19495CFF 00011001010010010101110011111111 yval
074B0DC5 00000111010010110000110111000101 xshf
00030DC5 00000000000000110000110111000101 xshf
000186E2 00000000000000011000011011100010 xshf
1800003F 00011000000000000000000000111111 yval
000186E2 00000000000000011000011011100010 xshf
0061B880 00000000011000011011100010000000 xshf
0061B880 00000000011000011011100010000000 xshf
1861B8BF 00011000011000011011100010111111 yval

2AE8944A 00101010111010001001010001001010 xval
625558EC 01100010010101010101100011101100 yval
02AE8944 00000010101011101000100101000100 xshf
00068944 00000000000001101000100101000100 xshf
000344A2 00000000000000110100010010100010 xshf
6200002C 01100010000000000000000000101100 yval
000344A2 00000000000000110100010010100010 xshf
00D12880 00000000110100010010100010000000 xshf
00D12880 00000000110100010010100010000000 xshf
62D128AC 01100010110100010010100010101100 yval

238E1F29 00100011100011100001111100101001 xval
46E87CCD 01000110111010000111110011001101 yval
0238E1F2 00000010001110001110000111110010 xshf
0000E1F2 00000000000000001110000111110010 xshf
000070F9 00000000000000000111000011111001 xshf
4600000D 01000110000000000000000000001101 yval
000070F9 00000000000000000111000011111001 xshf
001C3E40 00000000000111000011111001000000 xshf
001C3E40 00000000000111000011111001000000 xshf
461C3E4D 01000110000111000011111001001101 yval

3D1B58BA 00111101000110110101100010111010 xval
507ED7AB 01010000011111101101011110101011 yval
03D1B58B 00000011110100011011010110001011 xshf
0001B58B 00000000000000011011010110001011 xshf
0000DAC5 00000000000000001101101011000101 xshf
5000002B 01010000000000000000000000101011 yval
0000DAC5 00000000000000001101101011000101 xshf
0036B140 00000000001101101011000101000000 xshf
0036B140 00000000001101101011000101000000 xshf
5036B16B 01010000001101101011000101101011 yval

MIPS program:

This is set up for the mars simulator. In particular, the syscall 34 to print a value in hex only exists in mars and not in spim. So, either get mars: http://courses.missouristate.edu/KenVollmar/mars/ or [using spim] change the 34 to 1 to print in decimal

    # these work for the mars simulator
    .eqv    XHI         22
    .eqv    XLO         4
    .eqv    YHI         24
    .eqv    YLO         6

    # these work for the spim simulator
    ###XHI  =   22
    ###XLO  =   4
    ###YHI  =   24
    ###YLO  =   6

    .data
testdata:
    .word 0xFFFFFFFF,0x00000000,0x00FFFFC0
    .word 0x00000000,0xFFFFFFFF,0xFE00003F
    .word 0x6B8B4567,0x327B23C6,0x32168AC6
    .word 0x643C9869,0x66334873,0x667930F3
    .word 0x74B0DC51,0x19495CFF,0x1861B8BF
    .word 0x2AE8944A,0x625558EC,0x62D128AC
    .word 0x238E1F29,0x46E87CCD,0x461C3E4D
    .word 0x3D1B58BA,0x507ED7AB,0x5036B16B
edata:

msg_nl:     .asciiz     "\n"

msg_xrmsk:  .asciiz     "xrmsk "
msg_xlmsk:  .asciiz     "xlmsk "
msg_yrmsk:  .asciiz     "yrmsk "
msg_ylmsk:  .asciiz     "ylmsk "

msg_xval:   .asciiz     "xval  "
msg_yval:   .asciiz     "yval  "

msg_pass:   .asciiz     "pass  "
msg_fail:   .asciiz     "FAIL  "
msg_ans:    .asciiz     "answ  "

    .text
    .globl  main

main:
    jal     setup2

    la      $s6,testdata
    la      $s7,edata

main_loop:
    jal     test
    addiu   $s6,$s6,12
    blt     $s6,$s7,main_loop

    li      $v0,10
    syscall

# test -- test the merge
test:
    subu    $sp,$sp,4
    sw      $ra,0($sp)

    li      $v0,4
    la      $a0,msg_nl
    syscall

    lw      $t0,0($s6)                  # get source value
    lw      $t1,4($s6)                  # get destination value
    lw      $t2,8($s6)                  # get solution value

    # print the X value
    la      $a0,msg_xval
    move    $a1,$t0
    jal     print

    # print the Y value
    la      $a0,msg_yval
    move    $a1,$t1
    jal     print

    # do the operation
    jal     merge
    bne     $t1,$t2,test_fail

    la      $a0,msg_pass
    move    $a1,$t1
    jal     print
    j       test_done

test_fail:
    la      $a0,msg_fail
    move    $a1,$t1
    jal     print

    # print the result
    la      $a0,msg_ans
    move    $a1,$t2
    jal     print

test_done:
    lw      $ra,0($sp)
    addu    $sp,$sp,4
    jr      $ra

# print -- print number
#
# arguments:
#   a0 -- string
#   a1 -- value
print:
    # output the string
    li      $v0,4
    syscall

    li      $v0,34                      # syscall print hex
    move    $a0,$a1
    syscall

    li      $v0,4
    la      $a0,msg_nl
    syscall

    jr      $ra

# merge -- merge the data
#
# arguments:
#   t0 -- source value
#   t1 -- target value
#
# registers:
#   t7 -- xshf
#   t6 -- ~ylmsk
merge:
    srl     $t7,$t0,XLO                 # E1: xshf = xval >> xlo
    and     $t7,$t7,$s0                 # E2: xshf &= xrmsk
    srl     $t7,$t7,1                   # E3: xshf >>= 1

    not     $t6,$s3                     # M1: get ~ylmsk
    and     $t1,$t1,$t6                 # M1: yval &= ~ylmsk
    sll     $t7,$t7,YLO                 # M2: xshf <<= ylo
    and     $t7,$t7,$s3                 # M2: xshf &= ylmsk
    or      $t1,$t1,$t7                 # M3: yval |= xshf

    jr      $ra                         # return

# setup2 -- set up the mask values
#
# RETURNS:
#   s0 -- xrmsk
#   s1 -- xlmsk
#   s2 -- yrmsk
#   s3 -- ylmsk
#
# registers:
#   a0 -- hi bit value
#   a1 -- lo bit value
setup2:
    subu    $sp,$sp,4
    sw      $ra,0($sp)

    # set up the xval masks
    li      $a0,XHI
    li      $a1,XLO
    jal     setup1
    move    $s0,$v0
    move    $s1,$v1

    la      $a0,msg_xrmsk
    move    $a1,$s0
    jal     print

    la      $a0,msg_xlmsk
    move    $a1,$s1
    jal     print

    # set up the yval masks
    li      $a0,YHI
    li      $a1,YLO
    jal     setup1
    move    $s2,$v0
    move    $s3,$v1

    la      $a0,msg_yrmsk
    move    $a1,$s2
    jal     print

    la      $a0,msg_ylmsk
    move    $a1,$s3
    jal     print

    lw      $ra,0($sp)
    addu    $sp,$sp,4
    jr      $ra                     # return

# setup1 -- set up the mask values
#
# RETURNS:
#   v0 -- rmsk
#   v1 -- lmsk
#
# arguments:
#   a0 -- hi bit value
#   a1 -- lo bit value
#
# registers:
#   t0 -- wid
#   t1 -- 32 - wid
setup1:
    sub     $t0,$a0,$a1             # wid = hi - lo
    addi    $t0,$t0,1               # wid += 1

    li      $v0,0xFFFFFFFF          # rmsk = 0xFFFFFFFF

    # get 32 - wid
    li      $t1,32
    sub     $t1,$t1,$t0

    srlv    $v0,$v0,$t1             # rmsk >>= (32 - wid)
    sllv    $v1,$v0,$a1             # lmsk = rmsk << lo

    jr      $ra                     # return


标签: mips mips32