Conditional breakpoints

A PC breakpoint can carry a condition so it only fires when the machine is in a state you care about — far quicker than stepping by hand. Attach one with the ADP bp command:

bp add pc <addr> if <cond>

The condition is evaluated only once the address has already matched, so the hot path stays a cheap compare — the expression cost is paid only at that PC.

Grammar

The condition parser (crates/debugger/src/condition.rs) accepts:

or       := and ( "||" and )*
and      := cmp ( "&&" cmp )*
cmp      := unary ( ( "==" | "!=" | "<" | "<=" | ">" | ">=" ) unary )?
unary    := "!" unary | "(" or ")" | operand
operand  := value ( "&" number )?                 // optional bit-mask
value    := register | flag | "(" expr ")" | "nr[" n "]" | "nrraw[" n "]" | number

A bare expression (no comparison) is truthy when non-zero, so a & 0x80 breaks whenever bit 7 of A is set.

Operands

Registers

a f b c d e h l (8-bit) and af bc de hl ix iy sp pc i r (16-bit), plus the shadow set af' bc' de' hl' (also spelled af2 bc2 de2 hl2).

Flags

fs (sign), fz (zero), fh (half-carry), fpv / fp (parity/overflow), fn (add/subtract), fc (carry). Each is 1 or 0.

Memory

(expr) is the byte in memory at the address expr — paging-resolved (what the CPU sees). The inner expression can itself use registers: (hl), (0x4000), (sp).

Next registers

Two forms, and the distinction matters:

  • nr[N] — the CPU read-back value (decoded/gated — what software sees via IN $253B).
  • nrraw[N] — the stored/written byte (what was last written to the register).

These can differ where the hardware composes the read (e.g. MMU slots: nr[0x56] reads back the live effective slot). When you suspect a paging/register bug, check both.

Numbers

Decimal, or hex with 0x / $ (0x8000, $C0, 192 are all valid).

Operators

KindOperators
Comparison== != < <= > >=
Logic&& || !
Bit-mask& (applied to an operand, e.g. nr[7] & 3)
Grouping( … ) around a sub-condition

Examples

bp add pc 0000 if i == 0xbd                  # only the demo's IM2 crash, not a NextZXOS launch at $0000
bp add pc 8000 if a == 0x3b
bp add pc 9abc if (hl) > 0x4000 && b != 0    # memory deref + logic
bp add pc 6276 if fz && sp < 0xff00          # flag + register
bp add pc 7752 if nr[0x56] != nrraw[0x56]    # catch a raw-vs-read-back paging divergence
bp add pc 4a00 if a & 0x80                   # bit test — break when bit 7 of A is set

Watchpoints

To break on memory access rather than a PC, add a watchpoint (no condition expression):

bp add mem <addr> [r|w|rw]

It breaks when <addr> is read (r), written (w) or either (rw, the default; rw registers two breakpoints), reporting the hit exactly like a PC breakpoint — so you catch what touches a byte without knowing where in the code. For headless memory-write watching, the Rhai on_mem_write + watch_mem event is usually more convenient.

For anything beyond a single condition — reacting to events, accumulating state across hits — use Rhai scripting. (Note: script eval <expr> is the Rhai language, not this condition grammar — don’t confuse the two.)