Difference Between Logical Shift and Rotate Operations in NanoCore: SHL/SHR vs ROL/ROR
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, 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.
// 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.
// 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, 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(lines 62-65): Defines theOpenum variantsSHL,SHR,ROL, andRORwith documentation comments describing their behavior.src/nanocore.rs(lines 697-700): Contains the executionmatcharm 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:
; 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_shrin 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.rsand executed insrc/nanocore.rswithin 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.
Are SHL, SHR, ROL, and ROR available for all registers?
Yes, according to the instruction set definition in 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →