I need some pretty generic help?

I need some pretty generic help?

• Friday, May 11, 2012 1:25 AM

My problem is simple, but yet complex because I do not understand it completely. Basically, I'm writing an application that needs to know the mathematical relationship between a MIPS I instruction and an address. The instruction is the simple Jump and Link instruction. I've been pondering it for a while and keep hitting dead ends. I'm thinking it has something to do with the opcode but not entirely sure. Basically, if the user enters: jal 0x000a2000 the hexadecimal output should be something like 0x0c028800. But I'm not sure as to how to achieve this mathematically. So any help on this is appreciated. I have decent programming skills and actively switch back and forth between C# and C++. I'm using a custom class called Helpers to perform math, check for illegal characters and so on. Here are the two mathematical methods I've created to perform the math required in the application so far:

```public class Helpers {
public static string ToHex(int NumberToConvert) { return NumberToConvert.ToString("x"); }
public static int ToDecimal(string Hex) { return Convert.ToInt32(Hex, 16); }
}```

There is no need to check for illegal characters in the methods because there is an outside method that does it before hand. However, I'm needing to know as I stated before the mathematical relationship between the instruction and the address it links to.

Thanks,

Jamie

Programmer (noun) ["proh - grahm - er"]: A human being that can turn caffeine into code.

All Replies

• Friday, May 11, 2012 2:40 AM

See, take example you provided (jal 0x000a2000):

1) Convert target to 32-bit integer (00000000000010100010000000000000 when expressed in binary form)
2) As per instructions for conversion, only bit 27 to 2 is taken. Therefore we need to apply mask 0x3FFFFFC(i.e.: 00000011111111111111111111111100 in binary form) by bitwise AND (&) operator to target, then perform right shift by 2 bits. (0x000a2000 & 0x3FFFFFC) >> 2 = 0x28800
3) Apply OP code 0x0C000000 (i.e. 00001100000000000000000000000000) to it by bitwise OR (|) operator. ((0x000a2000 & 0x3FFFFFC) >> 2) | 0x0C000000 = 0x0C028800

```        public static void MIPSTest()
{
int opcode = 0x0C000000;
}```

• Edited by Friday, May 11, 2012 2:46 AM
• Edited by Friday, May 11, 2012 2:49 AM
• Edited by Friday, May 11, 2012 2:53 AM
•
• Friday, May 11, 2012 2:59 AM

Although be not understand very much, I mean concern
• Friday, May 11, 2012 3:08 AM

Okay so another quick question, in a simple format; how would I approach this with the other instructions? Do I get the opcodes and do a similar thing? I'm sure I can play around with that sample until I get the desired effects, but I just want to make sure there isn't something special to each instruction mathematically.

Programmer (noun) ["proh - grahm - er"]: A human being that can turn caffeine into code.

• Friday, May 11, 2012 3:10 AM

Also, what is .PadLeft(8, '0') doing? Is it just ensuring that the data is 8 characters in length at all times?

Programmer (noun) ["proh - grahm - er"]: A human being that can turn caffeine into code.

• Friday, May 11, 2012 3:14 AM

Also, here is the list of formats I have:

 R opcode (6) rs (5) rt (5) rd (5) shamt (5) funct (6) I opcode (6) rs (5) rt (5) immediate (16) J opcode (6) address (26)

Could you show a quick example of the same thing but for 'add' and 'lui'? That would be greatly appreciated. The reason I ask is because add uses registers, and lui uses a register and an immediate. Also 'lw' would be appreciated if it's not too much to ask. That way it would make it easier to convert the remaining 'a lot' of instructions left. LOL :)

lui t0 0x0000

lw t1 0x0000(t0)

Thanks,

Jamie

P.S. - If you can't just reply back, and let me know, and I'll mark the thread as solved. :)

EDIT: I just found, that by changing the opcode from 0x0c000000 to 0x08000000 it gives me the data for the simple jump instruction. I just noticed the link in your text so I'm going to go read that, and hopefully it helps, if so I'll mark as solved.

Programmer (noun) ["proh - grahm - er"]: A human being that can turn caffeine into code.

• Friday, May 11, 2012 3:21 AM

