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