@ -*- mode: asm; asm-comment-char: ?@ -*- @ μονοκοκκο: an even smaller model of Einkornix, Linux @ version. (See einkornix.h, einkornix.S, and @ einkornix-test.c.) This does the same thing as @ einkornix-test, but in a slightly different way: instead of @ storing the current-task pointer in memory, we store it in @ r10, where we can conveniently index off it for thread-local @ storage. This version also doesn't have functions to @ initialize threading, start up new threads, or shut down old @ threads; in this case all the threads are created at @ assembly time. @ This is not compatible with the standard ARM EABI because it @ requires r10 to always point to the current task when yield @ is called; the compiler is not free to save a caller's r10 @ and use r10 as a local variable. .syntax unified .thumb .fpu fpv4-sp-d16 .cpu cortex-m4 .thumb_func .globl _start _start: ldr r0, =run_writer @ Each child task starts at run_writer, ldr r1, =(task2_stack-4) @ so we have to put it on their stacks, str r0, [r1] @ as if it were a return address. adds r1, #(task3_stack - task2_stack) str r0, [r1] adds r1, #(stack_end - task3_stack) str r0, [r1] ldr r10, =main_task @ current task pointer is always in r10. 1: bl yield @ In a yielding loop, ldr r0, =n_live_kids @ wait for n_live_kids to drop to 0. ldr r1, [r0] cmp r1, #0 bne 1b movs r0, '\n @ to be goofy, let's do output from the stack push {r0} movs r0, #1 @ file descriptor to write to mov r1, sp @ load address of \n on stack movs r2, #1 bl write pop {r0} movs r0, #0 b exit .thumb_func exit: movs r7, #1 @ exit system call number svc 0 @ invoke system call .thumb_func write: movs r7, #4 @ system call number for write svc 0 bx lr @ Tasks, initialized at compile time, instead of runtime as in @ einkornix-test.c. .bss .align 8 task1_stack: .fill 512 task2_stack: .fill 512 task3_stack: .fill 512 stack_end: .data n_live_kids: .word 3 main_task: .word 0, first_kid @ The -32 offset is because yield saves and restores 8 @ registers (32 bytes). The last one is the program counter. first_kid: .word task2_stack-32, second_kid, 3, 'A second_kid: .word task3_stack-32, third_kid, 7, 32 @ space third_kid: .word stack_end-32, main_task, 13, 'b .text .thumb_func run_writer: ldr r8, [r10, #8] @ load thread-local count into callee-saved r8 1: movs r0, #1 @ file descriptor 1 add r1, r10, #12 @ load address of thread-local character movs r2, #1 @ byte length 1 bl write bl yield subs r8, #1 bne 1b @ only loop until count reaches 0 ldr r0, =n_live_kids ldr r1, [r0] subs.n r1, #1 str r1, [r0] 1: bl yield @ now yield in an infinite loop until shutdown b 1b @ (after main thread calls exit it won't yield again) .thumb_func yield: push {r4-r9, r11, lr} @ save all callee-saved regs except r10 str sp, [r10], #4 @ save stack pointer in current task ldr r10, [r10] @ load pointer to next task ldr sp, [r10] @ switch to next task's stack pop {r4-r9, r11, pc} @ return into yielded context there