Also, what is .PadLeft(8, '0') doing? Is it just ensuring that the data is 8 characters in length at all times?

Yes, so that you can directly relate the displayed result to the answer you provided.

Also, here is the list of formats I have:

 R opcode (6) rs (5) rt (5) rd (5) shamt (5) funct (6) I opcode (6) rs (5) rt (5) immediate (16) J opcode (6) address (26)

Could you show a quick example of the same thing but for 'add' and 'lui'? That would be greatly appreciated. :)

I can, but perheps you'll provide me the sample operands be used for the "add" and "lui" command, so I can produce sensible output from it.

• Friday, May 11, 2012 3:44 AM

By operands I'm guessing you mean the parameters (registers and immediates?). If so, then just a sample like the following:

addiu t0, t0 0x0001 = 0x24020001 // incorrect data, sorry; it's 0x25082000. 0x24020001 is addiu v0, zero 0x0001.

lui t0 0x000a = 0x3c08000a

addiu t0, t0 0x2000 = 0x25082000

I'm just figuring out what the hex opcodes are and am writing them all down. But the registers I'm still unclear on how to find them which hopefully your next sample will provide some clarity.

For the sample, just providing for assumed t0 register usage is fine.

EDIT:

All I can do so far is write down like this:

0x24 0x8 0x8 0x0001

0x3c 0x8 0x000a

0x24 0x8 0x8 0x2000

That's all I've learned so far from the link you provided. I've figured out a little more about how it works by playing with my assembler and a calculator, but still not that much further yet. I'm still unsure as to how addiu works. The opcode is 0x24, and if I use two identical registers (0x8 - t0) then use an immediate of 0x2000 then the data comes out as 0x25080000. Where if I switch the registers from t1, t1 it becomes 0x25292000 and I'm unsure as to why. Addition of 0x8 and 0x8 would be 0x10 in hex. So how is it getting the value of 0x2529?

Programmer (noun) ["proh - grahm - er"]: A human being that can turn caffeine into code.

• Friday, May 11, 2012 4:14 AM

Emmm... From the resulting code you provided, I'm assuming the compiler uses register \$r8 to store t0.

```            const int mask_5bit = 0x1F;
const int mask_16bit_i = 0xFFFC;    // 16-bit mask with lower 2 bits dropped
/*
{
int opcode_r = 0x0;
int rs = 0x0;
int rt = 0x0;
int rd = 0x1;
int sa = 0x0;   // for R type instructions, unused fields required to fill with 0
Console.WriteLine("0x" + (((opcode_r & mask_5bit) << 26) | ((rs & mask_5bit) << 21) |
((rt & mask_5bit) << 16) | ((rd & mask_5bit) << 11) | ((sa & mask_5bit) << 6) |
}
*/
{
int rs = 0x08;
int rt = 0x08;
int immediate = 0x2000;
}
{
int opcode_lui = 0x3C000000;
int rs = 0x0;   // ignored, fill with 0
int rt = 0x08;
int immediate = 0x000A;
Console.WriteLine("lui \$r8, 0x000a = 0x" + (opcode_lui | ((rs & mask_5bit) << 21) | ((rt & mask_5bit) << 16) | (immediate & mask_16bit_i)).ToString("X").PadLeft(8, '0'));
}```
However I have doubt (as you can see my output is different from you). From the instruction, immediate field needs to have two low order bits dropped in order to keep word alignment. Therefore 0x000A should have become 0x0008 with the lowest 2 bits zeroed out. You may want to check with others.

For 0x2529, you can write it as 001001 01001 01001, so you can see it really means register \$r9.
• Edited by Friday, May 11, 2012 4:21 AM
•
• Friday, May 11, 2012 4:23 AM

This will give me plenty to study up on. I just could understand your code. Just two more questions and then I promise I'll leave you be. :)

How did you get the 6-bit and 16-bit masks?

Also, how do you know when to shift left or right?

Thanks once again for your help, and I'll mark the thread as solved when you reply. :)

Programmer (noun) ["proh - grahm - er"]: A human being that can turn caffeine into code.

• Friday, May 11, 2012 4:34 AM

How did you get the 5-bit and 16-bit masks?

00011111 => 0x1F

