// example_blink.cpp
// Minimal C++ on STM32: blink LED at 1Hz
// Build: arm-none-eabi-g++ -mcpu=cortex-m4 -mthumb \
//        -mfloat-abi=hard -mfpu=fpv4-sp-d16 \
//        -fno-exceptions -fno-rtti -fno-threadsafe-statics \
//        -Os -flto -std=c++17 \
//        -I. -I../STM32F4xx_HAL_Driver/Inc \
//        -DSTM32F407xx \
//        example_blink.cpp \
//        -T stm32f407_flash.ld \
//        -o blink.elf

#include <cstdint>
#include "stm32f4xx.h"

// GPIO registers (direct register access for minimal example)
struct GpioRegs {
    volatile uint32_t MODER;
    volatile uint32_t OTYPER;
    volatile uint32_t OSPEEDR;
    volatile uint32_t PUPDR;
    volatile uint32_t IDR;
    volatile uint32_t ODR;
    volatile uint32_t BSRR;
    volatile uint32_t LCKR;
    volatile uint32_t AFRL;
    volatile uint32_t AFRH;
};

// RCC registers
struct RccRegs {
    volatile uint32_t CR;
    volatile uint32_t PLLCFGR;
    volatile uint32_t CFGR;
    volatile uint32_t CIR;
    volatile uint32_t AHB1RSTR;
    volatile uint32_t AHB2RSTR;
    uint32_t RESERVED0[2];
    volatile uint32_t APB1RSTR;
    volatile uint32_t APB2RSTR;
    uint32_t RESERVED1[2];
    volatile uint32_t AHB1ENR;
    volatile uint32_t AHB2ENR;
    volatile uint32_t AHB3ENR;
    uint32_t RESERVED2;
    volatile uint32_t APB1ENR;
    volatile uint32_t APB2ENR;
};

// Register base pointers
#define RCC     ((RccRegs*) 0x40023800)
#define GPIOA   ((GpioRegs*) 0x40020000)
#define GPIOD   ((GpioRegs*) 0x40020C00)

// Simple delay
static void delay_ms(uint32_t ms) {
    for (uint32_t i = 0; i < ms * 8000; i++) {
        __NOP();
    }
}

// LED class — C++ style, zero overhead
class Led {
public:
    // Template parameter: port, pin — compile-time constant
    template<GpioRegs* PORT, uint16_t PIN>
    static void Init() {
        // Enable GPIO clock
        if (PORT == GPIOD) {
            RCC->AHB1ENR |= (1 << 3);  // GPIOD clock
        } else if (PORT == GPIOA) {
            RCC->AHB1ENR |= (1 << 0);  // GPIOA clock
        }
        // Configure as output, push-pull
        PORT->MODER &= ~(3 << (PIN * 2));
        PORT->MODER |=  (1 << (PIN * 2));
    }

    template<GpioRegs* PORT, uint16_t PIN>
    static void On() {
        PORT->BSRR = (1 << PIN);       // Set
    }

    template<GpioRegs* PORT, uint16_t PIN>
    static void Off() {
        PORT->BSRR = (1 << (PIN + 16)); // Reset
    }

    template<GpioRegs* PORT, uint16_t PIN>
    static void Toggle() {
        if (PORT->ODR & (1 << PIN))
            Off<PORT, PIN>();
        else
            On<PORT, PIN>();
    }

    template<GpioRegs* PORT, uint16_t PIN, uint32_t PERIOD_MS>
    class Blinker {
    public:
        static void Init() {
            Led::Init<PORT, PIN>();
        }

        static void Run() {
            while (1) {
                Led::Toggle<PORT, PIN>();
                delay_ms(PERIOD_MS);
            }
        }
    };
};

// Instantiate blinker: LED on PD12, 500ms period
using Blinker = Led::Blinker<GPIOD, 12, 500>;

int main() {
    Blinker::Init();
    Blinker::Run();
    return 0;
}
