Development

Version 0.3.0 Released

Thursday, October 27, 2005

Vesion 0.3.0 is up here containing the latest multitasking code. Why not download the excelent and free VMWare Player and run AMOS on your own hardware! Heres the amos.vmx file you will need to boot the AMOS.IMA floppy image.

displayName = "AMOS"

uuid.location = "56 4d 6d bb d5 e3 d5 70-6a 11 ea e8 19 d6 0a 09"
uuid.bios = "56 4d 6d bb d5 e3 d5 70-6a 11 ea e8 19 d6 0a 09"

config.version = "7"
virtualHW.version = "3"

memsize = "16"

guestOS = "other"

floppy0.fileName = "AMOS.IMA"
floppy0.fileType = "file"
floppy0.startConnected = "TRUE"

powerType.powerOff = "hard"
powerType.powerOn = "hard"
powerType.suspend = "hard"
powerType.reset = "hard"


And here's it running on my machine:

Simple Multitasking

Sunday, October 23, 2005

Lots of new code up in CVS, simple multitasking is now working. Each task gets its own page directory so it has its own 4GB address space. The kernel is mapped across all tasks so we can still run code from the interrupts, very important if we wish to perform any task switching at all. The kernels heap must also be mapped in if we wish to access the tasks info structure during scheduling, it might be better to switch over to the kernels address space during this to avoid mapping the kernels heap into each task (especially as the heap will be changing over time), we could then switch back to the tasks address space when we are done. This would leave the task stack passed from the isr_dispatcher() unusable as it would be referencing memory mapped in the task and not the kernel, but that's unused at the moment so shouldn't be a problem. I'll have to play around with this to see which works best.

The paging code has been revamped to allow the creation, destruction and modification of multiple page directory's and all their entry's which is proving quite a powerful feature set for manipulating multiple address spaces and sharing memory.

Creating a task is easy enough:
struct TASK_INFO * task_create( void (*entrypoint)() )
{
struct TASK_STACK * stack;
struct TASK_INFO * task;
void * physicalAddress;

// create a new task info structure
task = mm_malloc( sizeof( struct TASK_INFO ) );
// assign a task id
task->id = task_total++;
// give it an initial tick slice
task->tick_slice = 1;
// set the initial task state
task->state = READY;
// create the new tasks page directory
task->page_dir = paging_createDirectory();
// map in the kernel
paging_mapKernel( task->page_dir );
// map in the kernel's heap
paging_mapKernelHeap( task->page_dir );
// identity map bottom 4MB's
for( physicalAddress=0L ; physicalAddress<(void *)(1024*PAGE_SIZE) ; physicalAddress+=PAGE_SIZE ) paging_setPageTableEntry( task->page_dir, physicalAddress, physicalAddress, TRUE );
// allocate a page for the stack
physicalAddress = physical_pageAlloc();
// map in the stack to the kernels address space so we can write to it
paging_setPageTableEntry( paging_kernelPageDir, TASK_STACKADDRESS, physicalAddress, TRUE );
// map in the stack to the tasks address space
paging_setPageTableEntry( task->page_dir, TASK_STACKADDRESS, physicalAddress, TRUE );
// save the physical stack address
task->stack = physicalAddress;
// create the initial stack fo we can perform a task switch
stack = (struct TASK_STACK *)( TASK_STACKADDRESS + TASK_STACKSIZE - sizeof(struct TASK_STACK) );
// clear the stack
mm_memset( (BYTE *)stack, 0x00, sizeof(struct TASK_STACK) );
// set the code segment
stack->cs = 0x08;
// set the data segments
stack->ds = 0x10;
stack->es = 0x10;
stack->fs = 0x10;
stack->gs = 0x10;
// set the eflags register
stack->eflags = 0x0202;
// set our initial entrypoint
stack->eip = (DWORD)entrypoint;
// set the interrupt number we will return from
stack->intnumber = IRQ0;
// set the tasks current esp to the stack
task->current_esp = (DWORD)stack;
// unmap the stack from the kernels address space
paging_setPageTableEntry( paging_kernelPageDir, TASK_STACKADDRESS, NULL, FALSE );
// add the task to the scheduler
scheduler_addTask( task );
// return with new task info
return task;
}

When I have the scheduler a bit more refined I'll post up the details about performing actual context switches.

Multitasking First Steps

Tuesday, October 11, 2005

Beginning to setup multitasking now via software context switching. By setting up the programmable interval timer (PIT) we can continuously interrupt the system at a set interval. Their are 3 timers available and timer 1 is already bound to IRQ0. Using the isr_setHandler() routine we bind the scheduler to this interrupt so when it fires the scheduler is called and a context switch may occur. Setting up the timer is fairly straight forward:

// calculate the timer interval
interval = 1193180 / 100000; //100Hz
// square wave mode
outportb( PIT_COMMAND_REG, 0x36);
// set the low interval for timer 0 (mapped to IRQ0)
outportb( PIT_TIMER_0, interval & 0xFF);
// set the high interval for timer 0
outportb( PIT_TIMER_0, interval >> 8);
// set the scheduler to run via the timer interrupt
isr_setHandler( IRQ0, tasking_schedule );

The scheduler for now will be a simple round robin affair where every task will have a time slice, not necessarily equal as some tasks may have a higher or lower priority then others. When a tasks time slice expires the next task will get switched in.

When creating a task we will take advantage of paging and give every task its own 4GB address space by crating a separate page directory and subsequent page tables for each task. We can also take advantage of hardware task switching and provide a single Task State Segment (TSS) in order employ protection, e.g. tasks run at ring 3 while the kernel runs at ring 0.




SourceForge.net Logo