137 строки
4,4 КиБ
ArmAsm
137 строки
4,4 КиБ
ArmAsm
.section .text.tinygo_startTask
|
|
.global tinygo_startTask
|
|
.type tinygo_startTask, %function
|
|
tinygo_startTask:
|
|
// Small assembly stub for starting a goroutine. This is already run on the
|
|
// new stack, with the callee-saved registers already loaded.
|
|
// Most importantly, r4 contains the pc of the to-be-started function and r5
|
|
// contains the only argument it is given. Multiple arguments are packed
|
|
// into one by storing them in a new allocation.
|
|
|
|
// Set the first argument of the goroutine start wrapper, which contains all
|
|
// the arguments.
|
|
mov r0, r5
|
|
|
|
// Branch to the "goroutine start" function. By using blx instead of bx,
|
|
// we'll return here instead of tail calling.
|
|
blx r4
|
|
|
|
// After return, exit this goroutine. This is a tail call.
|
|
bl tinygo_pause
|
|
|
|
.section .text.tinygo_getSystemStackPointer
|
|
.global tinygo_getSystemStackPointer
|
|
.type tinygo_getSystemStackPointer, %function
|
|
tinygo_getSystemStackPointer:
|
|
// The system stack pointer is always stored in the MSP register.
|
|
mrs r0, MSP
|
|
bx lr
|
|
|
|
|
|
// switchToScheduler and switchToTask are also in the same section, to make sure
|
|
// relative branches work.
|
|
.section .text.tinygo_swapTask
|
|
|
|
.global tinygo_switchToScheduler
|
|
.type tinygo_switchToScheduler, %function
|
|
tinygo_switchToScheduler:
|
|
// r0 = sp *uintptr
|
|
|
|
// Currently on the task stack (SP=PSP). We need to store the position on
|
|
// the stack where the in-use registers will be stored.
|
|
mov r1, sp
|
|
subs r1, #36
|
|
str r1, [r0]
|
|
|
|
b tinygo_swapTask
|
|
|
|
.global tinygo_switchToTask
|
|
.type tinygo_switchToTask, %function
|
|
tinygo_switchToTask:
|
|
// r0 = sp uintptr
|
|
|
|
// Currently on the scheduler stack (SP=MSP). We'll have to update the PSP,
|
|
// and then we can invoke swapTask.
|
|
msr PSP, r0
|
|
|
|
// Continue executing in the swapTask function, which swaps the stack
|
|
// pointer.
|
|
|
|
.global tinygo_swapTask
|
|
.type tinygo_swapTask, %function
|
|
tinygo_swapTask:
|
|
// This function stores the current register state to the stack, switches to
|
|
// the other stack (MSP/PSP), and loads the register state from the other
|
|
// stack. Apart from saving and restoring all relevant callee-saved
|
|
// registers, it also ends with branching to the last program counter (saved
|
|
// as the lr register, to follow the ARM calling convention).
|
|
|
|
// On pre-Thumb2 CPUs (Cortex-M0 in particular), registers r8-r15 cannot be
|
|
// used directly. Only very few operations work on them, such as mov. That's
|
|
// why the higher register values are first stored in the temporary register
|
|
// r3 when loading/storing them.
|
|
// It is possible to reduce the swapTask by two instructions (~2 cycles) on
|
|
// Cortex-M0 by reordering the layout of the pushed registers from {r4-r11,
|
|
// lr} to {r8-r11, r4-r8, lr}. However, that also requires a change on the
|
|
// Go side (depending on thumb1/thumb2!) and so is not really worth the
|
|
// complexity.
|
|
|
|
// Store state to old task. It saves the lr instead of the pc, because that
|
|
// will be the pc after returning back to the old task (in a different
|
|
// invocation of swapTask).
|
|
#if defined(__thumb2__)
|
|
push {r4-r11, lr}
|
|
#else
|
|
mov r0, r8
|
|
mov r1, r9
|
|
mov r2, r10
|
|
mov r3, r11
|
|
push {r0-r3, lr}
|
|
push {r4-r7}
|
|
#endif
|
|
|
|
// Switch the stack. This could either switch from PSP to MSP, or from MSP
|
|
// to PSP. By using an XOR (eor), it will just switch to the other stack.
|
|
mrs r0, CONTROL // load CONTROL register
|
|
movs r3, #2
|
|
eors r0, r0, r3 // flip the SPSEL (active stack pointer) bit
|
|
msr CONTROL, r0 // store CONTROL register
|
|
isb // required to flush the pipeline
|
|
|
|
// Load state from new task and branch to the previous position in the
|
|
// program.
|
|
#if defined(__thumb2__)
|
|
pop {r4-r11, pc}
|
|
#else
|
|
pop {r4-r7}
|
|
pop {r0-r3}
|
|
mov r8, r0
|
|
mov r9, r1
|
|
mov r10, r2
|
|
mov r11, r3
|
|
pop {pc}
|
|
#endif
|
|
|
|
.section .text.tinygo_scanCurrentStack
|
|
.global tinygo_scanCurrentStack
|
|
.type tinygo_scanCurrentStack, %function
|
|
tinygo_scanCurrentStack:
|
|
// Save callee-saved registers onto the stack.
|
|
#if defined(__thumb2__)
|
|
push {r4-r11, lr}
|
|
#else
|
|
mov r0, r8
|
|
mov r1, r9
|
|
mov r2, r10
|
|
mov r3, r11
|
|
push {r0-r3, lr}
|
|
push {r4-r7}
|
|
#endif
|
|
|
|
// Scan the stack.
|
|
mov r0, sp
|
|
bl tinygo_scanstack
|
|
|
|
// Restore stack state and return.
|
|
add sp, #32
|
|
pop {pc}
|