github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/internal/task/task_stack_esp8266.go (about)

     1  //go:build scheduler.tasks && esp8266
     2  
     3  package task
     4  
     5  // Stack switch implementation for the ESP8266, which does not use the windowed
     6  // ABI of Xtensa. Registers are assigned as follows:
     7  //   a0:      return address (link register)
     8  //   a1:      stack pointer (must be 16-byte aligned)
     9  //   a2-a7:   incoming arguments
    10  //   a8:      static chain (unused)
    11  //   a12-a15: callee-saved
    12  //   a15:     stack frame pointer (optional, unused)
    13  // Sources:
    14  // http://cholla.mmto.org/esp8266/xtensa.html
    15  // https://0x04.net/~mwk/doc/xtensa.pdf
    16  
    17  import "unsafe"
    18  
    19  var systemStack uintptr
    20  
    21  // calleeSavedRegs is the list of registers that must be saved and restored when
    22  // switching between tasks. Also see task_stack_esp8266.S that relies on the
    23  // exact layout of this struct.
    24  type calleeSavedRegs struct {
    25  	a12 uintptr
    26  	a13 uintptr
    27  	a14 uintptr
    28  	a15 uintptr
    29  
    30  	pc uintptr // also link register or r0
    31  }
    32  
    33  // archInit runs architecture-specific setup for the goroutine startup.
    34  func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
    35  	// Store the initial sp for the startTask function (implemented in assembly).
    36  	s.sp = uintptr(unsafe.Pointer(r))
    37  
    38  	// Initialize the registers.
    39  	// These will be popped off of the stack on the first resume of the goroutine.
    40  
    41  	// Start the function at tinygo_startTask (defined in
    42  	// src/internal/task/task_stack_esp8266.S).
    43  	// This assembly code calls a function (passed in a12) with a single argument
    44  	// (passed in a13). After the function returns, it calls Pause().
    45  	r.pc = uintptr(unsafe.Pointer(&startTask))
    46  
    47  	// Pass the function to call in a12.
    48  	// This function is a compiler-generated wrapper which loads arguments out of a struct pointer.
    49  	// See createGoroutineStartWrapper (defined in compiler/goroutine.go) for more information.
    50  	r.a12 = fn
    51  
    52  	// Pass the pointer to the arguments struct in a13.
    53  	r.a13 = uintptr(args)
    54  }
    55  
    56  func (s *state) resume() {
    57  	swapTask(s.sp, &systemStack)
    58  }
    59  
    60  func (s *state) pause() {
    61  	newStack := systemStack
    62  	systemStack = 0
    63  	swapTask(newStack, &s.sp)
    64  }
    65  
    66  // SystemStack returns the system stack pointer when called from a task stack.
    67  // When called from the system stack, it returns 0.
    68  func SystemStack() uintptr {
    69  	return systemStack
    70  }