Skip to content

Coding Standards

NtinosTheGamer2324 edited this page Feb 14, 2026 · 3 revisions

Coding Standards

Code style and conventions for ModuOS development.

General Principles

  1. Clarity over cleverness: Write readable code
    • Exception: in safety-/correctness-critical paths (fault handling, paging/memory management, IRQ/IDT setup, context switching, etc.), prioritize correctness and robustness even if the implementation is more complex or “clever”. Add comments/tests where possible (examples: src/kernel/fault.c, src/kernel/memory/paging.c, src/arch/*/interrupts/*).
  2. Consistency: Follow existing patterns
  3. Documentation: Comment complex logic
  4. Simplicity: Keep it simple when possible

C Code Style

Naming Conventions

Functions:

// Format: subsystem_action_object()
void process_create(const char *name);
int fs_mount_drive(int drive_index);
uint64_t memory_allocate_page(void);

// Private/internal: prefix with underscore or make static
static int _internal_helper(void);

Variables:

// Local: lowercase with underscores
int process_count;
uint64_t physical_address;
char *file_path;

// Global: prefix with subsystem or make explicit
extern int boot_drive_index;
int acpi_initialized;

Constants and Macros:

// All uppercase with underscores
#define MAX_PROCESSES 256
#define PAGE_SIZE 4096
#define MULTIBOOT_MAGIC 0x36d76289

// Function-like macros: can use lowercase
#define min(a, b) ((a) < (b) ? (a) : (b))

Types:

// Structs: suffix with _t
typedef struct process {
    // fields
} process_t;

// Enums: suffix with _t
typedef enum {
    STATE_READY,
    STATE_RUNNING
} process_state_t;

// Function pointers: suffix with _t
typedef void (*irq_handler_t)(registers_t *regs);

Indentation and Formatting

Indentation:

  • 4 spaces (no tabs)
  • Align continuation lines

Braces (K&R style):

// Functions: brace on same line
void function_name(int arg) {
    // code
}

// Control structures: brace on same line
if (condition) {
    // code
} else {
    // code
}

for (int i = 0; i < n; i++) {
    // code
}

while (condition) {
    // code
}

Line length: Maximum 100 characters (prefer 80)

Spaces:

// After keywords
if (condition)
for (int i = 0; i < n; i++)
while (running)

// Around operators
x = y + z;
if (a == b)

// After commas
function(arg1, arg2, arg3);

// No space before function call parenthesis
function(arg);

Comments

File headers:

//
// filename.c - Brief description
//
// Part of ModuOS kernel
//

Function documentation:

/**
 * Brief one-line description
 * 
 * Longer description if needed. Explain what the function
 * does, any important behavior, side effects, etc.
 * 
 * @param name Description of parameter
 * @param value Description of parameter
 * @return Description of return value (0 on success, negative on error)
 */
int function_name(const char *name, int value);

Inline comments:

// Explain WHY, not WHAT
// Good example:
cli();  // Disable interrupts to prevent race condition

// Bad example:
cli();  // Clear interrupt flag

// Use comments to explain complex logic
// Break down algorithms step-by-step

Block comments:

/*
 * Long explanation of complex algorithm or logic.
 * Multiple lines are okay.
 * Use for important sections.
 */

Error Handling

Return values:

// Use 0 for success, negative for errors
#define SUCCESS  0
#define ERROR   -1
#define ENOMEM  -4
#define EINVAL  -5

int function(void) {
    if (error_condition) {
        return ERROR;
    }
    return SUCCESS;
}

Null checks:

// Always check pointers
void *ptr = malloc(size);
if (ptr == NULL) {
    return ENOMEM;
}

// Or use assertions for programmer errors
assert(ptr != NULL);

Memory Management

Allocation:

// Use kernel heap functions
void *ptr = kmalloc(size);
if (!ptr) {
    return ENOMEM;
}

// Zero-initialize when needed
void *ptr = kzalloc(size);

Deallocation:

// Always free allocated memory
kfree(ptr);
ptr = NULL;  // Prevent use-after-free

Assembly Style

NASM Syntax

File structure:

; filename.asm - Brief description
; Part of ModuOS kernel

section .text
bits 64

global function_name
extern external_function

; Function: Brief description
; Parameters: RDI = arg1, RSI = arg2
; Returns: RAX = return value
function_name:
    push rbp
    mov rbp, rsp
    
    ; Function body
    ; Add comments for complex operations
    
    pop rbp
    ret

Indentation:

  • Labels: No indentation
  • Instructions: 4 spaces
  • Comments: Align with code

Comments:

; Use semicolons for comments
; Explain register usage
; Document calling convention

Header Files

Include Guards

Either style is acceptable. Prefer consistency within a directory/module.

Traditional include guards:

#ifndef FILENAME_H
#define FILENAME_H

// Header content

#endif // FILENAME_H

#pragma once:

// FILENAME_H
#pragma once

// Header content

Organization**:

// 1. System includes
#include <stdint.h>
#include <stddef.h>

// 2. Project includes
#include "moduos/kernel/kernel.h"

// 3. Constants
#define MAX_VALUE 100

// 4. Type definitions
typedef struct foo foo_t;

// 5. Function declarations
void init(void);

Best Practices

Functions

Keep functions short: Aim for < 50 lines Single responsibility: One function, one purpose Descriptive names: Name explains what it does

Variables

Minimize scope: Declare variables as locally as possible Initialize: Always initialize variables Const correctness: Use const for read-only data

Magic Numbers

Avoid:

// Bad
if (status == 2) { ... }

Use named constants:

// Good
#define STATUS_READY 2
if (status == STATUS_READY) { ... }

Pointer Usage

Null checks:

if (ptr == NULL) { ... }
// Or
if (!ptr) { ... }

Const pointers:

// Pointer to const data
const char *str = "hello";

// Const pointer
char * const ptr = buffer;

// Const pointer to const data
const char * const ptr = "hello";

Examples

Good Code

/**
 * Create a new process
 * 
 * @param name Process name
 * @param entry Entry point function
 * @param priority Priority level (0 = highest)
 * @return Pointer to process, or NULL on error
 */
process_t* process_create(const char *name, void (*entry)(void), int priority) {
    // Validate inputs
    if (!name || !entry) {
        return NULL;
    }
    
    // Find free PID
    int pid = find_free_pid();
    if (pid < 0) {
        return NULL;  // Process table full
    }
    
    // Allocate process structure
    process_t *proc = kzalloc(sizeof(process_t));
    if (!proc) {
        return NULL;  // Out of memory
    }
    
    // Initialize process
    proc->pid = pid;
    strncpy(proc->name, name, PROCESS_NAME_MAX);
    proc->state = PROCESS_STATE_READY;
    proc->priority = priority;
    
    return proc;
}

Bad Code

// No comments, poor naming, no error handling
process_t* pc(char *n,void(*e)(),int p){
process_t *p=malloc(sizeof(process_t));
p->pid=fpid();
strcpy(p->name,n);
p->state=0;
return p;
}

Hardware Driver Guidelines

Interrupt Initialization Order

Critical: Always install IRQ handlers before enabling hardware interrupts to prevent interrupt storms and system hangs.

Incorrect (causes hangs on real hardware):

void driver_init(device_t *dev) {
    // Reset device
    device_reset(dev);
    
    // Enable interrupts - WRONG! Handler not installed yet
    dev->regs->int_enable = INT_ALL;
    
    // Install handler - TOO LATE!
    irq_install_handler(dev->irq, driver_irq_handler);
}

Correct (safe initialization):

void driver_init(device_t *dev) {
    // Reset device
    device_reset(dev);
    
    // Clear any pending interrupts from BIOS/firmware
    dev->regs->int_status = 0xFFFFFFFF;  // W1C
    
    // Disable interrupts during initialization
    dev->regs->int_enable = 0;
    
    // Initialize device structures
    setup_dma_buffers(dev);
    setup_command_queues(dev);
    
    // Install IRQ handler FIRST
    irq_install_handler(dev->irq, driver_irq_handler);
    
    // NOW it's safe to enable interrupts
    dev->regs->int_status = 0xFFFFFFFF;  // Clear again
    dev->regs->int_enable = INT_ALL;
    
    // Posted read to ensure write completes (prevents PCI write posting issues)
    (void)dev->regs->int_enable;
}

Key Rules:

  1. Clear and disable interrupts during device initialization
  2. Install IRQ handler before enabling any hardware interrupts
  3. Clear pending interrupt status before and after handler installation
  4. Use posted reads after critical register writes (prevents PCI write posting races)
  5. Ensure all data structures are initialized before interrupts can fire

Common Mistakes:

  • Enabling global controller interrupts before all ports/channels are ready
  • Forgetting to clear stale interrupts left by BIOS/firmware
  • Not using posted reads after register writes on PCI devices
  • Enabling port/device-specific interrupts during probe/initialization

This pattern prevents interrupt storms that cause hangs on VirtualBox, VMware, Bochs, and real hardware.

Tools

Code Formatting

Consider using clang-format with these settings:

BasedOnStyle: LLVM
IndentWidth: 4
ColumnLimit: 100

Static Analysis

Use compiler warnings:

CFLAGS += -Wall -Wextra -Werror

Next Steps

Clone this wiki locally