# Difference Between Logical Shift and Rotate Operations in NanoCore: SHL/SHR vs ROL/ROR

> Understand the key differences between logical shift SHL SHR and rotate ROL ROR bitwise operations in NanoCore Learn how bits are handled in each to choose the right operation for your needs.

- Repository: [Afaan Bilal/nanocore](https://github.com/afaanbilal/nanocore)
- Tags: deep-dive
- Published: 2026-02-23

---

**Logical shifts (SHL/SHR) discard the bit shifted out and fill the vacated position with zero, while rotates (ROL/ROR) wrap the discarded bit around to the opposite end to preserve all 8 bits.**

The afaanbilal/nanocore repository implements a lightweight 8-bit CPU emulator that distinguishes between these two fundamental bitwise operation families. Understanding how logical shifts and rotates handle the carry flag and bit preservation is critical for writing correct assembly for this architecture.

## Understanding Logical Shifts (SHL and SHR)

Logical shifts move all bits toward one end of the register, introducing zeros at the vacated position and discarding the bit that falls off the edge.

### SHL (Shift Left)

In [`src/nanocore.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/nanocore.rs), the **SHL** instruction uses Rust’s `overflowing_shl` method to shift the register value left by one position. The most-significant bit (MSB) is discarded, a zero fills the least-significant bit (LSB), and the carry flag (`C`) captures the original MSB value.

```rust
// Conceptual implementation from src/nanocore.rs
let (result, overflow) = reg_value.overflowing_shl(1);
// C flag set if overflow is true (original MSB was 1)

```

### SHR (Shift Right)

Conversely, **SHR** uses `overflowing_shr` to shift right by one. The LSB is discarded, a zero fills the MSB position, and the carry flag reflects the original LSB.

```rust
// Conceptual implementation from src/nanocore.rs  
let (result, overflow) = reg_value.overflowing_shr(1);
// C flag set if overflow is true (original LSB was 1)

```

## Understanding Rotates (ROL and ROR)

Rotates perform circular bit movement where no data is lost—the bit exiting one end immediately enters the other.

### ROL (Rotate Left)

**ROL** rotates the register left by one bit. According to the source code in [`src/nanocore.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/nanocore.rs), this uses Rust’s `rotate_left` method combined with a manual check of the MSB before rotation to set the carry flag correctly.

The operation wraps the original MSB into the LSB position:

```

Reg = (Reg << 1) | (Reg >> 7)

```

### ROR (Rotate Right)

**ROR** rotates right, wrapping the LSB into the MSB position. The implementation uses `rotate_right` with a pre-check of the LSB to determine the carry flag state:

```

Reg = (Reg >> 1) | (Reg << 7)

```

## Implementation Details in the NanoCore Source

The distinction between these operations is encoded in two critical files:

- **[`src/lib.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/lib.rs)** (lines 62-65): Defines the `Op` enum variants `SHL`, `SHR`, `ROL`, and `ROR` with documentation comments describing their behavior.
- **[`src/nanocore.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/nanocore.rs)** (lines 697-700): Contains the execution `match` arm where the concrete bitwise logic resides.

For **logical shifts**, the emulator leverages Rust’s built-in `overflowing_shl` and `overflowing_shr`, which return a tuple containing the result and a boolean indicating overflow. This overflow boolean directly drives the carry flag.

For **rotates**, the code calls `rotate_left(1)` or `rotate_right(1)`, but must manually test the bit that will be wrapped (using bitwise masking) to set the carry flag appropriately, since rotation never overflows in the traditional sense.

## Practical Assembly Examples

Consider a register `R1` initialized to `0b1010_0110` (0xA6). Here is how each instruction transforms the value:

```asm
; Initial state: R1 = 0b1010_0110 (0xA6)

SHL R1        ; R1 = 0b0100_1100 (0x4C), C = 1 (MSB was 1, now discarded)
SHR R1        ; R1 = 0b0101_0011 (0x53), C = 0 (LSB was 0, now discarded)

ROL R1        ; R1 = 0b0100_1101 (0x4D), C = 1 (MSB wrapped to LSB)  
ROR R1        ; R1 = 0b0101_0011 (0x53), C = 0 (LSB wrapped to MSB)

```

Notice that after **SHL**, the original `1` from the MSB is gone forever, replaced by a zero. After **ROL**, that same `1` appears at the LSB position, preserving the total bit count.

## Summary

- **Logical shifts (SHL/SHR)** discard the shifted-out bit and fill with zeros, using `overflowing_shl`/`overflowing_shr` in the emulator.
- **Rotates (ROL/ROR)** preserve all 8 bits by wrapping the discarded bit to the opposite end, implemented with `rotate_left`/`rotate_right`.
- Both operation types update the **carry flag (`C`)**, but shifts use the overflow boolean while rotates manually check the wrapping bit.
- These behaviors are defined in [`src/lib.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/lib.rs) and executed in [`src/nanocore.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/nanocore.rs) within the afaanbilal/nanocore repository.

## Frequently Asked Questions

### What happens to the bit that gets shifted out in SHL versus ROL?

In **SHL**, the MSB is permanently discarded and replaced with a zero. In **ROL**, the MSB is preserved by moving it to the LSB position, creating a circular buffer effect where no data is lost.

### How does the carry flag behavior differ between logical shifts and rotates?

For both operation types, the carry flag reflects the bit that moved across the register boundary. However, logical shifts derive this from Rust’s `overflowing_shl`/`overflowing_shr` overflow boolean, while rotates manually test the specific bit (MSB for ROL, LSB for ROR) before performing the wrap-around operation.

### Which Rust methods implement these operations in the NanoCore source code?

Logical shifts use `overflowing_shl` and `overflowing_shr`, which return a tuple of `(result, overflow)`. Rotates use `rotate_left` and `rotate_right`, but require additional bitwise logic to extract the rotating bit for carry flag calculation, as seen in [`src/nanocore.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/nanocore.rs).

### Are SHL, SHR, ROL, and ROR available for all registers?

Yes, according to the instruction set definition in [`src/lib.rs`](https://github.com/afaanbilal/nanocore/blob/main/src/lib.rs) and the README documentation, these four bitwise operations work on any 8-bit general-purpose register in the NanoCore architecture, processing the entire byte as a single unit.