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
:
- "clean up"
xshf
so that only the correct bits in yval
get modified
- 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