Extending the Vector Table

Back when we added the vector table, I opted to only to the bare minimum to get the program to work. The only interrupt handler I defined was the reset handler.

I think it's time to change that and add the full definition of the vector table:

pub struct VectorTable {
	pub initial_stack_pointer: &'static u32,

	pub on_reset: fn(),

	pub _reserved_1: [u32; 1],

	pub on_hard_fault             : fn(),
	pub on_memory_management_fault: fn(),
	pub on_bus_fault              : fn(),
	pub on_usage_fault            : fn(),

	pub _reserved_2: [u32; 4],

	pub on_sv_call: fn(),

	pub _reserved_3: [u32; 2],

	pub on_pend_sv: fn(),
	pub on_systick: fn(),

	pub on_irq0 : fn(),
	pub on_irq1 : fn(),
	pub on_irq2 : fn(),
	pub on_irq3 : fn(),
	pub on_irq4 : fn(),
	pub on_irq5 : fn(),
	pub on_irq6 : fn(),
	pub on_irq7 : fn(),
	pub on_irq8 : fn(),
	pub on_irq9 : fn(),
	pub on_irq10: fn(),
	pub on_irq11: fn(),
	pub on_irq12: fn(),
	pub on_irq13: fn(),
	pub on_irq14: fn(),
	pub on_irq15: fn(),
	pub on_irq16: fn(),
	pub on_irq17: fn(),
	pub on_irq18: fn(),
	pub on_irq19: fn(),
	pub on_irq20: fn(),
	pub on_irq21: fn(),
	pub on_irq22: fn(),
	pub on_irq23: fn(),
	pub on_irq24: fn(),
	pub on_irq25: fn(),
	pub on_irq26: fn(),
	pub on_irq27: fn(),
	pub on_irq28: fn(),
	pub on_irq29: fn(),

unsafe impl Sync for VectorTable {}

This is our representation of the vector table[1] as a Rust struct. Since we're not using any of the new interrupts I've added here yet, I'm not going to explain them right now. I think it makes more sense to go into detail when we have an actual use case.

The above definition only tells the program what the vector table looks like. We still need to initialize it with actual values:

pub static VECTOR_TABLE: VectorTable = VectorTable {
	initial_stack_pointer: &_estack,

	on_reset: on_reset,

	_reserved_1: [0; 1],

	on_hard_fault             : abort,
	on_memory_management_fault: abort,
	on_bus_fault              : abort,
	on_usage_fault            : abort,

	_reserved_2: [0; 4],

	on_sv_call: abort,

	_reserved_3: [0; 2],

	on_pend_sv: abort,
	on_systick: abort,

	on_irq0 : abort,
	on_irq1 : abort,
	on_irq2 : abort,
	on_irq3 : abort,
	on_irq4 : abort,
	on_irq5 : abort,
	on_irq6 : abort,
	on_irq7 : abort,
	on_irq8 : abort,
	on_irq9 : abort,
	on_irq10: abort,
	on_irq11: abort,
	on_irq12: abort,
	on_irq13: abort,
	on_irq14: abort,
	on_irq15: abort,
	on_irq16: abort,
	on_irq17: abort,
	on_irq18: abort,
	on_irq19: abort,
	on_irq20: abort,
	on_irq21: abort,
	on_irq22: abort,
	on_irq23: abort,
	on_irq24: abort,
	on_irq25: abort,
	on_irq26: abort,
	on_irq27: abort,
	on_irq28: abort,
	on_irq29: abort,

Compared to before, two things have changed:

fn on_reset() {

The on_reset handler calls directly int our start function, which I've moved to a different module to cleanly separate the initialization code from the program logic.

So why this extra bit of indirection? Well, traditionally, some initialization code would need to be run on every reset. It's not being run right now, but if it were, this would be the place to do it. I actually explained this back in the day. For a more detailed explanation, you can check out the comments in the source code.

Let's go on to the final missing piece:

fn abort() {
	loop {}

This is the handler function for all the interrupts we aren't handling right now. It stops the program by just looping forever. At some point we'll need something better, but this is good enough for now.

That's it for today. As always, the full code is available on GitHub. See you next time!