const rcc: *[39]u32 = @ptrFromInt(0x40021000);
 const gpioa = gpio.gpioa;
 const gpioc = gpio.gpioc;
+const tick = timer.systick;
 
 export fn _start() callconv(.C) noreturn {
     cpu.interrupt_disable();
     interrupt.initialize();
     interrupt.register(.SVCall, svcall);
-    timer.initialize(1000);
+    tick.initialize(1000);
     cpu.interrupt_enable();
 
     rcc[19] |= 5; // gpio a and c
     while (true) {
         //asm volatile("svc 0");
         gpioa.toggle(5);
-        const next = timer.ticks() + 1000;
-        while (timer.ticks() < next) {
+        const next = tick.ticks() + 1000;
+        while (tick.ticks() < next) {
             asm volatile("nop");
         }
     }
 
 const interrupt = @import("interrupt.zig");
 
-const sys_tick_type = packed struct {
+const driver_armcortex = packed struct {
     enable: u1,
     tickint: u1,
     unused: u30,
     rvr: u32,
     cvr: u32,
     calib: u32,
-};
 
-var sys_tick: *volatile sys_tick_type = @ptrFromInt(0xE000E010);
+    pub fn initialize(self: *driver_armcortex, freq: u32) void {
+        self.rvr = 4000000 / 8 / freq;
+        self.tickint = 1;
+        self.enable = 1;
+    }
 
-pub var ticks_raw: u32 = 0;
+};
 
-pub fn initialize(freq: u32) void {
-    interrupt.register(.SysTick, tick);
-    sys_tick.rvr = 4000000 / 8 / freq;
-    sys_tick.tickint = 1;
-    sys_tick.enable = 1;
-}
+var ticks_raw: u32 = 0;
 
 fn tick() void {
     ticks_raw += 1;
 }
 
-pub fn ticks() u32 {
-    return @atomicLoad(u32, &ticks_raw, .acquire);
-}
+const driver = struct {
+    const lld_type = driver_armcortex;
+
+    lld: *driver_armcortex,
+
+    pub fn initialize(self: driver, freq: u32) void {
+        interrupt.register(.SysTick, tick);
+        self.lld.initialize(freq);
+    }
+
+    pub fn ticks(self: driver) u32 {
+        _ = self;
+        return @atomicLoad(u32, &ticks_raw, .acquire);
+    }
+};
+
+pub const systick = driver { .lld = @ptrFromInt(0xE000E010) };