DByte Native Compiler Contract
DByte 12.0 · Deep reference
SOURCE MIRROR. CONTENT BELOW IS RENDERED FROM THE TAGGED V12 SOURCE DOCUMENT. WEBSITE PROSE DOES NOT OVERRIDE THIS CONTRACT.
i686-dbyte-none is DByte's freestanding 32-bit systems
target. It reuses the shared lexer, parser, and AST, then follows a
target-owned pipeline:
DByte source modules -> native systems checking -> typed systems IR
-> bounded i686 encoder -> ELF32 ET_REL objects
-> DByte static linker -> Multiboot ELF32 ET_EXEC
This path does not invoke the interpreter, bytecode VM, LLVM,
Cranelift, a C or Rust transpiler, an assembler, ld, or
lld. Generated programs contain only DByte-owned machine
code and static data.
Build and inspect§
An integrated build resolves the import graph, emits deterministic
in-memory objects, and calls the same linker library exposed by
dbytec link:
dbytec build examples\native_kernel_multimodule\kernel.dby `
--target i686-dbyte-none --emit elf -o kernel.elfObjects can also be produced and linked independently:
dbytec build examples\native_kernel_multimodule\serial.dby `
--target i686-dbyte-none --emit obj -o serial.o
dbytec link arch_x86.o serial.o numbers.o memory.o kernel.o -o kernel.elf--emit bin writes the final file-backed load image.
--dump-ir and --dump-machine-code are
inspection outputs only. Compile and link failures leave an existing
output unchanged.
The original v11.0.0 i32/bool single-image source profile remains
accepted for existing kernels and applications. --emit obj
and any source that opts into modules or the systems type syntax use the
v11.1 object/linker pipeline; the legacy compatibility path remains
DByte-owned native code and does not invoke the interpreter, VM, or an
external toolchain.
Systems types and integer rules§
| Type | Size | Alignment | Signed |
|---|---|---|---|
bool |
1 | 1 | no |
i8 · u8 |
1 | 1 | yes · no |
i16 · u16 |
2 | 2 | yes · no |
i32 · u32 |
4 | 4 | yes · no |
usize |
4 | 4 | no |
ptr[T] |
4 | 4 | no |
Arithmetic wraps at the declared integer width. Comparisons use the
declared signedness. Literals are checked against their contextual type
before bytes are emitted. Mixed-width arithmetic, implicit narrowing,
and implicit integer-pointer conversion are rejected. Explicit
as casts are required; only usize has an
explicit pointer conversion.
The native operator set includes +, -,
*, /, %, &,
|, ^, ~, <<,
and >>. Signed division truncates toward zero and its
remainder has the sign of the dividend; unsigned division uses zero
extension. Hosted execution reports division by zero and signed minimum
divided by -1 as deterministic runtime errors. Freestanding
native execution deliberately leaves those cases to the i686
#DE exception contract. Shift counts use the i686
low-five-bit count rule.
&value takes an address and *pointer
loads or stores the pointee type. Pointers are typed; arbitrary
integer-pointer mixing is not accepted.
array[T, N] has a compile-time length, declaration-order
elements, and the alignment of T. Constant out-of-range
indices are rejected. Dynamic native indexing is intentionally unchecked
in this low-level profile; it does not claim runtime bounds safety.
Normal structs use declaration-order natural layout with tail padding
to their maximum field alignment. packed struct is an
explicit alignment-one layout with no inserted padding.
size_of(Type) and align_of(Type) are
compile-time constants. Aggregate values are local/static layout
facilities in this release, not a cross-function aggregate ABI.
Constants, statics, modules, and symbols§
const is evaluated at compile time. Immutable
static data is emitted in .rodata, initialized
static mut data in .data, and zero-initialized
storage in .bss; there are no runtime constructors.
import "relative.dby" as name resolves only on the local
filesystem relative to the importer. Traversal is canonical and
deterministic. Cycles, duplicate aliases/modules/exports, unresolved
imports, and URL or network resolution are rejected.
pub fn, pub static, and pub const
are visible across modules; unqualified definitions remain
object-local.
Standalone ET_REL modules do not need an entry point. A final
executable must contain exactly
fn kernel_main() -> int.
DBYTE32 ABI§
DBYTE32 is the native binary contract across object boundaries:
- scalar arguments are evaluated and pushed left to right in 32-bit words;
- callee parameter
iarrives at[EBP + 8 + 4 * (N - 1 - i)]; - the callee copies each argument to a deterministic width-correct local slot;
- the caller removes all argument words after
call; - scalar results are returned in
EAX; EAX,ECX, andEDXare caller-saved;EBX,ESI,EDI, andEBPare preserved when used;- every generated return restores
ESPandEBPexactly.
The DByte linker synthesizes _start, installs a bounded
16 KiB bootstrap stack, calls kernel_main, and enters a
cli · hlt loop after it returns.
Freestanding intrinsics§
The deliberately small native boundary lowers directly to x86:
native.volatile_load8(address) native.volatile_store8(address, value)
native.volatile_load16(address) native.volatile_store16(address, value)
native.volatile_load32(address) native.volatile_store32(address, value)
native.io_in8(port) native.io_out8(port, value)
native.io_in16(port) native.io_out16(port, value)
native.cpu_hlt()
native.cpu_cli() native.cpu_sti()
native.cpu_int3() native.cpu_read_eflags()
native.cpu_lgdt(descriptor_address) native.cpu_lidt(descriptor_address)
native.cpu_reload_segments(code_selector, data_selector)
Widths are checked statically. There is no host callback or hidden runtime.
Interrupt entry contract§
v11.2.0 adds first-class ring-zero i686 interrupt
functions. The ABI class is explicit and describes whether the CPU
pushes an error code:
interrupt(no_error_code) fn breakpoint(frame: ptr[InterruptFrame]):
return
interrupt(error_code) fn page_fault(frame: ptr[InterruptFrame], error_code: u32):
return
The exported handler symbol is a compiler-generated x86 entry stub.
It saves the interrupted general registers, normalizes the CPL0 frame,
crosses into a local DBYTE32 body, restores the registers, performs
class-specific stack cleanup, and emits iretd. It never
sends EOI or changes interrupt policy.
interrupt_address(vector, handler) is the only legal way
to take an interrupt handler address. Its vector is a compile-time
0..255 constant; the compiler rejects class mismatches for
i686 error-code vectors 8, 10, 11, 12, 13, 14, 17. Each
ET_REL carries deterministic non-loadable .dbyte.interrupts
metadata for linker validation, while the final executable strips
it.
See DByte Native Kernel Core for the executed GDT, IDT, exception, PIC, PIT, and IRQ0 proof.
Object and executable contract§
Objects are little-endian ELFCLASS32 ET_REL /
EM_386 files with .text, .rodata,
.data, .bss, .symtab,
.strtab, .shstrtab, and relevant
.rel.* sections. The supported i386 REL relocations are
R_386_32 and R_386_PC32. The in-process linker
validates all object bounds and symbols, applies relocations, and emits
deterministic ET_EXEC output with no dynamic interpreter,
dynamic section, libc, or DLL imports. See DByte native objects and static linking
for details.
Executable proof§
examples/native_kernel_multimodule/ contains five
meaningful DByte modules. They initialize COM1, cross object boundaries,
exercise narrow and address-width integers, pointers, arrays, structs,
packed layout, bitwise operations, shifts, and static storage, then
compute and print 42 without embedding the string "42".
Run the byte inspection and QEMU acceptance proof with:
powershell -ExecutionPolicy Bypass -File scripts\verify_native.ps1The exact serial result is:
DBYTE NATIVE SYSTEM
MULTI MODULE ONLINE
LINKER ONLINE
SYSTEM TYPES ONLINE
42
The v11.1 foundation did not include a heap, dynamic arrays, 64-bit arithmetic, division, dynamic linking, weak symbols, runtime module loading, a scheduler, a filesystem, a driver framework, or a self-hosted compiler. Later native milestones add only the bounded capabilities documented below.
v11.3 memory entry and paging boundary§
The linked entry remains backward compatible with
fn kernel_main() -> int and also accepts
fn kernel_main(multiboot_magic: u32, multiboot_info: usize) -> int.
Each new relocatable object carries canonical non-loadable
.dbyte.entry metadata; objects created before this metadata
existed are treated as the legacy zero-argument form. The linker
validates conflicts and synthesizes the matching Multiboot register
adapter.
The paging surface is deliberately typed and bounded:
native.cpu_read_cr0() -> u32
native.cpu_write_cr0(value: u32)
native.cpu_read_cr2() -> usize
native.cpu_read_cr3() -> usize
native.cpu_write_cr3(value: usize)
native.cpu_invlpg(address: ptr[T])
native.kernel_image_start() -> usize
native.kernel_image_end() -> usize
native.bootstrap_stack_start() -> usize
native.bootstrap_stack_end() -> usize
The encoder emits control-register moves and invlpg
directly. The four boundary queries resolve only linker-owned symbols
with checked R_386_32 relocations; there is no generic
linker-symbol or textual assembly escape.
Source mirror: source-docs/v12.0.0/DBYTE_NATIVE.md