I'm implementing an interrupt handler in assembler, which is able to call an arbitrary handler in C. Such a high-level function could then for example read from a channel end. Everything should operate on the program stack rather than a separate kernel stack. I've read the wiki article about interrupts, but from that example it is unclear to me, how to work with a single-stack program.
I came up with the following solution which seems to work:
Code: Select all
interruptHandler:
stw r0, dp[tempR0] # Store r0 into RAM
ldw r0, sp[0] # Store sp[0] into r0
stw sp, sp[0] # Prepare modification of ksp
krestsp 0 # Modify ksp, ksp=sp+0, sp=ksp[0]
stw r0, sp[0] # Restore sp[0]
ldw r0, dp[tempR0] # Restore r0
kentsp 10 # Switch to kernel stack and allocate 10 words
stw spc, sp[1] # Store caller-save and management registers
stw ssr, sp[2]
stw sed, sp[3]
stw lr, sp[4]
stw r0, sp[5]
stw r1, sp[6]
stw r2, sp[7]
stw r3, sp[8]
stw r11, sp[9]
get r11, ed # Load handler id from the environment vector
ldc r0, 0xff # Load a mask
and r0, r11, r0 # and mask the handler id in r0
bl handle # call C-function handle(id)
ldw spc, sp[1]
ldw ssr, sp[2]
ldw sed, sp[3]
ldw lr, sp[4]
ldw r0, sp[5]
ldw r1, sp[6]
ldw r2, sp[7]
ldw r3, sp[8]
ldw r11, sp[9]
krestsp 10 # Contract the kernel stack and switch to user stack
# Sets ksp to point to sp[0] and restores sp from there
kret # Return from interrupt
What I'm unsure about is, how to switch to kernel stack. I assume, that neither sp[0] nor any register must be touched during the epilogue. That's why I use dp[tempR0] as temporary variable.
Another odd thing is, that I have to mask the environment vector register because the high nibble is not correct when reading. I just use 1 Byte as handler id, so this is no problem. But am I missing something?
Do You think, that this is correct? Are there any other errors?
Thanks