Lab: Bit Manipulation¶
Overview¶
This hands-on lab session marks the transition from RISC-V assembly to RISC-V machine code. Before we can decode 32-bit instruction words by hand and in C, we need fluency with the bitwise operators. The session opens with Project03 interactive grading (IG) logistics and a careful diagram of the famous rstr null-terminator bug, then steps back to the course "big picture" before drilling into the core skill: manipulating individual bits with ~, &, |, ^, and the three shift operators (SLL, SRL, SRA) in both C and RISC-V. Work each example by hand in binary — by the end of the semester these truth tables and shift behaviors should be automatic.
Learning Objectives¶
- Trace the
rstr(reverse-string) bug and explain why it accidentally "worked" because of an adjacent string's null terminator - Place this lab in the course arc: data representation → assembly → machine code → emulator
- Distinguish bitwise operators (
~ & | ^ << >>) from relational/logical operators (&& || !) - Recall the truth tables for AND, OR, XOR, and NOT from memory
- Convert between binary and decimal using bit positions and place values in a byte and a 32-bit word
- Apply Shift Left Logical (SLL), Shift Right Logical (SRL), and Shift Right Arithmetic (SRA), and predict the fill bits
- Connect arithmetic shifts to two's complement and signed division by powers of two
- Map each C bitwise operator to its RISC-V instruction (
and/andi,or/ori,xor/xori,sll/slli,srl/srli,sra/srai)
Prerequisites¶
- Number bases: binary, decimal, hexadecimal, and the
0b/0xC literal prefixes (Lab04, Project01) - Two's complement representation of signed integers (Project03)
- C fundamentals:
uint8_t/uint32_t/int32_ttypes, arrays, strings, pointers (Project01) - RISC-V assembly basics: registers, instructions, function calling conventions (Lab02, Project02, Project03)
- The
argv/argclayout in memory (Project01)
1. Project03 Interactive Grading (IG)¶
Project03 was graded interactively: you sit with a grader, run your code, and explain it. The point is not to catch you — it is to confirm you understand the code you submitted.
- TAs grade most students; the instructor reviews for consistency and fairness.
- Using AI tools (ChatGPT, Copilot, etc.) is allowed, but you must be able to explain every line. During IG you will be asked:
- Design decisions — "Why did you do it this way?"
- Implementation walk-through — "Show me how
rstr_recworks." - Debugging — "What problems did you hit, and how did you find them?"
- Tooling — terminal editing (micro/vim) and single-stepping with
gdb.
- For Project04, a different group of students grades with the instructor, so everyone gets direct instructor interaction over the term.
The Project03 rubric (see /assignments/project03/) is 80 points automated tests + 20 points IG questions. The takeaway for this lab: be ready to reason about your code at the bit and byte level, which is exactly the skill we sharpen below.
2. The rstr Null-Terminator Bug¶
One of the most instructive moments of the session was diagramming a subtle bug in the reverse-string function. The function appeared to work for years, but only by accident.
What rstr is supposed to do¶
Reverse a C string from a source buffer src into a destination buffer dst. Recall a C string is an array of char terminated by a null byte '\0' (value 0).
// Reverse src into dst. len = strlen(src).
void rstr(char *dst, char *src, int len) {
int s = len - 1; // last real character of src
int d = 0; // first slot of dst
while (/* ??? termination condition ??? */) {
dst[d] = src[s];
d = d + 1;
s = s - 1;
}
dst[d] = '\0'; // terminate dst
}
For src = "FooBar", len = 6, indices are:
We copy src[5]='r' → dst[0], src[4]='a' → dst[1], and so on, walking s down toward 0.
The diagram from class¶
dst: | r | a | B | o | o | f | <- being filled left-to-right
^ ^ ^
d=0 d=1 d=2 ...
src: | f | o | o | B | a | r | \0 |
idx: 0 1 2 3 4 5 6
^ start here (s = len-1 = 5)
walk s downward: 5,4,3,2,1,0
len = 6
The bug¶
The buggy termination condition tested for the source's null terminator while scanning downward:
Walking backward from index len-1, you never hit src's own '\0' (it sits at index len, which you started past). So what stops the loop? When s decrements below 0, src[s] reads memory before src. Often that adjacent memory happens to be the null terminator of a previously stored string (e.g., another argv entry), so the loop coincidentally stops — but this is undefined behavior and depends entirely on memory layout.
Why adjacent strings matter — the argv picture¶
Command-line argument strings are packed contiguously in memory. For ./foo bar, the bytes for "foo" and "bar" sit next to each other, each with its own '\0'. A red brace in the notes highlighted that src points into this packed region, and the '\0' that "accidentally" stopped the loop belonged to a neighboring string.
high addr
| \0 |
| r | <-+
| a | | "bar" (src points here)
| b | <-+
| \0 | <- the null that "accidentally" stops a backward scan
| o | <-+
| o | | "foo"
| f | <-+
| ...|
argv[2] | |
argv[1] | |
argv[0] | |
low addr
The correct fix¶
Do not rely on memory you do not own. Stop when the source index goes below zero:
void rstr(char *dst, char *src, int len) {
int s = len - 1;
int d = 0;
while (s >= 0) { // CORRECT: stop when we run off the front
dst[d] = src[s];
d = d + 1;
s = s - 1;
}
dst[d] = '\0'; // always terminate the destination
}
Exam tip from class: understand both the bug and the fix pattern. The lesson generalizes — never let a loop read memory it has no guaranteed claim to, and choose a termination condition based on your own indices, not on incidental neighboring data.
3. Big Picture — Where This Lab Fits¶
Greg sketched the course as two converging tracks. The left track is the software/representation track; the right track is the hardware track. They meet at the machine.
flowchart TD
C["C / Data Representation"] --> ASM["RISC-V Assembly"]
ASM --> MC["RISC-V Machine Code (rv_emu)"]
DD["Digital Design"] --> PD["Processor Design"]
MC --> PD
style MC fill:#f9f,stroke:#333,stroke-width:2px
- We have covered data representation (bases, two's complement) and RISC-V assembly (Projects 02–03).
- Next we move to RISC-V machine code: 32-bit instruction words that we will decode and execute in software. That software is
rv_emu, an emulator written in C. - On the hardware side, digital design leads to processor design — eventually you run the same machine code on a processor you helped design.
Why bits now? Decoding a machine instruction means slicing a 32-bit word into fields (opcode, rd, rs1, funct3, …). That is pure bit manipulation: shift the field down, then mask off the bits you do not want. Master the operators here and the emulator becomes straightforward.
4. Bitwise vs. Relational Operators¶
A common source of bugs: confusing & with &&, or | with ||. They are different operators with different jobs.
| Category | C operators | What they do | Operate on |
|---|---|---|---|
| Bitwise | ~ & \| ^ << >> |
Manipulate individual bits, position by position | Whole bit patterns |
| Relational / Logical | && \|\| ! == < > |
Produce a true/false (0/1) result for control flow | Truth values |
The class table contrasted them side by side:
bitwise ops relational / logical
~ (not) && and
>> right shift || or
<< left shift ! not
& and
| or
^ xor
Rule of thumb:
- Use bitwise when you care about the bits: masking, packing, decoding instructions.
- Use logical when you care about a condition:
if (x && y).
// Bitwise: result is a bit pattern
uint8_t flags = 0b1100;
uint8_t mask = 0b0100;
uint8_t bit = flags & mask; // 0b0100 (the bit, in place)
// Logical: result is 0 or 1
int ready = (count > 0) && (error == 0); // 1 if both true
5. Binary, Bytes, and Words¶
The two values of a bit¶
A single bit is either 0 or 1. These two states go by many names; learn to read all of them:
0 |
1 |
|---|---|
| false | true |
| unset / clear | set |
| low | high |
| off | on |
A byte = 8 bits¶
Bits within a byte are numbered 7 (most significant) down to 0 (least significant). Each position has a place value that is a power of two.
bit: 7 6 5 4 3 2 1 0
1 1 0 1 1 0 0 1
value:128 64 32 16 8 4 2 1
^ ^
most significant least significant
bit (MSB) bit (LSB)
Converting that byte to decimal — add the place values where the bit is 1:
So 0b11011001 = 217.
A word = 4 bytes = 32 bits¶
A 32-bit value (a "word") is four bytes laid out from bit 31 down to bit 0. Knowing the byte boundaries is essential for instruction decoding.
| Byte | Bit range |
|---|---|
| byte 3 | 31–24 |
| byte 2 | 23–16 |
| byte 1 | 15–8 |
| byte 0 | 7–0 |
This layout is exactly how we will carve a 32-bit RISC-V instruction word into fields later.
6. The Four Bitwise Logical Operators¶
Each operator is applied positionally: bit i of the result depends only on bit i of the inputs. Memorize these truth tables — they are with you for the rest of the semester.
AND (&)¶
1 only when both inputs are 1.
| a | b | a & b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
OR (|) — inclusive or¶
1 when either input is 1.
| a | b | a | b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |
XOR (^) — exclusive or¶
1 when the inputs differ.
| a | b | a ^ b |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
Useful XOR identities: a ^ a = 0, a ^ 0 = a, and a ^ 0xFF flips all 8 bits (a cheap NOT for a byte).
NOT (~) — bitwise complement¶
Inverts every bit. (This is unary — one operand.)
| a | ~a |
|---|---|
| 0 | 1 |
| 1 | 0 |
Watch the distinction:
~is bitwise NOT (flips all bits);!is logical NOT (turns nonzero into 0, and 0 into 1).
Worked example (from class)¶
Declare two bytes and compute all four operators:
AND — keep a 1 only where both have a 1:
NOT of a — flip every bit:
OR — a 1 where either has a 1:
XOR — a 1 only where they differ:
Running C confirms it:
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint8_t a = 0b11001010; // 202
uint8_t b = 0b10011001; // 153
printf("a&b = 0x%02X\n", a & b); // 0x88
printf("~a = 0x%02X\n", (uint8_t)~a); // 0x35
printf("a|b = 0x%02X\n", a | b); // 0xDB
printf("a^b = 0x%02X\n", a ^ b); // 0x53
return 0;
}
RISC-V equivalents¶
Each operator maps to a register-register instruction (R-type) and, where it makes sense, an immediate form (I-type):
| C operator | RISC-V (reg) | RISC-V (immediate) |
|---|---|---|
& |
and rd, rs1, rs2 |
andi rd, rs1, imm |
\| |
or rd, rs1, rs2 |
ori rd, rs1, imm |
^ |
xor rd, rs1, rs2 |
xori rd, rs1, imm |
~x |
— | xori rd, rs1, -1 (flip all bits) |
# uint64_t r = a & b; a in a0, b in a1, result in a0
and a0, a0, a1
ret
# uint64_t r = ~a; a in a0
xori a0, a0, -1 # XOR with all-ones inverts every bit
ret
RISC-V has no dedicated NOT instruction; not rd, rs is a pseudo-instruction the assembler expands to xori rd, rs, -1.
7. Shift Operations¶
Shifting moves every bit left or right by a fixed number of positions. The two operands are the value to shift and the shift amount.
There are three shifts. The difference is entirely in what fills the vacated bits.
SLL — Shift Left Logical (<<)¶
Bits move toward the MSB. Vacated low bits are filled with 0; bits that fall off the left are discarded.
a = 0b 1 1 0 0 1 0 1 0 (0xCA)
\ \ \ \ bits move left by 2
a << 2 = 0b 0 0 1 0 1 0 0 0 (0x28)
^ ^
new zeros on the right
Effect: multiply an unsigned value by 2^n. (Bits shifted off the top are lost, so this can overflow.)
SRL — Shift Right Logical (>> on unsigned)¶
Bits move toward the LSB. Vacated high bits are filled with 0; bits that fall off the right are discarded.
a = 0b 1 1 0 0 1 0 1 0 (0xCA)
/ / / / bits move right by 2
a >> 2 = 0b 0 0 1 1 0 0 1 0 (0x32)
^ ^
new zeros on the left
Effect: unsigned divide by 2^n (the discarded low bits are the remainder).
SRA — Shift Right Arithmetic (>> on signed)¶
Same as SRL, except the vacated high bits are filled with copies of the original sign bit (the MSB), preserving the number's sign.
Interpret a as a signed int8_t. Its MSB is 1, so it is negative:
Arithmetic shift right by 2 — fill the top with the sign bit (1):
Effect: signed divide by 2^n, rounding toward negative infinity. Note -54 / 4 = -13.5, and SRA gives -14 (rounds down), whereas C's integer division / rounds toward zero and would give -13. This rounding difference matters.
We can verify the SRA result is -14 using two's complement (negate by invert + 1):
result = 1 1 1 1 0 0 1 0
invert = 0 0 0 0 1 1 0 1
add 1 = 0 0 0 0 1 1 1 0 = 8 + 4 + 2 = 14
so result = -14 ✓
And the original a = -54:
a = 1 1 0 0 1 0 1 0
invert = 0 0 1 1 0 1 0 1
add 1 = 0 0 1 1 0 1 1 0 = 32 + 16 + 4 + 2 = 54
so a = -54 ✓
Picking the right shift in C¶
The shift you get from >> in C depends on the type of the value:
uint8_t u = 0b11001010; // unsigned
int8_t s = 0b11001010; // signed (= -54)
u >> 2; // SRL (logical): 0b00110010 = 50
s >> 2; // SRA (arithmetic): 0b11110010 = -14
- Unsigned type →
>>is logical (SRL). - Signed type →
>>is arithmetic (SRA) on every mainstream compiler. (The C standard technically leaves signed>>implementation-defined, but in practice it is arithmetic; conceptually it preserves the sign.) <<is always logical (fills with 0). Left-shifting a negative signed value is undefined behavior in C — shift unsigned types when you mean to manipulate bits.
Comparison table¶
| Operation | Direction | Vacated bits filled with | Effect | RISC-V |
|---|---|---|---|---|
SLL << |
left | 0 (on the right) |
multiply by 2^n |
sll, slli |
SRL >> (unsigned) |
right | 0 (on the left) |
unsigned divide by 2^n |
srl, srli |
SRA >> (signed) |
right | sign bit (on the left) | signed divide by 2^n |
sra, srai |
RISC-V shift instructions¶
slli a0, a0, 2 # a0 = a0 << 2 (shift amount is an immediate)
srli a0, a0, 2 # a0 = a0 >> 2 logical
srai a0, a0, 2 # a0 = a0 >> 2 arithmetic (sign-preserving)
sll a0, a1, a2 # a0 = a1 << a2 (shift amount in a register)
Hardware aside from class: shifts are implemented with a barrel shifter, a block of digital logic that can shift by any amount in a single step — no looping over one-bit shifts.
8. Masking — Extracting Bit Fields¶
The reason we care so much about shifts and AND is decoding. To pull a field out of a larger word you combine two steps:
- Shift the field down so it starts at bit 0.
- Mask with AND to zero out everything above the field.
The mask for the low w bits is (1 << w) - 1:
w |
(1 << w) - 1 |
binary | hex |
|---|---|---|---|
| 1 | 1 | 0000 0001 |
0x01 |
| 3 | 7 | 0000 0111 |
0x07 |
| 4 | 15 | 0000 1111 |
0x0F |
| 7 | 127 | 0111 1111 |
0x7F |
| 8 | 255 | 1111 1111 |
0xFF |
Example: extract the middle 4 bits (bits 2–5) of a byte¶
uint8_t x = 0b11011010;
uint32_t v = x >> 2; // shift bits 2..5 down to 0..3 -> 0b00110110
v = v & 0b1111; // mask off everything above bit 3 -> 0b0110 = 6
x = 1 1 0 1 1 0 1 0
x >> 2 = 0 0 1 1 0 1 1 0 bits 2-5 are now in positions 0-3
& 0b1111 = 0 0 0 0 0 1 1 0 = 0x6
This is exactly the get_bits(iw, start, count) pattern you will write for the emulator: shift the instruction word right by the field's start bit, then AND with a count-bit mask. Looking ahead (Lab03/Project04), an R-type instruction word is sliced this way:
To get rd (bits 11–7): (iw >> 7) & 0b11111.
Building values: shift + OR¶
The inverse of extraction is assembly of a value from pieces — shift each piece into position and OR them together. (You will do this when reconstructing branch/jump immediates from their scattered bits.)
Sign extension with shifts¶
To sign-extend a narrow two's complement value to 32 bits, shift it fully left so its sign bit lands in bit 31, then arithmetic-shift it back. The SRA fill replicates the sign bit across the upper bits:
int32_t v = 0b1110; // -2 as a 4-bit two's complement value
v = (v << 28) >> 28; // -> 0xFFFFFFFE = -2 in 32 bits
This trick only works because >> on a signed int32_t is arithmetic.
9. Emulators — Looking Ahead¶
The session closed by defining the destination of this whole bit-manipulation effort.
- An emulator is software that simulates the behavior of hardware.
rv_emuis a RISC-V emulator written in C that executes RISC-V machine code. - The emulator holds the processor state in a struct:
#define NREGS 32
#define STACK_SIZE 8192
struct rv_state {
uint64_t regs[NREGS]; // x0..x31
uint64_t pc; // program counter
uint8_t stack[STACK_SIZE];
};
- The fetch-decode-execute cycle is bit manipulation end to end:
- Fetch the 32-bit instruction word:
iw = *pc. - Decode the
opcode(low 7 bits):iw & 0x7F. - Dispatch to a handler (
emu_r_type,emu_i_type, …) that further slicesfunct3,funct7,rd,rs1,rs2with shift + mask. - Execute: update
regsand/or memory. - Advance
pc(usuallypc += 4).
- Fetch the 32-bit instruction word:
Every one of those decode steps is the shift-and-mask pattern from Section 8. The next lecture begins decoding real instructions and building the emulator.
Key Concepts¶
| Concept | Definition | Example |
|---|---|---|
| Bitwise operator | Acts on each bit position independently | 0xCA & 0x99 = 0x88 |
| Relational/logical operator | Produces a 0/1 truth value for control flow | (x > 0) && y |
| Byte | 8 bits, numbered MSB 7 … LSB 0 | 0b11011001 = 217 |
| Word | 32 bits = 4 bytes (byte 3 … byte 0) | bits 31–24, 23–16, 15–8, 7–0 |
AND & |
1 only if both bits 1; used to mask |
0b1010 & 0b1100 = 0b1000 |
OR \| |
1 if either bit 1; used to set bits |
0b1010 \| 0b0100 = 0b1110 |
XOR ^ |
1 if bits differ; used to toggle |
0b1010 ^ 0b1100 = 0b0110 |
NOT ~ |
Inverts every bit | ~0b11001010 = 0b00110101 |
SLL << |
Shift left, fill 0; ×2^n |
0xCA << 2 = 0x28 |
SRL >> |
Logical shift right, fill 0; unsigned ÷2^n |
0xCA >> 2 = 0x32 |
SRA >> |
Arithmetic shift right, fill sign bit; signed ÷2^n |
(int8_t)0xCA >> 2 = -14 |
| Mask | (1 << w) - 1 isolates the low w bits |
width 4 → 0x0F |
| Field extraction | Shift field to bit 0, then AND a mask | (iw >> 7) & 0x1F for rd |
| Sign extension | Replicate the sign bit into upper bits | (v << 28) >> 28 |
| Emulator | Software that simulates hardware behavior | rv_emu runs RISC-V machine code |
Practice Problems¶
Problem 1: Apply All Four Logical Operators¶
Given a = 0b10110100 and b = 0b01101100, compute a & b, a | b, a ^ b, and ~a. Give each answer in binary and hex.
Click to reveal solution
Problem 2: Logical vs. Bitwise¶
Predict the output of this program and explain the difference between the two computations.
#include <stdio.h>
int main(void) {
int a = 4; // 0b100
int b = 2; // 0b010
printf("%d\n", a & b); // line 1
printf("%d\n", a && b); // line 2
return 0;
}
Click to reveal solution
`&` works bit by bit and finds no position where both have a `1`. `&&` only asks "are both operands true (nonzero)?" — they are, so it yields `1`. Confusing the two is a classic bug.Problem 3: The Three Shifts¶
Let x = 0b10010110. Compute:
x << 1(logical)(uint8_t)x >> 2(logical shift right)(int8_t)x >> 2(arithmetic shift right)
Give binary and decimal (signed for part 3).
Click to reveal solution
x = 1 0 0 1 0 1 1 0 (unsigned 150; signed int8 = -106)
1. x << 1: shift left, fill 0 on right
0 0 1 0 1 1 0 0 = 0x2C = 44 (top bit fell off; 150*2=300 overflows a byte)
2. (uint8_t)x >> 2: logical, fill 0 on left
0 0 1 0 0 1 0 1 = 0x25 = 37 (= 150 / 4, unsigned, truncated)
3. (int8_t)x >> 2: arithmetic, fill sign bit (1) on left
1 1 1 0 0 1 0 1 = 0xE5
Value: invert(1110 0101)=0001 1010, +1 = 0001 1011 = 27, so -27.
Check: -106 / 4 = -26.5, rounds toward -inf = -27. ✓
Problem 4: Extract a Bit Field¶
A 32-bit RISC-V instruction word is iw = 0x00A50533. The destination register field rd occupies bits 11–7. Extract rd using shift and mask, and give its value.
Click to reveal solution
iw = 0x00A50533
Step 1 - shift bits 11..7 down to position 0..4:
rd = iw >> 7
0x00A50533 = 0000 0000 1010 0101 0000 0101 0011 0011
>> 7 = 0000 0000 0000 0001 0100 1010 0000 1010
Step 2 - mask the low 5 bits (rd is 5 bits wide):
mask = (1 << 5) - 1 = 0b11111 = 0x1F
rd = (iw >> 7) & 0x1F
...0000 1010
& ...0001 1111
-----------
...0000 1010 = 0x0A = 10
rd = 10 (register x10 = a0)
Problem 5: Set, Clear, and Toggle a Bit¶
Using only bitwise operators and shifts, write C expressions that, for a uint8_t x and a bit position n (0–7):
- set bit
nto 1 - clear bit
nto 0 - toggle bit
n - test whether bit
nis 1 (result 0 or 1)
Click to reveal solution
These four idioms — set with `|`, clear with `& ~`, toggle with `^`, test with `>> & 1` — are the everyday vocabulary of bit manipulation.Problem 6: The rstr Termination Condition¶
Here is a backward-copy loop. Identify the bug and give the corrected condition.
void rstr(char *dst, char *src, int len) {
int s = len - 1, d = 0;
while (src[s] != '\0') { // <-- here
dst[d++] = src[s--];
}
dst[d] = '\0';
}
Click to reveal solution
The loop walks `s` **downward** from `len - 1`. The source's own `'\0'` lives at index `len`, which we already started below — so `src[s] != '\0'` never becomes false from `src`'s real terminator. When `s` goes negative, `src[s]` reads memory *before* `src` (undefined behavior). It only "works" when a neighboring string's `'\0'` happens to sit there. **Fix:** terminate based on your own index, not foreign memory:Further Reading¶
- Project03 spec: /assignments/project03/
- Key Concepts guide (bitwise operators, masking, sign extension): /guides/key-concepts/
- RISC-V Assembly guide (instructions, registers, calling conventions): /guides/riscv/
- Handwritten lecture notes (source PDF): /notes/CS315-01 2025-09-24 Lab Bit Manipulation.pdf
- Bitwise operations in C (Wikipedia)
- Arithmetic shift (Wikipedia)
- Two's complement (Wikipedia)
- Barrel shifter (Wikipedia)
Summary¶
-
Project03 IG rewards understanding, not just passing tests: you must explain your design, walk through your implementation, and demonstrate debugging — bit-level reasoning is central.
-
The
rstrbug stopped a backward copy on a neighboring string's'\0'(undefined behavior). The correct fix testss >= 0, relying only on indices you own. -
Bit manipulation is the bridge to machine code. Decoding 32-bit instructions for the
rv_emuemulator is shift + mask, end to end. -
Bitwise (
~ & | ^ << >>) and logical (&& || !) operators are different. Bitwise work per-bit on patterns; logical produce 0/1 for control flow. Confusing&/&&is a classic bug. -
Memorize the truth tables. AND keeps
1only when both are1; OR sets1if either is; XOR sets1when bits differ; NOT flips every bit. -
A byte is 8 bits (7…0); a word is 32 bits (4 bytes). Decimal value is the sum of place values where bits are
1. -
Three shifts differ only in their fill bit: SLL fills
0on the right (×2^n), SRL fills0on the left (unsigned ÷2^n), SRA fills the sign bit on the left (signed ÷2^n, rounding toward −∞). In C,>>is logical on unsigned types and arithmetic on signed types. -
Masking extracts fields: shift the field down to bit 0, then AND with
(1 << w) - 1. Shift-and-OR builds values back up, and(v << k) >> k(signed) sign-extends — the exact toolkit for the upcoming emulator.