Skip to content

Instantly share code, notes, and snippets.

@JoaoBaptMG
Last active March 15, 2023 00:11
Show Gist options
  • Save JoaoBaptMG/82d3eab16a24cbad072ed57b9198131f to your computer and use it in GitHub Desktop.
Save JoaoBaptMG/82d3eab16a24cbad072ed57b9198131f to your computer and use it in GitHub Desktop.
Simple context switching subroutines for ARMv4 and possibly other platforms
//----------------------------------------------------------------------------
// context.h
//----------------------------------------------------------------------------
// Provides an API for context switching (a primitive form of coroutines)
//----------------------------------------------------------------------------
// Copyright 2023 João Baptista de Paula e Silva
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
//----------------------------------------------------------------------------
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
// An opaque pointer (but deeply, nothing more than the sp of that context)
typedef struct __context* context_t;
// The context entry point, for beginning questions
typedef context_t (*context_entry_point_t)(context_t ctx, void* arg);
// Gives up ownership and switches to another context. This function will
// "wake up" the other context and, when this context is switched to again,
// the function will return with a new context
context_t context_switch(context_t ctx) IWRAM_CODE;
// All stack pointers must be aligned by 8 bytes
#define STACKPTR __attribute__((aligned(8)))
void ctx_entry_point() IWRAM_CODE;
// The stack layout after a context switch is
// (from the sp pointed by context_t) r4 r5 r6 r7 r8 r9 r10 r11 lr
// And ctx_entry_point will expect you to pass the entry point too.
// So this routine sets it to 8*(undefined) ctx_entry_point arg entry
inline static context_t context_new(void* stack_top, context_entry_point_t entry, void* arg)
{
// Set up the stack
unsigned int* stack = (unsigned int*)stack_top;
stack[-1] = (unsigned int)entry;
stack[-2] = (unsigned int)arg;
stack[-3] = (unsigned int)&ctx_entry_point;
// Return the correct pointer
return (context_t)(stack-11);
}
#ifdef __cplusplus
}
#endif
@----------------------------------------------------------------------------
@ context.s
@----------------------------------------------------------------------------
@ Provides an API for context switching (a primitive form of coroutines)
@----------------------------------------------------------------------------
@ This software is provided 'as-is', without any express or implied
@ warranty. In no event will the authors be held liable for any damages
@ arising from the use of this software.
@
@ Permission is granted to anyone to use this software for any purpose,
@ including commercial applications, and to alter it and redistribute it
@ freely, subject to the following restrictions:
@
@ 1. The origin of this software must not be misrepresented; you must not
@ claim that you wrote the original software. If you use this software
@ in a product, an acknowledgment in the product documentation would be
@ appreciated but is not required.
@ 2. Altered source versions must be plainly marked as such, and must not be
@ misrepresented as being the original software.
@ 3. This notice may not be removed or altered from any source distribution.
@----------------------------------------------------------------------------
@ context_t context_switch(context_t ctx)
@ switches to the context ctx, returning the original context on the other side
@ ctx: r0
.section .iwram, "ax", %progbits
.align 2
.arm
.global context_switch
.type context_switch STT_FUNC
context_switch:
@ Test first if it is the null pointer or the same register
cmp r0, #0
bxeq lr
cmp r0, sp
bxeq lr
push {r4-r11, lr} @ Save the static registers to stack
eor r0, sp, r0 @ XOR-swap r0 and the stack pointer
eor sp, r0, sp
eor r0, sp, r0
pop {r4-r11, lr} @ Restore the static registers and branch
bx lr @ popping to pc doesn't work because the T flag is not updated (GBA only)
@ ctx_entry_point pops the real entry_point and the first arg from stack
.section .iwram, "ax", %progbits
.align 2
.arm
.global ctx_entry_point
.type ctx_entry_point STT_FUNC
ctx_entry_point:
pop {r1, r2} @ get the entry point for the context
mov lr, pc @ "emulate" a bl
bx r2 @ jump to the entry point
mov sp, r0 @ null out the context pointer
mov r0, #0
pop {r4-r11, lr} @ Restore the static registers and branch
bx lr
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment