=== Timer (clock) ==========================================================

Clock thread needs to know whether clock is enabled, and what is the
clock interrupt (that may change in runtime when CPU modifications
are enabled or CPU is reset).

UI thread starts/stops the clock (clock_enabled).
CPU thread changes the interrupt (clock_int).

Memory order is relaxed, as program only cares about the current values.

=== CPU state ============================================================

CPU state can be changed asynchronously, and emulation needs to be able
to do fast state check in cpu cycle.

=== Logging ==============================================================

Logging functions can be used in any thread. Using LOG() aywhere requires
checking whether message should be written to log file, based on logging state
and enabled log components.

UI thread changes logging settings.

Variables describing current logging status are read and written
with relaxed memory order (only the variable itself needs to be atomic).

Additionally, log_mutex is there to make sure log entries don't get mixed.

=== Multix ===============================================================

Used for multix state. Relaxed only, as there is no barrier required,
only the state itself has to be written/read atomically.
Multix can be reset from CPU thread, making in "uninitialized".
All interface access should be immediately effectively cut off.

=== Interrupts (RP) ======================================================

=== IOtester =============================================================

interrupt specification (not really necessary)

=== Breakpoints ==========================================================

Breakpoints are a singly-linked list with a single writer (the UI thread:
brk_add/brk_delete/brk_del_all) and a single lock-free reader (the CPU thread:
brk_check, on the fetch-decode-execute hot path). No mutex on purpose, it
would tax the CPU thread on every cycle.

Publishing: new nodes are inserted at the head. The node is built first, then
made visible with a single release store to brk_list; the reader's acquire
load of brk_list (and of ->next) sees a fully-initialized node.

Deletion is two-phase so the reader never trips over freed memory:
  1. brk_delete() only flags the node (deleted = 1); the reader skips flagged
     nodes but the node stays linked and allocated.
  2. brk_cleanup() physically unlinks and frees flagged nodes, and only runs
     while the CPU is stopped (i.e. not inside brk_check). brk_del_all() frees
     unconditionally and carries the same "CPU stopped/joined" precondition.
