lince_core¶
The SPARC V8 integer unit. This module owns the architectural state of
each core, the decoder that turns 32-bit instruction words into
DecodedInsn, the per-category instruction handlers, and the top-level
step() driver.
It depends only on lince_interfaces — there is no awareness of
buses, peripherals, or the runtime.
Source layout¶
src/core/
├── include/lince/core/
│ ├── cpu_state.hpp ← per-core architectural state
│ ├── decoder.hpp ← Decoder + DecodedInsn
│ ├── decoded_insn.hpp ← InsnKind enum + operand fields
│ ├── handlers.hpp ← public execute() dispatcher
│ ├── step.hpp ← single-cycle driver
│ ├── trap.hpp ← tt constants + status_to_tt()
│ ├── fpu.hpp / fpu_handlers.hpp ← FPop1/FPop2 (Phase 7)
│ └── softfloat_context.hpp ← Berkeley SoftFloat 3e wrapper
└── src/
├── cpu_state.cpp
├── decoder.cpp
├── handlers.cpp ← execute() dispatcher
├── handlers_alu.cpp
├── handlers_branch.cpp
├── handlers_loadstore.cpp
├── handlers_regwin.cpp
├── handlers_special.cpp
├── handlers_internal.hpp ← shared helpers (alu_op2, eval_cond)
├── step.cpp
└── softfloat_context.cpp
CpuState¶
The full architectural state of one core:
- 8 register windows (
NWINDOWS = 8), 16 in/out + 8 local + 8 global = 128 physical slots + 8 globals. - PSR, WIM, TBR, Y registers.
- PC and nPC.
- The PSR write pipeline (
pending_psr_, see execution model). - A branch-pending flag (
branch_taken_,branch_target_) used by the delay-slot logic. - An annul flag for
Bicc,anot-taken paths. - A power-down flag for
asr19writes (idle loop). - An error-mode flag for traps that fire with
ET == 0.
Read accessors are noexcept; write accessors take strongly-typed
arguments. Window management goes through enter_trap, leave_trap,
save_window, restore_window, all of which respect the WIM check
order from SPARC V8 §B.26.
Decoder¶
Decoder::decode(uint32_t word) → DecodedInsn covers Format 1 (CALL),
Format 2 (SETHI/Bicc), and the entire Format 3 (op = 10/11) opcode
space. The result is a DecodedInsn carrying:
InsnKind— the canonical enum (Add/Sub/And/Or/Bicc/JMPL/SAVE/…).rd,rs1,rs2,imm,simm13,disp22,disp30.cc,branch_cond,is_imm,is_annul,is_link.
The decoder is stateless and pure: it never reads or writes
CpuState; that is the handler's job. A unit test can decode any
32-bit pattern in isolation and inspect the structured result.
Unknown opcodes decode to InsnKind::Unknown rather than throwing; the
handler then maps that to IllegalInstruction.
Handlers¶
The dispatcher in handlers.cpp calls a per-category handler:
| File | Covers |
|---|---|
handlers_alu.cpp |
ADD/SUB/AND/OR/XOR/SLL/SRL/SRA/MULSCC/UMUL/SMUL/UDIV/SDIV plus their cc variants and tagged-add/sub |
handlers_branch.cpp |
Bicc, BA, JMPL, CALL, RETT, Tcc |
handlers_loadstore.cpp |
LD/LDH/LDB (signed and unsigned) + ST/STH/STB + LDD/STD + LDSTUB + SWAP + CASA |
handlers_regwin.cpp |
SAVE, RESTORE, RETT (the privileged subset) |
handlers_special.cpp |
RDY, WRY, RDPSR, WRPSR, RDWIM, WRWIM, RDTBR, WRTBR, IFLUSH |
Shared helpers — alu_op2, eval_cond, condition-code update,
register-window arithmetic — live in
handlers_internal.hpp, only included from the handler .cpp files
to keep the public surface clean.
Each handler returns an ExecStatus:
enum class ExecStatus {
Ok, // normal completion
Branch, // branch taken (delay slot follows)
InsnFetchError, // tt = 0x01
IllegalInstruction, // tt = 0x02
PrivilegedInstruction, // tt = 0x03
FpDisabled, // tt = 0x04
WinOverflow, // tt = 0x05
WinUnderflow, // tt = 0x06
MemNotAligned, // tt = 0x07
BusError, // tt = 0x09
TagOverflow, // tt = 0x0a
DivisionByZero, // tt = 0x2a
SoftwareTrap, // tt = 0x80 + sw_trap_no
ErrorMode, // bubbled up; trap with ET=0
};
step()¶
lince::core::step(CpuState&, ICpuBus&) is the single-cycle driver.
Per call it does, in order:
- Sample interrupts (the runtime hooks here via
sample_interrupts). - If
error_mode_is set, returnHaltReason::ErrorMode. - If
is_powered_down_, return — the outer loop fast-forwards time. - Fetch the instruction at
PC(or skip it ifannul_next_). - Decode + execute.
- Apply branch / delay-slot logic.
commit_psr_pipeline().
The function is intentionally short (~80 LOC) so it is easy to keep correct.
Berkeley SoftFloat 3e (Phase 7)¶
fpu.hpp and softfloat_context.hpp wrap the vendored
third-party/softfloat3e build. The integration is currently scaffolding
(see plans/phase7-fpu.md for status); the actual FPop1/FPop2 handlers
that lift FPU semantics onto SoftFloat are the first deliverable of
Phase 7.
The wrapper is per-CpuState: each core has its own SoftFloat
fp_state so rounding mode / exception flags do not leak between cores.
What is intentionally out of lince_core¶
- No bus access — fetches go through
ICpuBus, loads/stores through the same. - No knowledge of peripherals.
- No I/O of any kind, not even logging — handlers return statuses, not error messages.
- No cache, no MMU, no JIT.