├── .gitattributes ├── README.md ├── SH ├── tn_port_asm.h ├── tn_port_config.h ├── tn_port_gas.S └── tn_port_sh.c ├── skeleton └── tn_user.c ├── tn.c ├── tn.h ├── tn_dqueue.c ├── tn_event.c ├── tn_mem.c ├── tn_mutex.c ├── tn_port.h ├── tn_sem.c ├── tn_tasks.c ├── tn_utils.c └── tn_utils.h /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SATNKernel 2 | ========== 3 | 4 | SATNKernel is a port of the [TNKernel](http://www.tnkernel.com/ "TNKernel") real-time kernel version 2.6 for the Sega Saturn game console. 5 | In addition to porting the kernel to the SH2 architecture, modifications have been made to allow running separate instances of the kernel on the master and slave CPUs. There is no SMP support included. 6 | 7 | Estimating task stack space requirements 8 | ---------------------------------------- 9 | 10 | If an interrupt stack is not used, the absolute minimum stack size can be calculated as follows: 11 | >size of context (21) + size of stack expansion at exit (21) + 11 * number of used interrupt levels 12 | 13 | If all 15 interrupt levels are used, this equals 207 words (828 bytes). 14 | 15 | If an interrupt stack is used, this changes to: 16 | >size of context (21) + size of stack expansion at exit (21) + 4 * (number of used interrupt levels - 1) + 5 17 | 18 | The minimum size of the interrupt stack is: 19 | >11 * (number of used interrupt levels - 1) + 6 20 | 21 | If all 15 interrupt levels are used, this equals 103 words (412 bytes) for tasks and 160 words (640 bytes) for the interrupt stack. 22 | 23 | This does not account for stack space used by the tasks or interrupt handlers. 24 | 25 | Notice regarding the division unit 26 | ---------------------------------- 27 | 28 | Neither the interrupt dispatch macros nor the thread context switch routine will attempt to preserve the 29 | state of the SH7095 division peripheral. This is mainly a concern if attempting to use 30 | SATNKernel with the SGL libraries, which use [GCC library routines](http://gcc.gnu.org/onlinedocs/gccint/Integer-library-routines.html) 31 | to implement integer division using the peripheral. 32 | 33 | For the same reason all users of the SGL libraries should be mindful of using integer division inside 34 | interrupt handlers. 35 | 36 | --- 37 | 38 | The kernel is released under the BSD license as follows: 39 | 40 | SATNKernel real-time kernel for the Sega Saturn 41 | Based on TNKernel version 2.6 42 | 43 | Copyright © 2004, 2010 Yuri Tiomkin 44 | Saturn version modifications copyright © 2013 Anders Montonen 45 | All rights reserved. 46 | 47 | Permission to use, copy, modify, and distribute this software in source 48 | and binary forms and its documentation for any purpose and without fee 49 | is hereby granted, provided that the above copyright notice appear 50 | in all copies and that both that copyright notice and this permission 51 | notice appear in supporting documentation. 52 | 53 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 54 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 55 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 56 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 57 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 58 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 59 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 60 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 61 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 62 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 63 | SUCH DAMAGE. 64 | -------------------------------------------------------------------------------- /SH/tn_port_asm.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #include "tn_port_config.h" 31 | 32 | /* Kernel context structure offsets in bytes */ 33 | #define TN_KERN_CURR_RUN_TASK 0 34 | #define TN_KERN_NEXT_TASK_TO_RUN 4 35 | #define TN_KERN_SYSTEM_STATE 8 36 | #define TN_KERN_NEST_COUNT 12 37 | #define TN_USER_SP 32 38 | #define TN_INT_SP 36 39 | 40 | 41 | /* Interrupt handler wrapper macro */ 42 | !----------------------------------------------------------------------------- 43 | .macro _tn_cpu_irq_isr handlerfunc:req 44 | ! create working space (may always go on user stack) 45 | mov.l r0, @-r15 46 | mov.l r1, @-r15 47 | 48 | ! disable interrupts 49 | stc sr, r1 50 | mov #0xf0, r0 51 | ldc r0, sr 52 | 53 | mov.l r2, @-r15 54 | 55 | ! increase nesting count 56 | mov #TN_KERNEL_VECTOR, r0 57 | shll2 r0 58 | stc vbr, r2 59 | mov.l @(r0, r2), r2 60 | 61 | mov.l @(TN_KERN_NEST_COUNT, r2), r0 62 | add #1, r0 63 | #ifdef TN_INT_STACK 64 | cmp/eq #1, r0 65 | bf/s 1f 66 | #endif 67 | mov.l r0, @(TN_KERN_NEST_COUNT, r2) 68 | #ifdef TN_INT_STACK 69 | ! switch to interrupt stack if first interrupt 70 | mov.l r15, @(TN_USER_SP, r2) 71 | mov.l @(TN_INT_SP, r2), r15 72 | 1: 73 | #endif 74 | 75 | ! re-enable interrupts 76 | ldc r1, sr 77 | 78 | ! save rest of caller-save registers 79 | mov.l r3, @-r15 80 | mov.l r4, @-r15 81 | mov.l r5, @-r15 82 | mov.l r6, @-r15 83 | sts.l pr, @-r15 84 | 85 | ! call ISR 86 | mov.l 2f, r0 87 | jsr @r0 88 | mov.l r7, @-r15 89 | 90 | ! restore caller-save registers 91 | mov.l @r15+, r7 92 | lds.l @r15+, pr 93 | mov.l @r15+, r6 94 | mov.l @r15+, r5 95 | mov.l @r15+, r4 96 | mov.l @r15+, r3 97 | 98 | ! disable interrupts 99 | mov #TN_KERNEL_VECTOR, r0 100 | shll2 r0 101 | stc vbr, r1 102 | mov.l @(r0, r1), r1 103 | stc sr, r2 104 | mov #0xf0, r0 105 | ldc r0, sr 106 | 107 | ! decrease nesting count 108 | mov.l @(TN_KERN_NEST_COUNT, r1), r0 109 | dt r0 110 | #ifdef TN_INT_STACK 111 | bf/s 1f 112 | #endif 113 | mov.l r0, @(TN_KERN_NEST_COUNT, r1) 114 | #ifdef TN_INT_STACK 115 | ! switch to user stack if last interrupt 116 | mov.l r15, @(TN_INT_SP, r1) 117 | mov.l @(TN_USER_SP, r1), r15 118 | 1: 119 | #endif 120 | ! re-enable interrupts 121 | ldc r2, sr 122 | 123 | ! restore rest of state, 124 | ! jump to context switch routine 125 | mov.l @r15+, r2 126 | mov.l @r15+, r1 127 | mov.l 3f, r0 128 | jmp @r0 129 | mov.l @r15+, r0 130 | 131 | .align 2 132 | 2: .long \handlerfunc 133 | 3: .long _tn_switch_context_trap 134 | .endm 135 | -------------------------------------------------------------------------------- /SH/tn_port_config.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | 31 | #ifndef _TN_PORT_CONFIG_H_ 32 | #define _TN_PORT_CONFIG_H_ 33 | 34 | #define TN_CHECK_PARAM 1 35 | 36 | #define TN_MEAS_PERFORMANCE 1 37 | 38 | #define USE_MUTEXES 1 39 | 40 | #define USE_EVENTS 1 41 | 42 | /* Use interrupt stack */ 43 | #define TN_INT_STACK 1 44 | 45 | /* Vector table index used as kernel context pointer */ 46 | #define TN_KERNEL_VECTOR 106 47 | 48 | /* System trap number used for context switch routine */ 49 | #define TN_CONTEXT_SWITCH_TRAP 107 50 | 51 | /* Interrupt stack size, in words */ 52 | #define TN_INT_STACK_SIZE 170 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /SH/tn_port_gas.S: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | ffs_asm() - this is the ffs algorithm devised by D.Seal and posted to 11 | comp.sys.arm on 16 Feb 1994. 12 | 13 | Interrupt context switch - this source code is derived on code 14 | written by WellsK 15 | 16 | 17 | Permission to use, copy, modify, and distribute this software in source 18 | and binary forms and its documentation for any purpose and without fee 19 | is hereby granted, provided that the above copyright notice appear 20 | in all copies and that both that copyright notice and this permission 21 | notice appear in supporting documentation. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 24 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 27 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 | SUCH DAMAGE. 34 | 35 | */ 36 | 37 | #include "tn_port_asm.h" 38 | #include "tn_port_config.h" 39 | 40 | .text 41 | 42 | ! External references 43 | 44 | ! Public functions declared in this file 45 | 46 | .global _tn_switch_context_exit 47 | .global _tn_switch_context 48 | .global _tn_switch_context_trap 49 | .global _tn_cpu_save_sr 50 | .global _tn_cpu_restore_sr 51 | .global _tn_start_exe 52 | .global _tn_chk_irq_disabled 53 | .global _tn_inside_int 54 | .global _ffs_asm 55 | .global _tn_kern_ctx_ptr 56 | .global _tn_hook_vec 57 | 58 | !----------------------------------------------------------------------------- 59 | ! Interrupts should be disabled here 60 | !----------------------------------------------------------------------------- 61 | _tn_start_exe: 62 | mov #TN_KERNEL_VECTOR, r0 63 | shll2 r0 64 | stc vbr, r1 65 | mov.l @(r0, r1), r1 66 | mov #1, r0 67 | mov.l r0, @(TN_KERN_SYSTEM_STATE, r1) 68 | 69 | ! set highest priority task to current 70 | mov.l @(TN_KERN_NEXT_TASK_TO_RUN, r1), r0 71 | mov.l r0, @(TN_KERN_CURR_RUN_TASK, r1) 72 | ! switch to new stack pointer 73 | mov.l @r0, r15 74 | 75 | lds.l @r15+, pr 76 | ldc.l @r15+, gbr 77 | lds.l @r15+, mach 78 | lds.l @r15+, macl 79 | mov.l @r15+, r14 80 | mov.l @r15+, r13 81 | mov.l @r15+, r12 82 | mov.l @r15+, r11 83 | mov.l @r15+, r10 84 | mov.l @r15+, r9 85 | mov.l @r15+, r8 86 | mov.l @r15+, r7 87 | mov.l @r15+, r6 88 | mov.l @r15+, r5 89 | mov.l @r15+, r4 90 | mov.l @r15+, r3 91 | mov.l @r15+, r2 92 | mov.l @r15+, r1 93 | mov.l @r15+, r0 94 | rte 95 | nop 96 | 97 | !----------------------------------------------------------------------------- 98 | ! Interrupts should be disabled here 99 | !----------------------------------------------------------------------------- 100 | _tn_switch_context_exit: 101 | mov #TN_KERNEL_VECTOR, r0 102 | shll2 r0 103 | stc vbr, r1 104 | mov.l @(r0, r1), r1 105 | 106 | ! set highest priority task to current 107 | mov.l @(TN_KERN_NEXT_TASK_TO_RUN, r1), r0 108 | mov.l r0, @(TN_KERN_CURR_RUN_TASK, r1) 109 | ! switch to new stack pointer 110 | mov.l @r0, r15 111 | 112 | lds.l @r15+, pr 113 | ldc.l @r15+, gbr 114 | lds.l @r15+, mach 115 | lds.l @r15+, macl 116 | mov.l @r15+, r14 117 | mov.l @r15+, r13 118 | mov.l @r15+, r12 119 | mov.l @r15+, r11 120 | mov.l @r15+, r10 121 | mov.l @r15+, r9 122 | mov.l @r15+, r8 123 | mov.l @r15+, r7 124 | mov.l @r15+, r6 125 | mov.l @r15+, r5 126 | mov.l @r15+, r4 127 | mov.l @r15+, r3 128 | mov.l @r15+, r2 129 | mov.l @r15+, r1 130 | mov.l @r15+, r0 131 | rte 132 | nop 133 | 134 | !----------------------------------------------------------------------------- 135 | _tn_switch_context: 136 | trapa #TN_CONTEXT_SWITCH_TRAP 137 | rts 138 | nop 139 | 140 | !----------------------------------------------------------------------------- 141 | _tn_switch_context_trap: 142 | ! save registers 143 | mov.l r0, @-r15 144 | 145 | ! read saved sr 146 | mov.l @(8, r15), r0 147 | 148 | ! returning to user level? 149 | tst #0xf0, r0 150 | bf 2f 151 | 152 | ! disable interrupts 153 | mov #0xf0, r0 154 | ldc r0, sr 155 | 156 | mov.l r1, @-r15 157 | mov.l r2, @-r15 158 | 159 | mov #TN_KERNEL_VECTOR, r0 160 | shll2 r0 161 | stc vbr, r1 162 | mov.l @(r0, r1), r1 163 | mov.l @(TN_KERN_CURR_RUN_TASK, r1), r0 164 | mov.l @(TN_KERN_NEXT_TASK_TO_RUN, r1), r2 165 | ! exit if not switching tasks 166 | cmp/eq r0, r2 167 | bt 1f 168 | 169 | mov.l r3, @-r15 170 | mov.l r4, @-r15 171 | mov.l r5, @-r15 172 | mov.l r6, @-r15 173 | mov.l r7, @-r15 174 | mov.l r8, @-r15 175 | mov.l r9, @-r15 176 | mov.l r10, @-r15 177 | mov.l r11, @-r15 178 | mov.l r12, @-r15 179 | mov.l r13, @-r15 180 | mov.l r14, @-r15 181 | sts.l macl, @-r15 182 | sts.l mach, @-r15 183 | stc.l gbr, @-r15 184 | sts.l pr, @-r15 185 | 186 | ! store SP in preempted task's TCB 187 | mov.l r15, @r0 188 | ! tn_curr_run_task = tn_next_task_to_run 189 | mov.l r2, @(TN_KERN_CURR_RUN_TASK, r1) 190 | 191 | ! get new task's SP 192 | mov.l @r2, r15 193 | 194 | ! restore registers 195 | lds.l @r15+, pr 196 | ldc.l @r15+, gbr 197 | lds.l @r15+, mach 198 | lds.l @r15+, macl 199 | mov.l @r15+, r14 200 | mov.l @r15+, r13 201 | mov.l @r15+, r12 202 | mov.l @r15+, r11 203 | mov.l @r15+, r10 204 | mov.l @r15+, r9 205 | mov.l @r15+, r8 206 | mov.l @r15+, r7 207 | mov.l @r15+, r6 208 | mov.l @r15+, r5 209 | mov.l @r15+, r4 210 | mov.l @r15+, r3 211 | 1: 212 | mov.l @r15+, r2 213 | mov.l @r15+, r1 214 | 2: 215 | mov.l @r15+, r0 216 | rte 217 | nop 218 | 219 | !----------------------------------------------------------------------------- 220 | _tn_cpu_save_sr: 221 | mov #0xf0, r1 222 | stc sr, r0 223 | extu.b r1, r1 224 | or r0, r1 225 | rts 226 | ldc r1, sr 227 | 228 | !----------------------------------------------------------------------------- 229 | _tn_cpu_restore_sr: 230 | rts 231 | ldc r4, sr 232 | 233 | !----------------------------------------------------------------------------- 234 | _tn_chk_irq_disabled: 235 | stc sr, r0 236 | and #0xf0, r0 237 | cmp/eq #0xf0, r0 238 | rts 239 | movt r0 240 | 241 | !----------------------------------------------------------------------------- 242 | _tn_inside_int: 243 | mov #TN_KERNEL_VECTOR, r0 244 | shll2 r0 245 | stc vbr, r1 246 | mov.l @(r0, r1), r1 247 | mov.l @(TN_KERN_NEST_COUNT, r1), r0 248 | cmp/eq #0, r0 249 | movt r0 250 | rts 251 | xor #1, r0 252 | 253 | !----------------------------------------------------------------------------- 254 | _ffs_asm: 255 | neg r4, r0 256 | and r4, r0 257 | cmp/eq #0, r0 258 | bt .Lffs_ret 259 | 260 | sts macl, r2 261 | mov.l ffs_hash, r1 262 | mul.l r0, r1 263 | mova ffs_table, r0 264 | sts macl, r1 265 | lds r2, macl 266 | 267 | shlr16 r1 268 | shlr8 r1 269 | shlr2 r1 270 | 271 | mov.b @(r0, r1), r0 272 | 273 | .Lffs_ret: 274 | rts 275 | nop 276 | 277 | .align 2 278 | ffs_hash: 279 | .long 0x0450fbaf 280 | 281 | ffs_table: 282 | /* 0 1 2 3 4 5 6 7 */ 283 | .byte 0, 1, 2, 13, 3, 7, 0, 14 /* 0- 7 */ 284 | .byte 4, 0, 8, 0, 0, 0, 0, 15 /* 8-15 */ 285 | .byte 11, 5, 0, 0, 9, 0, 0, 26 /* 16-23 */ 286 | .byte 0, 0, 0, 0, 0, 22, 28, 16 /* 24-31 */ 287 | .byte 32, 12, 6, 0, 0, 0, 0, 0 /* 32-39 */ 288 | .byte 10, 0, 0, 25, 0, 0, 21, 27 /* 40-47 */ 289 | .byte 31, 0, 0, 0, 0, 24, 0, 20 /* 48-55 */ 290 | .byte 30, 0, 23, 19, 29, 18, 17, 0 /* 56-63 */ 291 | 292 | !----------------------------------------------------------------------------- 293 | _tn_kern_ctx_ptr: 294 | mov #TN_KERNEL_VECTOR, r0 295 | shll2 r0 296 | stc vbr, r1 297 | rts 298 | mov.l @(r0, r1), r0 299 | 300 | !----------------------------------------------------------------------------- 301 | ! Interrupts should be disabled here 302 | !----------------------------------------------------------------------------- 303 | ! r4 = vector number 304 | ! r5 = pointer 305 | ! returns previous value 306 | _tn_hook_vec: 307 | stc vbr, r0 308 | shll2 r4 309 | add r0, r4 310 | mov.l @r4, r0 311 | rts 312 | mov.l r5, @r4 313 | 314 | !----------------------------------------------------------------------------- 315 | !----------------------------------------------------------------------------- 316 | !----------------------------------------------------------------------------- 317 | -------------------------------------------------------------------------------- /SH/tn_port_sh.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #include "tn.h" 31 | 32 | 33 | //---------------------------------------------------------------------------- 34 | // Processor specific routine - here for SuperH 35 | // sizeof(void*) = sizeof(int) 36 | //---------------------------------------------------------------------------- 37 | unsigned int * tn_stack_init(void * task_func, 38 | void * stack_start, 39 | void * param) 40 | { 41 | unsigned int * stk; 42 | 43 | //-- filling register's position in the stack - for debugging only 44 | 45 | stk = (unsigned int *)stack_start; //-- Load stack pointer 46 | 47 | *stk-- = 0L; //-- SR 48 | *stk-- = (unsigned int)task_func; //-- PC - Entry Point 49 | *stk-- = 0x00000000L; //-- R0 50 | *stk-- = 0x01010101L; //-- R1 51 | *stk-- = 0x02020202L; //-- R2 52 | *stk-- = 0x03030303L; //-- R3 53 | *stk-- = (unsigned int)param; //-- R4 - task's function argument 54 | *stk-- = 0x05050505L; //-- R5 55 | *stk-- = 0x06060606L; //-- R6 56 | *stk-- = 0x07070707L; //-- R7 57 | *stk-- = 0x08080808L; //-- R8 58 | *stk-- = 0x09090909L; //-- R9 59 | *stk-- = 0x10101010L; //-- R10 60 | *stk-- = 0x11111111L; //-- R11 61 | *stk-- = 0x12121212L; //-- R12 62 | *stk-- = 0x13131313L; //-- R13 63 | *stk-- = 0x14141414L; //-- R14 64 | *stk-- = 0x16161616L; //-- MACL 65 | *stk-- = 0x17171717L; //-- MACH 66 | *stk-- = 0x18181818L; //-- GBR 67 | *stk = (unsigned int)tn_task_exit; //-- PR 68 | 69 | return stk; 70 | } 71 | 72 | //---------------------------------------------------------------------------- 73 | //---------------------------------------------------------------------------- 74 | //---------------------------------------------------------------------------- 75 | //---------------------------------------------------------------------------- 76 | -------------------------------------------------------------------------------- /skeleton/tn_user.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.6 5 | 6 | Copyright © 2004, 2010 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #include "tn.h" 31 | 32 | TN_KERN_CTX kernelcontext; 33 | 34 | //---------------------------------------------------------------------------- 35 | // Timer tick handler - must be called periodically from timer interrupt 36 | //---------------------------------------------------------------------------- 37 | void tick_handler(void) 38 | { 39 | tn_tick_int_processing(); 40 | } 41 | 42 | //---------------------------------------------------------------------------- 43 | // Main function 44 | //---------------------------------------------------------------------------- 45 | int main(void) 46 | { 47 | // disable interrupts 48 | 49 | // perform hardware initialization 50 | 51 | // start kernel - does not return 52 | tn_start_system(&kernelcontext); 53 | 54 | return 1; 55 | } 56 | 57 | //---------------------------------------------------------------------------- 58 | // Application initialization callback - create and init OS objects here 59 | //---------------------------------------------------------------------------- 60 | void tn_app_init(void) 61 | { 62 | } 63 | 64 | //---------------------------------------------------------------------------- 65 | // CPU-specific function that enables interrupts 66 | //---------------------------------------------------------------------------- 67 | void tn_cpu_int_enable(void) 68 | { 69 | } 70 | -------------------------------------------------------------------------------- /tn.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #include "tn.h" 31 | #include "tn_utils.h" 32 | #include "tn_port_config.h" 33 | 34 | // The System uses two levels of priorities for the own purpose: 35 | // 36 | // - level 0 (highest) for system timers task 37 | // - level (TN_NUM_PRIORITY-1) (lowest) for system idle task 38 | 39 | //-- System tasks 40 | 41 | //-- timer task - priority 0 - highest 42 | 43 | static void tn_timer_task_func(void * par); 44 | 45 | //-- idle task - priority (TN_NUM_PRIORITY-1) - lowest 46 | 47 | static void tn_idle_task_func(void * par); 48 | 49 | 50 | //---------------------------------------------------------------------------- 51 | // TN main function (never return) 52 | //---------------------------------------------------------------------------- 53 | void tn_start_system(TN_KERN_CTX *kctx) 54 | { 55 | int i; 56 | 57 | //-- Hook vectors 58 | tn_hook_vec(TN_KERNEL_VECTOR, kctx); 59 | tn_hook_vec(TN_CONTEXT_SWITCH_TRAP, tn_switch_context_trap); 60 | 61 | //-- Clear/set all globals (vars, lists, etc) 62 | 63 | for (i = 0; i < TN_NUM_PRIORITY; i++) 64 | { 65 | queue_reset(&(kctx->tn_ready_list[i])); 66 | kctx->tn_tslice_ticks[i] = NO_TIME_SLICE; 67 | } 68 | 69 | queue_reset(&kctx->tn_create_queue); 70 | kctx->tn_created_tasks_qty = 0; 71 | 72 | kctx->tn_system_state = TN_ST_STATE_NOT_RUN; 73 | 74 | kctx->tn_int_nest_count = 0; 75 | 76 | kctx->tn_ready_to_run_bmp = 0; 77 | 78 | kctx->tn_idle_count = 0; 79 | kctx->tn_curr_performance = 0; 80 | 81 | kctx->tn_next_task_to_run = NULL; 82 | kctx->tn_curr_run_task = NULL; 83 | 84 | #ifdef TN_INT_STACK 85 | // SH uses pre-decrement stack pointer 86 | kctx->tn_int_sp = &kctx->tn_int_stack[TN_INT_STACK_SIZE]; 87 | #endif 88 | 89 | //-- System tasks 90 | 91 | queue_reset(&kctx->tn_wait_timeout_list); 92 | 93 | //--- Timer task 94 | 95 | tn_task_create(&kctx->tn_timer_task, //-- task TCB 96 | tn_timer_task_func, //-- task function 97 | 0, //-- task priority 98 | &(kctx->tn_timer_task_stack //-- task stack first addr in memory 99 | [TN_TIMER_STACK_SIZE-1]), 100 | TN_TIMER_STACK_SIZE, //-- task stack size (in int,not bytes) 101 | NULL, //-- task function parameter 102 | TN_TASK_TIMER); //-- Creation option 103 | 104 | //--- Idle task 105 | 106 | tn_task_create(&kctx->tn_idle_task, //-- task TCB 107 | tn_idle_task_func, //-- task function 108 | TN_NUM_PRIORITY-1, //-- task priority 109 | &(kctx->tn_idle_task_stack //-- task stack first addr in memory 110 | [TN_IDLE_STACK_SIZE-1]), 111 | TN_IDLE_STACK_SIZE, //-- task stack size (in int,not bytes) 112 | NULL, //-- task function parameter 113 | TN_TASK_IDLE); //-- Creation option 114 | 115 | //-- Activate timer & idle tasks 116 | 117 | kctx->tn_next_task_to_run = &kctx->tn_idle_task; //-- Just for the task_to_runnable() proper op 118 | 119 | task_to_runnable(&kctx->tn_idle_task); 120 | task_to_runnable(&kctx->tn_timer_task); 121 | 122 | kctx->tn_curr_run_task = &kctx->tn_idle_task; //-- otherwise it is NULL 123 | 124 | //-- Run OS - first context switch 125 | 126 | tn_start_exe(); 127 | } 128 | 129 | //---------------------------------------------------------------------------- 130 | static void tn_timer_task_func(void * par) 131 | { 132 | TN_INTSAVE_DATA 133 | volatile TN_TCB * task; 134 | volatile CDLL_QUEUE * curr_que; 135 | TN_KERN_CTX * kctx; 136 | 137 | //-- User application init - user's objects initial (tasks etc.) creation 138 | 139 | tn_app_init(); 140 | 141 | //-- Enable interrupt here (include tick int) 142 | 143 | tn_cpu_int_enable(); 144 | 145 | kctx = tn_kern_ctx_ptr(); 146 | 147 | //------------------------------------------------------------------------- 148 | 149 | for (;;) 150 | { 151 | 152 | //------------ OS timer tick ------------------------------------- 153 | 154 | tn_disable_interrupt(); 155 | 156 | curr_que = kctx->tn_wait_timeout_list.next; 157 | while (curr_que != &kctx->tn_wait_timeout_list) 158 | { 159 | task = get_task_by_timer_queque((CDLL_QUEUE*)curr_que); 160 | if (task->tick_count != TN_WAIT_INFINITE) 161 | { 162 | if (task->tick_count > 0) 163 | { 164 | task->tick_count--; 165 | if (task->tick_count == 0) //-- Time out expiried 166 | { 167 | queue_remove_entry(&(((TN_TCB*)task)->task_queue)); 168 | task_wait_complete((TN_TCB*)task); 169 | task->task_wait_rc = TERR_TIMEOUT; 170 | } 171 | } 172 | } 173 | 174 | curr_que = curr_que->next; 175 | } 176 | 177 | task_curr_to_wait_action(NULL, 178 | TSK_WAIT_REASON_SLEEP, 179 | TN_WAIT_INFINITE); 180 | tn_enable_interrupt(); 181 | 182 | tn_switch_context(); 183 | } 184 | } 185 | 186 | //---------------------------------------------------------------------------- 187 | // In fact, this task is always in RUNNABLE state 188 | //---------------------------------------------------------------------------- 189 | static void tn_idle_task_func(void * par) 190 | { 191 | TN_KERN_CTX * kctx; 192 | 193 | #ifdef TN_MEAS_PERFORMANCE 194 | TN_INTSAVE_DATA 195 | #endif 196 | 197 | kctx = tn_kern_ctx_ptr(); 198 | 199 | for(;;) 200 | { 201 | #ifdef TN_MEAS_PERFORMANCE 202 | tn_disable_interrupt(); 203 | #endif 204 | 205 | kctx->tn_idle_count++; 206 | 207 | #ifdef TN_MEAS_PERFORMANCE 208 | tn_enable_interrupt(); 209 | #endif 210 | } 211 | } 212 | 213 | //--- Set time slice ticks value for priority for round-robin scheduling 214 | //--- If value is NO_TIME_SLICE there are no round-robin scheduling 215 | //--- for tasks with priority. NO_TIME_SLICE is default value. 216 | //---------------------------------------------------------------------------- 217 | int tn_sys_tslice_ticks(int priority, int value) 218 | { 219 | TN_CHECK_NON_INT_CONTEXT 220 | 221 | if (priority <= 0 || priority >= TN_NUM_PRIORITY-1 || 222 | value < 0 || value > MAX_TIME_SLICE) 223 | return TERR_WRONG_PARAM; 224 | 225 | tn_kern_ctx_ptr()->tn_tslice_ticks[priority] = value; 226 | 227 | return TERR_NO_ERR; 228 | } 229 | 230 | //---------------------------------------------------------------------------- 231 | void tn_tick_int_processing() 232 | { 233 | TN_INTSAVE_DATA_INT 234 | 235 | volatile CDLL_QUEUE * curr_que; //-- Need volatile here only to solve 236 | volatile CDLL_QUEUE * pri_queue; //-- IAR(c) compiler's high optimization mode problem 237 | volatile int priority; 238 | 239 | TN_KERN_CTX * kctx; 240 | 241 | TN_CHECK_INT_CONTEXT_NORETVAL 242 | 243 | kctx = tn_kern_ctx_ptr(); 244 | 245 | tn_idisable_interrupt(); 246 | 247 | //------- Round -robin (if is used) 248 | 249 | priority = kctx->tn_curr_run_task->priority; 250 | 251 | if (kctx->tn_tslice_ticks[priority] != NO_TIME_SLICE) 252 | { 253 | kctx->tn_curr_run_task->tslice_count++; 254 | if (kctx->tn_curr_run_task->tslice_count > kctx->tn_tslice_ticks[priority]) 255 | { 256 | kctx->tn_curr_run_task->tslice_count = 0; 257 | 258 | pri_queue = &(kctx->tn_ready_list[priority]); 259 | //-- If ready queue is not empty and qty of queue's tasks > 1 260 | if (!(is_queue_empty((CDLL_QUEUE *)pri_queue)) && pri_queue->next->next != pri_queue) 261 | { 262 | // v.2.7 - Thanks to Vyacheslav Ovsiyenko 263 | 264 | //-- Remove task from head and add it to the tail of 265 | //-- ready queue for current priority 266 | 267 | curr_que = queue_remove_head(&(kctx->tn_ready_list[priority])); 268 | queue_add_tail(&(kctx->tn_ready_list[priority]), (CDLL_QUEUE *)curr_que); 269 | } 270 | } 271 | } 272 | 273 | //-- Enable a task with priority 0 - tn_timer_task 274 | 275 | queue_remove_entry(&(kctx->tn_timer_task.task_queue)); 276 | kctx->tn_timer_task.task_wait_reason = 0; 277 | kctx->tn_timer_task.task_state = TSK_STATE_RUNNABLE; 278 | kctx->tn_timer_task.pwait_queue = NULL; 279 | kctx->tn_timer_task.task_wait_rc = TERR_NO_ERR; 280 | 281 | queue_add_tail(&(kctx->tn_ready_list[0]), &(kctx->tn_timer_task.task_queue)); 282 | kctx->tn_ready_to_run_bmp |= 1; // priority 0; 283 | 284 | kctx->tn_next_task_to_run = &kctx->tn_timer_task; 285 | 286 | tn_ienable_interrupt(); //-- !!! thanks to Audrius Urmanavicius !!! 287 | } 288 | 289 | //---------------------------------------------------------------------------- 290 | //---------------------------------------------------------------------------- 291 | //---------------------------------------------------------------------------- 292 | //---------------------------------------------------------------------------- 293 | -------------------------------------------------------------------------------- /tn.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #ifndef _TH_H_ 31 | #define _TH_H_ 32 | 33 | //--- Types 34 | 35 | #ifndef BOOL 36 | #define BOOL int 37 | #endif 38 | 39 | #ifndef TRUE 40 | #define TRUE 1 41 | #endif 42 | 43 | #ifndef FALSE 44 | #define FALSE 0 45 | #endif 46 | 47 | #ifndef NULL 48 | #define NULL 0 49 | #endif 50 | 51 | //--- The system configuration (change it for your particular project) 52 | 53 | #include "tn_port_config.h" 54 | 55 | //--- Port 56 | 57 | #include "tn_port.h" 58 | 59 | 60 | //--- Constants 61 | 62 | #define TN_ST_STATE_NOT_RUN 0 63 | #define TN_ST_STATE_RUNNING 1 64 | 65 | 66 | #define TN_TASK_START_ON_CREATION 1 67 | #define TN_TASK_TIMER 0x80 68 | #define TN_TASK_IDLE 0x40 69 | 70 | #define TN_ID_TASK ((int)0x47ABCF69) 71 | #define TN_ID_SEMAPHORE ((int)0x6FA173EB) 72 | #define TN_ID_EVENT ((int)0x5E224F25) 73 | #define TN_ID_DATAQUEUE ((int)0x8C8A6C89) 74 | #define TN_ID_FSMEMORYPOOL ((int)0x26B7CE8B) 75 | #define TN_ID_MUTEX ((int)0x17129E45) 76 | #define TN_ID_RENDEZVOUS ((int)0x74289EBD) 77 | 78 | 79 | #define TN_EXIT_AND_DELETE_TASK 1 80 | 81 | //-- Task states 82 | 83 | #define TSK_STATE_RUNNABLE 0x01 84 | #define TSK_STATE_WAIT 0x04 85 | #define TSK_STATE_SUSPEND 0x08 86 | #define TSK_STATE_WAITSUSP (TSK_STATE_SUSPEND | TSK_STATE_WAIT) 87 | #define TSK_STATE_DORMANT 0x10 88 | 89 | //--- Waiting 90 | 91 | #define TSK_WAIT_REASON_SLEEP 0x0001 92 | #define TSK_WAIT_REASON_SEM 0x0002 93 | #define TSK_WAIT_REASON_EVENT 0x0004 94 | #define TSK_WAIT_REASON_DQUE_WSEND 0x0008 95 | #define TSK_WAIT_REASON_DQUE_WRECEIVE 0x0010 96 | #define TSK_WAIT_REASON_MUTEX_C 0x0020 //-- ver 2.x 97 | #define TSK_WAIT_REASON_MUTEX_C_BLK 0x0040 //-- ver 2.x 98 | #define TSK_WAIT_REASON_MUTEX_I 0x0080 //-- ver 2.x 99 | #define TSK_WAIT_REASON_MUTEX_H 0x0100 //-- ver 2.x 100 | #define TSK_WAIT_REASON_RENDEZVOUS 0x0200 //-- ver 2.x 101 | #define TSK_WAIT_REASON_WFIXMEM 0x2000 102 | 103 | #define TN_EVENT_ATTR_SINGLE 1 104 | #define TN_EVENT_ATTR_MULTI 2 105 | #define TN_EVENT_ATTR_CLR 4 106 | 107 | #define TN_EVENT_WCOND_OR 8 108 | #define TN_EVENT_WCOND_AND 0x10 109 | 110 | #define TN_MUTEX_ATTR_CEILING 1 111 | #define TN_MUTEX_ATTR_INHERIT 2 112 | 113 | //-- Errors 114 | 115 | #define TERR_NO_ERR 0 116 | #define TERR_OVERFLOW (-1) //-- OOV 117 | #define TERR_WCONTEXT (-2) //-- Wrong context context error 118 | #define TERR_WSTATE (-3) //-- Wrong state state error 119 | #define TERR_TIMEOUT (-4) //-- Polling failure or timeout 120 | #define TERR_WRONG_PARAM (-5) 121 | #define TERR_UNDERFLOW (-6) 122 | #define TERR_OUT_OF_MEM (-7) 123 | #define TERR_ILUSE (-8) //-- Illegal using 124 | #define TERR_NOEXS (-9) //-- Non-valid or Non-existent object 125 | #define TERR_DLT (-10) //-- Waiting object deleted 126 | 127 | #define NO_TIME_SLICE 0 128 | #define MAX_TIME_SLICE 0xFFFE 129 | 130 | //-- Circular double-linked list queue - for internal using 131 | 132 | typedef struct _CDLL_QUEUE 133 | { 134 | struct _CDLL_QUEUE * prev; 135 | struct _CDLL_QUEUE * next; 136 | }CDLL_QUEUE; 137 | 138 | //-- Task Control Block -- 139 | 140 | typedef struct _TN_TCB 141 | { 142 | unsigned int * task_stk; //-- Pointer to task's top of stack 143 | CDLL_QUEUE task_queue; //-- Queue is used to include task in ready/wait lists 144 | CDLL_QUEUE timer_queue; //-- Queue is used to include task in timer (timeout,etc.) list 145 | CDLL_QUEUE * pwait_queue; //-- Ptr to object's (semaphor,event,etc.) wait list, 146 | // that task has been included for waiting (ver 2.x) 147 | CDLL_QUEUE create_queue; //-- Queue is used to include task in create list only 148 | 149 | #ifdef USE_MUTEXES 150 | 151 | CDLL_QUEUE mutex_queue; //-- List of all mutexes that tack locked (ver 2.x) 152 | 153 | #endif 154 | 155 | unsigned int * stk_start; //-- Base address of task's stack space 156 | int stk_size; //-- Task's stack size (in sizeof(void*),not bytes) 157 | void * task_func_addr; //-- filled on creation (ver 2.x) 158 | void * task_func_param; //-- filled on creation (ver 2.x) 159 | 160 | int base_priority; //-- Task base priority (ver 2.x) 161 | int priority; //-- Task current priority 162 | int id_task; //-- ID for verification (is it a task or another object?) 163 | // All tasks have the same id_task magic number (ver 2.x) 164 | 165 | int task_state; //-- Task state 166 | int task_wait_reason; //-- Reason for waiting 167 | int task_wait_rc; //-- Waiting return code (reason why waiting finished) 168 | unsigned long tick_count; //-- Remaining time until timeout 169 | int tslice_count; //-- Time slice counter 170 | 171 | #ifdef USE_EVENTS 172 | 173 | int ewait_pattern; //-- Event wait pattern 174 | int ewait_mode; //-- Event wait mode: _AND or _OR 175 | 176 | #endif 177 | 178 | void * data_elem; //-- Store data queue entry,if data queue is full 179 | 180 | int activate_count; //-- Activation request count - for statistic 181 | int wakeup_count; //-- Wakeup request count - for statistic 182 | int suspend_count; //-- Suspension count - for statistic 183 | 184 | // Other implementation specific fields may be added below 185 | 186 | }TN_TCB; 187 | 188 | //----- Semaphore ----- 189 | 190 | typedef struct _TN_SEM 191 | { 192 | CDLL_QUEUE wait_queue; 193 | int count; 194 | int max_count; 195 | int id_sem; //-- ID for verification(is it a semaphore or another object?) 196 | // All semaphores have the same id_sem magic number (ver 2.x) 197 | }TN_SEM; 198 | 199 | //----- Eventflag ----- 200 | 201 | typedef struct _TN_EVENT 202 | { 203 | CDLL_QUEUE wait_queue; 204 | int attr; //-- Eventflag attribute 205 | unsigned int pattern; //-- Initial value of the eventflag bit pattern 206 | int id_event; //-- ID for verification (is it a event or another object?) 207 | // All events have the same id_event magic number (ver 2.x) 208 | }TN_EVENT; 209 | 210 | //----- Data queue ----- 211 | 212 | typedef struct _TN_DQUE 213 | { 214 | CDLL_QUEUE wait_send_list; 215 | CDLL_QUEUE wait_receive_list; 216 | 217 | void ** data_fifo; //-- Array of void* to store data queue entries 218 | int num_entries; //-- Capacity of data_fifo (num entries) 219 | int tail_cnt; //-- Counter to processing data queue's Array of void* 220 | int header_cnt; //-- Counter to processing data queue's Array of void* 221 | int id_dque; //-- ID for verification(is it a data queue or another object?) 222 | // All data queues have the same id_dque magic number (ver 2.x) 223 | }TN_DQUE; 224 | 225 | //----- Fixed-sized blocks memory pool -------------- 226 | 227 | typedef struct _TN_FMP 228 | { 229 | CDLL_QUEUE wait_queue; 230 | 231 | unsigned int block_size; //-- Actual block size (in bytes) 232 | int num_blocks; //-- Capacity (Fixed-sized blocks actual max qty) 233 | void * start_addr; //-- Memory pool actual start address 234 | void * free_list; //-- Ptr to free block list 235 | int fblkcnt; //-- Num of free blocks 236 | int id_fmp; //-- ID for verification (is it a fixed-sized blocks memory pool or another object?) 237 | // All Fixed-sized blocks memory pool have the same id_fmp magic number (ver 2.x) 238 | }TN_FMP; 239 | 240 | 241 | //----- Mutex ------------ 242 | 243 | typedef struct _TN_MUTEX 244 | { 245 | CDLL_QUEUE wait_queue; //-- List of tasks that wait a mutex 246 | CDLL_QUEUE mutex_queue; //-- To include in task's locked mutexes list (if any) 247 | CDLL_QUEUE lock_mutex_queue; //-- To include in system's locked mutexes list 248 | int attr; //-- Mutex creation attr - CEILING or INHERIT 249 | 250 | TN_TCB * holder; //-- Current mutex owner (task that locked mutex) 251 | int ceil_priority; //-- When mutex created with CEILING attr 252 | int cnt; //-- Reserved 253 | int id_mutex; //-- ID for verification (is it a mutex or another object?) 254 | // All mutexes have the same id_mutex magic number (ver 2.x) 255 | }TN_MUTEX; 256 | 257 | //----- Kernel context ------------ 258 | 259 | typedef struct _TN_KERN_CTX 260 | { 261 | TN_TCB * tn_curr_run_task; //-- Task that run now 262 | TN_TCB * tn_next_task_to_run; //-- Task to be run after switch context 263 | 264 | volatile int tn_system_state; //-- System state - (running/not running,etc.) 265 | volatile int tn_int_nest_count; //-- interrupt nesting count 266 | volatile int tn_created_tasks_qty; //-- num of created tasks 267 | 268 | volatile unsigned int tn_ready_to_run_bmp; 269 | volatile unsigned long tn_idle_count; 270 | volatile unsigned long tn_curr_performance; 271 | 272 | #ifdef TN_INT_STACK 273 | void * tn_user_sp; 274 | void * tn_int_sp; 275 | #endif 276 | 277 | CDLL_QUEUE tn_wait_timeout_list; //-- all tasks that wait timeout expiration 278 | CDLL_QUEUE tn_create_queue; //-- all created tasks (now - for statictic only) 279 | 280 | TN_TCB tn_timer_task; 281 | TN_TCB tn_idle_task; 282 | 283 | unsigned short tn_tslice_ticks[TN_NUM_PRIORITY]; //-- for round-robin only 284 | 285 | unsigned int tn_timer_task_stack[TN_TIMER_STACK_SIZE]; 286 | unsigned int tn_idle_task_stack[TN_IDLE_STACK_SIZE]; 287 | #ifdef TN_INT_STACK 288 | unsigned int tn_int_stack[TN_INT_STACK_SIZE]; 289 | #endif 290 | 291 | CDLL_QUEUE tn_ready_list[TN_NUM_PRIORITY]; //-- all ready to run (RUNNABLE) tasks 292 | }TN_KERN_CTX; 293 | 294 | //-- Thanks to Vyacheslav Ovsiyenko - for his highly optimized code 295 | 296 | #ifndef CONTAINING_RECORD 297 | #define CONTAINING_RECORD(address, type, field) \ 298 | ((type *)((unsigned char *)(address) - (unsigned char *)(&((type *)0)->field))) 299 | #endif 300 | 301 | //-- v.2.7 302 | 303 | #define get_task_by_tsk_queue(que) \ 304 | que ? CONTAINING_RECORD(que, TN_TCB, task_queue) : 0 305 | 306 | #define get_task_by_timer_queque(que) \ 307 | que ? CONTAINING_RECORD(que, TN_TCB, timer_queue) : 0 308 | 309 | #define get_mutex_by_mutex_queque(que) \ 310 | que ? CONTAINING_RECORD(que, TN_MUTEX, mutex_queue) : 0 311 | 312 | #define get_mutex_by_wait_queque(que) \ 313 | que ? CONTAINING_RECORD(que, TN_MUTEX, wait_queue) : 0 314 | 315 | #define get_task_by_block_queque(que) \ 316 | que ? CONTAINING_RECORD(que, TN_TCB, block_queue) : 0 317 | 318 | #define get_mutex_by_lock_mutex_queque(que) \ 319 | que ? CONTAINING_RECORD(que, TN_MUTEX, mutex_queue) : 0 320 | 321 | #ifdef __cplusplus 322 | extern "C" { 323 | #endif 324 | 325 | //--- User function 326 | 327 | void tn_app_init(void); 328 | 329 | //----- tn.c ---------------------------------- 330 | 331 | void tn_start_system(TN_KERN_CTX *kctx); 332 | void tn_tick_int_processing(void); 333 | int tn_sys_tslice_ticks(int priority, int value); 334 | 335 | //----- tn_tasks.c ---------------------------------- 336 | 337 | int tn_task_create(TN_TCB * task, 338 | void (*task_func)(void *param), 339 | int priority, 340 | unsigned int * task_stack_start, 341 | int task_stack_size, 342 | void * param, 343 | int option); 344 | int tn_task_suspend(TN_TCB * task); 345 | int tn_task_resume(TN_TCB * task); 346 | int tn_task_sleep(unsigned long timeout); 347 | int tn_task_wakeup(TN_TCB * task); 348 | int tn_task_iwakeup(TN_TCB * task); 349 | int tn_task_activate(TN_TCB * task); 350 | int tn_task_iactivate(TN_TCB * task); 351 | int tn_task_release_wait(TN_TCB * task); 352 | int tn_task_irelease_wait(TN_TCB * task); 353 | void tn_task_exit(int attr); 354 | int tn_task_terminate(TN_TCB * task); 355 | int tn_task_delete(TN_TCB * task); 356 | int tn_task_change_priority(TN_TCB * task, int new_priority); 357 | 358 | //-- Routines 359 | 360 | void task_set_dormant_state(TN_TCB* task); 361 | void task_to_non_runnable(TN_TCB * task); 362 | void task_to_runnable(TN_TCB * task); 363 | int task_wait_complete(TN_TCB * task); 364 | void task_curr_to_wait_action(CDLL_QUEUE * wait_que, 365 | int wait_reason, 366 | unsigned long timeout); 367 | int change_running_task_priority(TN_TCB * task, int new_priority); 368 | void set_current_priority(TN_TCB * task, int priority); 369 | void find_next_task_to_run(void); 370 | 371 | //----- tn_sem.c ---------------------------------- 372 | 373 | int tn_sem_create(TN_SEM * sem, int start_value, int max_val); 374 | int tn_sem_delete(TN_SEM * sem); 375 | int tn_sem_signal(TN_SEM * sem); 376 | int tn_sem_isignal(TN_SEM * sem); 377 | int tn_sem_acquire(TN_SEM * sem, unsigned long timeout); 378 | int tn_sem_polling(TN_SEM * sem); 379 | int tn_sem_ipolling(TN_SEM * sem); 380 | 381 | //----- tn_dqueue.c ---------------------------------- 382 | 383 | int tn_queue_create(TN_DQUE * dque, 384 | void ** data_fifo, 385 | int num_entries); 386 | int tn_queue_delete(TN_DQUE * dque); 387 | int tn_queue_send(TN_DQUE * dque, void * data_ptr, unsigned long timeout); 388 | int tn_queue_send_polling(TN_DQUE * dque, void * data_ptr); 389 | int tn_queue_isend_polling(TN_DQUE * dque, void * data_ptr); 390 | int tn_queue_receive(TN_DQUE * dque, void ** data_ptr, unsigned long timeout); 391 | int tn_queue_receive_polling(TN_DQUE * dque, void ** data_ptr); 392 | int tn_queue_ireceive(TN_DQUE * dque, void ** data_ptr); 393 | 394 | //-------- tn_event.c ----------------------------- 395 | 396 | int tn_event_create(TN_EVENT * evf, 397 | int attr, 398 | unsigned int pattern); 399 | int tn_event_delete(TN_EVENT * evf); 400 | int tn_event_wait(TN_EVENT * evf, 401 | unsigned int wait_pattern, 402 | int wait_mode, 403 | unsigned int * p_flags_pattern, 404 | unsigned long timeout); 405 | int tn_event_wait_polling(TN_EVENT * evf, 406 | unsigned int wait_pattern, 407 | int wait_mode, 408 | unsigned int * p_flags_pattern); 409 | int tn_event_iwait(TN_EVENT * evf, 410 | unsigned int wait_pattern, 411 | int wait_mode, 412 | unsigned int * p_flags_pattern); 413 | int tn_event_set(TN_EVENT * evf, unsigned int pattern); 414 | int tn_event_iset(TN_EVENT * evf, unsigned int pattern); 415 | int tn_event_clear(TN_EVENT * evf, unsigned int pattern); 416 | int tn_event_iclear(TN_EVENT * evf, unsigned int pattern); 417 | 418 | //----- tn_mem.c ---------------------------------- 419 | 420 | int tn_fmem_create(TN_FMP * fmp, 421 | void * start_addr, 422 | unsigned int block_size, 423 | int num_blocks); 424 | int tn_fmem_delete(TN_FMP * fmp); 425 | int tn_fmem_get(TN_FMP * fmp, void ** p_data, unsigned long timeout); 426 | int tn_fmem_get_polling(TN_FMP * fmp, void ** p_data); 427 | int tn_fmem_get_ipolling(TN_FMP * fmp, void ** p_data); 428 | int tn_fmem_release(TN_FMP * fmp, void * p_data); 429 | int tn_fmem_irelease(TN_FMP * fmp, void * p_data); 430 | 431 | //--- tn_mutex.c --- 432 | 433 | int tn_mutex_create(TN_MUTEX * mutex, 434 | int attribute, 435 | int ceil_priority); 436 | int tn_mutex_delete(TN_MUTEX * mutex); 437 | int tn_mutex_lock(TN_MUTEX * mutex, unsigned long timeout); 438 | int tn_mutex_lock_polling(TN_MUTEX * mutex); 439 | int tn_mutex_unlock(TN_MUTEX * mutex); 440 | 441 | //-- Routines 442 | 443 | int find_max_blocked_priority(TN_MUTEX * mutex, int ref_priority); 444 | int try_lock_mutex(TN_TCB * task); 445 | int do_unlock_mutex(TN_MUTEX * mutex); 446 | 447 | //--- tn_port.c --- 448 | 449 | unsigned int * tn_stack_init(void * task_func, 450 | void * stack_start, 451 | void * param); 452 | 453 | TN_KERN_CTX * tn_kern_ctx_ptr(void); 454 | 455 | //--- tn_user.c --- 456 | 457 | void tn_cpu_int_enable(void); 458 | 459 | 460 | #ifdef __cplusplus 461 | } /* extern "C" */ 462 | #endif 463 | 464 | #endif 465 | -------------------------------------------------------------------------------- /tn_dqueue.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #include "tn.h" 31 | #include "tn_utils.h" 32 | 33 | //------------------------------------------------------------------------- 34 | // Structure's field dque->id_dque have to be set to 0 35 | //------------------------------------------------------------------------- 36 | int tn_queue_create(TN_DQUE * dque, //-- Ptr to already existing TN_DQUE 37 | void ** data_fifo, //-- Ptr to already existing array of void * to store data queue entries.Can be NULL 38 | int num_entries) //-- Capacity of data queue(num entries).Can be 0 39 | 40 | { 41 | 42 | #if TN_CHECK_PARAM 43 | if (dque == NULL) 44 | return TERR_WRONG_PARAM; 45 | if (num_entries < 0 || dque->id_dque == TN_ID_DATAQUEUE) 46 | return TERR_WRONG_PARAM; 47 | #endif 48 | 49 | queue_reset(&(dque->wait_send_list)); 50 | queue_reset(&(dque->wait_receive_list)); 51 | 52 | dque->data_fifo = data_fifo; 53 | dque->num_entries = num_entries; 54 | if (dque->data_fifo == NULL) 55 | dque->num_entries = 0; 56 | 57 | dque->tail_cnt = 0; 58 | dque->header_cnt = 0; 59 | 60 | dque->id_dque = TN_ID_DATAQUEUE; 61 | 62 | return TERR_NO_ERR; 63 | } 64 | 65 | //---------------------------------------------------------------------------- 66 | int tn_queue_delete(TN_DQUE * dque) 67 | { 68 | TN_INTSAVE_DATA 69 | CDLL_QUEUE * que; 70 | TN_TCB * task; 71 | 72 | #if TN_CHECK_PARAM 73 | if (dque == NULL) 74 | return TERR_WRONG_PARAM; 75 | if (dque->id_dque != TN_ID_DATAQUEUE) 76 | return TERR_NOEXS; 77 | #endif 78 | 79 | TN_CHECK_NON_INT_CONTEXT 80 | 81 | tn_disable_interrupt(); // v.2.7 - thanks to Eugene Scopal 82 | 83 | while (!is_queue_empty(&(dque->wait_send_list))) 84 | { 85 | //--- delete from sem wait queue 86 | 87 | que = queue_remove_head(&(dque->wait_send_list)); 88 | task = get_task_by_tsk_queue(que); 89 | if (task_wait_complete(task)) 90 | { 91 | task->task_wait_rc = TERR_DLT; 92 | tn_enable_interrupt(); 93 | tn_switch_context(); 94 | tn_disable_interrupt(); // v.2.7 95 | } 96 | } 97 | 98 | while (!is_queue_empty(&(dque->wait_receive_list))) 99 | { 100 | //--- delete from sem wait queue 101 | 102 | que = queue_remove_head(&(dque->wait_receive_list)); 103 | task = get_task_by_tsk_queue(que); 104 | if (task_wait_complete(task)) 105 | { 106 | task->task_wait_rc = TERR_DLT; 107 | tn_enable_interrupt(); 108 | tn_switch_context(); 109 | tn_disable_interrupt(); // v.2.7 110 | } 111 | } 112 | 113 | dque->id_dque = 0; // Data queue not exists now 114 | 115 | tn_enable_interrupt(); 116 | 117 | return TERR_NO_ERR; 118 | 119 | } 120 | 121 | //---------------------------------------------------------------------------- 122 | int tn_queue_send(TN_DQUE * dque, void * data_ptr, unsigned long timeout) 123 | { 124 | TN_INTSAVE_DATA 125 | int rc; 126 | CDLL_QUEUE * que; 127 | TN_TCB * task; 128 | TN_TCB * curr_run_task; 129 | 130 | #if TN_CHECK_PARAM 131 | if (dque == NULL || timeout == 0) 132 | return TERR_WRONG_PARAM; 133 | if (dque->id_dque != TN_ID_DATAQUEUE) 134 | return TERR_NOEXS; 135 | #endif 136 | 137 | TN_CHECK_NON_INT_CONTEXT 138 | 139 | tn_disable_interrupt(); 140 | 141 | //-- there are task(s) in the data queue's wait_receive list 142 | 143 | if (!is_queue_empty(&(dque->wait_receive_list))) 144 | { 145 | que = queue_remove_head(&(dque->wait_receive_list)); 146 | task = get_task_by_tsk_queue(que); 147 | 148 | task->data_elem = data_ptr; 149 | 150 | if (task_wait_complete(task)) 151 | { 152 | tn_enable_interrupt(); 153 | tn_switch_context(); 154 | return TERR_NO_ERR; 155 | } 156 | rc = TERR_NO_ERR; 157 | } 158 | else //-- the data queue's wait_receive list is empty 159 | { 160 | rc = dque_fifo_write(dque,data_ptr); 161 | if (rc != TERR_NO_ERR) //-- No free entries in the data queue 162 | { 163 | curr_run_task = tn_kern_ctx_ptr()->tn_curr_run_task; 164 | curr_run_task->data_elem = data_ptr; //-- Store data_ptr 165 | task_curr_to_wait_action(&(dque->wait_send_list), 166 | TSK_WAIT_REASON_DQUE_WSEND, timeout); 167 | tn_enable_interrupt(); 168 | tn_switch_context(); 169 | return curr_run_task->task_wait_rc; 170 | } 171 | } 172 | 173 | tn_enable_interrupt(); 174 | return rc; 175 | } 176 | 177 | //---------------------------------------------------------------------------- 178 | int tn_queue_send_polling(TN_DQUE * dque, void * data_ptr) 179 | { 180 | TN_INTSAVE_DATA 181 | int rc; 182 | CDLL_QUEUE * que; 183 | TN_TCB * task; 184 | 185 | #if TN_CHECK_PARAM 186 | if (dque == NULL) 187 | return TERR_WRONG_PARAM; 188 | if (dque->id_dque != TN_ID_DATAQUEUE) 189 | return TERR_NOEXS; 190 | #endif 191 | 192 | TN_CHECK_NON_INT_CONTEXT 193 | 194 | tn_disable_interrupt(); 195 | 196 | //-- there are task(s) in the data queue's wait_receive list 197 | 198 | if (!is_queue_empty(&(dque->wait_receive_list))) 199 | { 200 | que = queue_remove_head(&(dque->wait_receive_list)); 201 | task = get_task_by_tsk_queue(que); 202 | 203 | task->data_elem = data_ptr; 204 | 205 | if (task_wait_complete(task)) 206 | { 207 | tn_enable_interrupt(); 208 | tn_switch_context(); 209 | return TERR_NO_ERR; 210 | } 211 | rc = TERR_NO_ERR; 212 | } 213 | else //-- the data queue's wait_receive list is empty 214 | { 215 | rc = dque_fifo_write(dque, data_ptr); 216 | if (rc != TERR_NO_ERR) //-- No free entries in data queue 217 | rc = TERR_TIMEOUT; //-- Just convert errorcode 218 | } 219 | tn_enable_interrupt(); 220 | return rc; 221 | } 222 | 223 | //---------------------------------------------------------------------------- 224 | int tn_queue_isend_polling(TN_DQUE * dque, void * data_ptr) 225 | { 226 | TN_INTSAVE_DATA_INT 227 | int rc; 228 | CDLL_QUEUE * que; 229 | TN_TCB * task; 230 | 231 | #if TN_CHECK_PARAM 232 | if (dque == NULL) 233 | return TERR_WRONG_PARAM; 234 | if (dque->id_dque != TN_ID_DATAQUEUE) 235 | return TERR_NOEXS; 236 | #endif 237 | 238 | TN_CHECK_INT_CONTEXT 239 | 240 | tn_idisable_interrupt(); 241 | 242 | //-- there are task(s) in the data queue's wait_receive list 243 | 244 | if (!is_queue_empty(&(dque->wait_receive_list))) 245 | { 246 | que = queue_remove_head(&(dque->wait_receive_list)); 247 | task = get_task_by_tsk_queue(que); 248 | 249 | task->data_elem = data_ptr; 250 | 251 | if (task_wait_complete(task)) 252 | { 253 | tn_ienable_interrupt(); 254 | return TERR_NO_ERR; 255 | } 256 | rc = TERR_NO_ERR; 257 | } 258 | else //-- the data queue's wait_receive list is empty 259 | { 260 | rc = dque_fifo_write(dque, data_ptr); 261 | 262 | if (rc != TERR_NO_ERR) //-- No free entries in data queue 263 | rc = TERR_TIMEOUT; //-- Just convert errorcode 264 | } 265 | 266 | tn_ienable_interrupt(); 267 | 268 | return rc; 269 | } 270 | 271 | //---------------------------------------------------------------------------- 272 | int tn_queue_receive(TN_DQUE * dque, void ** data_ptr, unsigned long timeout) 273 | { 274 | TN_INTSAVE_DATA 275 | int rc; //-- return code 276 | CDLL_QUEUE * que; 277 | TN_TCB * task; 278 | TN_TCB * curr_run_task; 279 | 280 | #if TN_CHECK_PARAM 281 | if (dque == NULL || timeout == 0 || data_ptr == NULL) 282 | return TERR_WRONG_PARAM; 283 | if (dque->id_dque != TN_ID_DATAQUEUE) 284 | return TERR_NOEXS; 285 | #endif 286 | 287 | TN_CHECK_NON_INT_CONTEXT 288 | 289 | tn_disable_interrupt(); 290 | 291 | rc = dque_fifo_read(dque, data_ptr); 292 | if (rc == TERR_NO_ERR) //-- There was entry(s) in data queue 293 | { 294 | if (!is_queue_empty(&(dque->wait_send_list))) 295 | { 296 | que = queue_remove_head(&(dque->wait_send_list)); 297 | task = get_task_by_tsk_queue(que); 298 | 299 | dque_fifo_write(dque, task->data_elem); //-- Put to data FIFO 300 | 301 | if (task_wait_complete(task)) 302 | { 303 | tn_enable_interrupt(); 304 | tn_switch_context(); 305 | return TERR_NO_ERR; 306 | } 307 | } 308 | } 309 | else //-- data FIFO is empty 310 | { 311 | if (!is_queue_empty(&(dque->wait_send_list))) 312 | { 313 | que = queue_remove_head(&(dque->wait_send_list)); 314 | task = get_task_by_tsk_queue(que); 315 | 316 | *data_ptr = task->data_elem; //-- Return to caller 317 | 318 | if (task_wait_complete(task)) 319 | { 320 | tn_enable_interrupt(); 321 | tn_switch_context(); 322 | return TERR_NO_ERR; 323 | } 324 | rc = TERR_NO_ERR; 325 | } 326 | else //-- wait_send_list is empty 327 | { 328 | task_curr_to_wait_action(&(dque->wait_receive_list), 329 | TSK_WAIT_REASON_DQUE_WRECEIVE, timeout); 330 | tn_enable_interrupt(); 331 | tn_switch_context(); 332 | 333 | //-- When returns to this point, in the data_elem have to be valid value 334 | curr_run_task = tn_kern_ctx_ptr()->tn_curr_run_task; 335 | 336 | *data_ptr = curr_run_task->data_elem; //-- Return to caller 337 | 338 | return curr_run_task->task_wait_rc; 339 | } 340 | } 341 | 342 | tn_enable_interrupt(); 343 | 344 | return rc; 345 | } 346 | 347 | //---------------------------------------------------------------------------- 348 | int tn_queue_receive_polling(TN_DQUE * dque, void ** data_ptr) 349 | { 350 | TN_INTSAVE_DATA 351 | int rc; 352 | CDLL_QUEUE * que; 353 | TN_TCB * task; 354 | 355 | #if TN_CHECK_PARAM 356 | if (dque == NULL || data_ptr == NULL) 357 | return TERR_WRONG_PARAM; 358 | if (dque->id_dque != TN_ID_DATAQUEUE) 359 | return TERR_NOEXS; 360 | #endif 361 | 362 | TN_CHECK_NON_INT_CONTEXT 363 | 364 | tn_disable_interrupt(); 365 | 366 | rc = dque_fifo_read(dque, data_ptr); 367 | if (rc == TERR_NO_ERR) //-- There was entry(s) in data queue 368 | { 369 | if (!is_queue_empty(&(dque->wait_send_list))) 370 | { 371 | que = queue_remove_head(&(dque->wait_send_list)); 372 | task = get_task_by_tsk_queue(que); 373 | 374 | dque_fifo_write(dque, task->data_elem); //-- Put to data FIFO 375 | 376 | if (task_wait_complete(task)) 377 | { 378 | tn_enable_interrupt(); 379 | tn_switch_context(); 380 | return TERR_NO_ERR; 381 | } 382 | } 383 | } 384 | else //-- data FIFO is empty 385 | { 386 | if (!is_queue_empty(&(dque->wait_send_list))) 387 | { 388 | que = queue_remove_head(&(dque->wait_send_list)); 389 | task = get_task_by_tsk_queue(que); 390 | 391 | *data_ptr = task->data_elem; //-- Return to caller 392 | 393 | if (task_wait_complete(task)) 394 | { 395 | tn_enable_interrupt(); 396 | tn_switch_context(); 397 | return TERR_NO_ERR; 398 | } 399 | rc = TERR_NO_ERR; 400 | } 401 | else //-- wait_send_list is empty 402 | rc = TERR_TIMEOUT; 403 | } 404 | 405 | tn_enable_interrupt(); 406 | 407 | return rc; 408 | } 409 | 410 | //---------------------------------------------------------------------------- 411 | int tn_queue_ireceive(TN_DQUE * dque, void ** data_ptr) 412 | { 413 | TN_INTSAVE_DATA_INT 414 | int rc; 415 | CDLL_QUEUE * que; 416 | TN_TCB * task; 417 | 418 | #if TN_CHECK_PARAM 419 | if (dque == NULL || data_ptr == NULL) 420 | return TERR_WRONG_PARAM; 421 | if (dque->id_dque != TN_ID_DATAQUEUE) 422 | return TERR_NOEXS; 423 | #endif 424 | 425 | TN_CHECK_INT_CONTEXT 426 | 427 | tn_idisable_interrupt(); 428 | 429 | rc = dque_fifo_read(dque, data_ptr); 430 | if (rc == TERR_NO_ERR) //-- There was entry(s) in data queue 431 | { 432 | if (!is_queue_empty(&(dque->wait_send_list))) 433 | { 434 | que = queue_remove_head(&(dque->wait_send_list)); 435 | task = get_task_by_tsk_queue(que); 436 | 437 | dque_fifo_write(dque, task->data_elem); //-- Put to data FIFO 438 | 439 | if (task_wait_complete(task)) 440 | { 441 | tn_ienable_interrupt(); 442 | return TERR_NO_ERR; 443 | } 444 | } 445 | } 446 | else //-- data FIFO is empty 447 | { 448 | if (!is_queue_empty(&(dque->wait_send_list))) 449 | { 450 | que = queue_remove_head(&(dque->wait_send_list)); 451 | task = get_task_by_tsk_queue(que); 452 | 453 | *data_ptr = task->data_elem; //-- Return to caller 454 | 455 | if (task_wait_complete(task)) 456 | { 457 | tn_ienable_interrupt(); 458 | return TERR_NO_ERR; 459 | } 460 | rc = TERR_NO_ERR; 461 | } 462 | else 463 | { 464 | rc = TERR_TIMEOUT; 465 | } 466 | } 467 | 468 | tn_ienable_interrupt(); 469 | 470 | return rc; 471 | } 472 | //---------------------------------------------------------------------------- 473 | //---------------------------------------------------------------------------- 474 | //---------------------------------------------------------------------------- 475 | //---------------------------------------------------------------------------- 476 | //---------------------------------------------------------------------------- 477 | //---------------------------------------------------------------------------- 478 | -------------------------------------------------------------------------------- /tn_event.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #include "tn.h" 31 | #include "tn_utils.h" 32 | 33 | #ifdef USE_EVENTS 34 | 35 | static int scan_event_waitqueue(TN_EVENT * evf); 36 | 37 | //---------------------------------------------------------------------------- 38 | // Structure's field evf->id_event have to be set to 0 39 | //---------------------------------------------------------------------------- 40 | int tn_event_create(TN_EVENT * evf, 41 | int attr, //-- Eventflag attribute 42 | unsigned int pattern) //-- Initial value of the eventflag bit pattern 43 | { 44 | 45 | #if TN_CHECK_PARAM 46 | if (evf == NULL) 47 | return TERR_WRONG_PARAM; 48 | if (evf->id_event == TN_ID_EVENT || 49 | (((attr & TN_EVENT_ATTR_SINGLE) == 0) && 50 | ((attr & TN_EVENT_ATTR_MULTI) == 0))) 51 | return TERR_WRONG_PARAM; 52 | #endif 53 | 54 | queue_reset(&(evf->wait_queue)); 55 | evf->pattern = pattern; 56 | evf->attr = attr; 57 | if ((attr & TN_EVENT_ATTR_CLR) && ((attr & TN_EVENT_ATTR_SINGLE)== 0)) 58 | { 59 | evf->attr = TN_INVALID_VAL; 60 | return TERR_WRONG_PARAM; 61 | } 62 | evf->id_event = TN_ID_EVENT; 63 | 64 | return TERR_NO_ERR; 65 | } 66 | 67 | //---------------------------------------------------------------------------- 68 | int tn_event_delete(TN_EVENT * evf) 69 | { 70 | TN_INTSAVE_DATA 71 | CDLL_QUEUE * que; 72 | TN_TCB * task; 73 | 74 | #if TN_CHECK_PARAM 75 | if (evf == NULL) 76 | return TERR_WRONG_PARAM; 77 | if (evf->id_event != TN_ID_EVENT) 78 | return TERR_NOEXS; 79 | #endif 80 | 81 | TN_CHECK_NON_INT_CONTEXT 82 | 83 | tn_disable_interrupt(); // v.2.7 - thanks to Eugene Scopal 84 | 85 | while (!is_queue_empty(&(evf->wait_queue))) 86 | { 87 | //--- delete from sem wait queue 88 | 89 | que = queue_remove_head(&(evf->wait_queue)); 90 | task = get_task_by_tsk_queue(que); 91 | if (task_wait_complete(task)) 92 | { 93 | task->task_wait_rc = TERR_DLT; 94 | tn_enable_interrupt(); 95 | tn_switch_context(); 96 | tn_disable_interrupt(); // v.2.7 97 | } 98 | } 99 | 100 | evf->id_event = 0; // Event not exists now 101 | 102 | tn_enable_interrupt(); 103 | 104 | return TERR_NO_ERR; 105 | } 106 | 107 | //---------------------------------------------------------------------------- 108 | int tn_event_wait(TN_EVENT * evf, 109 | unsigned int wait_pattern, 110 | int wait_mode, 111 | unsigned int * p_flags_pattern, 112 | unsigned long timeout) 113 | { 114 | TN_INTSAVE_DATA 115 | int rc; 116 | int fCond; 117 | TN_TCB * curr_run_task; 118 | 119 | #if TN_CHECK_PARAM 120 | if (evf == NULL || wait_pattern == 0 || 121 | p_flags_pattern == NULL || timeout == 0) 122 | return TERR_WRONG_PARAM; 123 | if (evf->id_event != TN_ID_EVENT) 124 | return TERR_NOEXS; 125 | #endif 126 | 127 | TN_CHECK_NON_INT_CONTEXT 128 | 129 | tn_disable_interrupt(); 130 | 131 | //-- If event attr is TN_EVENT_ATTR_SINGLE and another task already 132 | //-- in event wait queue - return ERROR without checking release condition 133 | 134 | if ((evf->attr & TN_EVENT_ATTR_SINGLE) && !is_queue_empty(&(evf->wait_queue))) 135 | { 136 | rc = TERR_ILUSE; 137 | } 138 | else 139 | { 140 | //-- Check release condition 141 | 142 | if (wait_mode & TN_EVENT_WCOND_OR) //-- any setted bit is enough for release condition 143 | fCond = ((evf->pattern & wait_pattern) != 0); 144 | else //-- TN_EVENT_WCOND_AND is default mode 145 | fCond = ((evf->pattern & wait_pattern) == wait_pattern); 146 | 147 | if (fCond) 148 | { 149 | *p_flags_pattern = evf->pattern; 150 | if(evf->attr & TN_EVENT_ATTR_CLR) 151 | evf->pattern = 0; 152 | rc = TERR_NO_ERR; 153 | } 154 | else 155 | { 156 | curr_run_task = tn_kern_ctx_ptr()->tn_curr_run_task; 157 | curr_run_task->ewait_mode = wait_mode; 158 | curr_run_task->ewait_pattern = wait_pattern; 159 | task_curr_to_wait_action(&(evf->wait_queue), 160 | TSK_WAIT_REASON_EVENT, 161 | timeout); 162 | tn_enable_interrupt(); 163 | tn_switch_context(); 164 | 165 | if (curr_run_task->task_wait_rc == TERR_NO_ERR) 166 | *p_flags_pattern = curr_run_task->ewait_pattern; 167 | return curr_run_task->task_wait_rc; 168 | } 169 | } 170 | 171 | tn_enable_interrupt(); 172 | 173 | return rc; 174 | } 175 | 176 | //---------------------------------------------------------------------------- 177 | int tn_event_wait_polling(TN_EVENT * evf, 178 | unsigned int wait_pattern, 179 | int wait_mode, 180 | unsigned int * p_flags_pattern) 181 | { 182 | TN_INTSAVE_DATA 183 | int rc; 184 | int fCond; 185 | 186 | #if TN_CHECK_PARAM 187 | if (evf == NULL || wait_pattern == 0 || p_flags_pattern == NULL) 188 | return TERR_WRONG_PARAM; 189 | if (evf->id_event != TN_ID_EVENT) 190 | return TERR_NOEXS; 191 | #endif 192 | 193 | TN_CHECK_NON_INT_CONTEXT 194 | 195 | tn_disable_interrupt(); 196 | 197 | //-- If event attr is TN_EVENT_ATTR_SINGLE and another task already 198 | //-- in event wait queue - return ERROR without checking release condition 199 | 200 | if ((evf->attr & TN_EVENT_ATTR_SINGLE) && !is_queue_empty(&(evf->wait_queue))) 201 | { 202 | rc = TERR_ILUSE; 203 | } 204 | else 205 | { 206 | //-- Check release condition 207 | 208 | if (wait_mode & TN_EVENT_WCOND_OR) //-- any setted bit is enough for release condition 209 | fCond = ((evf->pattern & wait_pattern) != 0); 210 | else //-- TN_EVENT_WCOND_AND is default mode 211 | fCond = ((evf->pattern & wait_pattern) == wait_pattern); 212 | 213 | if (fCond) 214 | { 215 | *p_flags_pattern = evf->pattern; 216 | if (evf->attr & TN_EVENT_ATTR_CLR) 217 | evf->pattern = 0; 218 | rc = TERR_NO_ERR; 219 | } 220 | else 221 | rc = TERR_TIMEOUT; 222 | } 223 | 224 | tn_enable_interrupt(); 225 | 226 | return rc; 227 | } 228 | 229 | //---------------------------------------------------------------------------- 230 | int tn_event_iwait(TN_EVENT * evf, 231 | unsigned int wait_pattern, 232 | int wait_mode, 233 | unsigned int * p_flags_pattern) 234 | { 235 | TN_INTSAVE_DATA_INT 236 | int rc; 237 | int fCond; 238 | 239 | #if TN_CHECK_PARAM 240 | if (evf == NULL || wait_pattern == 0 || p_flags_pattern == NULL) 241 | return TERR_WRONG_PARAM; 242 | if (evf->id_event != TN_ID_EVENT) 243 | return TERR_NOEXS; 244 | #endif 245 | 246 | TN_CHECK_INT_CONTEXT 247 | 248 | tn_idisable_interrupt(); 249 | 250 | //-- If event attr is TN_EVENT_ATTR_SINGLE and another task already 251 | //-- in event wait queue - return ERROR without checking release condition 252 | 253 | if ((evf->attr & TN_EVENT_ATTR_SINGLE) && !is_queue_empty(&(evf->wait_queue))) 254 | { 255 | rc = TERR_ILUSE; 256 | } 257 | else 258 | { 259 | //-- Check release condition 260 | 261 | if (wait_mode & TN_EVENT_WCOND_OR) //-- any setted bit is enough for release condition 262 | fCond = ((evf->pattern & wait_pattern) != 0); 263 | else //-- TN_EVENT_WCOND_AND is default mode 264 | fCond = ((evf->pattern & wait_pattern) == wait_pattern); 265 | 266 | if (fCond) 267 | { 268 | *p_flags_pattern = evf->pattern; 269 | if (evf->attr & TN_EVENT_ATTR_CLR) 270 | evf->pattern = 0; 271 | rc = TERR_NO_ERR; 272 | } 273 | else 274 | rc = TERR_TIMEOUT; 275 | } 276 | 277 | tn_ienable_interrupt(); 278 | 279 | return rc; 280 | } 281 | 282 | //---------------------------------------------------------------------------- 283 | int tn_event_set(TN_EVENT * evf, unsigned int pattern) 284 | { 285 | TN_INTSAVE_DATA 286 | 287 | #if TN_CHECK_PARAM 288 | if (evf == NULL || pattern == 0) 289 | return TERR_WRONG_PARAM; 290 | if (evf->id_event != TN_ID_EVENT) 291 | return TERR_NOEXS; 292 | #endif 293 | 294 | TN_CHECK_NON_INT_CONTEXT 295 | 296 | tn_disable_interrupt(); 297 | 298 | evf->pattern |= pattern; 299 | 300 | if (scan_event_waitqueue(evf)) //-- There are task(s) that waiting state is complete 301 | { 302 | if (evf->attr & TN_EVENT_ATTR_CLR) 303 | evf->pattern = 0; 304 | 305 | tn_enable_interrupt(); 306 | tn_switch_context(); 307 | 308 | return TERR_NO_ERR; 309 | } 310 | 311 | tn_enable_interrupt(); 312 | 313 | return TERR_NO_ERR; 314 | } 315 | 316 | //---------------------------------------------------------------------------- 317 | int tn_event_iset(TN_EVENT * evf, unsigned int pattern) 318 | { 319 | TN_INTSAVE_DATA_INT 320 | 321 | #if TN_CHECK_PARAM 322 | if (evf == NULL || pattern == 0) 323 | return TERR_WRONG_PARAM; 324 | if (evf->id_event != TN_ID_EVENT) 325 | return TERR_NOEXS; 326 | #endif 327 | 328 | TN_CHECK_INT_CONTEXT 329 | 330 | tn_idisable_interrupt(); 331 | 332 | evf->pattern |= pattern; 333 | 334 | if (scan_event_waitqueue(evf)) //-- There are task(s) that waiting state is complete 335 | { 336 | if (evf->attr & TN_EVENT_ATTR_CLR) 337 | evf->pattern = 0; 338 | 339 | tn_ienable_interrupt(); 340 | 341 | return TERR_NO_ERR; 342 | } 343 | 344 | tn_ienable_interrupt(); 345 | 346 | return TERR_NO_ERR; 347 | } 348 | 349 | //---------------------------------------------------------------------------- 350 | int tn_event_clear(TN_EVENT * evf, unsigned int pattern) 351 | { 352 | TN_INTSAVE_DATA 353 | 354 | if (evf == NULL || pattern == TN_INVALID_VAL) 355 | return TERR_WRONG_PARAM; 356 | if (evf->id_event != TN_ID_EVENT) 357 | return TERR_NOEXS; 358 | 359 | TN_CHECK_NON_INT_CONTEXT 360 | 361 | tn_disable_interrupt(); 362 | 363 | evf->pattern &= pattern; 364 | 365 | tn_enable_interrupt(); 366 | return TERR_NO_ERR; 367 | } 368 | 369 | //---------------------------------------------------------------------------- 370 | int tn_event_iclear(TN_EVENT * evf, unsigned int pattern) 371 | { 372 | TN_INTSAVE_DATA_INT 373 | 374 | #if TN_CHECK_PARAM 375 | if (evf == NULL || pattern == TN_INVALID_VAL) 376 | return TERR_WRONG_PARAM; 377 | if (evf->id_event != TN_ID_EVENT) 378 | return TERR_NOEXS; 379 | #endif 380 | 381 | TN_CHECK_INT_CONTEXT 382 | 383 | tn_idisable_interrupt(); 384 | 385 | evf->pattern &= pattern; 386 | 387 | tn_ienable_interrupt(); 388 | 389 | return TERR_NO_ERR; 390 | } 391 | 392 | //---------------------------------------------------------------------------- 393 | static int scan_event_waitqueue(TN_EVENT * evf) 394 | { 395 | CDLL_QUEUE * que; 396 | TN_TCB * task; 397 | int fCond; 398 | int rc = 0; 399 | 400 | que = evf->wait_queue.next; 401 | 402 | // checking ALL of the tasks waiting on the event. 403 | 404 | // for the event with attr TN_EVENT_ATTR_SINGLE the only one task 405 | // may be in the queue 406 | 407 | while (que != &(evf->wait_queue)) 408 | { 409 | task = get_task_by_tsk_queue(que); 410 | que = que->next; 411 | 412 | //-- cond --- 413 | 414 | if (task->ewait_mode & TN_EVENT_WCOND_OR) 415 | fCond = ((evf->pattern & task->ewait_pattern) != 0); 416 | else //-- TN_EVENT_WCOND_AND is default mode 417 | fCond = ((evf->pattern & task->ewait_pattern) == task->ewait_pattern); 418 | 419 | if (fCond) //-- Condition to finish the waiting 420 | { 421 | queue_remove_entry(&task->task_queue); 422 | task->ewait_pattern = evf->pattern; 423 | if(task_wait_complete(task)) // v.2.7 - thanks to Eugene Scopal 424 | rc = 1; 425 | } 426 | } 427 | 428 | return rc; 429 | } 430 | 431 | //---------------------------------------------------------------------------- 432 | 433 | #endif //#ifdef USE_EVENTS 434 | 435 | //---------------------------------------------------------------------------- 436 | //---------------------------------------------------------------------------- 437 | //---------------------------------------------------------------------------- 438 | //---------------------------------------------------------------------------- 439 | //---------------------------------------------------------------------------- 440 | //---------------------------------------------------------------------------- 441 | //---------------------------------------------------------------------------- 442 | //---------------------------------------------------------------------------- 443 | -------------------------------------------------------------------------------- /tn_mem.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #include "tn.h" 31 | #include "tn_utils.h" 32 | 33 | //-- Local function prototypes -- 34 | 35 | static void * fm_get(TN_FMP * fmp); 36 | static int fm_put(TN_FMP * fmp, void * mem); 37 | 38 | //---------------------------------------------------------------------------- 39 | // Structure's field fmp->id_id_fmp have to be set to 0 40 | //---------------------------------------------------------------------------- 41 | int tn_fmem_create(TN_FMP * fmp, 42 | void * start_addr, 43 | unsigned int block_size, 44 | int num_blocks) 45 | { 46 | void ** p_tmp; 47 | unsigned char * p_block; 48 | unsigned long i,j; 49 | 50 | #if TN_CHECK_PARAM 51 | if (fmp == NULL) 52 | return TERR_WRONG_PARAM; 53 | if (fmp->id_fmp == TN_ID_FSMEMORYPOOL) 54 | return TERR_WRONG_PARAM; 55 | #endif 56 | 57 | if (start_addr == NULL || num_blocks < 2 || block_size < sizeof(int)) 58 | { 59 | fmp->fblkcnt = 0; 60 | fmp->num_blocks = 0; 61 | fmp->id_fmp = 0; 62 | fmp->free_list = NULL; 63 | return TERR_WRONG_PARAM; 64 | } 65 | 66 | queue_reset(&(fmp->wait_queue)); 67 | 68 | //-- Prepare addr/block aligment 69 | 70 | i = ((unsigned long)start_addr + (TN_ALIG -1)) & (~(TN_ALIG-1)); 71 | fmp->start_addr = (void*)i; 72 | fmp->block_size = (block_size + (TN_ALIG -1)) & (~(TN_ALIG-1)); 73 | 74 | i = (unsigned long)start_addr + block_size * num_blocks; 75 | j = (unsigned long)fmp->start_addr + fmp->block_size * num_blocks; 76 | 77 | fmp->num_blocks = num_blocks; 78 | 79 | while (j > i) //-- Get actual num_blocks 80 | { 81 | j -= fmp->block_size; 82 | fmp->num_blocks--; 83 | } 84 | 85 | if (fmp->num_blocks < 2) 86 | { 87 | fmp->fblkcnt = 0; 88 | fmp->num_blocks = 0; 89 | fmp->free_list = NULL; 90 | return TERR_WRONG_PARAM; 91 | } 92 | 93 | //-- Set blocks ptrs for allocation ------- 94 | 95 | p_tmp = (void **)fmp->start_addr; 96 | p_block = (unsigned char *)fmp->start_addr + fmp->block_size; 97 | for (i = 0; i < (fmp->num_blocks - 1); i++) 98 | { 99 | *p_tmp = (void *)p_block; //-- contents of cell = addr of next block 100 | p_tmp = (void **)p_block; 101 | p_block += fmp->block_size; 102 | } 103 | *p_tmp = NULL; //-- Last memory block first cell contents - NULL 104 | 105 | fmp->free_list = fmp->start_addr; 106 | fmp->fblkcnt = fmp->num_blocks; 107 | 108 | fmp->id_fmp = TN_ID_FSMEMORYPOOL; 109 | 110 | //----------------------------------------- 111 | 112 | return TERR_NO_ERR; 113 | } 114 | 115 | //---------------------------------------------------------------------------- 116 | int tn_fmem_delete(TN_FMP * fmp) 117 | { 118 | TN_INTSAVE_DATA 119 | CDLL_QUEUE * que; 120 | TN_TCB * task; 121 | 122 | #if TN_CHECK_PARAM 123 | if (fmp == NULL) 124 | return TERR_WRONG_PARAM; 125 | if (fmp->id_fmp != TN_ID_FSMEMORYPOOL) 126 | return TERR_NOEXS; 127 | #endif 128 | 129 | TN_CHECK_NON_INT_CONTEXT 130 | 131 | tn_disable_interrupt(); // v.2.7 - thanks to Eugene Scopal 132 | 133 | while (!is_queue_empty(&(fmp->wait_queue))) 134 | { 135 | //--- delete from sem wait queue 136 | 137 | que = queue_remove_head(&(fmp->wait_queue)); 138 | task = get_task_by_tsk_queue(que); 139 | if (task_wait_complete(task)) 140 | { 141 | task->task_wait_rc = TERR_DLT; 142 | tn_enable_interrupt(); 143 | tn_switch_context(); 144 | tn_disable_interrupt(); // v.2.7 145 | } 146 | } 147 | 148 | fmp->id_fmp = 0; //-- Fixed-size memory pool not exists now 149 | 150 | tn_enable_interrupt(); 151 | 152 | return TERR_NO_ERR; 153 | } 154 | 155 | //---------------------------------------------------------------------------- 156 | int tn_fmem_get(TN_FMP * fmp, void ** p_data, unsigned long timeout) 157 | { 158 | TN_INTSAVE_DATA 159 | int rc; 160 | void * ptr; 161 | TN_TCB * curr_run_task; 162 | 163 | #if TN_CHECK_PARAM 164 | if (fmp == NULL || p_data == NULL || timeout == 0) 165 | return TERR_WRONG_PARAM; 166 | if (fmp->id_fmp != TN_ID_FSMEMORYPOOL) 167 | return TERR_NOEXS; 168 | #endif 169 | 170 | TN_CHECK_NON_INT_CONTEXT 171 | 172 | tn_disable_interrupt(); 173 | 174 | ptr = fm_get(fmp); 175 | if (ptr != NULL) //-- Get memory 176 | { 177 | *p_data = ptr; 178 | rc = TERR_NO_ERR; 179 | } 180 | else 181 | { 182 | task_curr_to_wait_action(&(fmp->wait_queue), 183 | TSK_WAIT_REASON_WFIXMEM, timeout); 184 | tn_enable_interrupt(); 185 | tn_switch_context(); 186 | 187 | //-- When returns to this point, in the 'data_elem' have to be valid value 188 | 189 | curr_run_task = tn_kern_ctx_ptr()->tn_curr_run_task; 190 | 191 | *p_data = curr_run_task->data_elem; //-- Return to caller 192 | 193 | return curr_run_task->task_wait_rc; 194 | } 195 | 196 | tn_enable_interrupt(); 197 | 198 | return rc; 199 | } 200 | 201 | //---------------------------------------------------------------------------- 202 | int tn_fmem_get_polling(TN_FMP * fmp, void ** p_data) 203 | { 204 | TN_INTSAVE_DATA 205 | int rc; 206 | void * ptr; 207 | 208 | #if TN_CHECK_PARAM 209 | if (fmp == NULL || p_data == NULL) 210 | return TERR_WRONG_PARAM; 211 | if (fmp->id_fmp != TN_ID_FSMEMORYPOOL) 212 | return TERR_NOEXS; 213 | #endif 214 | 215 | TN_CHECK_NON_INT_CONTEXT 216 | 217 | tn_disable_interrupt(); 218 | 219 | ptr = fm_get(fmp); 220 | if (ptr != NULL) //-- Get memory 221 | { 222 | *p_data = ptr; 223 | rc = TERR_NO_ERR; 224 | } 225 | else 226 | rc = TERR_TIMEOUT; 227 | 228 | tn_enable_interrupt(); 229 | 230 | return rc; 231 | } 232 | 233 | //---------------------------------------------------------------------------- 234 | int tn_fmem_get_ipolling(TN_FMP * fmp, void ** p_data) 235 | { 236 | TN_INTSAVE_DATA_INT 237 | int rc; 238 | void * ptr; 239 | 240 | #if TN_CHECK_PARAM 241 | if (fmp == NULL || p_data == NULL) 242 | return TERR_WRONG_PARAM; 243 | if (fmp->id_fmp != TN_ID_FSMEMORYPOOL) 244 | return TERR_NOEXS; 245 | #endif 246 | 247 | TN_CHECK_INT_CONTEXT 248 | 249 | tn_idisable_interrupt(); 250 | 251 | ptr = fm_get(fmp); 252 | if (ptr != NULL) //-- Get memory 253 | { 254 | *p_data = ptr; 255 | rc = TERR_NO_ERR; 256 | } 257 | else 258 | rc = TERR_TIMEOUT; 259 | 260 | tn_ienable_interrupt(); 261 | 262 | return rc; 263 | } 264 | 265 | //---------------------------------------------------------------------------- 266 | int tn_fmem_release(TN_FMP * fmp, void * p_data) 267 | { 268 | TN_INTSAVE_DATA 269 | 270 | CDLL_QUEUE * que; 271 | TN_TCB * task; 272 | 273 | #if TN_CHECK_PARAM 274 | if (fmp == NULL || p_data == NULL) 275 | return TERR_WRONG_PARAM; 276 | if (fmp->id_fmp != TN_ID_FSMEMORYPOOL) 277 | return TERR_NOEXS; 278 | #endif 279 | 280 | TN_CHECK_NON_INT_CONTEXT 281 | 282 | tn_disable_interrupt(); 283 | 284 | if (!is_queue_empty(&(fmp->wait_queue))) 285 | { 286 | que = queue_remove_head(&(fmp->wait_queue)); 287 | task = get_task_by_tsk_queue(que); 288 | 289 | task->data_elem = p_data; 290 | 291 | if (task_wait_complete(task)) 292 | { 293 | tn_enable_interrupt(); 294 | tn_switch_context(); 295 | 296 | return TERR_NO_ERR; 297 | } 298 | } 299 | else 300 | fm_put(fmp, p_data); 301 | 302 | tn_enable_interrupt(); 303 | 304 | return TERR_NO_ERR; 305 | } 306 | 307 | //---------------------------------------------------------------------------- 308 | int tn_fmem_irelease(TN_FMP * fmp, void * p_data) 309 | { 310 | TN_INTSAVE_DATA_INT 311 | 312 | CDLL_QUEUE * que; 313 | TN_TCB * task; 314 | 315 | #if TN_CHECK_PARAM 316 | if (fmp == NULL || p_data == NULL) 317 | return TERR_WRONG_PARAM; 318 | if (fmp->id_fmp != TN_ID_FSMEMORYPOOL) 319 | return TERR_NOEXS; 320 | #endif 321 | 322 | TN_CHECK_INT_CONTEXT 323 | 324 | tn_idisable_interrupt(); 325 | 326 | if (!is_queue_empty(&(fmp->wait_queue))) 327 | { 328 | que = queue_remove_head(&(fmp->wait_queue)); 329 | task = get_task_by_tsk_queue(que); 330 | 331 | task->data_elem = p_data; 332 | 333 | if (task_wait_complete(task)) 334 | { 335 | tn_ienable_interrupt(); 336 | return TERR_NO_ERR; 337 | } 338 | } 339 | else 340 | fm_put(fmp, p_data); 341 | 342 | tn_ienable_interrupt(); 343 | 344 | return TERR_NO_ERR; 345 | } 346 | 347 | //---------------------------------------------------------------------------- 348 | static void * fm_get(TN_FMP * fmp) 349 | { 350 | void * p_tmp; 351 | 352 | if (fmp->fblkcnt > 0) 353 | { 354 | p_tmp = fmp->free_list; 355 | fmp->free_list = *(void **)fmp->free_list; //-- ptr - to new free list 356 | fmp->fblkcnt--; 357 | 358 | return p_tmp; 359 | } 360 | 361 | return NULL; 362 | } 363 | //---------------------------------------------------------------------------- 364 | static int fm_put(TN_FMP * fmp, void * mem) 365 | { 366 | if (fmp->fblkcnt < fmp->num_blocks) 367 | { 368 | *(void **)mem = fmp->free_list; //-- insert block into free block list 369 | fmp->free_list = mem; 370 | fmp->fblkcnt++; 371 | 372 | return TERR_NO_ERR; 373 | } 374 | 375 | return TERR_OVERFLOW; 376 | } 377 | 378 | //---------------------------------------------------------------------------- 379 | //---------------------------------------------------------------------------- 380 | //---------------------------------------------------------------------------- 381 | //---------------------------------------------------------------------------- 382 | //---------------------------------------------------------------------------- 383 | -------------------------------------------------------------------------------- /tn_mutex.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #include "tn.h" 31 | #include "tn_utils.h" 32 | 33 | 34 | #ifdef USE_MUTEXES 35 | 36 | /* 37 | The ceiling protocol in ver 2.6 and latest is more "lightweight" in comparison 38 | to previous versions. 39 | The code of ceiling protocol is derived from Vyacheslav Ovsiyenko version 40 | */ 41 | 42 | 43 | // L. Sha, R. Rajkumar, J. Lehoczky, Priority Inheritance Protocols: An Approach 44 | // to Real-Time Synchronization, IEEE Transactions on Computers, Vol.39, No.9, 1990 45 | 46 | //---------------------------------------------------------------------------- 47 | // Structure's Field mutex->id_mutex should be set to 0 48 | //---------------------------------------------------------------------------- 49 | int tn_mutex_create(TN_MUTEX * mutex, 50 | int attribute, 51 | int ceil_priority) 52 | { 53 | 54 | #if TN_CHECK_PARAM 55 | if (mutex == NULL) 56 | return TERR_WRONG_PARAM; 57 | if (mutex->id_mutex != 0) //-- no recreation 58 | return TERR_WRONG_PARAM; 59 | if (attribute != TN_MUTEX_ATTR_CEILING && attribute != TN_MUTEX_ATTR_INHERIT) 60 | return TERR_WRONG_PARAM; 61 | if (attribute == TN_MUTEX_ATTR_CEILING && 62 | (ceil_priority < 1 || ceil_priority > TN_NUM_PRIORITY - 2)) 63 | return TERR_WRONG_PARAM; 64 | #endif 65 | 66 | queue_reset(&(mutex->wait_queue)); 67 | queue_reset(&(mutex->mutex_queue)); 68 | queue_reset(&(mutex->lock_mutex_queue)); 69 | 70 | mutex->attr = attribute; 71 | mutex->holder = NULL; 72 | mutex->ceil_priority = ceil_priority; 73 | mutex->cnt = 0; 74 | mutex->id_mutex = TN_ID_MUTEX; 75 | 76 | return TERR_NO_ERR; 77 | } 78 | 79 | //---------------------------------------------------------------------------- 80 | int tn_mutex_delete(TN_MUTEX * mutex) 81 | { 82 | TN_INTSAVE_DATA 83 | 84 | CDLL_QUEUE * que; 85 | TN_TCB * task; 86 | 87 | #if TN_CHECK_PARAM 88 | if (mutex == NULL) 89 | return TERR_WRONG_PARAM; 90 | if (mutex->id_mutex != TN_ID_MUTEX) 91 | return TERR_NOEXS; 92 | #endif 93 | 94 | TN_CHECK_NON_INT_CONTEXT 95 | 96 | if (tn_kern_ctx_ptr()->tn_curr_run_task != mutex->holder) 97 | return TERR_ILUSE; 98 | 99 | //-- Remove all tasks(if any) from mutex's wait queue 100 | 101 | tn_disable_interrupt(); // v.2.7 - thanks to Eugene Scopal 102 | 103 | while (!is_queue_empty(&(mutex->wait_queue))) 104 | { 105 | que = queue_remove_head(&(mutex->wait_queue)); 106 | task = get_task_by_tsk_queue(que); 107 | 108 | //-- If the task in system's blocked list, remove it 109 | 110 | if (task_wait_complete(task)) 111 | { 112 | task->task_wait_rc = TERR_DLT; 113 | tn_enable_interrupt(); 114 | tn_switch_context(); 115 | tn_disable_interrupt(); // v.2.7 116 | } 117 | } 118 | 119 | if (mutex->holder != NULL) //-- If the mutex is locked 120 | { 121 | do_unlock_mutex(mutex); 122 | queue_reset(&(mutex->mutex_queue)); 123 | } 124 | mutex->id_mutex = 0; // Mutex not exists now 125 | 126 | tn_enable_interrupt(); 127 | 128 | return TERR_NO_ERR; 129 | } 130 | 131 | //---------------------------------------------------------------------------- 132 | int tn_mutex_lock(TN_MUTEX * mutex, unsigned long timeout) 133 | { 134 | TN_INTSAVE_DATA 135 | TN_TCB * curr_run_task; 136 | 137 | #if TN_CHECK_PARAM 138 | if (mutex == NULL || timeout == 0) 139 | return TERR_WRONG_PARAM; 140 | if (mutex->id_mutex != TN_ID_MUTEX) 141 | return TERR_NOEXS; 142 | #endif 143 | 144 | TN_CHECK_NON_INT_CONTEXT 145 | 146 | tn_disable_interrupt(); 147 | 148 | curr_run_task = tn_kern_ctx_ptr()->tn_curr_run_task; 149 | 150 | if (curr_run_task == mutex->holder) //-- Recursive locking not enabled 151 | { 152 | tn_enable_interrupt(); 153 | return TERR_ILUSE; 154 | } 155 | 156 | if (mutex->attr == TN_MUTEX_ATTR_CEILING) 157 | { 158 | if (curr_run_task->base_priority < mutex->ceil_priority) //-- base pri of task higher 159 | { 160 | tn_enable_interrupt(); 161 | return TERR_ILUSE; 162 | } 163 | 164 | if (mutex->holder == NULL) //-- mutex not locked 165 | { 166 | mutex->holder = curr_run_task; 167 | 168 | //-- Add mutex to task's locked mutexes queue 169 | 170 | queue_add_tail(&(curr_run_task->mutex_queue), &(mutex->mutex_queue)); 171 | 172 | //-- Ceiling protocol 173 | 174 | if (curr_run_task->priority > mutex->ceil_priority) 175 | change_running_task_priority(curr_run_task, mutex->ceil_priority); 176 | 177 | tn_enable_interrupt(); 178 | return TERR_NO_ERR; 179 | } 180 | else //-- the mutex is already locked 181 | { 182 | //--- Task -> to the mutex wait queue 183 | 184 | task_curr_to_wait_action(&(mutex->wait_queue), 185 | TSK_WAIT_REASON_MUTEX_C, 186 | timeout); 187 | tn_enable_interrupt(); 188 | tn_switch_context(); 189 | 190 | return curr_run_task->task_wait_rc; 191 | } 192 | } 193 | else if (mutex->attr == TN_MUTEX_ATTR_INHERIT) 194 | { 195 | if (mutex->holder == NULL) //-- mutex not locked 196 | { 197 | mutex->holder = curr_run_task; 198 | 199 | queue_add_tail(&(curr_run_task->mutex_queue), &(mutex->mutex_queue)); 200 | 201 | tn_enable_interrupt(); 202 | 203 | return TERR_NO_ERR; 204 | } 205 | else //-- the mutex is already locked 206 | { 207 | //-- Base priority inheritance protocol 208 | //-- if run_task curr priority higher holder's curr priority 209 | 210 | if (curr_run_task->priority < mutex->holder->priority) 211 | set_current_priority(mutex->holder, curr_run_task->priority); 212 | 213 | task_curr_to_wait_action(&(mutex->wait_queue), 214 | TSK_WAIT_REASON_MUTEX_I, 215 | timeout); 216 | tn_enable_interrupt(); 217 | tn_switch_context(); 218 | 219 | return curr_run_task->task_wait_rc; 220 | } 221 | } 222 | 223 | tn_enable_interrupt(); //-- Never reach 224 | return TERR_NO_ERR; 225 | } 226 | 227 | //---------------------------------------------------------------------------- 228 | // Try to lock mutex 229 | //---------------------------------------------------------------------------- 230 | int tn_mutex_lock_polling(TN_MUTEX * mutex) 231 | { 232 | TN_INTSAVE_DATA 233 | int rc; 234 | TN_TCB * curr_run_task; 235 | 236 | #if TN_CHECK_PARAM 237 | if (mutex == NULL) 238 | return TERR_WRONG_PARAM; 239 | if (mutex->id_mutex != TN_ID_MUTEX) 240 | return TERR_NOEXS; 241 | #endif 242 | 243 | TN_CHECK_NON_INT_CONTEXT 244 | 245 | tn_disable_interrupt(); 246 | 247 | curr_run_task = tn_kern_ctx_ptr()->tn_curr_run_task; 248 | 249 | rc = TERR_NO_ERR; 250 | for (;;) //-- Single iteration loop 251 | { 252 | if (curr_run_task == mutex->holder) //-- Recursive locking not enabled 253 | { 254 | rc = TERR_ILUSE; 255 | break; 256 | } 257 | if (mutex->attr == TN_MUTEX_ATTR_CEILING && //-- base pri of task higher 258 | curr_run_task->base_priority < mutex->ceil_priority) 259 | { 260 | rc = TERR_ILUSE; 261 | break; 262 | } 263 | if (mutex->holder == NULL) //-- the mutex is not locked 264 | { 265 | mutex->holder = curr_run_task; 266 | queue_add_tail(&(curr_run_task->mutex_queue), &(mutex->mutex_queue)); 267 | 268 | if (mutex->attr == TN_MUTEX_ATTR_CEILING) 269 | { 270 | //-- Ceiling protocol 271 | 272 | if (curr_run_task->priority > mutex->ceil_priority) 273 | change_running_task_priority(curr_run_task, mutex->ceil_priority); 274 | } 275 | } 276 | else //-- the mutex is already locked 277 | { 278 | rc = TERR_TIMEOUT; 279 | } 280 | break; 281 | } 282 | 283 | tn_enable_interrupt(); 284 | 285 | return rc; 286 | } 287 | 288 | //---------------------------------------------------------------------------- 289 | int tn_mutex_unlock(TN_MUTEX * mutex) 290 | { 291 | TN_INTSAVE_DATA 292 | 293 | #if TN_CHECK_PARAM 294 | if (mutex == NULL) 295 | return TERR_WRONG_PARAM; 296 | if (mutex->id_mutex != TN_ID_MUTEX) 297 | return TERR_NOEXS; 298 | #endif 299 | 300 | TN_CHECK_NON_INT_CONTEXT 301 | 302 | tn_disable_interrupt(); 303 | 304 | //-- Unlocking is enabled only for the owner and already locked mutex 305 | 306 | if (tn_kern_ctx_ptr()->tn_curr_run_task != mutex->holder) 307 | { 308 | tn_enable_interrupt(); 309 | return TERR_ILUSE; 310 | } 311 | 312 | do_unlock_mutex(mutex); 313 | tn_enable_interrupt(); 314 | tn_switch_context(); 315 | 316 | return TERR_NO_ERR; 317 | } 318 | 319 | //---------------------------------------------------------------------------- 320 | // Routines 321 | //---------------------------------------------------------------------------- 322 | int do_unlock_mutex(TN_MUTEX * mutex) 323 | { 324 | CDLL_QUEUE * curr_que; 325 | TN_MUTEX * tmp_mutex; 326 | TN_TCB * task; 327 | int pr; 328 | TN_TCB * curr_run_task; 329 | 330 | curr_run_task = tn_kern_ctx_ptr()->tn_curr_run_task; 331 | 332 | //-- Delete curr mutex from task's locked mutexes queue 333 | 334 | queue_remove_entry(&(mutex->mutex_queue)); 335 | pr = curr_run_task->base_priority; 336 | 337 | //---- No more mutexes, locked by the our task 338 | 339 | if (!is_queue_empty(&(curr_run_task->mutex_queue))) 340 | { 341 | curr_que = curr_run_task->mutex_queue.next; 342 | while (curr_que != &(curr_run_task->mutex_queue)) 343 | { 344 | tmp_mutex = get_mutex_by_mutex_queque(curr_que); 345 | 346 | if (tmp_mutex->attr == TN_MUTEX_ATTR_CEILING) 347 | { 348 | if (tmp_mutex->ceil_priority < pr) 349 | pr = tmp_mutex->ceil_priority; 350 | } 351 | else if (tmp_mutex->attr == TN_MUTEX_ATTR_INHERIT) 352 | { 353 | pr = find_max_blocked_priority(tmp_mutex, pr); 354 | } 355 | curr_que = curr_que->next; 356 | } 357 | } 358 | 359 | //-- Restore original priority 360 | 361 | if (pr != curr_run_task->priority) 362 | change_running_task_priority(curr_run_task, pr); 363 | 364 | 365 | //-- Check for the task(s) that want to lock the mutex 366 | 367 | if (is_queue_empty(&(mutex->wait_queue))) 368 | { 369 | mutex->holder = NULL; 370 | return TRUE; 371 | } 372 | 373 | //--- Now lock the mutex by the first task in the mutex queue 374 | 375 | curr_que = queue_remove_head(&(mutex->wait_queue)); 376 | task = get_task_by_tsk_queue(curr_que); 377 | mutex->holder = task; 378 | 379 | if (mutex->attr == TN_MUTEX_ATTR_CEILING && 380 | task->priority > mutex->ceil_priority) 381 | task->priority = mutex->ceil_priority; 382 | 383 | task_wait_complete(task); 384 | queue_add_tail(&(task->mutex_queue), &(mutex->mutex_queue)); 385 | 386 | return TRUE; 387 | } 388 | 389 | //---------------------------------------------------------------------------- 390 | int find_max_blocked_priority(TN_MUTEX * mutex, int ref_priority) 391 | { 392 | int priority; 393 | CDLL_QUEUE * curr_que; 394 | TN_TCB * task; 395 | 396 | priority = ref_priority; 397 | curr_que = mutex->wait_queue.next; 398 | while (curr_que != &(mutex->wait_queue)) 399 | { 400 | task = get_task_by_tsk_queue(curr_que); 401 | if (task->priority < priority) //-- task priority is higher 402 | priority = task->priority; 403 | 404 | curr_que = curr_que->next; 405 | } 406 | 407 | return priority; 408 | } 409 | 410 | //---------------------------------------------------------------------------- 411 | #endif //-- USE_MUTEXES 412 | //---------------------------------------------------------------------------- 413 | //---------------------------------------------------------------------------- 414 | //---------------------------------------------------------------------------- 415 | //---------------------------------------------------------------------------- 416 | -------------------------------------------------------------------------------- /tn_port.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | 31 | #ifndef _TN_PORT_H_ 32 | #define _TN_PORT_H_ 33 | 34 | #define align_attr_start 35 | #define align_attr_end __attribute__((aligned(0x8))) 36 | 37 | 38 | #define TN_TIMER_STACK_SIZE 80 39 | #define TN_IDLE_STACK_SIZE 60 40 | #define TN_MIN_STACK_SIZE 53 41 | 42 | #define TN_BITS_IN_INT 32 43 | 44 | #define TN_ALIG sizeof(void*) 45 | 46 | #define MAKE_ALIG(a) ((sizeof(a) + (TN_ALIG-1)) & (~(TN_ALIG-1))) 47 | 48 | #define TN_PORT_STACK_EXPAND_AT_EXIT 21 49 | 50 | #define USE_ASM_FFS 51 | 52 | //---------------------------------------------------- 53 | 54 | #define TN_NUM_PRIORITY TN_BITS_IN_INT //-- 0..31 Priority 0 always is used by timers task 55 | 56 | #define TN_WAIT_INFINITE 0xFFFFFFFF 57 | #define TN_FILL_STACK_VAL 0xFFFFFFFF 58 | #define TN_INVALID_VAL 0xFFFFFFFF 59 | 60 | //-- Assembler functions prototypes 61 | 62 | #ifdef __cplusplus 63 | extern "C" { 64 | #endif 65 | 66 | void tn_switch_context_exit(void); 67 | void tn_switch_context(void); 68 | 69 | unsigned int tn_cpu_save_sr(void); 70 | void tn_cpu_restore_sr(unsigned int sr); 71 | void tn_start_exe(void); 72 | int tn_chk_irq_disabled(void); 73 | int tn_inside_int(void); 74 | int ffs_asm(unsigned int val); 75 | void *tn_hook_vec(int vec, void *func); 76 | 77 | void tn_switch_context_trap(void); 78 | 79 | #ifdef __cplusplus 80 | } /* extern "C" */ 81 | #endif 82 | 83 | //-- Interrupt processing - processor specific 84 | 85 | #define TN_INTSAVE_DATA_INT int tn_save_status_reg = 0; 86 | #define TN_INTSAVE_DATA int tn_save_status_reg = 0; 87 | #define tn_disable_interrupt() tn_save_status_reg = tn_cpu_save_sr() 88 | #define tn_enable_interrupt() tn_cpu_restore_sr(tn_save_status_reg) 89 | 90 | #define tn_idisable_interrupt() tn_save_status_reg = tn_cpu_save_sr() 91 | #define tn_ienable_interrupt() tn_cpu_restore_sr(tn_save_status_reg) 92 | 93 | #define TN_CHECK_INT_CONTEXT \ 94 | if(!tn_inside_int()) \ 95 | return TERR_WCONTEXT; 96 | 97 | #define TN_CHECK_INT_CONTEXT_NORETVAL \ 98 | if(!tn_inside_int()) \ 99 | return; 100 | 101 | #define TN_CHECK_NON_INT_CONTEXT \ 102 | if(tn_inside_int()) \ 103 | return TERR_WCONTEXT; 104 | 105 | #define TN_CHECK_NON_INT_CONTEXT_NORETVAL \ 106 | if(tn_inside_int()) \ 107 | return ; 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /tn_sem.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #include "tn.h" 31 | #include "tn_utils.h" 32 | 33 | //---------------------------------------------------------------------------- 34 | // Structure's field sem->id_sem have to be set to 0 35 | //---------------------------------------------------------------------------- 36 | int tn_sem_create(TN_SEM * sem, 37 | int start_value, 38 | int max_val) 39 | { 40 | 41 | #if TN_CHECK_PARAM 42 | if (sem == NULL) //-- Thanks to Michael Fisher 43 | return TERR_WRONG_PARAM; 44 | if (max_val <= 0 || start_value < 0 || 45 | start_value > max_val || sem->id_sem != 0) //-- no recreation 46 | { 47 | sem->max_count = 0; 48 | return TERR_WRONG_PARAM; 49 | } 50 | #endif 51 | 52 | TN_CHECK_NON_INT_CONTEXT 53 | 54 | queue_reset(&(sem->wait_queue)); 55 | 56 | sem->count = start_value; 57 | sem->max_count = max_val; 58 | sem->id_sem = TN_ID_SEMAPHORE; 59 | 60 | return TERR_NO_ERR; 61 | } 62 | 63 | //---------------------------------------------------------------------------- 64 | int tn_sem_delete(TN_SEM * sem) 65 | { 66 | TN_INTSAVE_DATA 67 | CDLL_QUEUE * que; 68 | TN_TCB * task; 69 | 70 | #if TN_CHECK_PARAM 71 | if (sem == NULL) 72 | return TERR_WRONG_PARAM; 73 | if (sem->id_sem != TN_ID_SEMAPHORE) 74 | return TERR_NOEXS; 75 | #endif 76 | 77 | TN_CHECK_NON_INT_CONTEXT 78 | 79 | tn_disable_interrupt(); // v.2.7 - thanks to Eugene Scopal 80 | 81 | while (!is_queue_empty(&(sem->wait_queue))) 82 | { 83 | //--- delete from the sem wait queue 84 | 85 | que = queue_remove_head(&(sem->wait_queue)); 86 | task = get_task_by_tsk_queue(que); 87 | if (task_wait_complete(task)) 88 | { 89 | task->task_wait_rc = TERR_DLT; 90 | tn_enable_interrupt(); 91 | tn_switch_context(); 92 | tn_disable_interrupt(); // v.2.7 93 | } 94 | } 95 | 96 | sem->id_sem = 0; // Semaphore not exists now 97 | 98 | tn_enable_interrupt(); 99 | 100 | return TERR_NO_ERR; 101 | } 102 | 103 | //---------------------------------------------------------------------------- 104 | // Release Semaphore Resource 105 | //---------------------------------------------------------------------------- 106 | int tn_sem_signal(TN_SEM * sem) 107 | { 108 | TN_INTSAVE_DATA 109 | int rc; //-- return code 110 | CDLL_QUEUE * que; 111 | TN_TCB * task; 112 | 113 | #if TN_CHECK_PARAM 114 | if (sem == NULL) 115 | return TERR_WRONG_PARAM; 116 | if (sem->max_count == 0) 117 | return TERR_WRONG_PARAM; 118 | if (sem->id_sem != TN_ID_SEMAPHORE) 119 | return TERR_NOEXS; 120 | #endif 121 | 122 | TN_CHECK_NON_INT_CONTEXT 123 | 124 | tn_disable_interrupt(); 125 | 126 | if (!(is_queue_empty(&(sem->wait_queue)))) 127 | { 128 | //--- delete from the sem wait queue 129 | 130 | que = queue_remove_head(&(sem->wait_queue)); 131 | task = get_task_by_tsk_queue(que); 132 | 133 | if (task_wait_complete(task)) 134 | { 135 | tn_enable_interrupt(); 136 | tn_switch_context(); 137 | 138 | return TERR_NO_ERR; 139 | } 140 | rc = TERR_NO_ERR; 141 | } 142 | else 143 | { 144 | if (sem->count < sem->max_count) 145 | { 146 | sem->count++; 147 | rc = TERR_NO_ERR; 148 | } 149 | else 150 | rc = TERR_OVERFLOW; 151 | } 152 | 153 | tn_enable_interrupt(); 154 | 155 | return rc; 156 | } 157 | 158 | //---------------------------------------------------------------------------- 159 | // Release Semaphore Resource inside Interrupt 160 | //---------------------------------------------------------------------------- 161 | int tn_sem_isignal(TN_SEM * sem) 162 | { 163 | TN_INTSAVE_DATA_INT 164 | int rc; 165 | CDLL_QUEUE * que; 166 | TN_TCB * task; 167 | 168 | #if TN_CHECK_PARAM 169 | if (sem == NULL) 170 | return TERR_WRONG_PARAM; 171 | if (sem->max_count == 0) 172 | return TERR_WRONG_PARAM; 173 | if (sem->id_sem != TN_ID_SEMAPHORE) 174 | return TERR_NOEXS; 175 | #endif 176 | 177 | TN_CHECK_INT_CONTEXT 178 | 179 | tn_idisable_interrupt(); 180 | 181 | if (!(is_queue_empty(&(sem->wait_queue)))) 182 | { 183 | //--- delete from the sem wait queue 184 | 185 | que = queue_remove_head(&(sem->wait_queue)); 186 | task = get_task_by_tsk_queue(que); 187 | 188 | if (task_wait_complete(task)) 189 | { 190 | tn_ienable_interrupt(); 191 | 192 | return TERR_NO_ERR; 193 | } 194 | rc = TERR_NO_ERR; 195 | } 196 | else 197 | { 198 | if (sem->count < sem->max_count) 199 | { 200 | sem->count++; 201 | rc = TERR_NO_ERR; 202 | } 203 | else 204 | rc = TERR_OVERFLOW; 205 | } 206 | 207 | tn_ienable_interrupt(); 208 | 209 | return rc; 210 | } 211 | 212 | //---------------------------------------------------------------------------- 213 | // Acquire Semaphore Resource 214 | //---------------------------------------------------------------------------- 215 | int tn_sem_acquire(TN_SEM * sem, unsigned long timeout) 216 | { 217 | TN_INTSAVE_DATA 218 | int rc; //-- return code 219 | 220 | #if TN_CHECK_PARAM 221 | if (sem == NULL || timeout == 0) 222 | return TERR_WRONG_PARAM; 223 | if (sem->max_count == 0) 224 | return TERR_WRONG_PARAM; 225 | if (sem->id_sem != TN_ID_SEMAPHORE) 226 | return TERR_NOEXS; 227 | #endif 228 | 229 | TN_CHECK_NON_INT_CONTEXT 230 | 231 | tn_disable_interrupt(); 232 | 233 | if (sem->count >= 1) 234 | { 235 | sem->count--; 236 | rc = TERR_NO_ERR; 237 | } 238 | else 239 | { 240 | task_curr_to_wait_action(&(sem->wait_queue), TSK_WAIT_REASON_SEM, timeout); 241 | tn_enable_interrupt(); 242 | tn_switch_context(); 243 | 244 | return tn_kern_ctx_ptr()->tn_curr_run_task->task_wait_rc; 245 | } 246 | 247 | tn_enable_interrupt(); 248 | 249 | return rc; 250 | } 251 | 252 | //---------------------------------------------------------------------------- 253 | // Acquire(Polling) Semaphore Resource (do not call in the interrupt) 254 | //---------------------------------------------------------------------------- 255 | int tn_sem_polling(TN_SEM * sem) 256 | { 257 | TN_INTSAVE_DATA 258 | int rc; 259 | 260 | #if TN_CHECK_PARAM 261 | if (sem == NULL) 262 | return TERR_WRONG_PARAM; 263 | if (sem->max_count == 0) 264 | return TERR_WRONG_PARAM; 265 | if (sem->id_sem != TN_ID_SEMAPHORE) 266 | return TERR_NOEXS; 267 | #endif 268 | 269 | TN_CHECK_NON_INT_CONTEXT 270 | 271 | tn_disable_interrupt(); 272 | 273 | if (sem->count >= 1) 274 | { 275 | sem->count--; 276 | rc = TERR_NO_ERR; 277 | } 278 | else 279 | rc = TERR_TIMEOUT; 280 | 281 | tn_enable_interrupt(); 282 | 283 | return rc; 284 | } 285 | 286 | //---------------------------------------------------------------------------- 287 | // Acquire(Polling) Semaphore Resource inside interrupt 288 | //---------------------------------------------------------------------------- 289 | int tn_sem_ipolling(TN_SEM * sem) 290 | { 291 | TN_INTSAVE_DATA_INT 292 | int rc; 293 | 294 | #if TN_CHECK_PARAM 295 | if (sem == NULL) 296 | return TERR_WRONG_PARAM; 297 | if (sem->max_count == 0) 298 | return TERR_WRONG_PARAM; 299 | if (sem->id_sem != TN_ID_SEMAPHORE) 300 | return TERR_NOEXS; 301 | #endif 302 | 303 | TN_CHECK_INT_CONTEXT 304 | 305 | tn_idisable_interrupt(); 306 | 307 | if (sem->count >= 1) 308 | { 309 | sem->count--; 310 | rc = TERR_NO_ERR; 311 | } 312 | else 313 | rc = TERR_TIMEOUT; 314 | 315 | tn_ienable_interrupt(); 316 | 317 | return rc; 318 | } 319 | 320 | //---------------------------------------------------------------------------- 321 | //---------------------------------------------------------------------------- 322 | //---------------------------------------------------------------------------- 323 | //---------------------------------------------------------------------------- 324 | -------------------------------------------------------------------------------- /tn_tasks.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersm/SATNKernel/2e733f69d1d4ff2eba73f92e7f24c1c3ee80eae0/tn_tasks.c -------------------------------------------------------------------------------- /tn_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #include "tn.h" 31 | #include "tn_utils.h" 32 | 33 | //---------------------------------------------------------------------------- 34 | // Circular double-linked list queue 35 | //---------------------------------------------------------------------------- 36 | 37 | //---------------------------------------------------------------------------- 38 | void queue_reset(CDLL_QUEUE *que) 39 | { 40 | que->prev = que->next = que; 41 | } 42 | 43 | //---------------------------------------------------------------------------- 44 | BOOL is_queue_empty(CDLL_QUEUE *que) 45 | { 46 | if (que->next == que && que->prev == que) 47 | return TRUE; 48 | return FALSE; 49 | } 50 | 51 | //---------------------------------------------------------------------------- 52 | void queue_add_head(CDLL_QUEUE * que, CDLL_QUEUE * entry) 53 | { 54 | //-- Insert an entry at the head of the queue. 55 | 56 | entry->next = que->next; 57 | entry->prev = que; 58 | entry->next->prev = entry; 59 | que->next = entry; 60 | } 61 | 62 | //---------------------------------------------------------------------------- 63 | void queue_add_tail(CDLL_QUEUE * que, CDLL_QUEUE * entry) 64 | { 65 | //-- Insert an entry at the tail of the queue. 66 | 67 | entry->next = que; 68 | entry->prev = que->prev; 69 | entry->prev->next = entry; 70 | que->prev = entry; 71 | } 72 | 73 | //---------------------------------------------------------------------------- 74 | CDLL_QUEUE * queue_remove_head(CDLL_QUEUE * que) 75 | { 76 | //-- Remove and return an entry at the head of the queue. 77 | 78 | CDLL_QUEUE * entry; 79 | 80 | if (que == NULL || que->next == que) 81 | return (CDLL_QUEUE *) 0; 82 | 83 | entry = que->next; 84 | entry->next->prev = que; 85 | que->next = entry->next; 86 | return entry; 87 | } 88 | 89 | //---------------------------------------------------------------------------- 90 | CDLL_QUEUE * queue_remove_tail(CDLL_QUEUE * que) 91 | { 92 | //-- Remove and return an entry at the tail of the queue. 93 | 94 | CDLL_QUEUE * entry; 95 | 96 | if (que->prev == que) 97 | return (CDLL_QUEUE *) 0; 98 | 99 | entry = que->prev; 100 | entry->prev->next = que; 101 | que->prev = entry->prev; 102 | return entry; 103 | } 104 | 105 | //---------------------------------------------------------------------------- 106 | void queue_remove_entry(CDLL_QUEUE * entry) 107 | { 108 | //-- Remove an entry from the queue. 109 | 110 | entry->prev->next = entry->next; 111 | entry->next->prev = entry->prev; 112 | } 113 | 114 | //---------------------------------------------------------------------------- 115 | BOOL queue_contains_entry(CDLL_QUEUE * que, CDLL_QUEUE * entry) 116 | { 117 | //-- The entry in the queue ??? 118 | 119 | CDLL_QUEUE * curr_que; 120 | 121 | curr_que = que->next; 122 | while (curr_que != que) 123 | { 124 | if (curr_que == entry) 125 | return TRUE; //-- Found 126 | 127 | curr_que = curr_que->next; 128 | } 129 | 130 | return FALSE; 131 | } 132 | 133 | //--------------------------------------------------------------------------- 134 | // Data queue storage FIFO processing 135 | //--------------------------------------------------------------------------- 136 | 137 | //--------------------------------------------------------------------------- 138 | int dque_fifo_write(TN_DQUE * dque, void * data_ptr) 139 | { 140 | register int flag; 141 | 142 | #if TN_CHECK_PARAM 143 | if (dque == NULL) 144 | return TERR_WRONG_PARAM; 145 | #endif 146 | 147 | //-- v.2.7 148 | 149 | if (dque->num_entries <= 0) 150 | return TERR_OUT_OF_MEM; 151 | 152 | flag = ((dque->tail_cnt == 0 && dque->header_cnt == dque->num_entries - 1) 153 | || dque->header_cnt == dque->tail_cnt-1); 154 | if (flag) 155 | return TERR_OVERFLOW; //-- full 156 | 157 | //-- wr data 158 | 159 | dque->data_fifo[dque->header_cnt] = data_ptr; 160 | dque->header_cnt++; 161 | if (dque->header_cnt >= dque->num_entries) 162 | dque->header_cnt = 0; 163 | return TERR_NO_ERR; 164 | } 165 | 166 | //---------------------------------------------------------------------------- 167 | int dque_fifo_read(TN_DQUE * dque, void ** data_ptr) 168 | { 169 | 170 | #if TN_CHECK_PARAM 171 | if (dque == NULL || data_ptr == NULL) 172 | return TERR_WRONG_PARAM; 173 | #endif 174 | 175 | //-- v.2.7 Thanks to kosyak© from electronix.ru 176 | 177 | if (dque->num_entries <= 0) 178 | return TERR_OUT_OF_MEM; 179 | 180 | if (dque->tail_cnt == dque->header_cnt) 181 | return TERR_UNDERFLOW; //-- empty 182 | 183 | //-- rd data 184 | 185 | *data_ptr = dque->data_fifo[dque->tail_cnt]; 186 | dque->tail_cnt++; 187 | if (dque->tail_cnt >= dque->num_entries) 188 | dque->tail_cnt = 0; 189 | 190 | return TERR_NO_ERR; 191 | } 192 | 193 | //---------------------------------------------------------------------------- 194 | //---------------------------------------------------------------------------- 195 | //---------------------------------------------------------------------------- 196 | //---------------------------------------------------------------------------- 197 | //---------------------------------------------------------------------------- 198 | -------------------------------------------------------------------------------- /tn_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | SATNKernel real-time kernel for the Sega Saturn 4 | Based on TNKernel version 2.7 5 | 6 | Copyright © 2004, 2013 Yuri Tiomkin 7 | Saturn version modifications copyright © 2013 Anders Montonen 8 | All rights reserved. 9 | 10 | Permission to use, copy, modify, and distribute this software in source 11 | and binary forms and its documentation for any purpose and without fee 12 | is hereby granted, provided that the above copyright notice appear 13 | in all copies and that both that copyright notice and this permission 14 | notice appear in supporting documentation. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE YURI TIOMKIN AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL YURI TIOMKIN OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | 28 | */ 29 | 30 | #ifndef _TN_UTILS_H_ 31 | #define _TN_UTILS_H_ 32 | 33 | //-- Circular double-linked list queue -- 34 | 35 | #include "tn.h" 36 | 37 | #ifdef __cplusplus 38 | extern "C" { 39 | #endif 40 | 41 | void queue_reset(CDLL_QUEUE *que); 42 | BOOL is_queue_empty(CDLL_QUEUE *que); 43 | void queue_add_head(CDLL_QUEUE * que, CDLL_QUEUE * entry); 44 | void queue_add_tail(CDLL_QUEUE * que, CDLL_QUEUE * entry); 45 | CDLL_QUEUE * queue_remove_head(CDLL_QUEUE * que); 46 | CDLL_QUEUE * queue_remove_tail(CDLL_QUEUE * que); 47 | void queue_remove_entry(CDLL_QUEUE * entry); 48 | BOOL queue_contains_entry(CDLL_QUEUE * que, CDLL_QUEUE * entry); 49 | 50 | int dque_fifo_write(TN_DQUE * dque, void * data_ptr); 51 | int dque_fifo_read(TN_DQUE * dque, void ** data_ptr); 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | 58 | #endif 59 | --------------------------------------------------------------------------------