Direct Mapped Cache simulation
Here are some things that may help you improve your program.

Use parentheses to clarify expressions

The code contains this line:

return (1 &lt;&lt; num_of_bits)-1 &lt;&lt; pos;


A programmer reading it might wonder which of the following is the correct interpretation:

return ((1 &lt;&lt; num_of_bits)-1) &lt;&lt; pos;
return (1 &lt;&lt; num_of_bits)-(1 &lt;&lt; pos);


To interpret correctly (the first one), one would have to remember that according to C operator precedence, the - operator has higher precedence than the &lt;&lt; operator.  We can easily avoid this potential confusion by simply writing the code with parentheses, even though, technically, they're redunandant.

Eliminate unused variables

The load_to_cache function includes unused parameter offset.  If it's not used, it probably should be removed.

Be wary of plain char

Whether char is signed or unsigned is implementation defined.  I'd suggest instead using uint8_t from &lt;stdint.h&gt; to make sure that there aren't any surprises when doing right shifts.  A short example shows the difference:

#include &lt;stdio.h&gt;
int main() {
    signed char a = -1;
    unsigned char b = -1;
    printf("%x\n", a&gt;&gt;2);    // prints ffffffff 
    printf("%x\n", b&gt;&gt;2);    // prints 3f
}


For similar reasons, most of the places the code is using int should probably be either unsigned or size_t.

Use a struct where appropriate

The cache is just memory, of course, but with a simulation, the bytes have structure and meaning.  The code has these three lines:

cache[set][0] = 1;
cache[set][1] = tag;
cache[set][2] = memory[memory_index][0];
cache[set][3] = memory[memory_index][1];


This suggests the following struct definition:

#define CACHESIZE 2
typedef struct cacheline_s {
    uint8_t valid;
    uint8_t tag;
    uint8_t mem[CACHESIZE];
} cacheline;


Now the lines above become this:

cache[set].valid = 1;
cache[set].tag = tag;
cache[set].mem[0] = memory[memory_index][0];
cache[set].mem[1] = memory[memory_index][1];


Reconsider the interfaces

Instead of this:

if (!is_valid(cache, set, tag)) {


I think it might make more sense to pass only a single cache line.  Using the struct defined above, the function now looks like this:

int is_valid(const cacheline *cache, int tag) {
    return cache-&gt;valid &amp;&amp; (cache-&gt;tag == tag);
}


Note too that we're passing a const pointer, advertising the fact that the underlying cacheline will not be changed by this function.  A similar interface change could be made to load_to_cache.

Eliminate "magic numbers"

This code has a number of inscrutable "magic numbers," that is, unnamed constants such as 2, 4, 8, etc.  Generally it's better to avoid that and give such constants meaningful names.  That way, if anything ever needs to be changed, you won't have to go hunting through the code for all instances of "2" and then trying to determine if this particular 2 is relevant to the desired change or if it is some other constant that happens to have the same value.  This is especially beneficial in cases like this in which some numbers depend on others.  For example, we can use these to make sure that the two constants are easily changed and kept consistent.  Only the first variable needs to be touched.

#define CACHESIZE_BITS 1
#define CACHESIZE (1u &lt;&lt; CACHESIZE_BITS)