/tmp/sos-code-article6/sos/kthread.c (1970-01-01 01:00:00.000000000 +0100
) |
|
../sos-code-article6.5/sos/kthread.c (2005-01-04 04:12:16.000000000 +0100
) |
|
|
|
| /* Copyright (C) 2004 David Decotigny |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License |
| as published by the Free Software Foundation; either version 2 |
| of the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| USA. |
| */ |
| |
| #include <sos/physmem.h> |
| #include <sos/kmem_slab.h> |
| #include <sos/kmalloc.h> |
| #include <sos/klibc.h> |
| #include <sos/list.h> |
| #include <sos/assert.h> |
| |
| #include <hwcore/irq.h> |
| |
| #include "kthread.h" |
| |
| |
| /** |
| * The size of the stack of a kernel thread |
| */ |
| #define SOS_KTHREAD_STACK_SIZE (1*SOS_PAGE_SIZE) |
| |
| |
| /** |
| * The identifier of the thread currently running on CPU. |
| * |
| * We only support a SINGLE processor, ie a SINGLE kernel thread |
| * running at any time in the system. This greatly simplifies the |
| * implementation of the system, since we don't have to complicate |
| * things in order to retrieve the identifier of the threads running |
| * on the CPU. On multiprocessor systems the current_kthread below is |
| * an array indexed by the id of the CPU, so that the challenge is to |
| * retrieve the identifier of the CPU. This is usually done based on |
| * the stack address (Linux implementation) or on some form of TLS |
| * ("Thread Local Storage": can be implemented by way of LDTs for the |
| * processes, accessed through the fs or gs registers). |
| */ |
| static volatile struct sos_kthread *current_kthread = NULL; |
| |
| |
| /* |
| * The list of kernel threads currently in the system. |
| * |
| * @note We could have used current_kthread for that... |
| */ |
| static struct sos_kthread *kthread_list = NULL; |
| |
| |
| /** |
| * The Cache of kthread structures |
| */ |
| static struct sos_kslab_cache *cache_kthread; |
| |
| |
| struct sos_kthread *sos_kthread_get_current() |
| { |
| SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING); |
| return (struct sos_kthread*)current_kthread; |
| } |
| |
| |
| inline static sos_ret_t _set_current(struct sos_kthread *thr) |
| { |
| SOS_ASSERT_FATAL(thr->state == SOS_KTHR_READY); |
| current_kthread = thr; |
| current_kthread->state = SOS_KTHR_RUNNING; |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t sos_kthread_subsystem_setup(sos_vaddr_t init_thread_stack_base_addr, |
| sos_size_t init_thread_stack_size) |
| { |
| struct sos_kthread *myself; |
| |
| /* Allocate the cache of kthreads */ |
| cache_kthread = sos_kmem_cache_create("kthread", |
| sizeof(struct sos_kthread), |
| 2, |
| 0, |
| SOS_KSLAB_CREATE_MAP |
| | SOS_KSLAB_CREATE_ZERO); |
| if (! cache_kthread) |
| return -SOS_ENOMEM; |
| |
| /* Allocate a new kthread structure for the current running thread */ |
| myself = (struct sos_kthread*) sos_kmem_cache_alloc(cache_kthread, |
| SOS_KSLAB_ALLOC_ATOMIC); |
| if (! myself) |
| return -SOS_ENOMEM; |
| |
| /* Initialize the thread attributes */ |
| strzcpy(myself->name, "[kinit]", SOS_KTHR_MAX_NAMELEN); |
| myself->state = SOS_KTHR_CREATED; |
| myself->stack_base_addr = init_thread_stack_base_addr; |
| myself->stack_size = init_thread_stack_size; |
| |
| /* Do some stack poisoning on the bottom of the stack, if needed */ |
| sos_cpu_kstate_prepare_detect_stack_overflow(myself->cpu_kstate, |
| myself->stack_base_addr, |
| myself->stack_size); |
| |
| /* Add the thread in the global list */ |
| list_singleton_named(kthread_list, myself, gbl_prev, gbl_next); |
| |
| /* Ok, now pretend that the running thread is ourselves */ |
| myself->state = SOS_KTHR_READY; |
| _set_current(myself); |
| |
| return SOS_OK; |
| } |
| |
| |
| struct sos_kthread *sos_kthread_create(const char *name, |
| sos_kthread_start_routine_t start_func, |
| void *start_arg) |
| { |
| __label__ undo_creation; |
| struct sos_kthread *new_thread; |
| |
| if (! start_func) |
| return NULL; |
| |
| /* Allocate a new kthread structure for the current running thread */ |
| new_thread |
| = (struct sos_kthread*) sos_kmem_cache_alloc(cache_kthread, |
| SOS_KSLAB_ALLOC_ATOMIC); |
| if (! new_thread) |
| return NULL; |
| |
| /* Initialize the thread attributes */ |
| strzcpy(new_thread->name, ((name)?name:"[NONAME]"), SOS_KTHR_MAX_NAMELEN); |
| new_thread->state = SOS_KTHR_CREATED; |
| |
| /* Allocate the stack for the new thread */ |
| new_thread->stack_base_addr = sos_kmalloc(SOS_KTHREAD_STACK_SIZE, 0); |
| new_thread->stack_size = SOS_KTHREAD_STACK_SIZE; |
| if (! new_thread->stack_base_addr) |
| goto undo_creation; |
| |
| /* Initialize the CPU context of the new thread */ |
| if (SOS_OK |
| != sos_cpu_kstate_init(& new_thread->cpu_kstate, |
| (sos_cpu_kstate_function_arg1_t*) start_func, |
| (sos_ui32_t) start_arg, |
| new_thread->stack_base_addr, |
| new_thread->stack_size, |
| (sos_cpu_kstate_function_arg1_t*) sos_kthread_exit, |
| (sos_ui32_t) NULL)) |
| goto undo_creation; |
| |
| /* Add the thread in the global list */ |
| list_add_tail_named(kthread_list, new_thread, gbl_prev, gbl_next); |
| |
| /* Mark the thread ready */ |
| if (SOS_OK != sos_sched_set_ready(new_thread)) |
| goto undo_creation; |
| |
| /* Normal non-erroneous end of function */ |
| return new_thread; |
| |
| undo_creation: |
| sos_kmem_cache_free((sos_vaddr_t) new_thread); |
| return NULL; |
| } |
| |
| |
| /** Function called after thr has terminated. Called from inside the context |
| of another thread, interrupts disabled */ |
| static void delete_thread(struct sos_kthread *thr) |
| { |
| list_delete_named(kthread_list, thr, gbl_prev, gbl_next); |
| |
| sos_cpu_kstate_detect_stack_overflow(thr->cpu_kstate, |
| thr->stack_base_addr, |
| thr->stack_size); |
| |
| sos_kfree((sos_vaddr_t) thr->stack_base_addr); |
| memset(thr, 0x0, sizeof(struct sos_kthread)); |
| sos_kmem_cache_free((sos_vaddr_t) thr); |
| } |
| |
| |
| void sos_kthread_exit() |
| { |
| sos_ui32_t flags; |
| struct sos_kthread *myself, *next_thread; |
| |
| myself = sos_kthread_get_current(); |
| |
| /* Refuse to end the current executing thread if it still holds a |
| resource ! */ |
| SOS_ASSERT_FATAL(list_is_empty_named(myself->kwaitq_list, |
| prev_entry_for_kthread, |
| next_entry_for_kthread)); |
| |
| /* Prepare to run the next thread */ |
| sos_disable_IRQs(flags); |
| myself->state = SOS_KTHR_ZOMBIE; |
| next_thread = sos_reschedule(myself, FALSE); |
| _set_current(next_thread); |
| |
| /* No need for sos_restore_IRQs() here because the IRQ flag will be |
| restored to that of the next thread upon context switch */ |
| |
| /* Immediate switch to next thread */ |
| sos_cpu_kstate_exit_to(next_thread->cpu_kstate, |
| (sos_cpu_kstate_function_arg1_t*) delete_thread, |
| (sos_ui32_t) myself); |
| } |
| |
| |
| sos_kthread_state_t sos_kthread_get_state(struct sos_kthread *thr) |
| { |
| if (! thr) |
| thr = (struct sos_kthread*)current_kthread; |
| |
| return thr->state; |
| } |
| |
| |
| typedef enum { YIELD_MYSELF, BLOCK_MYSELF } switch_type_t; |
| /** |
| * Helper function to initiate a context switch in case the current |
| * thread becomes blocked, waiting for a timeout, or calls yield. |
| */ |
| static sos_ret_t _switch_to_next_thread(switch_type_t operation) |
| { |
| struct sos_kthread *myself, *next_thread; |
| |
| SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING); |
| |
| /* Interrupt handlers are NOT allowed to block ! */ |
| SOS_ASSERT_FATAL(! sos_servicing_irq()); |
| |
| myself = (struct sos_kthread*)current_kthread; |
| |
| /* Make sure that if we are to be marked "BLOCKED", we have any |
| reason of effectively being blocked */ |
| if (BLOCK_MYSELF == operation) |
| { |
| myself->state = SOS_KTHR_BLOCKED; |
| } |
| |
| /* Identify the next thread */ |
| next_thread = sos_reschedule(myself, YIELD_MYSELF == operation); |
| |
| /* Avoid context switch if the context does not change */ |
| if (myself != next_thread) |
| { |
| /* Sanity checks for the next thread */ |
| sos_cpu_kstate_detect_stack_overflow(next_thread->cpu_kstate, |
| next_thread->stack_base_addr, |
| next_thread->stack_size); |
| |
| /* Actual context switch */ |
| _set_current(next_thread); |
| sos_cpu_kstate_switch(& myself->cpu_kstate, next_thread->cpu_kstate); |
| |
| /* Back here ! */ |
| SOS_ASSERT_FATAL(current_kthread == myself); |
| SOS_ASSERT_FATAL(current_kthread->state == SOS_KTHR_RUNNING); |
| } |
| else |
| { |
| /* No context switch but still update ID of current thread */ |
| _set_current(next_thread); |
| } |
| |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t sos_kthread_yield() |
| { |
| sos_ui32_t flags; |
| sos_ret_t retval; |
| |
| sos_disable_IRQs(flags); |
| |
| retval = _switch_to_next_thread(YIELD_MYSELF); |
| |
| sos_restore_IRQs(flags); |
| return retval; |
| } |
| |
| |
| /** |
| * Internal sleep timeout management |
| */ |
| struct sleep_timeout_params |
| { |
| struct sos_kthread *thread_to_wakeup; |
| sos_bool_t timeout_triggered; |
| }; |
| |
| |
| /** |
| * Callback called when a timeout happened |
| */ |
| static void sleep_timeout(struct sos_timeout_action *act) |
| { |
| struct sleep_timeout_params *sleep_timeout_params |
| = (struct sleep_timeout_params*) act->routine_data; |
| |
| /* Signal that we have been woken up by the timeout */ |
| sleep_timeout_params->timeout_triggered = TRUE; |
| |
| /* Mark the thread ready */ |
| SOS_ASSERT_FATAL(SOS_OK == |
| sos_kthread_force_unblock(sleep_timeout_params |
| ->thread_to_wakeup)); |
| } |
| |
| |
| sos_ret_t sos_kthread_sleep(struct sos_time *timeout) |
| { |
| sos_ui32_t flags; |
| struct sleep_timeout_params sleep_timeout_params; |
| struct sos_timeout_action timeout_action; |
| sos_ret_t retval; |
| |
| /* Block forever if no timeout is given */ |
| if (NULL == timeout) |
| { |
| sos_disable_IRQs(flags); |
| retval = _switch_to_next_thread(BLOCK_MYSELF); |
| sos_restore_IRQs(flags); |
| |
| return retval; |
| } |
| |
| /* Initialize the timeout action */ |
| sos_time_init_action(& timeout_action); |
| |
| /* Prepare parameters used by the sleep timeout callback */ |
| sleep_timeout_params.thread_to_wakeup |
| = (struct sos_kthread*)current_kthread; |
| sleep_timeout_params.timeout_triggered = FALSE; |
| |
| sos_disable_IRQs(flags); |
| |
| /* Now program the timeout ! */ |
| SOS_ASSERT_FATAL(SOS_OK == |
| sos_time_register_action_relative(& timeout_action, |
| timeout, |
| sleep_timeout, |
| & sleep_timeout_params)); |
| |
| /* Prepare to block: wait for sleep_timeout() to wakeup us in the |
| timeout kwaitq, or for someone to wake us up in any other |
| waitq */ |
| retval = _switch_to_next_thread(BLOCK_MYSELF); |
| /* Unblocked by something ! */ |
| |
| /* Unblocked by timeout ? */ |
| if (sleep_timeout_params.timeout_triggered) |
| { |
| /* Yes */ |
| SOS_ASSERT_FATAL(sos_time_is_zero(& timeout_action.timeout)); |
| retval = SOS_OK; |
| } |
| else |
| { |
| /* No: We have probably been woken up while in some other |
| kwaitq */ |
| SOS_ASSERT_FATAL(SOS_OK == sos_time_unregister_action(& timeout_action)); |
| retval = -SOS_EINTR; |
| } |
| |
| sos_restore_IRQs(flags); |
| |
| /* Update the remaining timeout */ |
| memcpy(timeout, & timeout_action.timeout, sizeof(struct sos_time)); |
| |
| return retval; |
| } |
| |
| |
| sos_ret_t sos_kthread_force_unblock(struct sos_kthread *kthread) |
| { |
| sos_ret_t retval; |
| sos_ui32_t flags; |
| |
| if (! kthread) |
| return -SOS_EINVAL; |
| |
| sos_disable_IRQs(flags); |
| |
| /* Thread already woken up ? */ |
| retval = SOS_OK; |
| switch(sos_kthread_get_state(kthread)) |
| { |
| case SOS_KTHR_RUNNING: |
| case SOS_KTHR_READY: |
| /* Do nothing */ |
| break; |
| |
| case SOS_KTHR_ZOMBIE: |
| retval = -SOS_EFATAL; |
| break; |
| |
| default: |
| retval = sos_sched_set_ready(kthread); |
| break; |
| } |
| |
| sos_restore_IRQs(flags); |
| |
| return retval; |
| } |
| |
/tmp/sos-code-article6/sos/main.c (2004-12-03 16:27:45.000000000 +0100
) |
|
../sos-code-article6.5/sos/main.c (2005-01-04 04:12:15.000000000 +0100
) |
|
|
|
#include <hwcore/paging.h> | #include <hwcore/paging.h> |
#include <sos/kmem_vmm.h> | #include <sos/kmem_vmm.h> |
#include <sos/kmalloc.h> | #include <sos/kmalloc.h> |
| #include <sos/time.h> |
| #include <sos/kthread.h> |
#include <sos/klibc.h> | #include <sos/klibc.h> |
#include <sos/assert.h> | #include <sos/assert.h> |
#include <drivers/x86_videomem.h> | #include <drivers/x86_videomem.h> |
|
|
SOS_X86_VIDEO_FG_LTGREEN | SOS_X86_VIDEO_BG_BLUE, | SOS_X86_VIDEO_FG_LTGREEN | SOS_X86_VIDEO_BG_BLUE, |
clock_count); | clock_count); |
clock_count++; | clock_count++; |
| |
| /* Execute the expired timeout actions (if any) */ |
| sos_time_do_tick(); |
} | } |
| |
| |
|
|
} | } |
| |
| |
| |
/* ====================================================================== | |
* Demonstrate the use of the CPU kernet context management API: | |
* - A coroutine prints "Hlowrd" and switches to the other after each | |
* letter | |
* - A coroutine prints "el ol\n" and switches back to the other after | |
* each letter. | |
* The first to reach the '\n' returns back to main. | |
*/ | |
struct sos_cpu_kstate *ctxt_hello1; | |
struct sos_cpu_kstate *ctxt_hello2; | |
struct sos_cpu_kstate *ctxt_main; | |
sos_vaddr_t hello1_stack, hello2_stack; | |
| |
static void reclaim_stack(sos_vaddr_t stack_vaddr) | |
{ | |
sos_kfree(stack_vaddr); | |
} | |
| |
| |
static void exit_hello12(sos_vaddr_t stack_vaddr) | |
{ | |
sos_cpu_kstate_exit_to(ctxt_main, | |
(sos_cpu_kstate_function_arg1_t*) reclaim_stack, | |
stack_vaddr); | |
} | |
| |
| |
static void hello1 (char *str) | |
{ | |
for ( ; *str != '\n' ; str++) | |
{ | |
sos_bochs_printf("hello1: %c\n", *str); | |
sos_cpu_kstate_switch(& ctxt_hello1, ctxt_hello2); | |
} | |
| |
/* You can uncomment this in case you explicitly want to exit | |
now. But returning from the function will do the same */ | |
/* sos_cpu_kstate_exit_to(ctxt_main, | |
(sos_cpu_kstate_function_arg1_t*) reclaim_stack, | |
hello1_stack); */ | |
} | |
| |
| |
static void hello2 (char *str) | |
{ | |
for ( ; *str != '\n' ; str++) | |
{ | |
sos_bochs_printf("hello2: %c\n", *str); | |
sos_cpu_kstate_switch(& ctxt_hello2, ctxt_hello1); | |
} | |
| |
/* You can uncomment this in case you explicitly want to exit | |
now. But returning from the function will do the same */ | |
/* sos_cpu_kstate_exit_to(ctxt_main, | |
(sos_cpu_kstate_function_arg1_t*) reclaim_stack, | |
hello2_stack); */ | |
} | |
| |
| |
void print_hello_world () | |
{ | |
#define DEMO_STACK_SIZE 1024 | |
/* Allocate the stacks */ | |
hello1_stack = sos_kmalloc(DEMO_STACK_SIZE, 0); | |
hello2_stack = sos_kmalloc(DEMO_STACK_SIZE, 0); | |
| |
/* Initialize the coroutines' contexts */ | |
sos_cpu_kstate_init(&ctxt_hello1, | |
(sos_cpu_kstate_function_arg1_t*) hello1, | |
(sos_ui32_t) "Hlowrd", | |
(sos_vaddr_t) hello1_stack, DEMO_STACK_SIZE, | |
(sos_cpu_kstate_function_arg1_t*) exit_hello12, | |
(sos_ui32_t) hello1_stack); | |
sos_cpu_kstate_init(&ctxt_hello2, | |
(sos_cpu_kstate_function_arg1_t*) hello2, | |
(sos_ui32_t) "el ol\n", | |
(sos_vaddr_t) hello2_stack, DEMO_STACK_SIZE, | |
(sos_cpu_kstate_function_arg1_t*) exit_hello12, | |
(sos_ui32_t) hello2_stack); | |
| |
/* Go to first coroutine */ | |
sos_bochs_printf("Printing Hello World\\n...\n"); | |
sos_cpu_kstate_switch(& ctxt_main, ctxt_hello1); | |
| |
/* The first coroutine to reach the '\n' switched back to us */ | |
sos_bochs_printf("Back in main !\n"); | |
} | |
| |
| |
/* ====================================================================== | |
* Generate page faults on an unmapped but allocated kernel virtual | |
* region, which results in a series of physical memory mappings for the | |
* faulted pages. | |
*/ | |
static void test_demand_paging(int nb_alloc_vpages, int nb_alloc_ppages) | |
{ | |
int i; | |
sos_vaddr_t base_vaddr; | |
| |
sos_x86_videomem_printf(10, 0, | |
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_LTGREEN, | |
"Demand paging test (alloc %dMB of VMM, test %dkB RAM)", | |
nb_alloc_vpages >> 8, nb_alloc_ppages << 2); | |
| |
/* Allocate virtual memory */ | |
base_vaddr = sos_kmem_vmm_alloc(nb_alloc_vpages, 0); | |
| |
SOS_ASSERT_FATAL(base_vaddr != (sos_vaddr_t)NULL); | |
sos_x86_videomem_printf(11, 0, | |
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
"Allocated virtual region [0x%x, 0x%x[", | |
base_vaddr, | |
base_vaddr + nb_alloc_vpages*SOS_PAGE_SIZE); | |
| |
/* Now use part of it in physical memory */ | |
for (i = 0 ; (i < nb_alloc_ppages) && (i < nb_alloc_vpages) ; i++) | |
{ | |
/* Compute an address inside the range */ | |
sos_ui32_t *value, j; | |
sos_vaddr_t vaddr = base_vaddr; | |
vaddr += (nb_alloc_vpages - (i + 1))*SOS_PAGE_SIZE; | |
vaddr += 2345; | |
| |
sos_x86_videomem_printf(12, 0, | |
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
"Writing %d at virtual address 0x%x...", | |
i, vaddr); | |
| |
/* Write at this address */ | |
value = (sos_ui32_t*)vaddr; | |
*value = i; | |
| |
/* Yep ! A new page should normally have been allocated for us */ | |
sos_x86_videomem_printf(13, 0, | |
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
"Value read at address 0x%x = %d", | |
vaddr, (unsigned)*value); | |
} | |
| |
SOS_ASSERT_FATAL(SOS_OK == sos_kmem_vmm_free(base_vaddr)); | |
/* Yep ! A new page should normally have been allocated for us */ | |
sos_x86_videomem_printf(14, 0, | |
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
"Done (area un-allocated)"); | |
} | |
| |
| |
| |
* Shows how the backtrace stuff works | * Demonstrate the use of SOS kernel threads |
| * - Kernel Threads are created with various priorities and their |
| * state is printed on both the console and the bochs' 0xe9 port |
| * - For tests regarding threads' synchronization, see mouse_sim.c |
| |
/* Recursive function. Print the backtrace from the innermost function */ | struct thr_arg |
static void test_backtrace(int i, int magic, sos_vaddr_t stack_bottom, | |
sos_size_t stack_size) | |
if (i <= 0) | char character; |
{ | int color; |
/* The page fault exception handler will print the backtrace of | |
this function, because address 0x42 is not mapped */ | |
*((char*)0x42) = 12; | |
| |
/* More direct variant: */ | |
/* dump_backtrace(NULL, stack_bottom, stack_size, TRUE, TRUE); */ | |
} | |
else | |
test_backtrace(i-1, magic, stack_bottom, stack_size); | |
} | |
| |
/* ====================================================================== | int col; |
* Parsing of Mathematical expressions | int row; |
* | }; |
* This is a recursive lexer/parser/evaluator for arithmetical | |
* expressions. Supports both binary +/-* and unary +- operators, as | |
* well as parentheses. | |
* | |
* Terminal tokens (Lexer): | |
* - Number: positive integer number | |
* - Variable: ascii name (regexp: [a-zA-Z]+) | |
* - Operator: +*-/ | |
* - Opening/closing parentheses | |
* | |
* Grammar (Parser): | |
* Expression ::= Term E' | |
* Expr_lr ::= + Term Expr_lr | - Term Expr_lr | Nothing | |
* Term ::= Factor Term_lr | |
* Term_lr ::= * Factor Term_lr | / Factor Term_lr | Nothing | |
* Factor ::= - Factor | + Factor | Scalar | ( Expression ) | |
* Scalar ::= Number | Variable | |
* | |
* Note. This is the left-recursive equivalent of the following basic grammar: | |
* Expression ::= Expression + Term | Expression - Term | |
* Term ::= Term * Factor | Term / Factor | |
* factor ::= - Factor | + Factor | Scalar | Variable | ( Expression ) | |
* Scalar ::= Number | Variable | |
* | |
* The parsing is composed of a 3 stages pipeline: | |
* - The reader: reads a string 1 character at a time, transferring | |
* the control back to lexer after each char. This function shows the | |
* interest in using coroutines, because its state (str) is | |
* implicitely stored in the stack between each iteration. | |
* - The lexer: consumes the characters from the reader and identifies | |
* the terminal tokens, 1 token at a time, transferring control back | |
* to the parser after each token. This function shows the interest | |
* in using coroutines, because its state (c and got_what_before) is | |
* implicitely stored in the stack between each iteration. | |
* - The parser: consumes the tokens from the lexer and builds the | |
* syntax tree of the expression. There is no real algorithmic | |
* interest in defining a coroutine devoted to do this. HOWEVER, we | |
* do use one for that because this allows us to switch to a much | |
* deeper stack. Actually, the parser is highly recursive, so that | |
* the default 16kB stack of the sos_main() function might not be | |
* enough. Here, we switch to a 64kB stack, which is safer for | |
* recursive functions. The Parser uses intermediary functions: these | |
* are defined and implemented as internal nested functions. This is | |
* just for the sake of clarity, and is absolutely not mandatory for | |
* the algorithm: one can transfer these functions out of the parser | |
* function without restriction. | |
* | |
* The evaluator is another recursive function that reuses the | |
* parser's stack to evaluate the parsed expression with the given | |
* values for the variables present in the expression. As for the | |
* parser function, this function defines and uses a nested function, | |
* which can be extracted from the main evaluation function at will. | |
* | |
* All these functions support a kind of "exception" feature: when | |
* something goes wrong, control is transferred DIRECTLY back to the | |
* sos_main() context, without unrolling the recursions. This shows | |
* how exceptions basically work, but one should not consider this as | |
* a reference exceptions implementation. Real exception mechanisms | |
* (such as that in the C++ language) call the destructors to the | |
* objects allocated on the stack during the "stack unwinding" process | |
* upon exception handling, which complicates a lot the mechanism. We | |
* don't have real Objects here (in the OOP sense, full-featured with | |
* destructors), so we don't have to complicate things. | |
* | |
* After this little coroutine demo, one should forget all about such | |
* a low-level manual direct manipulation of stacks. This would | |
* probably mess up the whole kernel to do what we do here (locked | |
* resources such as mutex/semaphore won't be correctly unlocked, | |
* ...). Higher level "kernel thread" primitives will soon be | |
* presented, which provide a higher-level set of APIs to manage CPU | |
* contexts. You'll have to use EXCLUSIVELY those APIs. If you still | |
* need a huge stack to do recursion for example, please don't even | |
* think of changing manually the stack for something bigger ! Simply | |
* rethink your algorithm, making it non-recursive. | |
*/ | |
| |
| |
/* The stacks involved */ | |
static char stack_reader[1024]; | |
static char stack_lexer[1024]; | |
static char deep_stack[65536]; /* For the parser and the evaluator */ | |
| |
/* The CPU states for the various coroutines */ | |
static struct sos_cpu_kstate *st_reader, *st_lexer, *st_parser, | |
*st_eval, *st_free, *st_main; | |
| |
/* | static void demo_thread(void *arg) |
* Default exit/reclaim functions: return control to the "sos_main()" | |
* context | |
*/ | |
static void reclaim(int unused) | |
} | struct thr_arg *thr_arg = (struct thr_arg*)arg; |
static void func_exit(sos_ui32_t unused) | int progress = 0; |
{ | |
sos_cpu_kstate_exit_to(st_main, (sos_cpu_kstate_function_arg1_t*)reclaim, 0); | |
} | |
| |
| |
/* | |
* The reader coroutine and associated variable. This coroutine could | |
* have been a normal function, except that the current parsed | |
* character would have to be stored somewhere. | |
*/ | |
static char data_reader_to_lexer; | |
static void func_reader(const char *str) | sos_bochs_printf("start %c", thr_arg->character); |
{ | while (1) |
for ( ; str && (*str != '\0') ; str++) | |
data_reader_to_lexer = *str; | progress ++; |
sos_cpu_kstate_switch(& st_reader, st_lexer); | display_bits(thr_arg->row, thr_arg->col+1, thr_arg->color, progress); |
} | |
| |
data_reader_to_lexer = '\0'; | |
sos_cpu_kstate_switch(& st_reader, st_lexer); | |
} | |
| sos_bochs_putchar(thr_arg->character); |
| |
/* | /* Yield the CPU to another thread sometimes... */ |
* The Lexer coroutine and associated types/variables. This coroutine | if ((random() % 100) == 0) |
* could have been a normal function, except that the current parsed | |
* character, token and previous token would have to be stored | |
* somewhere. | |
*/ | |
#define STR_VAR_MAXLEN 16 | |
static struct lex_elem | |
{ | |
enum { LEX_IS_NUMBER, LEX_IS_OPER, LEX_IS_VAR, | |
LEX_IS_OPENPAR, LEX_IS_CLOSEPAR, LEX_END } type; | |
union { | |
int number; | |
char operator; | |
char var[STR_VAR_MAXLEN]; | |
}; | |
} data_lexer_to_parser; | |
| |
static void func_lexer(sos_ui32_t unused) | |
{ | |
char c; | |
enum { GOT_SPACE, GOT_NUM, GOT_OP, GOT_STR, | |
GOT_OPENPAR, GOT_CLOSEPAR } got_what, got_what_before; | |
| |
data_lexer_to_parser.number = 0; | |
got_what_before = GOT_SPACE; | |
do | |
{ | |
/* Consume one character from the reader */ | |
sos_cpu_kstate_switch(& st_lexer, st_reader); | |
c = data_reader_to_lexer; | |
| |
/* Classify the consumed character */ | |
if ( (c >= '0') && (c <= '9') ) | |
got_what = GOT_NUM; | |
else if ( (c == '+') || (c == '-') || (c == '*') || (c == '/') ) | |
got_what = GOT_OP; | |
else if ( ( (c >= 'a') && (c <= 'z') ) | |
|| ( (c >= 'A') && (c <= 'Z') ) ) | |
got_what = GOT_STR; | |
else if (c == '(') | |
got_what = GOT_OPENPAR; | |
else if (c == ')') | |
got_what = GOT_CLOSEPAR; | |
else | |
got_what = GOT_SPACE; | |
| |
/* Determine whether the current token is ended */ | |
if ( (got_what != got_what_before) | |
|| (got_what_before == GOT_OP) | |
|| (got_what_before == GOT_OPENPAR) | |
|| (got_what_before == GOT_CLOSEPAR) ) | |
/* return control back to the parser if the previous token | sos_bochs_printf("[37myield(%c)[m\n", thr_arg->character); |
has been recognized */ | sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'Y'); |
if ( (got_what_before != GOT_SPACE) ) | SOS_ASSERT_FATAL(SOS_OK == sos_kthread_yield()); |
sos_cpu_kstate_switch(& st_lexer, st_parser); | sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R'); |
| |
data_lexer_to_parser.number = 0; | |
| |
/* Update the token being currently recognized */ | /* Go to sleep some other times... */ |
if (got_what == GOT_OP) | else if ((random() % 200) == 0) |
{ | |
data_lexer_to_parser.type = LEX_IS_OPER; | |
data_lexer_to_parser.operator = c; | |
} | |
else if (got_what == GOT_NUM) | |
data_lexer_to_parser.type = LEX_IS_NUMBER; | struct sos_time t = (struct sos_time){ .sec=0, .nanosec=50000000 }; |
data_lexer_to_parser.number *= 10; | sos_bochs_printf("[37msleep1(%c)[m\n", thr_arg->character); |
data_lexer_to_parser.number += (c - '0'); | sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 's'); |
| SOS_ASSERT_FATAL(SOS_OK == sos_kthread_sleep(& t)); |
| SOS_ASSERT_FATAL(sos_time_is_zero(& t)); |
| sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R'); |
else if (got_what == GOT_STR) | |
| /* Go to sleep for a longer time some other times... */ |
| else if ((random() % 300) == 0) |
char to_cat[] = { c, '\0' }; | struct sos_time t = (struct sos_time){ .sec=0, .nanosec=300000000 }; |
data_lexer_to_parser.type = LEX_IS_VAR; | sos_bochs_printf("[37msleep2(%c)[m\n", thr_arg->character); |
strzcat(data_lexer_to_parser.var, to_cat, STR_VAR_MAXLEN); | sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'S'); |
| SOS_ASSERT_FATAL(SOS_OK == sos_kthread_sleep(& t)); |
| SOS_ASSERT_FATAL(sos_time_is_zero(& t)); |
| sos_x86_videomem_putchar(thr_arg->row, thr_arg->col, 0x1e, 'R'); |
else if (got_what == GOT_OPENPAR) | |
data_lexer_to_parser.type = LEX_IS_OPENPAR; | |
else if (got_what == GOT_CLOSEPAR) | |
data_lexer_to_parser.type = LEX_IS_CLOSEPAR; | |
got_what_before = got_what; | /* Infinite loop otherwise */ |
while (c != '\0'); | |
| |
/* Transfer last recognized token to the parser */ | |
if ( (got_what_before != GOT_SPACE) ) | |
sos_cpu_kstate_switch(& st_lexer, st_parser); | |
| |
/* Signal that no more token are available */ | |
data_lexer_to_parser.type = LEX_END; | |
sos_cpu_kstate_switch(& st_lexer, st_parser); | |
| |
/* Exception: parser asks for a token AFTER having received the last | |
one */ | |
sos_bochs_printf("Error: end of string already reached !\n"); | |
sos_cpu_kstate_switch(& st_lexer, st_main); | |
| |
| |
/* | static void test_kthread() |
* The Parser coroutine and associated types/variables | |
*/ | |
struct syntax_node | |
enum { YY_IS_BINOP, YY_IS_UNAROP, YY_IS_NUM, YY_IS_VAR } type; | /* "static" variables because we want them to remain even when the |
union | function returns */ |
{ | static struct thr_arg arg_b, arg_c, arg_d, arg_e, arg_R, arg_S; |
int number; | sos_ui32_t flags; |
char var[STR_VAR_MAXLEN]; | |
struct | |
{ | |
char op; | |
struct syntax_node *parm_left, *parm_right; | |
} binop; | |
struct | |
{ | |
char op; | |
struct syntax_node *parm; | |
} unarop; | |
}; | |
}; | |
static void func_parser(struct syntax_node ** syntax_tree) | sos_disable_IRQs(flags); |
{ | |
static struct syntax_node *alloc_node_num(int val); | |
static struct syntax_node *alloc_node_var(const char * name); | |
static struct syntax_node *alloc_node_binop(char op, | |
struct syntax_node *parm_left, | |
struct syntax_node *parm_right); | |
static struct syntax_node *alloc_node_unarop(char op, | |
struct syntax_node *parm); | |
static struct syntax_node * get_expr(); | |
static struct syntax_node * get_expr_lr(struct syntax_node *n); | |
static struct syntax_node * get_term(); | |
static struct syntax_node * get_term_lr(struct syntax_node *n); | |
static struct syntax_node * get_factor(); | |
static struct syntax_node * get_scalar(); | |
/* Create a new node to store a number */ | arg_b = (struct thr_arg) { .character='b', .col=0, .row=21, .color=0x14 }; |
static struct syntax_node *alloc_node_num(int val) | sos_kthread_create("YO[b]", demo_thread, (void*)&arg_b); |
{ | |
struct syntax_node *n | |
= (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | |
n->type = YY_IS_NUM; | |
n->number = val; | |
return n; | |
} | |
/* Create a new node to store a variable */ | |
static struct syntax_node *alloc_node_var(const char * name) | |
{ | |
struct syntax_node *n | |
= (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | |
n->type = YY_IS_VAR; | |
strzcpy(n->var, name, STR_VAR_MAXLEN); | |
return n; | |
} | |
/* Create a new node to store a binary operator */ | |
static struct syntax_node *alloc_node_binop(char op, | |
struct syntax_node *parm_left, | |
struct syntax_node *parm_right) | |
{ | |
struct syntax_node *n | |
= (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | |
n->type = YY_IS_BINOP; | |
n->binop.op = op; | |
n->binop.parm_left = parm_left; | |
n->binop.parm_right = parm_right; | |
return n; | |
} | |
/* Create a new node to store a unary operator */ | |
static struct syntax_node *alloc_node_unarop(char op, | |
struct syntax_node *parm) | |
{ | |
struct syntax_node *n | |
= (struct syntax_node*) sos_kmalloc(sizeof(struct syntax_node), 0); | |
n->type = YY_IS_UNAROP; | |
n->unarop.op = op; | |
n->unarop.parm = parm; | |
return n; | |
} | |
/* Raise an exception: transfer control back to main context, | arg_c = (struct thr_arg) { .character='c', .col=46, .row=21, .color=0x14 }; |
without unrolling the whole recursion */ | sos_kthread_create("YO[c]", demo_thread, (void*)&arg_c); |
static void parser_exception(const char *str) | |
{ | |
sos_bochs_printf("Parser exception: %s\n", str); | |
sos_cpu_kstate_switch(& st_parser, st_main); | |
} | |
/* Consume the current terminal "number" token and ask for a new | arg_d = (struct thr_arg) { .character='d', .col=0, .row=20, .color=0x14 }; |
token */ | sos_kthread_create("YO[d]", demo_thread, (void*)&arg_d); |
static int get_number() | |
{ | |
int v; | |
if (data_lexer_to_parser.type != LEX_IS_NUMBER) | |
parser_exception("Expected number"); | |
v = data_lexer_to_parser.number; | |
sos_cpu_kstate_switch(& st_parser, st_lexer); | |
return v; | |
} | |
/* Consume the current terminal "variable" token and ask for a new | |
token */ | |
static void get_str(char name[STR_VAR_MAXLEN]) | |
{ | |
if (data_lexer_to_parser.type != LEX_IS_VAR) | |
parser_exception("Expected variable"); | |
strzcpy(name, data_lexer_to_parser.var, STR_VAR_MAXLEN); | |
sos_cpu_kstate_switch(& st_parser, st_lexer); | |
} | |
/* Consume the current terminal "operator" token and ask for a new | |
token */ | |
static char get_op() | |
{ | |
char op; | |
if (data_lexer_to_parser.type != LEX_IS_OPER) | |
parser_exception("Expected operator"); | |
op = data_lexer_to_parser.operator; | |
sos_cpu_kstate_switch(& st_parser, st_lexer); | |
return op; | |
} | |
/* Consume the current terminal "parenthese" token and ask for a new | |
token */ | |
static void get_par() | |
{ | |
if ( (data_lexer_to_parser.type != LEX_IS_OPENPAR) | |
&& (data_lexer_to_parser.type != LEX_IS_CLOSEPAR) ) | |
parser_exception("Expected parenthese"); | |
sos_cpu_kstate_switch(& st_parser, st_lexer); | |
} | |
| |
/* Parse an Expression */ | |
static struct syntax_node * get_expr() | |
{ | |
struct syntax_node *t = get_term(); | |
return get_expr_lr(t); | |
} | |
/* Parse an Expr_lr */ | |
static struct syntax_node * get_expr_lr(struct syntax_node *n) | |
{ | |
if ( (data_lexer_to_parser.type == LEX_IS_OPER) | |
&& ( (data_lexer_to_parser.operator == '+') | |
|| (data_lexer_to_parser.operator == '-') ) ) | |
{ | |
char op = get_op(); | |
struct syntax_node *term = get_term(); | |
struct syntax_node *node_op = alloc_node_binop(op, n, term); | |
return get_expr_lr(node_op); | |
} | |
return n; | |
} | |
/* Parse a Term */ | |
static struct syntax_node * get_term() | |
{ | |
struct syntax_node *f1 = get_factor(); | |
return get_term_lr(f1); | |
} | |
/* Parse a Term_lr */ | |
static struct syntax_node * get_term_lr(struct syntax_node *n) | |
{ | |
if ( (data_lexer_to_parser.type == LEX_IS_OPER) | |
&& ( (data_lexer_to_parser.operator == '*') | |
|| (data_lexer_to_parser.operator == '/') ) ) | |
{ | |
char op = get_op(); | |
struct syntax_node *factor = get_factor(); | |
struct syntax_node *node_op = alloc_node_binop(op, n, factor); | |
return get_term_lr(node_op); | |
} | |
return n; | |
} | |
/* Parse a Factor */ | |
static struct syntax_node * get_factor() | |
{ | |
if ( (data_lexer_to_parser.type == LEX_IS_OPER) | |
&& ( (data_lexer_to_parser.operator == '-') | |
|| (data_lexer_to_parser.operator == '+') ) ) | |
{ char op = data_lexer_to_parser.operator; | |
get_op(); return alloc_node_unarop(op, get_factor()); } | |
else if (data_lexer_to_parser.type == LEX_IS_OPENPAR) | |
{ | |
struct syntax_node *expr; | |
get_par(); | |
expr = get_expr(); | |
if (data_lexer_to_parser.type != LEX_IS_CLOSEPAR) | |
parser_exception("Mismatched parentheses"); | |
get_par(); | |
return expr; | |
} | |
| |
return get_scalar(); | |
} | |
/* Parse a Scalar */ | |
static struct syntax_node * get_scalar() | |
{ | |
if (data_lexer_to_parser.type != LEX_IS_NUMBER) | |
{ | |
char var[STR_VAR_MAXLEN]; | |
get_str(var); | |
return alloc_node_var(var); | |
} | |
return alloc_node_num(get_number()); | |
} | |
| arg_e = (struct thr_arg) { .character='e', .col=0, .row=19, .color=0x14 }; |
| sos_kthread_create("YO[e]", demo_thread, (void*)&arg_e); |
| |
/* | arg_R = (struct thr_arg) { .character='R', .col=0, .row=17, .color=0x1c }; |
* Body of the function | sos_kthread_create("YO[R]", demo_thread, (void*)&arg_R); |
*/ | |
/* Get the first token */ | arg_S = (struct thr_arg) { .character='S', .col=0, .row=16, .color=0x1c }; |
sos_cpu_kstate_switch(& st_parser, st_lexer); | sos_kthread_create("YO[S]", demo_thread, (void*)&arg_S); |
/* Begin the parsing ! */ | sos_restore_IRQs(flags); |
*syntax_tree = get_expr(); | |
/* The result is returned in the syntax_tree parameter */ | |
| |
| |
/* | /* ====================================================================== |
* Setup the parser's pipeline | * An operating system MUST always have a ready thread ! Otherwise: |
*/ | * what would the CPU have to execute ?! |
static struct syntax_node * parse_expression(const char *expr) | |
{ | |
struct syntax_node *retval = NULL; | |
| |
/* Build the context of the functions in the pipeline */ | |
sos_cpu_kstate_init(& st_reader, | |
(sos_cpu_kstate_function_arg1_t*)func_reader, | |
(sos_ui32_t)expr, | |
(sos_vaddr_t)stack_reader, sizeof(stack_reader), | |
(sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |
sos_cpu_kstate_init(& st_lexer, | |
(sos_cpu_kstate_function_arg1_t*)func_lexer, | |
0, | |
(sos_vaddr_t)stack_lexer, sizeof(stack_lexer), | |
(sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |
sos_cpu_kstate_init(& st_parser, | |
(sos_cpu_kstate_function_arg1_t*)func_parser, | |
(sos_ui32_t) /* syntax tree ! */&retval, | |
(sos_vaddr_t)deep_stack, sizeof(deep_stack), | |
(sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |
| |
/* Parse the expression */ | |
sos_cpu_kstate_switch(& st_main, st_parser); | |
return retval; | |
} | |
| |
| |
/* | |
* The Evaluator coroutine and associated types/variables | |
struct func_eval_params | static void idle_kthread() |
const struct syntax_node *e; | sos_ui32_t idle_twiddle = 0; |
const char **var_name; | |
int *var_val; | |
int nb_vars; | |
int result; | while (1) |
}; | |
| |
static void func_eval(struct func_eval_params *parms) | |
{ | |
/* The internal (recursive) nested function to evaluate each node of | |
the syntax tree */ | |
static int rec_eval(const struct syntax_node *n, | |
const char* var_name[], int var_val[], int nb_vars) | |
switch (n->type) | /* Remove this instruction if you get an "Invalid opcode" CPU |
{ | exception (old 80386 CPU) */ |
case YY_IS_NUM: | asm("hlt\n"); |
return n->number; | |
case YY_IS_VAR: | idle_twiddle ++; |
{ | display_bits(0, 0, SOS_X86_VIDEO_FG_GREEN | SOS_X86_VIDEO_BG_BLUE, |
int i; | idle_twiddle); |
for (i = 0 ; i < nb_vars ; i++) | |
if (0 == strcmp(var_name[i], n->var)) | |
return var_val[i]; | |
| |
/* Exception: no variable with that name ! */ | |
sos_bochs_printf("ERROR: unknown variable %s\n", n->var); | |
sos_cpu_kstate_switch(& st_eval, st_main); | |
} | |
| |
case YY_IS_BINOP: | |
{ | |
int left = rec_eval(n->binop.parm_left, | |
var_name, var_val, nb_vars); | |
int right = rec_eval(n->binop.parm_right, | |
var_name, var_val, nb_vars); | |
switch (n->binop.op) | |
{ | |
case '+': return left + right; | |
case '-': return left - right; | |
case '*': return left * right; | |
case '/': return left / right; | |
default: | |
/* Exception: no such operator (INTERNAL error) ! */ | |
sos_bochs_printf("ERROR: unknown binop %c\n", n->binop.op); | |
sos_cpu_kstate_switch(& st_eval, st_main); | |
} | |
} | |
| |
case YY_IS_UNAROP: | |
{ | |
int arg = rec_eval(n->unarop.parm, var_name, var_val, nb_vars); | |
switch (n->unarop.op) | |
{ | |
case '-': return -arg; | |
case '+': return arg; | |
default: | |
/* Exception: no such operator (INTERNAL error) ! */ | |
sos_bochs_printf("ERROR: unknown unarop %c\n", n->unarop.op); | |
sos_cpu_kstate_switch(& st_eval, st_main); | |
} | |
} | |
} | |
/* Exception: no such syntax node (INTERNAL error) ! */ | /* Lend the CPU to some other thread */ |
sos_bochs_printf("ERROR: invalid node type\n"); | sos_kthread_yield(); |
sos_cpu_kstate_switch(& st_eval, st_main); | |
return -1; /* let's make gcc happy */ | |
| |
| |
/* | |
* Function BODY | |
*/ | |
/* Update p.result returned back to calling function */ | |
parms->result | |
= rec_eval(parms->e, parms->var_name, parms->var_val, parms->nb_vars); | |
} | |
| |
/* | |
* Change the stack for something larger in order to call the | |
* recursive function above in a safe way | |
*/ | |
static int eval_expression(const struct syntax_node *e, | |
const char* var_name[], int var_val[], int nb_vars) | |
{ | |
struct func_eval_params p | |
= (struct func_eval_params){ .e=e, | |
.var_name=var_name, | |
.var_val=var_val, | |
.nb_vars=nb_vars, | |
.result = 0 }; | |
| |
sos_cpu_kstate_init(& st_eval, | |
(sos_cpu_kstate_function_arg1_t*)func_eval, | |
(sos_ui32_t)/* p.result is modified upon success */&p, | |
(sos_vaddr_t)deep_stack, sizeof(deep_stack), | |
(sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |
| |
/* Go ! */ | |
sos_cpu_kstate_switch(& st_main, st_eval); | |
return p.result; | |
| |
| |
/* | /* ====================================================================== |
* Function to free the syntax tree | * The C entry point of our operating system |
*/ | |
static void func_free(struct syntax_node *n) | |
{ | |
switch (n->type) | |
{ | |
case YY_IS_NUM: | |
case YY_IS_VAR: | |
break; | |
| |
case YY_IS_BINOP: | |
func_free(n->binop.parm_left); | |
func_free(n->binop.parm_right); | |
break; | |
| |
case YY_IS_UNAROP: | |
func_free(n->unarop.parm); | |
break; | |
} | |
| |
sos_kfree((sos_vaddr_t)n); | |
} | |
| |
/* | |
* Change the stack for something larger in order to call the | |
* recursive function above in a safe way | |
static void free_syntax_tree(struct syntax_node *tree) | |
{ | |
sos_cpu_kstate_init(& st_free, | |
(sos_cpu_kstate_function_arg1_t*)func_free, | |
(sos_ui32_t)tree, | |
(sos_vaddr_t)deep_stack, sizeof(deep_stack), | |
(sos_cpu_kstate_function_arg1_t*)func_exit, 0); | |
| |
/* Go ! */ | |
sos_cpu_kstate_switch(& st_main, st_free); | |
} | |
| |
| |
/* The C entry point of our operating system */ | |
{ | { |
unsigned i; | unsigned i; |
sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr; | sos_paddr_t sos_kernel_core_base_paddr, sos_kernel_core_top_paddr; |
struct syntax_node *syntax_tree; | struct sos_time tick_resolution; |
/* Grub sends us a structure, called multiboot_info_t with a lot of | /* Grub sends us a structure, called multiboot_info_t with a lot of |
precious informations about the system, see the multiboot | precious informations about the system, see the multiboot |
|
|
sos_bochs_putstring("Message in a bochs\n"); | sos_bochs_putstring("Message in a bochs\n"); |
| |
/* Setup CPU segmentation and IRQ subsystem */ | /* Setup CPU segmentation and IRQ subsystem */ |
sos_gdt_setup(); | sos_gdt_subsystem_setup(); |
sos_idt_setup(); | sos_idt_subsystem_setup(); |
/* Setup SOS IRQs and exceptions subsystem */ | /* Setup SOS IRQs and exceptions subsystem */ |
sos_exceptions_setup(); | sos_exception_subsystem_setup(); |
sos_irq_setup(); | sos_irq_subsystem_setup(); |
/* Configure the timer so as to raise the IRQ0 at a 100Hz rate */ | /* Configure the timer so as to raise the IRQ0 at a 100Hz rate */ |
sos_i8254_set_frequency(100); | sos_i8254_set_frequency(100); |
| |
| /* Setup the kernel time subsystem to get prepared to take the timer |
| ticks into account */ |
| tick_resolution = (struct sos_time) { .sec=0, .nanosec=10000000UL }; |
| sos_time_subsysem_setup(& tick_resolution); |
| |
/* We need a multiboot-compliant boot loader to get the size of the RAM */ | /* We need a multiboot-compliant boot loader to get the size of the RAM */ |
if (magic != MULTIBOOT_BOOTLOADER_MAGIC) | if (magic != MULTIBOOT_BOOTLOADER_MAGIC) |
{ | { |
|
|
/* Multiboot says: "The value returned for upper memory is maximally | /* Multiboot says: "The value returned for upper memory is maximally |
the address of the first upper memory hole minus 1 megabyte.". It | the address of the first upper memory hole minus 1 megabyte.". It |
also adds: "It is not guaranteed to be this value." aka "YMMV" ;) */ | also adds: "It is not guaranteed to be this value." aka "YMMV" ;) */ |
sos_physmem_setup((mbi->mem_upper<<10) + (1<<20), | sos_physmem_subsystem_setup((mbi->mem_upper<<10) + (1<<20), |
& sos_kernel_core_base_paddr, | & sos_kernel_core_base_paddr, |
& sos_kernel_core_top_paddr); | & sos_kernel_core_top_paddr); |
/* | /* |
* Switch to paged-memory mode | * Switch to paged-memory mode |
|
|
| |
/* Disabling interrupts should seem more correct, but it's not really | /* Disabling interrupts should seem more correct, but it's not really |
necessary at this stage */ | necessary at this stage */ |
if (sos_paging_setup(sos_kernel_core_base_paddr, | SOS_ASSERT_FATAL(SOS_OK == |
sos_kernel_core_top_paddr)) | sos_paging_subsystem_setup(sos_kernel_core_base_paddr, |
sos_bochs_printf("Could not setup paged memory mode\n"); | sos_kernel_core_top_paddr)); |
sos_x86_videomem_printf(2, 0, | |
SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, | |
"Paged-memory mode is activated"); | |
| |
sos_exception_set_routine(SOS_EXCEPT_PAGE_FAULT, | sos_exception_set_routine(SOS_EXCEPT_PAGE_FAULT, |
pgflt_ex); | pgflt_ex); |
|
|
* Setup kernel virtual memory allocator | * Setup kernel virtual memory allocator |
*/ | */ |
| |
if (sos_kmem_vmm_setup(sos_kernel_core_base_paddr, | if (sos_kmem_vmm_subsystem_setup(sos_kernel_core_base_paddr, |
sos_kernel_core_top_paddr, | sos_kernel_core_top_paddr, |
bootstrap_stack_bottom, | bootstrap_stack_bottom, |
bootstrap_stack_bottom + bootstrap_stack_size)) | bootstrap_stack_bottom |
| + bootstrap_stack_size)) |
| |
if (sos_kmalloc_setup()) | if (sos_kmalloc_subsystem_setup()) |
| |
/* | /* |
* Enabling the HW interrupts here, this will make the timer HW | * Initialize the Kernel thread and scheduler subsystems |
* interrupt call our clk_it handler | |
asm volatile ("sti\n"); | |
| /* Initialize kernel thread subsystem */ |
/* | sos_kthread_subsystem_setup(bootstrap_stack_bottom, |
* Print hello world using coroutines | bootstrap_stack_size); |
*/ | |
print_hello_world(); | |
| /* Initialize the scheduler */ |
| sos_sched_subsystem_setup(); |
| |
/* | /* Declare the IDLE thread */ |
* Run coroutine tests | SOS_ASSERT_FATAL(sos_kthread_create("idle", idle_kthread, NULL) != NULL); |
*/ | |
sos_x86_videomem_printf(4, 0, | |
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_LTGREEN, | |
"Coroutine test"); | |
sos_x86_videomem_printf(5, 0, | |
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
"Parsing..."); | |
syntax_tree = parse_expression(" - ( (69/ toto)+ ( (( - +-- 1))) + --toto*((toto+ - - y - +2*(y-toto))*y) +2*(y-toto) )/- (( y - toto)*2)"); | |
if (syntax_tree != NULL) | |
{ | |
sos_x86_videomem_printf(6, 0, | |
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
"Evaluating..."); | |
sos_x86_videomem_printf(7, 0, | |
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
"Result=%d (if 0: check bochs output)", | |
eval_expression(syntax_tree, | |
(const char*[]){"toto", "y"}, | |
(int[]){3, 4}, | |
2)); | |
free_syntax_tree(syntax_tree); | |
sos_x86_videomem_printf(8, 0, | |
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
"Done (un-allocated syntax tree)"); | |
} | |
else | |
{ | |
sos_x86_videomem_printf(6, 0, | |
SOS_X86_VIDEO_BG_BLUE | SOS_X86_VIDEO_FG_YELLOW, | |
"Error in parsing (see bochs output)"); | |
} | |
/* | /* Enabling the HW interrupts here, this will make the timer HW |
* Run some demand-paging tests | interrupt call the scheduler */ |
*/ | asm volatile ("sti\n"); |
test_demand_paging(234567, 500); | |
| |
| |
/* | |
* Create an un-resolved page fault, which will make the page fault | |
* handler print the backtrace. | |
*/ | |
test_backtrace(6, 0xdeadbeef, bootstrap_stack_bottom, bootstrap_stack_size); | |
/* | /* |
* System should be halted BEFORE here ! | * Force the idle thread to run at least once to force a context |
*/ | * switch. This way the "cpu_kstate" of the kernel thread for the |
| * sos_main thread gets a chance to be filled with the current CPU |
| * context. Useful only if we call sos_kthread_exit() too early from |
/* An operatig system never ends */ | * sos_main: a "stack overflow" will be wrongly detected simply |
for (;;) | * because the "cpu_kstate" of the thread has not be correctly |
continue; | * initialised. A context switch is a good way to initialise it. |
| */ |
return; | sos_kthread_yield(); |
| |
| |
| /* Now run some Kernel threads just for fun ! */ |
| extern void MouseSim(); |
| MouseSim(); |
| test_kthread(); |
| |
| /* |
| * We can safely exit from this function now, for there is already |
| * an idle Kernel thread ready to make the CPU busy working... |
| * |
| * However, we must EXPLICITELY call sos_kthread_exit() because a |
| * simple "return" will return nowhere ! Actually this first thread |
| * was initialized by the Grub bootstrap stage, at a time when the |
| * word "thread" did not exist. This means that the stack was not |
| * setup in order for a return here to call sos_kthread_exit() |
| * automagically. Hence we must call it manually. This is the ONLY |
| * kernel thread where we must do this manually. |
| */ |
| sos_bochs_printf("Bye from primary thread !\n"); |
| sos_kthread_exit(); |
| SOS_FATAL_ERROR("No trespassing !"); |
| |
/tmp/sos-code-article6/sos/mouse_sim.c (1970-01-01 01:00:00.000000000 +0100
) |
|
../sos-code-article6.5/sos/mouse_sim.c (2005-01-04 04:12:16.000000000 +0100
) |
|
|
|
| /*************************************************************************** |
| * Copyright (C) 2004 by cyril dupuit * |
| * cyrildupuit@hotmail.com * |
| * http://perso.wanadoo.fr/koalys/ * |
| * (Adaptation for SOS by d2 -- 2004/12/20) * |
| * * |
| * This program is free software; you can redistribute it and/or modify * |
| * it under the terms of the GNU General Public License as published by * |
| * the Free Software Foundation; either version 2 of the License, or * |
| * (at your option) any later version. * |
| * * |
| * This program is distributed in the hope that it will be useful, * |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
| * GNU General Public License for more details. * |
| * * |
| * You should have received a copy of the GNU General Public License * |
| * along with this program; if not, write to the * |
| * Free Software Foundation, Inc., * |
| * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * |
| ***************************************************************************/ |
| |
| //***************************************************************************** |
| // Nom du module : MouseSim.c |
| // Description : Creation et destruction de souris mangeuse de fromages |
| //***************************************************************************** |
| |
| #include <sos/assert.h> |
| #include <sos/klibc.h> |
| #include <sos/kthread.h> |
| #include <sos/ksynch.h> |
| #include <sos/kmalloc.h> |
| #include <drivers/x86_videomem.h> |
| |
| // Historique : |
| // 20/12/04 : Suppr DestroyMap et suppr handler kbd dans version LM (d2) |
| // 26/11/04 : Bug trouve et resolu dans la fonction DestroyMap |
| // 21/11/04 : Creation du module V1.0 |
| |
| //***************************************************************************** |
| // Definition des equivalences : |
| //***************************************************************************** |
| #define MAP_X 76 |
| #define MAP_Y 12 |
| #define MAP_SIZE MAP_X * MAP_Y |
| |
| #define MOUSE 0x01 |
| #define CHEESE 0x02 |
| #define OBSTACLE 0x04 |
| #define INPUT 0x08 |
| #define OUTPUT 0x10 |
| |
| #define OBSTACLE_COUNT 100 |
| #define CHEESE_COUNT 650 |
| |
| #define MOUSE_FULL 0x01 |
| #define MOUSE_EMPTY 0x02 |
| #define CHEESE_FOUND 0x04 |
| #define MOUSE_EXITED 0x08 |
| |
| #define MOUSE_SPEED_MAX 1000 |
| #define MOUSE_SPEED_MIN 4 |
| |
| typedef unsigned int Color_t; |
| |
| struct Point{ |
| int X; |
| int Y; |
| }; |
| |
| typedef struct Point Point_t; |
| |
| #define Set(Reg, Flag) Reg = (Reg | Flag) |
| #define Reset(Reg, Flag) Reg = (Reg &(~Flag)) |
| #define IsSet(Reg, Flag) (Reg & Flag) |
| |
| |
| //***************************************************************************** |
| // Structure de gestion d'un element |
| //***************************************************************************** |
| struct Element{ |
| sos_ui32_t Type;//Type d'element |
| sos_ui32_t Status; |
| Color_t Color;//Couleur de l'element |
| Point_t P;//Coordonnees de l'element |
| struct sos_kthread * ThreadID;//Thread associe a la souris |
| int Way;//Direction de la souris |
| }; |
| |
| typedef struct Element Element_t; |
| |
| //***************************************************************************** |
| // Prototypes des fonctions/procedures : |
| //***************************************************************************** |
| static void MouseCommander(void); |
| static void DrawMap(void); |
| static sos_ret_t CreateMap(void); |
| static sos_ret_t InitMapInput(Element_t * * pMap); |
| static sos_ret_t InitMapOutput(Element_t * * pMap); |
| static sos_ret_t ElementInit(Element_t * * pMap, unsigned int Type); |
| static void Mouse(unsigned long Param); |
| static void MouseMove(Point_t * P); |
| static Point_t ChoosePosition(Element_t * pMouse, int Positions[], int Count); |
| static int EvaluatePositions(Point_t Org, int Positions[], Point_t * Cheese); |
| static sos_bool_t IsCollision(Point_t Org, Point_t p, Point_t *Cheese); |
| static sos_bool_t AffectMovement(Point_t Org, Point_t p); |
| static void MouseCreator(void); |
| static sos_ret_t CreateMouse(void); |
| |
| //***************************************************************************** |
| // Variables globales de ce module : |
| //***************************************************************************** |
| |
| static Element_t * * pMap; |
| static struct sos_ksema SemMap; |
| static struct sos_ksema SemMouse; |
| static int MouseCount = 0; |
| static int CheeseCount = 0; |
| static int ObstacleCount = 0; |
| static int MouseSpeed = 100; |
| |
| //***************************************************************************** |
| // Koalys Glue |
| //***************************************************************************** |
| void DrawPixel(int x, int y, Color_t color) |
| { |
| sos_x86_videomem_putchar(y+3, x+2, color, 219); |
| } |
| |
| |
| void ThreadDelete(struct sos_kthread *tid) |
| { |
| SOS_ASSERT_FATAL(! "TODO !"); |
| } |
| |
| //***************************************************************************** |
| // Point d'entre de la 'simulation' |
| //***************************************************************************** |
| void MouseSim(void) |
| { |
| //Creation du semaphore de protection de la carte |
| SOS_ASSERT_FATAL(SOS_OK == sos_ksema_init(& SemMap, "SemMap", 1)); |
| |
| //Creation du semaphore de creation de souris |
| SOS_ASSERT_FATAL(SOS_OK == sos_ksema_init(& SemMouse, "SemMouse", 2)); |
| |
| //Creation de la carte |
| SOS_ASSERT_FATAL(SOS_OK == CreateMap()); |
| |
| //Creation du thread createur de souris |
| SOS_ASSERT_FATAL(sos_kthread_create("MouseCreator", |
| (sos_kthread_start_routine_t)MouseCreator, |
| 0) != NULL); |
| |
| } |
| |
| |
| //***************************************************************************** |
| // But de la fonction : Creer et initialiser la carte |
| // Entree : Aucune |
| // Parametre retourne : ERROR si la memoire est insuffisante, TRUE sinon |
| //***************************************************************************** |
| static sos_ret_t CreateMap(void) |
| { |
| pMap = (Element_t * *)sos_kmalloc(MAP_SIZE * sizeof(Element_t *), 0); |
| if(pMap == NULL) return -SOS_ENOMEM; |
| |
| //Mettre la carte a 0 |
| memset(pMap, 0, MAP_SIZE * sizeof(Element_t *)); |
| |
| //Initialisation de l'entree de la carte |
| if(SOS_OK != InitMapInput(pMap)) |
| {//Memoire insuffisante |
| return -SOS_EFATAL; |
| } |
| |
| //Initialisation de la sortie de la carte |
| if(InitMapOutput(pMap) != SOS_OK) |
| {//Memoire insuffisante |
| return -SOS_EFATAL; |
| } |
| |
| //Initialisation du fromage |
| if(ElementInit(pMap, CHEESE) != SOS_OK) |
| {//Memoire insuffisante |
| return -SOS_EFATAL; |
| } |
| |
| //Initialisation des obstacles |
| if(ElementInit(pMap, OBSTACLE) != SOS_OK) |
| {//Memoire insuffisante |
| return -SOS_EFATAL; |
| } |
| |
| DrawMap();//Afficher la carte creee |
| |
| return SOS_OK; |
| } |
| |
| //***************************************************************************** |
| // But de la procedure : Dessiner la carte a l'ecran |
| // Entree : Aucune |
| // Sortie : Aucune |
| //***************************************************************************** |
| static void DrawMap(void) |
| { |
| unsigned int I; |
| |
| for(I = 0; I < MAP_SIZE; I++) |
| { |
| if(pMap[I] != NULL) |
| { |
| DrawPixel(I % MAP_X, I/MAP_X, pMap[I]->Color); |
| } |
| else DrawPixel(I % MAP_X, I/MAP_X, SOS_X86_VIDEO_FG_BLACK); |
| } |
| sos_x86_videomem_printf(23, 0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, |
| "Souris = %d; Fromages = %d; Obstacles = %d ", |
| MouseCount, CheeseCount, ObstacleCount); |
| } |
| |
| //***************************************************************************** |
| // But de la fonction : Initialiser l'entree de la carte |
| // Entree : |
| // pMap : Pointeur sur la carte |
| // Parametre retourne : ERROR si memoire insuffisante, TRUE sinon |
| //***************************************************************************** |
| static sos_ret_t InitMapInput(Element_t * * pMap) |
| { |
| Element_t * pElement; |
| |
| //Definir le point d'entree |
| pElement = (Element_t *)sos_kmalloc(sizeof(Element_t), 0); |
| if(pElement == NULL) return -SOS_ENOMEM; |
| |
| //Initialiser l'entree |
| pElement->Type = INPUT; |
| pElement->Status = 0; |
| pElement->Color = SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE; |
| pElement->P.X = 0; |
| pElement->P.Y = MAP_Y / 2; |
| pElement->ThreadID = 0; |
| |
| pMap[(pElement->P.Y * MAP_X) + pElement->P.X] = pElement; |
| |
| return SOS_OK; |
| } |
| |
| //***************************************************************************** |
| // But de la fonction : Initialiser la sortie de la carte |
| // Entree : |
| // pMap : Pointeur sur la carte |
| // Parametre retourne : ERROR si memoire insuffisante, TRUE sinon |
| //***************************************************************************** |
| static sos_ret_t InitMapOutput(Element_t * * pMap) |
| { |
| Element_t * pElement; |
| |
| //Definir le point de sortie |
| pElement = (Element_t *)sos_kmalloc(sizeof(Element_t), 0); |
| if(pElement == NULL) return -SOS_ENOMEM; |
| |
| //Initialiser l'entree |
| pElement->Type = OUTPUT; |
| pElement->Status = 0; |
| pElement->Color = SOS_X86_VIDEO_FG_LTBLUE; |
| pElement->P.X = MAP_X - 1; |
| pElement->P.Y = MAP_Y / 2; |
| pElement->ThreadID = 0; |
| |
| pMap[(pElement->P.Y * MAP_X) + pElement->P.X] = pElement; |
| |
| return SOS_OK; |
| } |
| |
| //***************************************************************************** |
| // But de la fonction : Initialiser un type d'objet sur la carte |
| // Entree : |
| // pMap : Pointeur sur la carte |
| // Type : Type d'objet a initialiser |
| // Parametre retourne : ERROR si memoire insuffisante, TRUE sinon |
| //***************************************************************************** |
| static sos_ret_t ElementInit(Element_t * * pMap, unsigned int Type) |
| { |
| unsigned int I, J; |
| unsigned int Max; |
| Color_t Color; |
| |
| if(Type == CHEESE) |
| {//Type d'element = fromage |
| Max = CHEESE_COUNT; |
| Color = SOS_X86_VIDEO_FG_YELLOW; |
| } |
| else if(Type == OBSTACLE) |
| {//Type d'element = Obstacle |
| Max = OBSTACLE_COUNT; |
| Color = SOS_X86_VIDEO_FG_GREEN; |
| } |
| else |
| {//Aucune autre type reconnu |
| return -SOS_EINVAL; |
| } |
| |
| for(I = 0; I < Max; I++) |
| {//Tirer les fromages |
| J = random(); |
| J += random(); |
| J %= MAP_SIZE; |
| if(pMap[J] == NULL) |
| {//Si l'emplacement est libre |
| pMap[J] = (Element_t *)sos_kmalloc(sizeof(Element_t), |
| 0); |
| if(pMap[J] == NULL) return -SOS_ENOMEM; |
| |
| pMap[J]->Type = Type; |
| //Initialiser l'element |
| if(Type == CHEESE) |
| {//Type d'element = fromage |
| CheeseCount++; |
| } |
| else if(Type == OBSTACLE) |
| {//Type d'element = Obstacle |
| ObstacleCount++; |
| } |
| |
| pMap[J]->Color = Color; |
| pMap[J]->Status = 0; |
| pMap[J]->Color = Color; |
| pMap[J]->P.X = J % MAP_X; |
| pMap[J]->P.Y = J / MAP_X; |
| pMap[J]->ThreadID = 0; |
| } |
| } |
| |
| return SOS_OK; |
| } |
| |
| |
| //***************************************************************************** |
| // But du thread : Deplacer la souris sur la carte selon les regles etablies. |
| // Regles : |
| // - La souris doit se placer devant l'entree puis commence a recolter du |
| // fromage. |
| // - Des que la souris a ramasse un morceau de fromage, elle doit aller en |
| // entree de la carte afin de deposer sa recolte. |
| // - Si une souris a prouve sa recolte, une autre souris est creee. |
| // - Si une souris prend la sortie, elle est eliminee. |
| //***************************************************************************** |
| static void Mouse(unsigned long Param) |
| { |
| Element_t * pMouse = (Element_t *)Param; |
| Point_t P; |
| |
| SOS_ASSERT_FATAL(pMouse != NULL); |
| |
| //Position de depart de la souris |
| P = pMouse->P; |
| P = pMouse->P; |
| |
| while(1) |
| { |
| int delay_ms; |
| struct sos_time delay; |
| |
| //La souris doit se deplacer |
| sos_ksema_down(& SemMap, NULL); |
| |
| MouseMove(&P); |
| |
| sos_ksema_up(& SemMap); |
| |
| // Est-ce que la souris est sortie ? |
| if (IsSet(pMouse->Status, MOUSE_EXITED)) |
| // Oui => on sort |
| break; |
| |
| // Delai entre MOUSE_SPEED_MIN et MouseSpeed - 1 |
| delay_ms = MOUSE_SPEED_MIN + (random() % MouseSpeed); |
| delay.sec = delay_ms / 1000; |
| delay.nanosec = (delay_ms % 1000) * 1000000; |
| sos_kthread_sleep(& delay); |
| } |
| |
| // Libere la structure associee |
| sos_kfree((sos_vaddr_t)pMouse); |
| } |
| |
| //***************************************************************************** |
| // But de la procedure : Deplacer la souris de maniere aleatoire sur la carte |
| // Entrees : |
| // P : Position courante de la souris |
| // Sorties : |
| // P : Position suivante de la souris |
| //***************************************************************************** |
| static void MouseMove(Point_t * P) |
| { |
| Point_t Org; |
| Point_t p; |
| Point_t Cheese; |
| int Positions[8]; |
| int Count = 0; |
| Element_t * pMouse; |
| |
| Org = *P; |
| |
| pMouse = pMap[Org.X + (Org.Y * MAP_X)]; |
| |
| Count = EvaluatePositions(Org, Positions, &Cheese); |
| |
| if(Count == 0) return; |
| |
| p = Org; |
| |
| if(IsSet(pMouse->Status, CHEESE_FOUND)) |
| {//Prendre le fromage |
| Reset(pMouse->Status, CHEESE_FOUND); |
| p = Cheese; |
| } |
| else |
| {//Choisir une position au hasard |
| p = ChoosePosition(pMouse, Positions, Count); |
| } |
| if(AffectMovement(Org, p) == FALSE) return; |
| //Deplacer la souris |
| pMap[Org.X + (Org.Y * MAP_X)] = NULL; |
| pMap[p.X + (p.Y * MAP_X)] = pMouse; |
| pMouse->P = p; |
| //Mettre a jour l'affichage |
| DrawPixel(Org.X, Org.Y, SOS_X86_VIDEO_FG_BLACK); |
| DrawPixel(p.X, p.Y, pMouse->Color); |
| sos_x86_videomem_printf( 23,0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, "Souris = %d; Fromages = %d; Obstacles = %d ", MouseCount, CheeseCount, ObstacleCount); |
| //Mettre a jour les coordonnees |
| *P = p; |
| } |
| |
| //***************************************************************************** |
| // But de la fonction : Choisir un mouvement |
| // Entree : |
| // pMouse : Pointeur sur la souris |
| // Positions : Tableau de position possible |
| // Count :Nombre de positions valides |
| // Sortie : Aucune |
| // Parametre retourne : Position choisie |
| //***************************************************************************** |
| static Point_t ChoosePosition(Element_t * pMouse, int Positions[], int Count) |
| { |
| int I, J; |
| Point_t p; |
| |
| for(J = 0; J < Count; J++) |
| {//Chercher dans le tableau si cette position est disponible |
| I = Positions[J]; |
| if(I == pMouse->Way) |
| {//Poursuivre ce sens d'avance |
| p = pMouse->P; |
| switch(I) |
| { |
| case 0: |
| p.Y++; |
| break; |
| case 1: |
| p.X++; |
| p.Y++; |
| break; |
| case 2: |
| p.X++; |
| break; |
| case 3: |
| p.Y--; |
| p.X++; |
| break; |
| case 4: |
| p.Y--; |
| break; |
| case 5: |
| p.Y--; |
| p.X--; |
| break; |
| case 6: |
| p.X--; |
| break; |
| case 7: |
| p.X--; |
| p.Y++; |
| break; |
| } |
| return p; |
| } |
| } |
| |
| J = random() % Count; |
| I = Positions[J]; |
| if(((I + 4) % 8) == pMouse->Way) |
| {//Eviter le sens inverse |
| J = (J + 1) % Count; |
| I = Positions[J]; |
| } |
| |
| p = pMouse->P; |
| switch(I) |
| {//Repere le deplacement |
| case 0: |
| p.Y++; |
| break; |
| case 1: |
| p.X++; |
| p.Y++; |
| break; |
| case 2: |
| p.X++; |
| break; |
| case 3: |
| p.Y--; |
| p.X++; |
| break; |
| case 4: |
| p.Y--; |
| break; |
| case 5: |
| p.Y--; |
| p.X--; |
| break; |
| case 6: |
| p.X--; |
| break; |
| case 7: |
| p.X--; |
| p.Y++; |
| break; |
| } |
| |
| pMouse->Way = I;//Memoriser la direction selectionnee |
| |
| return p; |
| } |
| |
| //***************************************************************************** |
| // But de la fonction : Evaluer les positions possibles et les memoriser dans |
| // un tableau de positions si aucun fromage n'a ete detecte. Si du fromage a |
| // ete detecte, il sera selectionne en premier. La presence d'un fromage est |
| // indiquee par le drapeau CHEESE_FOUND |
| // Entree : |
| // Org : Position de la souris |
| // Sorties : |
| // Positions : Tableau de positions valides |
| // Cheese : Position du fromage |
| // Parametre retourne : Nombre de positions valides |
| //***************************************************************************** |
| static int EvaluatePositions(Point_t Org, int Positions[], Point_t * Cheese) |
| { |
| int I; |
| int Count = 0; |
| Point_t p; |
| Point_t CheesePos; |
| |
| for(I = 0; I < 8; I++) |
| {//Explorer toute les directions |
| p = Org; |
| switch(I) |
| {//Repere le deplacement |
| case 0: |
| p.Y++; |
| break; |
| case 1: |
| p.X++; |
| p.Y++; |
| break; |
| case 2: |
| p.X++; |
| break; |
| case 3: |
| p.Y--; |
| p.X++; |
| break; |
| case 4: |
| p.Y--; |
| break; |
| case 5: |
| p.Y--; |
| p.X--; |
| break; |
| case 6: |
| p.X--; |
| break; |
| case 7: |
| p.X--; |
| p.Y++; |
| break; |
| } |
| //Tester la collision |
| if(IsCollision(Org, p, &CheesePos) == FALSE) |
| {//La souris n'a rencontre aucun obstacle |
| Positions[Count] = I; |
| Count++; |
| } |
| } |
| |
| *Cheese = CheesePos; |
| |
| return Count; |
| } |
| |
| //***************************************************************************** |
| // But de la fonction : Affecter un mouvement a la souris |
| // Entrees : |
| // Org : Coordonnees de la souris |
| // p : Coordonnees voulu par la souris |
| // Parametre retourne : TRUE si le mouvement a eu lieu, FALSE sinon |
| //***************************************************************************** |
| static sos_bool_t AffectMovement(Point_t Org, Point_t p) |
| { |
| Element_t * pMouse = pMap[Org.X + (Org.Y * MAP_X)]; |
| Element_t * pElement; |
| |
| pElement = pMap[p.X + (p.Y * MAP_X)]; |
| |
| //La place est libre |
| if(pElement == NULL) return TRUE;//Autoriser le mouvement |
| |
| switch(pElement->Type) |
| { |
| case CHEESE: |
| // Liberer l'emplacement memoire du fromage |
| sos_kfree((sos_vaddr_t)pElement); |
| pMap[p.X + (p.Y * MAP_X)] = NULL; |
| |
| //Donner le fromage a la souris |
| Set(pMouse->Status, MOUSE_FULL); |
| Reset(pMouse->Status, MOUSE_EMPTY); |
| pMouse->Color = SOS_X86_VIDEO_FG_MAGENTA; |
| CheeseCount--; |
| return TRUE; |
| case OUTPUT: |
| //Supprimer la souris |
| pMap[Org.X + (Org.Y * MAP_X)] = NULL; |
| MouseCount--; |
| DrawPixel(Org.X, Org.Y, SOS_X86_VIDEO_FG_BLACK); |
| sos_x86_videomem_printf( 23,0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, |
| "Souris = %d; Fromages = %d; Obstacles = %d ", |
| MouseCount, CheeseCount, |
| ObstacleCount); |
| Set(pMouse->Status, MOUSE_EXITED); |
| return FALSE; |
| default : |
| return FALSE; |
| } |
| |
| return FALSE; |
| } |
| |
| //***************************************************************************** |
| // But de la fonction : Tester si une collision a eu lieu avec un obstacle |
| // Entrees : |
| // Org : Coordonnees de la souris |
| // p : coordonnees desirees par la souris |
| // Sortie : |
| // Cheese : Coordonnees du fromage |
| // Parametre retourne : TRUE si une collision a eu lieu, FALSE sinon |
| //***************************************************************************** |
| static sos_bool_t IsCollision(Point_t Org, Point_t p, Point_t *Cheese) |
| { |
| Element_t * pMouse = pMap[Org.X + (Org.Y * MAP_X)]; |
| Element_t * pElement; |
| |
| //Tester les bordures de la map |
| if((p.X < 0)||(p.Y < 0)) return TRUE; |
| |
| if((p.Y >= MAP_Y)||(p.X >= MAP_X)) return TRUE; |
| |
| pElement = pMap[p.X + (p.Y * MAP_X)]; |
| |
| //L'element est vide |
| if(pElement == NULL) return FALSE; |
| |
| //Si du fromage a ete trouve, stopper la recherche |
| if(IsSet(pMouse->Status, CHEESE_FOUND)) return FALSE; |
| |
| switch(pElement->Type) |
| { |
| case CHEESE: |
| if(IsSet(pMouse->Status, MOUSE_FULL)) return TRUE; |
| //Indiquer que du fromage a ete trouve |
| Set(pMouse->Status, CHEESE_FOUND); |
| //Retenir la position du fromage |
| (*Cheese).X = p.X; |
| (*Cheese).Y = p.Y; |
| break; |
| case INPUT: |
| if(IsSet(pMouse->Status, MOUSE_EMPTY)) return TRUE; |
| //Remplir les reserves de fromage |
| Set(pMouse->Status, MOUSE_EMPTY); |
| Reset(pMouse->Status, MOUSE_FULL); |
| pMouse->Color = SOS_X86_VIDEO_FG_LTRED; |
| //Autoriser la creation d'une autre souris |
| sos_ksema_up(& SemMouse); |
| return TRUE; |
| case OUTPUT: |
| break; |
| default : |
| return TRUE; |
| } |
| |
| return FALSE;//Aucune collision |
| } |
| |
| //***************************************************************************** |
| // But du thread : Creer une souris et la placer autour de l'entree |
| //***************************************************************************** |
| static void MouseCreator(void) |
| { |
| while(1) |
| { |
| sos_ksema_down(& SemMouse, NULL); |
| sos_ksema_down(& SemMap, NULL); |
| CreateMouse(); |
| sos_ksema_up(& SemMap); |
| } |
| } |
| |
| //***************************************************************************** |
| // But de la fonction : Creer une souris et l'inserer dans la carte |
| // Entree : Aucune |
| // Parametre retourne : ERROR si memoire insuffisante, FALSE si souris non |
| // cree, TRUE sinon |
| //***************************************************************************** |
| static sos_ret_t CreateMouse(void) |
| { |
| Element_t * pElement; |
| unsigned int I; |
| |
| Point_t p; |
| |
| for(I = 0; I < 8; I++) |
| {//Explorer tous les emplacements |
| p.X = 0; |
| p.Y = MAP_Y / 2; |
| switch(I) |
| {//Repere le deplacement |
| case 0: |
| p.Y++; |
| break; |
| case 1: |
| p.X++; |
| p.Y++; |
| break; |
| case 2: |
| p.X++; |
| break; |
| case 3: |
| p.Y--; |
| p.X++; |
| break; |
| case 4: |
| p.Y--; |
| break; |
| case 5: |
| p.Y--; |
| p.X--; |
| break; |
| case 6: |
| p.X--; |
| break; |
| case 7: |
| p.X--; |
| p.Y++; |
| break; |
| } |
| if((p.X >= 0)&&(p.Y >= 0)&&(p.X < MAP_X)&&(p.Y < MAP_Y)) |
| {//L'emplacement est valide |
| pElement = pMap[p.X + (p.Y * MAP_X)]; |
| if(pElement == NULL) |
| {//Creer la souris |
| pElement = (Element_t *)sos_kmalloc(sizeof(Element_t), 0); |
| if(pElement != NULL) |
| {//Initialiser l'entree |
| pElement->Type = MOUSE; |
| Set(pElement->Status, MOUSE_EMPTY); |
| pElement->Color = SOS_X86_VIDEO_FG_LTRED; |
| pElement->P = p; |
| pElement->Way = 0; |
| pElement->ThreadID = sos_kthread_create("Mouse", (sos_kthread_start_routine_t)Mouse, pElement); |
| if(pElement->ThreadID == 0) |
| { |
| sos_kfree((sos_vaddr_t)pElement); |
| pElement = NULL; |
| } |
| pMap[p.X + (p.Y * MAP_X)] = pElement; |
| MouseCount++; |
| |
| DrawPixel(p.X, p.Y, pElement->Color); |
| sos_x86_videomem_printf(23, 0, SOS_X86_VIDEO_FG_YELLOW | SOS_X86_VIDEO_BG_BLUE, "Souris = %d; Fromages = %d; Obstacles = %d ", MouseCount, CheeseCount, ObstacleCount); |
| |
| return SOS_OK; |
| } |
| } |
| } |
| } |
| return -SOS_EBUSY; |
| } |
| |
| //***************************************************************************** |
| // C'est fini !!!! |
| //***************************************************************************** |
| |
/tmp/sos-code-article6/sos/time.c (1970-01-01 01:00:00.000000000 +0100
) |
|
../sos-code-article6.5/sos/time.c (2005-01-04 04:12:16.000000000 +0100
) |
|
|
|
| /* Copyright (C) 2004 David Decotigny |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License |
| as published by the Free Software Foundation; either version 2 |
| of the License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| USA. |
| */ |
| |
| #include <sos/assert.h> |
| #include <sos/klibc.h> |
| #include <hwcore/irq.h> |
| #include <sos/list.h> |
| #include <sos/kthread.h> |
| |
| #include "time.h" |
| |
| |
| /** |
| * Number of nanoseconds in 1 second |
| */ |
| #define NS_IN_SEC 1000000000UL |
| |
| |
| /** |
| * The list of timeout actions waiting for a timeout. The timeout |
| * actions are stored in the list in increasing initial timeout |
| * order. Actually, the "timeout" field won't reflect this initial |
| * timeout: for each element in the list, it stores the timeout |
| * _difference_ between the timeout action and the previous in the |
| * list. |
| */ |
| static struct sos_timeout_action *timeout_action_list; |
| |
| |
| /** |
| * Current resolution of a time tick |
| */ |
| static struct sos_time tick_resolution; |
| |
| |
| /** |
| * Time elapsed between boot and last timer tick |
| * |
| * @note No 'volatile' here because the tick value is NEVER modified |
| * while in any of the functions below: it is modified only out of |
| * these functions by the IRQ timer handler because these functions |
| * are protected against timer IRQ and are "one shot" (no busy waiting |
| * for a change in the tick's value). |
| */ |
| static struct sos_time last_tick_time; |
| |
| |
| sos_ret_t sos_time_inc(struct sos_time *dest, |
| const struct sos_time *to_add) |
| { |
| /* nanosec is always < 1e9 so that their sum is always < 2e9, which |
| is smaller than 2^32-1 */ |
| sos_ui32_t sigma_ns = dest->nanosec + to_add->nanosec; |
| |
| dest->sec += to_add->sec; |
| dest->sec += sigma_ns / NS_IN_SEC; |
| dest->nanosec = sigma_ns % NS_IN_SEC; |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t sos_time_dec(struct sos_time *dest, |
| const struct sos_time *to_dec) |
| { |
| /* nanosec is always < 1e9 so that their difference is always in |
| (-1e9, 1e9), which is compatible with the (-2^31, 2^31 - 1) |
| cpacity of a signed dword */ |
| sos_si32_t diff_ns = ((sos_si32_t)dest->nanosec) |
| - ((sos_si32_t)to_dec->nanosec); |
| |
| /* Make sure substraction is possible */ |
| SOS_ASSERT_FATAL(dest->sec >= to_dec->sec); |
| if (dest->sec == to_dec->sec) |
| SOS_ASSERT_FATAL(dest->nanosec >= to_dec->nanosec); |
| |
| dest->sec -= to_dec->sec; |
| if (diff_ns > 0) |
| dest->sec += diff_ns / NS_IN_SEC; |
| else |
| dest->sec -= ((-diff_ns) / NS_IN_SEC); |
| dest->nanosec = (NS_IN_SEC + diff_ns) % NS_IN_SEC; |
| if (diff_ns < 0) |
| dest->sec --; |
| return SOS_OK; |
| } |
| |
| |
| int sos_time_cmp(const struct sos_time *t1, |
| const struct sos_time *t2) |
| { |
| /* Compare seconds */ |
| if (t1->sec < t2->sec) |
| return -1; |
| else if (t1->sec > t2->sec) |
| return 1; |
| |
| /* seconds are equal => compare nanoseconds */ |
| else if (t1->nanosec < t2->nanosec) |
| return -1; |
| else if (t1->nanosec > t2->nanosec) |
| return 1; |
| |
| /* else: sec and nanosecs are equal */ |
| return 0; |
| } |
| |
| |
| sos_bool_t sos_time_is_zero(const struct sos_time *tm) |
| { |
| return ( (0 == tm->sec) && (0 == tm->nanosec) ); |
| } |
| |
| |
| sos_ret_t sos_time_subsysem_setup(const struct sos_time *initial_resolution) |
| { |
| timeout_action_list = NULL; |
| last_tick_time = (struct sos_time) { .sec = 0, .nanosec = 0 }; |
| memcpy(& tick_resolution, initial_resolution, sizeof(struct sos_time)); |
| |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t sos_time_get_tick_resolution(struct sos_time *resolution) |
| { |
| sos_ui32_t flags; |
| sos_disable_IRQs(flags); |
| |
| memcpy(resolution, & tick_resolution, sizeof(struct sos_time)); |
| |
| sos_restore_IRQs(flags); |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t sos_time_set_tick_resolution(const struct sos_time *resolution) |
| { |
| sos_ui32_t flags; |
| sos_disable_IRQs(flags); |
| |
| memcpy(& tick_resolution, resolution, sizeof(struct sos_time)); |
| |
| sos_restore_IRQs(flags); |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t sos_time_get_now(struct sos_time *now) |
| { |
| sos_ui32_t flags; |
| sos_disable_IRQs(flags); |
| |
| memcpy(now, & last_tick_time, sizeof(struct sos_time)); |
| |
| sos_restore_IRQs(flags); |
| return SOS_OK; |
| } |
| |
| |
| /** |
| * Helper routine to add the action in the list. MUST be called with |
| * interrupts disabled ! |
| */ |
| static sos_ret_t _add_action(struct sos_timeout_action *act, |
| const struct sos_time *due_date, |
| sos_bool_t is_relative_due_date, |
| sos_timeout_routine_t *routine, |
| void *routine_data) |
| { |
| struct sos_timeout_action *other, *insert_before; |
| int nb_act; |
| |
| /* Delay must be specified */ |
| if (due_date == NULL) |
| return -SOS_EINVAL; |
| |
| /* Action container MUST be specified */ |
| if (act == NULL) |
| return -SOS_EINVAL; |
| |
| /* Refuse to add an empty action */ |
| if (NULL == routine) |
| return -SOS_EINVAL; |
| |
| /* Refuse to add the action if it is already added */ |
| if (NULL != act->tmo_next) |
| return -SOS_EBUSY; |
| |
| /* Compute action absolute due date */ |
| if (is_relative_due_date) |
| { |
| /* The provided due_date is relative to the current time */ |
| memcpy(& act->timeout, & last_tick_time, sizeof(struct sos_time)); |
| sos_time_inc(& act->timeout, due_date); |
| } |
| else |
| { |
| /* The provided due_date is absolute (ie relative to the system |
| boot instant) */ |
| |
| if (sos_time_cmp(due_date, & last_tick_time) < 0) |
| /* Refuse to add a past action ! */ |
| return -SOS_EINVAL; |
| |
| memcpy(& act->timeout, due_date, sizeof(struct sos_time)); |
| } |
| |
| /* Prepare the action data structure */ |
| act->routine = routine; |
| act->routine_data = routine_data; |
| |
| /* Find the right place in the list of the timeout action. */ |
| insert_before = NULL; |
| list_foreach_forward_named(timeout_action_list, |
| other, nb_act, |
| tmo_prev, tmo_next) |
| { |
| if (sos_time_cmp(& act->timeout, & other->timeout) < 0) |
| { |
| insert_before = other; |
| break; |
| } |
| |
| /* Loop over to next timeout */ |
| } |
| |
| /* Now insert the action in the list */ |
| if (insert_before != NULL) |
| list_insert_before_named(timeout_action_list, insert_before, act, |
| tmo_prev, tmo_next); |
| else |
| list_add_tail_named(timeout_action_list, act, |
| tmo_prev, tmo_next); |
| |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t |
| sos_time_register_action_relative(struct sos_timeout_action *act, |
| const struct sos_time *delay, |
| sos_timeout_routine_t *routine, |
| void *routine_data) |
| { |
| sos_ui32_t flags; |
| sos_ret_t retval; |
| |
| sos_disable_IRQs(flags); |
| retval = _add_action(act, delay, TRUE, routine, routine_data); |
| sos_restore_IRQs(flags); |
| |
| return retval; |
| } |
| |
| |
| sos_ret_t |
| sos_time_register_action_absolute(struct sos_timeout_action *act, |
| const struct sos_time *date, |
| sos_timeout_routine_t *routine, |
| void *routine_data) |
| { |
| sos_ui32_t flags; |
| sos_ret_t retval; |
| |
| sos_disable_IRQs(flags); |
| retval = _add_action(act, date, FALSE, routine, routine_data); |
| sos_restore_IRQs(flags); |
| |
| return retval; |
| } |
| |
| |
| /** |
| * Helper routine to remove the action from the list. MUST be called |
| * with interrupts disabled ! |
| */ |
| static sos_ret_t _remove_action(struct sos_timeout_action *act) |
| { |
| /* Don't do anything if action is not in timeout list */ |
| if (NULL == act->tmo_next) |
| return -SOS_EINVAL; |
| |
| /* Update the action's remaining timeout */ |
| if (sos_time_cmp(& act->timeout, & last_tick_time) <= 0) |
| act->timeout = (struct sos_time){ .sec=0, .nanosec=0 }; |
| else |
| sos_time_dec(& act->timeout, & last_tick_time); |
| |
| /* Actually remove the action from the list */ |
| list_delete_named(timeout_action_list, act, |
| tmo_prev, tmo_next); |
| act->tmo_prev = act->tmo_next = NULL; |
| |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t sos_time_unregister_action(struct sos_timeout_action *act) |
| { |
| sos_ret_t retval; |
| sos_ui32_t flags; |
| |
| sos_disable_IRQs(flags); |
| retval = _remove_action(act); |
| sos_restore_IRQs(flags); |
| |
| return SOS_OK; |
| } |
| |
| |
| sos_ret_t sos_time_do_tick() |
| { |
| sos_ui32_t flags; |
| |
| sos_disable_IRQs(flags); |
| |
| /* Update kernel time */ |
| sos_time_inc(& last_tick_time, & tick_resolution); |
| |
| while (! list_is_empty_named(timeout_action_list, tmo_prev, tmo_next)) |
| { |
| struct sos_timeout_action *act; |
| act = list_get_head_named(timeout_action_list, tmo_prev, tmo_next); |
| |
| /* Did we go too far in the actions' list ? */ |
| if (sos_time_cmp(& last_tick_time, & act->timeout) < 0) |
| { |
| /* Yes: No need to look further. */ |
| break; |
| } |
| |
| /* Remove the action from the list */ |
| _remove_action(act); |
| |
| /* Call the action's routine */ |
| act->routine(act); |
| } |
| |
| sos_restore_IRQs(flags); |
| return SOS_OK; |
| } |
| |