How the CMP Instruction Works in NanoCore: Flag Effects and Implementation

The CMP instruction in NanoCore compares two registers, stores a result code (0, 1, or 2) in the destination register, and updates the Zero (Z) and Negative (N) status flags to enable conditional branching.

The CMP (compare) instruction is a fundamental register-to-register operation in the NanoCore educational CPU emulator written in Rust. Understanding how this instruction manipulates the processor status flags is essential for writing correct conditional logic in NanoCore assembly programs.

CMP Instruction Opcode Definition and Decoding

The CMP opcode is formally defined in the Op enum located in src/lib.rs. The source code documents its purpose with the comment: "Comparison: CMP Rx Ry (Set zero flag if Rx = Ry, otherwise reset)" at lines 55–56.

During the instruction fetch and decode cycle, the fetch_decode method in src/nanocore.rs (lines 81–82) identifies the CMP opcode and parses the operands as Operands::RegReg(rd, rs). This encoding treats the first operand as the destination register (rd) and the second as the source register (rs).

Execution Logic and Comparison Algorithm

The core execution logic resides in the execute method within src/nanocore.rs (lines 25–66). CMP shares an execution arm with bitwise operations (AND, OR, XOR), but implements distinct comparison semantics:

let v1 = self.cpu.registers[rs as usize];
let v2 = self.cpu.registers[rd as usize];
let result = if v1 == v2 { 0 } else if v1 > v2 { 1 } else { 2 };
self.cpu.registers[rd as usize] = result;
self.cpu.update_zn_flags(result);

The algorithm performs the following steps:

  • Read values: v1 loads the value from the source register (rs), while v2 loads from the destination register (rd)
  • Compare: The instruction evaluates the relationship between the two values
  • Store result: The destination register rd receives the comparison outcome (0 for equal, 1 for greater-than, 2 for less-than)
  • Update flags: The CPU updates status flags based on the result value

Flag Updates and Status Register Effects

After computing the comparison result, the instruction calls self.cpu.update_zn_flags(result) defined in src/cpu.rs (lines 69–79). This helper function modifies the processor status register according to the result value.

Zero Flag (Z) Behavior

The Zero flag (Z) is set if and only if the two compared registers contain identical values. When v1 == v2, the result is 0, causing update_zn_flags to set FLAG_Z. For any non-zero result (1 or 2), the Zero flag is cleared. Conditional jump instructions JZ and JNZ query this flag via the CPU::FLAG_Z constant defined in src/cpu.rs (lines 49–52) to control program flow.

Negative Flag (N) Behavior

The Negative flag (N) is updated according to the most significant bit (MSB) of the result. However, since CMP can only produce results of 0, 1, or 2, the Negative flag is effectively always cleared after a comparison operation. The flag update logic still executes for consistency, but no CMP operation can yield a negative result.

Unaffected Flags

The CMP instruction leaves the Carry (C) and Overflow (Y) flags unchanged. No logic in the CMP execution path modifies these status bits.

Practical Assembly Examples

Equality Test with Conditional Jump

This example demonstrates detecting equal values and branching based on the Zero flag:

LDI R0 0x05   ; R0 = 5
LDI R1 0x05   ; R1 = 5
CMP R0 R1     ; R0 = 0 (equal), Z flag set
JZ  label     ; Branch taken because Z is set

After execution, register R0 contains 0 and the program counter jumps to label.

Greater-Than Comparison

Detecting when one register exceeds another:

LDI R2 0x09   ; R2 = 9
LDI R3 0x03   ; R3 = 3
CMP R2 R3     ; R2 = 1 (greater than), Z flag cleared
JZ  equal     ; Not taken (Z is 0)
JNZ not_equal ; Taken because Z is cleared

The result 1 in R2 indicates the first operand was larger than the second.

Less-Than Detection

Identifying when the destination is smaller than the source:

LDI R4 0x01   ; R4 = 1
LDI R5 0x04   ; R5 = 4
CMP R4 R5     ; R4 = 2 (less than), Z flag cleared

Register R4 receives the value 2, signaling that the original R4 value was less than R5.

Summary

  • Opcode location: Defined in src/lib.rs as Op::CMP with explicit documentation about zero flag behavior
  • Decode logic: Parsed in src/nanocore.rs as Operands::RegReg(rd, rs) where rd is the destination and rs is the source
  • Result encoding: Stores 0 for equal, 1 for greater-than, and 2 for less-than in the destination register
  • Flag effects: Sets Z (zero) flag only on equality; updates N (negative) flag but always clears it; leaves C and Y unchanged
  • Branch dependency: Conditional jumps JZ and JNZ read FLAG_Z to determine branching decisions

Frequently Asked Questions

What flags does the CMP instruction affect in NanoCore?

The CMP instruction affects only the Zero (Z) and Negative (N) flags. The Zero flag is set when the compared registers are equal and cleared otherwise. The Negative flag is updated by the update_zn_flags function but remains cleared because CMP results (0, 1, or 2) never have the most significant bit set. The Carry (C) and Overflow (Y) flags remain unchanged.

Where is the CMP instruction implemented in the NanoCore source code?

The implementation spans three files: the opcode definition exists in src/lib.rs (lines 55–56), the decoding logic resides in src/nanocore.rs within the fetch_decode method (lines 81–82), and the execution engine lives in the same file's execute method (lines 25–66). The flag update helper function is located in src/cpu.rs (lines 69–79).

How does CMP handle register comparison results?

CMP performs a three-way comparison. It subtracts the conceptual values and stores an integer code in the destination register: 0 if the registers are equal, 1 if the destination register was greater than the source, and 2 if the destination was less than the source. This result overwrites the original value in the destination register rd.

Can CMP trigger a negative flag in NanoCore?

No, CMP cannot set the Negative flag because the possible result values (0, 1, and 2) are all non-negative integers with the most significant bit equal to zero. While the update_zn_flags function checks the MSB of the result, the comparison algorithm guarantees the Negative flag will always be cleared after executing CMP.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →