/**
 * Copyright (C) 2023-2026 Linaro Limited (or its affiliates). All rights reserved.
 * Copyright (C) 2013-2023 Arm Limited (or its affiliates).
 * All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */

.text
.globl _switchStackInternal

/*
1. Save register windows to memory (stack) including return address' for FROM.
2. Switch stack pointers.
3. Restore register windows from memory for TO.
4. Continue execution.

' The CALL instruction also writes the value of PC, which contains the
  address of the CALL, into r[15] (out register 7). (A.8 Call and Link) */

/* void _switchStackInternal(void* to, void** from) */
_switchStackInternal:
    /* nop */               /* for debugging */

                            /* %o7 contains return address */
    save %sp, -192, %sp     /* create our own register window --> %i7 := %o7 */
                            /* (Point A) */

    stx %sp, [%i1]          /* (*from) := sp */

    mov %i0, %o0            /* FLUSHW doesn't save the active register window */
    save %sp, -192, %sp     /* to memory, but we need to keep it. */
    flushw                  /* Solution: temporary register window. */

    mov %i0, %fp            /* Setting fp := to ... */
    restore                 /* and "removing" temporary register window will */
                            /* restore the register windows at point A
                               (of course, for the TO coroutine). */

    ret                     /* return from subroutine (jmpl %i7+8, %g0) */
    restore                 /* remove our register window */

    /* nop */               /* for debugging */
