github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/internal/task/task_stack_avr.S (about) 1 //go:build tinygo 2 3 .section .bss.tinygo_systemStack 4 .global tinygo_systemStack 5 .type tinygo_systemStack, %object 6 tinygo_systemStack: 7 .short 0 8 9 .section .text.tinygo_startTask 10 .global tinygo_startTask 11 .type tinygo_startTask, %function 12 tinygo_startTask: 13 // Small assembly stub for starting a goroutine. This is already run on the 14 // new stack, with the callee-saved registers already loaded. 15 // Most importantly, r2r3 contain the pc of the to-be-started function and 16 // r4r5 contain the only argument it is given. Multiple arguments are packed 17 // into one by storing them in a new allocation. 18 19 // Set the first argument of the goroutine start wrapper, which contains all 20 // the arguments. 21 movw r24, r4 22 23 // Branch to the "goroutine start" function. Note that the Z register is 24 // call-clobbered, so does not need to be restored after use. 25 movw Z, r2 26 icall 27 28 // After return, exit this goroutine. This is a tail call. 29 #if __AVR_ARCH__ == 2 || __AVR_ARCH__ == 25 30 // Small memory devices (≤8kB flash) that do not have the long call 31 // instruction availble will need to use rcall instead. 32 // Note that they will probably not be able to run more than the main 33 // goroutine anyway, but this file is compiled for all AVRs so it needs to 34 // compile at least. 35 rcall tinygo_pause 36 #else 37 // Other devices can (and must) use the regular call instruction. 38 call tinygo_pause 39 #endif 40 41 .global tinygo_swapTask 42 .type tinygo_swapTask, %function 43 tinygo_swapTask: 44 // This function gets the following parameters: 45 // r24:r25 = newStack uintptr 46 // r22:r23 = oldStack *uintptr 47 48 // Save all call-saved registers: 49 // https://gcc.gnu.org/wiki/avr-gcc#Call-Saved_Registers 50 push r29 // Y 51 push r28 // Y 52 push r17 53 push r16 54 push r15 55 push r14 56 push r13 57 push r12 58 push r11 59 push r10 60 push r9 61 push r8 62 push r7 63 push r6 64 push r5 65 push r4 66 push r3 67 push r2 68 69 // Save the current stack pointer in oldStack. 70 in r2, 0x3d; SPL 71 in r3, 0x3e; SPH 72 movw Y, r22 73 std Y+0, r2 74 std Y+1, r3 75 76 // Switch to the new stack pointer. 77 in r0, 0x3f ; SREG 78 cli 79 out 0x3d, r24; SPL 80 out 0x3f, r0 ; SREG, restore interrupts (after the next instruction) 81 out 0x3e, r25; SPH 82 83 // Load saved register from the new stack. 84 pop r2 85 pop r3 86 pop r4 87 pop r5 88 pop r6 89 pop r7 90 pop r8 91 pop r9 92 pop r10 93 pop r11 94 pop r12 95 pop r13 96 pop r14 97 pop r15 98 pop r16 99 pop r17 100 pop r28 // Y 101 pop r29 // Y 102 103 // Return into the new task, as if tinygo_swapTask was a regular call. 104 ret