1// +build scheduler.tasks,avr
2
3package task
4
5import "unsafe"
6
7const stackSize = 256
8
9// calleeSavedRegs is the list of registers that must be saved and restored when
10// switching between tasks. Also see scheduler_avr.S that relies on the
11// exact layout of this struct.
12//
13// https://gcc.gnu.org/wiki/avr-gcc#Call-Saved_Registers
14type calleeSavedRegs struct {
15	r2r3   uintptr
16	r4r5   uintptr
17	r6r7   uintptr
18	r8r9   uintptr
19	r10r11 uintptr
20	r12r13 uintptr
21	r14r15 uintptr
22	r16r17 uintptr
23	r28r29 uintptr // Y register
24
25	pc uintptr
26}
27
28// registers gets a pointer to the registers stored at the top of the stack.
29func (s *state) registers() *calleeSavedRegs {
30	return (*calleeSavedRegs)(unsafe.Pointer(s.sp + 1))
31}
32
33// startTask is a small wrapper function that sets up the first (and only)
34// argument to the new goroutine and makes sure it is exited when the goroutine
35// finishes.
36//go:extern tinygo_startTask
37var startTask [0]uint8
38
39// archInit runs architecture-specific setup for the goroutine startup.
40// Note: adding //go:noinline to work around an AVR backend bug.
41//go:noinline
42func (s *state) archInit(stack []uintptr, fn uintptr, args unsafe.Pointer) {
43	// Set up the stack canary, a random number that should be checked when
44	// switching from the task back to the scheduler. The stack canary pointer
45	// points to the first word of the stack. If it has changed between now and
46	// the next stack switch, there was a stack overflow.
47	s.canaryPtr = &stack[0]
48	*s.canaryPtr = stackCanary
49
50	// Store the initial sp for the startTask function (implemented in assembly).
51	s.sp = uintptr(unsafe.Pointer(&stack[uintptr(len(stack))-(unsafe.Sizeof(calleeSavedRegs{})/unsafe.Sizeof(uintptr(0)))])) - 1
52
53	// Initialize the registers.
54	// These will be popped off of the stack on the first resume of the goroutine.
55	r := s.registers()
56
57	// Start the function at tinygo_startTask.
58	startTask := uintptr(unsafe.Pointer(&startTask))
59	r.pc = startTask/2>>8 | startTask/2<<8
60
61	// Pass the function to call in r2:r3.
62	// This function is a compiler-generated wrapper which loads arguments out
63	// of a struct pointer. See createGoroutineStartWrapper (defined in
64	// compiler/goroutine.go) for more information.
65	r.r2r3 = fn
66
67	// Pass the pointer to the arguments struct in r4:45.
68	r.r4r5 = uintptr(args)
69}
70
71func (s *state) resume() {
72	switchToTask(s.sp)
73}
74
75//export tinygo_switchToTask
76func switchToTask(uintptr)
77
78//export tinygo_switchToScheduler
79func switchToScheduler(*uintptr)
80
81func (s *state) pause() {
82	switchToScheduler(&s.sp)
83}
84
85//export tinygo_pause
86func pause() {
87	Pause()
88}
89