1111111111111111 => 0xFFFF

It's really that straight forward. :)

Also, how do you know when to shift left or right?

It's also by counting bits

Take "I" opcode for example.

Rightmost operand needs no position adjustment.
The second operand need to shift left by 16 bits, so it needs "<< 16".
The first operand needs to shift by another 5 bits, hence "<< (16 + 5)" => "<< 21".
For opcode it should need another 5 bits shift, but since it's likely to be a constant value, it's better to apply the shift to the value directly.

• Saturday, May 12, 2012 1:47 AM

I took the liberty of getting a lot of the opcodes (not including the co-processors) and plugging them into a dll, anyone in the future that reads this that doesn't want to do the math for the opcodes or anything else here's a list of them.

```        // Masks
public const int Mask5Bit = 0x1f;
public const int Mask16Bit = 0xfffc;
public const int Mask26Bit = 0x3fffffc;

// R Types
public const int SllOpcode = 0x00000000;
public const int SrlOpcode = 0x00000002;
public const int SraOpcode = 0x00000003;
public const int SllvOpcode = 0x00000004;
public const int SravOpcode = 0x00000007;
public const int JrOpcode = 0x00000008;
public const int JalrOpcode = 0x00000009;
public const int SyscallOpcode = 0x0000000c;
public const int BreakOpcode = 0x0000000d;
public const int MfhiOpcode = 0x00000010;
public const int MthiOpcode = 0x00000011;
public const int MfloOpcode = 0x00000012;
public const int MtloOpcode = 0x00000013;
public const int MultOpcode = 0x00000018;
public const int MultuOpcode = 0x00000019;
public const int DivOpcode = 0x0000001a;
public const int DivuOpcode = 0x0000001b;
public const int AddOpcode = 0x00000020;
public const int AdduOpcode = 0x00000021;
public const int SubOpcode = 0x00000022;
public const int SubuOpcode = 0x00000023;
public const int AndOpcode = 0x00000024;
public const int OrOpcode = 0x00000025;
public const int XorOpcode = 0x00000026;
public const int NorOpcode = 0x00000027;
public const int SltOpcode = 0x0000002a;
public const int SltuOpcode = 0x0000002b;

// I Types
public const int BltzOpcode = 0x04000000;
public const int BgezOpcode = 0x04010000;
public const int BeqOpcode = 0x10000000;
public const int BneOpcode = 0x14000000;
public const int BlezOpcode = 0x18000000;
public const int BgtzOpcode = 0x1c000000;
public const int AddiOpcode = 0x20000000;
public const int AddiuOpcode = 0x24000000;
public const int SltiOpcode = 0x28000000;
public const int AndiOpcode = 0x30000000;
public const int OriOpcode = 0x34000000;
public const int XoriOpcode = 0x38000000;
public const int LuiOpcode = 0x3c000000;
public const uint LbOpcode = 0x80000000;
public const uint LhOpcode = 0x84000000;
public const uint LwOpcode = 0x8c000000;
public const uint LbuOpcode = 0x90000000;
public const uint LhuOpcode = 0x94000000;
public const uint SbOpcode = 0xa0000000;
public const uint ShOpcode = 0xa4000000;
public const uint SwOpcode = 0xac000000;
public const uint Lwc1Opcode = 0xc4000000;
public const uint Swc1Opcode = 0xe4000000;

// J Types
public const int JOpcode = 0x08000000;
public const int JalOpcode = 0x0c000000;```

That's what I came up with in the end that is. Good luck to the future programmers that use it. Also if some are incorrect then I apologize. I tried my best. :)

Programmer (noun) ["proh - grahm - er"]: A human being that can turn caffeine into code.

• Saturday, May 12, 2012 3:02 AM

Btw, for R Type of instructions, there's only 1 Opcode (0x000000). The instruction for "add" or else should be named "function code" as in the commented "R Type" section shown in my code.

Think about the similar case of good old DOS interrupt calls. For the function to terminate program, INT 21h is DOS function call, setting register AH to 0x4C is to select function 4Ch.

And you're right that the opcodes (and the completed instructions too) should be in uint instead of int.

• Edited by Saturday, May 12, 2012 3:05 AM
•