commit 4fb85d090f08846c57983eaf0e6c9e6d30458f43
parent 6a121abe685d378dd68813044283f763bdbc3107
Author: Sylvia Ivory <git@sivory.net>
Date: Tue, 10 Feb 2026 20:12:11 -0800
Functional SWI
Diffstat:
5 files changed, 129 insertions(+), 25 deletions(-)
diff --git a/pi/devices/gpio.zig b/pi/devices/gpio.zig
@@ -227,8 +227,9 @@ pub fn enable_interrupts() !void {
mem.barrier(.Write);
}
-noinline fn gpio_handler(pc: usize) void {
+noinline fn gpio_handler(pc: usize, registers: *interrupts.Registers) void {
_ = pc;
+ _ = registers;
mem.barrier(.Write);
defer mem.barrier(.Write);
diff --git a/pi/devices/mini-uart.zig b/pi/devices/mini-uart.zig
@@ -135,6 +135,11 @@ pub fn enable_interrupts() Error!void {
interrupts_enabled = true;
}
+// Temporarily disables TX interrupting
+pub fn toggle_tx_interrupts() void {
+ interrupts_enabled = !interrupts_enabled;
+}
+
fn enable_tx_interrupt() void {
mem.put_u32_barrier(@ptrFromInt(AUX_MU_IER_REG), 0b1111);
}
@@ -173,8 +178,10 @@ pub fn get_rx_written() usize {
return rx_writer_written;
}
-noinline fn uart_handler(pc: usize) void {
+noinline fn uart_handler(pc: usize, registers: *interrupts.Registers) void {
_ = pc;
+ _ = registers;
+
mem.barrier(.Write);
defer mem.barrier(.Write);
diff --git a/pi/interrupts.zig b/pi/interrupts.zig
@@ -1,4 +1,5 @@
const mem = @import("./mem.zig");
+const pi = @import("./root.zig");
const BASE_ADDRESS: usize = mem.BASE_ADDRESS + 0xB200;
@@ -93,17 +94,39 @@ pub inline fn pending_peripheral_interrupt(i: PeripheralsInterrupt) bool {
}
}
-fn empty(lr: u32) void {
- _ = lr;
+fn empty(pc: u32, registers: *Registers) void {
+ _ = pc;
+ _ = registers;
}
-var reset_handler: *const fn (u32) void = empty;
-var undefined_instruction_handler: *const fn (u32) void = empty;
-var software_interrupt_handler: *const fn (u32) void = empty;
-var prefetch_abort_handler: *const fn (u32) void = empty;
-var data_abort_handler: *const fn (u32) void = empty;
-var interrupt_handler: *const fn (u32) void = empty;
-var fast_interrupt_handler: *const fn (u32) void = empty;
+var reset_handler: *const fn (u32, *Registers) void = empty;
+var undefined_instruction_handler: *const fn (u32, *Registers) void = empty;
+var software_interrupt_handler: *const fn (u32, *Registers) void = empty;
+var prefetch_abort_handler: *const fn (u32, *Registers) void = empty;
+var data_abort_handler: *const fn (u32, *Registers) void = empty;
+var interrupt_handler: *const fn (u32, *Registers) void = empty;
+var fast_interrupt_handler: *const fn (u32, *Registers) void = empty;
+
+pub const Registers = struct {
+ gp: [13]usize,
+ lr: usize,
+
+ inline fn get_svc() *@This() {
+ return @ptrFromInt(@intFromPtr(&pi.svc_stack) + (pi.svc_stack.len * @sizeOf(usize)));
+ }
+
+ inline fn get_abort() *@This() {
+ return @ptrCast(&pi.abort_stack);
+ }
+
+ inline fn get_irq() *@This() {
+ return @ptrCast(&pi.irq_stack);
+ }
+
+ inline fn get_fiq() *@This() {
+ return @ptrCast(&pi.fiq_stack);
+ }
+};
inline fn get_lr() u32 {
return asm volatile ("mov %[result], lr"
@@ -132,31 +155,51 @@ inline fn clobber_all() void {
export fn reset() callconv(.{ .arm_interrupt = .{ .type = .generic } }) void {
clobber_all();
- reset_handler(get_lr() - 4);
+ reset_handler(get_lr() - 4, Registers.get_abort());
}
export fn undefined_instruction() callconv(.{ .arm_interrupt = .{ .type = .undef } }) void {
clobber_all();
- undefined_instruction_handler(get_lr() - 4);
+ undefined_instruction_handler(get_lr() - 4, Registers.get_abort());
}
-export fn software_interrupt() callconv(.{ .arm_interrupt = .{ .type = .swi } }) void {
- clobber_all();
- software_interrupt_handler(get_lr() - 4);
+// export fn software_interrupt() callconv(.{ .arm_interrupt = .{ .type = .swi } }) void {
+// clobber_all();
+// software_interrupt_handler(get_lr() - 4, Registers.get_svc());
+// }
+export fn software_interrupt_handler_v(pc: u32, reg: *Registers) void {
+ software_interrupt_handler(pc, reg);
}
+
+comptime {
+ asm (
+ \\ .global software_interrupt
+ \\ .type software_interrupt, %function
+ \\ software_interrupt:
+ \\ push {r0-r12, lr}
+ \\ sub lr, lr, #4
+ \\ mov r0, lr
+ \\ mov r1, sp
+ \\ blx software_interrupt_handler
+ \\ pop {r0-r12, lr}
+ \\ movs pc, lr
+ );
+}
+extern fn software_interrupt() void;
+
export fn prefetch_abort() callconv(.{ .arm_interrupt = .{ .type = .abort } }) void {
clobber_all();
- prefetch_abort_handler(get_lr() - 8);
+ prefetch_abort_handler(get_lr() - 8, Registers.get_abort());
}
export fn data_abort() callconv(.{ .arm_interrupt = .{ .type = .abort } }) void {
clobber_all();
- data_abort_handler(get_lr() - 4);
+ data_abort_handler(get_lr() - 4, Registers.get_abort());
}
export fn interrupt() callconv(.{ .arm_interrupt = .{ .type = .irq } }) void {
clobber_all();
- interrupt_handler(get_lr() - 4);
+ interrupt_handler(get_lr() - 4, Registers.get_irq());
}
export fn fast_interrupt() callconv(.{ .arm_interrupt = .{ .type = .fiq } }) void {
clobber_all();
- fast_interrupt_handler(get_lr() - 4);
+ fast_interrupt_handler(get_lr() - 4, Registers.get_fiq());
}
// https://leiradel.github.io/2019/02/09/Initialization.html
@@ -204,7 +247,7 @@ const ExceptionVector = enum {
FIQ,
};
-pub fn set_exception_handler(vector: ExceptionVector, handler: *const fn (u32) void) void {
+pub fn set_exception_handler(vector: ExceptionVector, handler: *const fn (u32, *Registers) void) void {
switch (vector) {
.Reset => reset_handler = handler,
.UndefinedInstruction => undefined_instruction_handler = handler,
diff --git a/pi/root.zig b/pi/root.zig
@@ -51,14 +51,17 @@ pub inline fn get_sp() usize {
// TODO; should do something better
const stack_size = 256; // 1kb stacks
-export var svc_stack: [stack_size]usize align(8) = undefined;
-export var abort_stack: [stack_size]usize align(8) = undefined;
-export var irq_stack: [stack_size]usize align(8) = undefined;
-export var fiq_stack: [stack_size]usize align(8) = undefined;
+pub export var svc_stack: [stack_size]usize align(8) = undefined;
+pub export var abort_stack: [stack_size]usize align(8) = undefined;
+pub export var irq_stack: [stack_size]usize align(8) = undefined;
+pub export var fiq_stack: [stack_size]usize align(8) = undefined;
pub fn setup_stacks() void {
const original_cpsr = PSR.get_c();
+ _ = PSR.switch_current_mode(.Supervisor);
+ set_sp(@ptrFromInt(@intFromPtr(&svc_stack) + (svc_stack.len * @sizeOf(usize))));
+
_ = PSR.switch_current_mode(.Undefined);
set_sp(@ptrFromInt(@intFromPtr(&abort_stack) + (abort_stack.len * @sizeOf(usize))));
diff --git a/programs/syscall.zig b/programs/syscall.zig
@@ -0,0 +1,50 @@
+const std = @import("std");
+const pi = @import("pi");
+
+const uart = pi.devices.mini_uart;
+const interrupts = pi.interrupts;
+
+comptime {
+ asm (
+ \\ .global syscall_test;
+ \\ .type syscall_test, %function;
+ \\ syscall_test:
+ \\ mov r0, 0
+ \\ mov r1, 1
+ \\ mov r2, 2
+ \\ mov r3, 3
+ \\ mov r4, 4
+ \\ mov r5, 5
+ \\ mov r6, 6
+ \\ mov r7, 7
+ \\ mov r8, 8
+ \\ mov r9, 9
+ \\ mov r10, 10
+ \\ mov r11, 11
+ \\ mov r12, 12
+ \\ swi 1
+ \\ bx lr
+ );
+}
+extern fn syscall_test() void;
+
+noinline fn software_interrupt_handler(pc: u32, registers: *interrupts.Registers) void {
+ uart.toggle_tx_interrupts();
+ const dup = registers.*;
+
+ uart.writer.print("instruction=0x{X}\n", .{pc}) catch {};
+ for (dup.gp, 0..dup.gp.len) |r, i| {
+ uart.writer.print("r{d} = {d}\n", .{ i, r }) catch {};
+ }
+ uart.writer.print("lr = 0x{X}\n", .{dup.lr}) catch {};
+ uart.toggle_tx_interrupts();
+}
+
+pub fn main() !void {
+ interrupts.set_exception_handler(.SoftwareInterrupt, software_interrupt_handler);
+
+ try uart.writer.print("testing syscall\n", .{});
+ uart.flush();
+
+ syscall_test();
+}