CPU flags
The CPU flags are boolean states that can be either 1 (true) or 0 (false). Instructions can set a flag (make it 1) or clear a flag (make it 0).
The flags are represented as bits of a byte called the flags register:
| Bit # | Flag | Flag type | Notes |
|---|---|---|---|
| 0 | Zero (ZF) | condition flag | |
| 1 | Negative (NF) | condition flag | for signed values |
| 2 | Overflow (OF) | condition flag | for signed values |
| 3 | Carry (CF) | condition flag | for unsigned values |
| 4 | With (WF) | control flag | set by with instructions |
| 5 | Skip (SF) | control flag | set by if instructions |
The NF, OF, ZF, and CF flags are called condition flags. They are updated by various instructions to report arithmetic outcomes, which then typically serve as conditions to be checked by the if instruction.
The WF and SF flags are called control flags because they modify the behavior of the next instruction. WF is normally set by the with instruction. SF is normally set by the if instruction. Except for special cases noted below, both are cleared when the next instruction executes.
The flags byte can also be accessed directly using the push flags and pop flags instructions (but note that pop flags excludes WF and SF). When a CPU event is triggered, the flags byte is stored in io::cpu_event_flags, and can be restored by a with context pop ip sequence.
Edge cases
- Conventional registers don't set the condition flags: the
addinstruction can acceptspandipas operands, for exampleadd sp, 123oradd ip, 123. These forms will NOT modify ZF, NF, OF, or CF.- For a
shiftinstruction whose shift count is zero, no operation is performed, and therefore no flags are modified.
Zero (ZF)
- Updated by:
add,and,compare,convert,divide,multiply,negate,or,shift,subtract,xor - Tested using:
if zero
Instructions set ZF if the result of the operation is zero, and ZF=0 otherwise.
Negative (NF)
- Updated by:
add,and,compare,convert,divide,multiply,negate,or,shift,subtract,xor - Tested using:
if negative - Meaningful for signed numbers
Instructions put the most significant bit of their result into NF. For signed arithmetic using two's complement, NF=1 indicates a negative number.
Overflow (OF)
- Updated by:
add,and,compare,convert,divide,multiply,negate,or,subtract,xor - Tested using:
if overfloworfail if overflow - Meaningful for signed numbers
For add, subtract, compare, multiply, and negate, OF=1 indicates that the result exceeded the register's signed range. The divide instruction sets OF when the quotient cannot be represented (including division by zero). The convert instruction sets OF whenever a narrowing conversion exceeds the target register's signed range.
The other instructions listed above will simply clear OF to avoid confusion.
Note that shift does not affect this flag at all.
Carry (CF)
- Updated by:
add,and,compare,convert,divide,multiply,negate,or,shift,subtract,xor - Tested using:
if less unsignedorfail if carry - Meaningful for unsigned numbers
For add and multiply, CF=1 indicates that the result exceeded the register's unsigned range. The convert instruction sets CF whenever a narrowing conversion exceeds the target register's unsigned range. The shift instruction puts the last shifted bit into the carry flag.
The subtract and compare instructions use the subtract-with-carry model (familiar from ARM and 6502 processors), where CF=0 indicates an underflow or "less than" comparison.
The other instructions listed above will simply clear CF to avoid confusion.
With (WF)
- Set by:
with carryorwith contextorwith euclid - Cleared by every other instruction (except when restored by
with contextpop ip)
This flag enables certain instructions to perform specialized behaviors that are useful but rarely needed. The with instruction sets this flag. The three forms are actually the same opcode, just renamed by the assembler for clarity.
with carry sets WF=1 to cause an immediately following add, subtract, or compare instruction to include the carry flag in its calculation.
with context sets WF=1 to cause an immediately following pop ip to restore the stack pointer (SP) and CPU flags as well.
with euclid sets WF=1 to cause an immediately following divide instruction to perform Euclidean integer division instead of truncated division.
Skip (SF)
- Updated by:
if - Cleared by every other instruction except
with(or when restored bywith contextpop ip)
The if instruction tests a condition and only executes the next instruction if that condition is satisfied. In other words, if the condition is unsatisfied, then the next Chombit instruction will be skipped over.
This skipping is implemented by the SF flag. When SF=1, the current instruction is skipped; no action is performed other than clearing the SF flag and advancing IP. Skipped instructions only require 1 CPU cycle.
xor b:1, b:1 # zf=1 because the result is zero
if not zero # condition is unsatisfied, so sf=1
add b:1, 1 # this is skipped over, revert to sf=0
add b:1, 2 # execution continues here