Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Разработка операционных систем - для начинающих и не только!


Информационный Канал Subscribe.Ru

Разработка операционных систем

Выпуск 25 от 2004-02-16

Intro от Lonesome'a

Возрадуйтесь, страждущие по практическим примерам, ибо сегодня жажда ваша утолится и изопьете до дна вы чашу низкоуровнего кода :)

А если серьезно, то вашему вниманию предлагается выпуск рассылки, который подготовил nvm ( http://math.nsc.ru/LBRT/i1/nedelko/ruswin.html), за что ему большой респект :)

Переключение задач

Содержание:

Введение

Рассматривается пример кода, реализующего переключение задач.

Этот пример не минимальный, поскольку основан на материалах разработки прототипа ОС и реализует некоторые функции более общего назначения, чем необходимо для простого переключения задач. В частности, достаточно детально реализована функция монтирования страниц памяти.

<< к оглавлению<<


Файл app.asm - код задачи.

Этот код будет загружен в качестве отдельной задачи.
Физически код будет находиться внутри основного модуля (по адресу 0x25000), при этом в пространстве задачи он будет спроецирован с адреса 4 (первые 4 байта достанутся от "охватывающего" файла - см. далее). С адреса 0x2000 к задаче будет подключена видеопамять.
Задача будет в бесконечном цикле выводить символы в заданную позицию.

[BITS 32]
[ORG 4]

start: 
 mov eax, [count]
 inc eax
 mov [count], eax
 and eax, 0x3f
 add eax, 0x20
 mov [0x2080], al ; put a symbol
 jmp start

count: dd 0

<< к оглавлению<<


Распределение памяти и определения

Файл Defs.h

Определения для простых типов:
#define bool int
#define true  1
#define false 0
#define NULL 0
#define const
#define pointer void*
#define address unsigned long
#define DWORD unsigned long

Файл Map.h

Распределение памяти ядра. Данные и код (все, кроме кучи) находятся в пространстве, где физические адреса совпадают с линейными.

Приведенная карта памяти рассчитана на использование в прототипе ОС, и некоторые элементы в данном примере не используются. В том числе не используется точка монтирования TSS_APP_LINK, вместо этого TSS задачи располагается по фиксированному адресу.

#include "Defs.h"

#define KERNEL_STACK_BOTTOM 0x1000
// нижняя граница стека для ядра, стек имеет фиксированный размер

#define KERNEL_STACK_ORG (0x10000-4)
// начало (верхняя граница) стека для ядра

#define GDT_MAIN        0x10000
// физический адрес главной (общей для всех и единственной) GDT

#define IDT_MAIN        0x20000
// физический адрес IDT

#define TSS_KERNEL_MAIN 0x21000
// сегмент статуса для ядра

#define PAGE_DIRECTORY  0x23000
// каталог разделов для ядра
#define PAGE_TABLE      0x24000
// каталог страниц для первого раздела 

#define KERNEL_START    0x25000
// начало кода ядра

#define VIRTUAL_START  0x100000
// начало действия страничного преобразования (для ядра)
// с этого момента все адреса линейные и не совпадают с физическими

#define PAGE_DIR_LINK  0x100000
// точка монтирования для каталогов разделов задач 
// ядро не имеет постоянного доступа к данным процессов, 
// поэтому нужны точки монтирования для временного подключения

#define PAGE_TAB_LINK  0x101000
// точка монтирования для каталогов страниц задач 

#define TSS_APP_LINK   0x102000
// точка монтирования для сегмента статуса процессов

#define IPC_LINK       0x104000
// точка монтирования для данных

#define KERNEL_HEAP    0x200000
// начало кучи ядра

#define GDT_VIRTUAL        0xFFF00000
// дополнительный линейный адрес GDT (ссылка на тот же объект)
// нужен для обращения из процессов (которым недоступен первичный адрес)
// линейное пространство процессов ограничено этим адресом (0xFFEFFFFF)

#define IDT_VIRTUAL        0xFFF10000
// дополнительный линейный адрес IDT

#define TSS_KERNEL_VIRTUAL 0xFFF11000
// дополнительный линейный адрес сегмента статуса ядра

#define TSS_APP_VIRTUAL    0xFFF13000
// сегмент статуса для процессов

<< к оглавлению<<


Файл startup.asm - головной модуль.

Данный модуль агрегирует компоненты и осуществляет передачу управления. Предполагается, что он начинает работу уже в защищенном режиме, но с отключенным страничным преобразованием.

В начале кода помещается адрес точки входа в программу, поэтому загрузчик должен осуществлять косвенный переход.

В тело модуля непосредственно вставлен код задачи, на которую в конце передается управление. Передача управления делается командой jmp с указанием селектора TSS, при этом последующий адрес (смещение) не используется, так как EIP загружается из TSS.

[BITS 32]
[EXTERN kernel_main]
[EXTERN kernel_last]
[GLOBAL start]

start: dd begin  ; loader jumps indirectly

incbin 'app.bin'

begin:
 mov esp, 0x10000 ; init stack
 mov ax, ds
 mov es, ax

 call kernel_main

 call kernel_last

 jmp 0x20: 0 ; to app task

<< к оглавлению<<


Основной модуль kernel.c

Модуль разбит на две функции. Их вполне можно объединить в одну, так как они вызываются последовательно.

Функция kernel_main() первым шагом проводит очистку экрана.

Далее вычисляется и выводится объем доступной памяти. Метод вычисления основан на последовательном переборе адресов, с попыткой записи в начало каждой страницы и последующим чтением.

Далее создаются пустые каталог разделов и каталог страниц первого раздела, после чего второй подключается к первому.

В пределах первого мегабайта проводится монтирование страниц один-к-одному, т.е. каждая страница монтируется на физический адрес, равный линейному. Для проверки содержимое начальных элементов каталогов выдается на экран.

Наконец, ассемблерная вставка загружает регистр CR3 и включает страничное преобразование (бит PE).

Последней вызывается функция обновления GDT, для подготовки к переключению задач.

Далее выполняется kernel_last().

В начале этой функции помещена демонстрация работы перемонтирования страниц: видеобуфер монтируется на свободный адрес и уже по этому адресу выводится символ '!'.

Наконец, вызывается функция, подготавливающая сегмент статуса для новой задачи.

#include "Process.h"
#include "ktty.h"

void kernel_main()
{
  unsigned long addr;
  int i;
  unsigned long* p_dir=(unsigned long*) PAGE_DIRECTORY;
  
  init_tty();
  clear();
 
  puts("  Kernel started..\n");

  for (addr=0x100000; addr<0xFFF00000; addr+=0x1000){
    *((unsigned long*)addr)=0xF0B0C;
    if (*((unsigned long*)addr)!=0xF0B0C) break;
    }
  puts("We have ");  put16(addr); puts(" bytes of memory\n");
  g_PhysicalPagesNumber=addr/0x1000;

  create_Directory(PAGE_DIRECTORY);
  create_Directory(PAGE_TABLE);
  p_dir[0]=(PAGE_TABLE&0xfffff000)|1; // link to first page dir

  for (addr=0; addr<VIRTUAL_START; addr+=4096)
    if (mount_page_kernel(addr,addr)==0) puts("##error -- mount_page()\n");
  puts("\n");
  for (i=0; i<9; i++) put16(p_dir[i]);
  for (i=0; i<9; i++) put16(p_dir[1024+i]);

  puts("Pages directory prepared.\n");

  asm("cli\n mov $0x23000, %eax\n mov %eax, %cr3");
  asm("mov %cr0, %eax\n or $0x80000000, %eax\n mov %eax, %cr0");
  puts("Pagination made on!\n");
 
  init_GDT();
  puts("GDT reloaded.\n");
}

void kernel_last()
{
  char* b=(char*) 0x70000;
  puts("Kernel: last part..\n");
  if (mount_page(0x70000,0xb8000,PAGE_DIRECTORY)==0) puts("##error -- mount_page() - new\n");
  b[0]='!';
  init_TSS();
  puts("init_TSS - completed");
}

<< к оглавлению<<


Инициализация GDT и TSS.

Файл Process.h

Определяем структуру сегмента статуса задачи (TSS).

#include "Memory.h"

bool init_GDT();
bool init_TSS();

struct TSS
{
 DWORD back_link;
 DWORD ESP0;
 DWORD SS0;
 DWORD ESP1;
 DWORD SS1;
 DWORD ESP2;
 DWORD SS2;
 DWORD CR3;
 DWORD EIP;
 DWORD EFLAGS;
 DWORD EAX;
 DWORD ECX;
 DWORD EDX;
 DWORD EBX;
 DWORD ESP;
 DWORD EBP;
 DWORD ESI;
 DWORD EDI;
 DWORD ES;
 DWORD CS;
 DWORD SS;
 DWORD DS;
 DWORD FS;
 DWORD GS;
 DWORD LDT;
    DWORD offset_andT;
    DWORD IOPB;
};

Файл Process.c

Функция init_GDT() подготавливает и загружает глобальную таблицу дескрипторов.

Первые два - это дескрипторы сегментов кода и данных, такие же, что использовались для перехода в защищенный режим.

Следующий - дескриптор TSS для исходной задачи (той, откуда будем переключаться). К соответствующему TSS обращение будет вестись только на запись (так как возвращаться мы не планируем), поэтому выделим под него свободную область, не инициализируя ее.

И, наконец, дескриптор TSS целевой задачи. Под него также выделим свободную область (TSS_APP), которую проинициализируем позже.

Теперь ассемблерной вставкой загружаем GDT и TR. Последнее необходимо для сохранения состояния текущей задачи.

Функция init_TSS() подготавливает TSS для новой задачи.

Все регистры необходимо загрузить корректными значениями, в частности в EIP поместить 4 - адрес точки входа, в CR3 - адрес созданного нового каталога страниц.

В адресное пространство задачи по адресу 0 монтируется страница с кодом, по адресу 0x1000 выделяется память под стек, а следом монтируется видеобуфер. Также необходимо смонтировать GDT, причем по тому же адресу, что и в текущей задаче.

#include "Process.h"

unsigned long g_gdtr[2];

#define TSS_APP (TSS_KERNEL_MAIN+0x1000)

bool init_GDT()
{
  unsigned long* gdt=(unsigned long*) GDT_MAIN;
  gdt[0]=0;     // not used
  gdt[1]=0;
  gdt[2]=0x0000FFFF;    // code
  gdt[3]=0x00CF9A00;    
  gdt[4]=0x0000FFFF;    // data
  gdt[5]=0x00CF9200;
  gdt[6]=((TSS_KERNEL_MAIN<<16)&0xFFFF0000)|((sizeof(struct TSS))&0x0000FFFF);
  gdt[7]=(TSS_KERNEL_MAIN&0xFF000000)|0x8900|((TSS_KERNEL_MAIN>>16)&0x000000FF);
        // TSS kernel
  gdt[8]=((TSS_APP<<16)&0xFFFF0000)|((sizeof(struct TSS))&0x0000FFFF);
  gdt[9]=(TSS_APP&0xFF000000)|0x8900|((TSS_APP>>16)&0x000000FF);
        // TSS app
  g_gdtr[0]=(GDT_MAIN<<16)|0xFFFF;
  g_gdtr[1]=(GDT_MAIN>>16)&0xFFFF;
  asm("lgdt g_gdtr");
  asm("mov $0x18, %eax\n ltr %ax");
  return true;
}

bool init_TSS()
{
  struct TSS* tss=(struct TSS*)TSS_APP;
  address Dir;
  tss->back_link=0;
  tss->ESP0=0x2000;
  tss->SS0=0x10;
  tss->ESP1=0x2000;
  tss->SS1=0x10;
  tss->ESP2=0x2000;
  tss->SS2=0x10;
  tss->CR3=0;
  tss->EIP=0x4;
  tss->EFLAGS=0;
  tss->EAX=0;
  tss->ECX=0;
  tss->EDX=0;
  tss->EBX=0;
  tss->ESP=0x2000;
  tss->EBP=0;
  tss->ESI=0;
  tss->EDI=0;
  tss->ES=0x10;
  tss->CS=0x8;
  tss->SS=0x10;
  tss->DS=0x10;
  tss->FS=0x10;
  tss->GS=0x10;
  tss->LDT=0;
  tss->offset_andT=0;
  tss->IOPB=0xFFFFFFFF;

  Dir=alloc_page(0);
  put16(Dir);
  create_Directory(Dir);
  mount_page(0,KERNEL_START,Dir);
  put16(*((unsigned long*)Dir));
  mount_page(GDT_MAIN,GDT_MAIN,Dir);    // GDT
  mount_page(0x1000,alloc_page(0),Dir); // for stack
  mount_page(0x2000,0xB8000,Dir); // screen
  tss->CR3=Dir;
  return true;
}

<< к оглавлению<<


Управление страницами памяти (memory.c)

Файл Memory.h

#include "Map.h"

extern address g_FreePhysicalMemory; 
extern unsigned long g_PhysicalPagesNumber;

address alloc_page(long owner);
// выделить свободную страницу для owner

bool mount_page(address logical, address physical, address Directory);
// подключает логическую страницу к физической (address=long)
// Directory - физический адрес каталога, в котором проводится подключение
// если это не каталог ядра, то производится временное монтирование на PAGE_DIR_LINK

#define mount_page_kernel(l,p) mount_page(l,p,PAGE_DIRECTORY)

void create_Directory(address add);
// инициализирует новый каталог

Файл Memory.c

Модуль памяти отвечает за выделение физических и монтирование логических страниц.

Освобождение памяти не предусмотрено, поэтому выделение физических страниц реализовано просто: через адрес начала области свободной памяти.

Функция монтирования страниц имеет три параметра:
- линейный адрес, на который проводится монтирование;
- физический адрес монтируемой страницы;
- физический адрес каталога страниц, в котором ведется монтирование.

Одна из трудностей реализации данной функции состоит в том, что каталог страниц, в который нужно внести изменения, может быть недоступен в адресном пространстве ядра, и требуется провести его временное монтирование. Для этого функция вызывается рекурсивно. Для того, чтобы изменение страничного назначения вступило в силу, используется команда invlpg.

#include "Memory.h"

unsigned long g_PhysicalPagesNumber=0;
struct MemPage* g_PhysicalPages=NULL;

address g_FreePhysicalMemory=0x00100000; 

address alloc_page(long owner)
{
  address a=g_FreePhysicalMemory;
  if ((a&0xfff) || (a/4096>=g_PhysicalPagesNumber)) return 0;
  g_FreePhysicalMemory+=4096;
  return a;
}

unsigned long g_inv_addr;

bool mount_page(address logical, address physical, address Directory)
// подключает логическую страницу к физической
// Directory - физический адрес каталога, в котором проводится подключение
// при необходимости производится временное монтирование на PAGE_DIR_LINK
{
  address Page;
  unsigned long* p_dir=(unsigned long*) Directory;
  int i_dir=(logical>>22)&0x3ff;
  int i_page=(logical>>12)&0x3ff;
  unsigned long* p_page=NULL; 
  int i;
  bool new_dir;

  if (Directory>=VIRTUAL_START){
    p_dir=(unsigned long*) PAGE_DIR_LINK;
    mount_page_kernel(PAGE_DIR_LINK,Directory);
    g_inv_addr=PAGE_DIR_LINK;
    asm("mov g_inv_addr, %eax\n invlpg (%eax)");
    }

  new_dir=!((p_dir[i_dir])&1);
  if (new_dir){ // directory does not exist => create
    Page=alloc_page(0);
    if (!Page) return false;
    p_dir[i_dir]=(Page&0xfffff000) | 1;
    }
  Page=p_dir[i_dir]&0xfffff000; 
  if (Page>=VIRTUAL_START){
    p_page=(unsigned long*) PAGE_TAB_LINK;
    mount_page_kernel(PAGE_TAB_LINK,Page);
    g_inv_addr=PAGE_TAB_LINK;
    asm("mov g_inv_addr, %eax\n invlpg (%eax)");
    }
  else p_page=(unsigned long*) Page; 

  if (new_dir) for (i=0; i<1024; i++) p_page[i]=0;
  p_page[i_page]=(physical&0xfffff000) | 1;
  return true;
}

void create_Directory(address add)
// создать новый каталог
{
  unsigned long* p=(unsigned long*) add;
  int i;
  if (add>=VIRTUAL_START){
    p=(unsigned long*) PAGE_DIR_LINK;
    mount_page_kernel(PAGE_DIR_LINK,add);
    g_inv_addr=PAGE_DIR_LINK;
    asm("mov g_inv_addr, %eax\n invlpg (%eax)");
    }
  for (i=0; i<1024; i++) p[i]=0;
}

<< к оглавлению<<


Модуль вывода на экран.

Использован, с небольшими изменениями, модуль из 9-го выпуска.

Файл Ktty.h

void init_tty();
void clear();
void putchar(char c);
void puts(const char *s);
void put16(unsigned long addr);

extern int tty_cursor;
extern int tty_attribute;

Файл Ktty.c

#define VIDEO_WIDTH 80
#define VIDEO_HEIGHT 25
#define VIDEO_RAM 0xb8000
#include "ktty.h"

int tty_cursor;
int tty_attribute;

void init_tty()
{
  tty_cursor = 0;
  tty_attribute = 10;
}

void clear()
{
  char *video = (char*) VIDEO_RAM;
  int i;
  for (i = 0; i < VIDEO_HEIGHT*VIDEO_WIDTH; i++) {
    *(video + i*2) = ' ';
  }
  tty_cursor = 0;
}

void putchar(char c)
{
  char *video = (char*) VIDEO_RAM;
  int i;
  switch (c) {
  case '\n': //Если это символ новой строки
    tty_cursor+=VIDEO_WIDTH;
    tty_cursor-=tty_cursor%VIDEO_WIDTH;
    break;
  default:
    *(video + tty_cursor*2) = c;
    *(video + tty_cursor*2+1) = tty_attribute;
    tty_cursor++;
    break;
  }
  //Если курсор вышел за границу экрана, сдвинем экран вверх на одну строку
  if(tty_cursor>VIDEO_WIDTH*VIDEO_HEIGHT){
    for(i=VIDEO_WIDTH*2;i<=VIDEO_WIDTH*VIDEO_HEIGHT*2+VIDEO_WIDTH*2;i++){
      *(video+i-VIDEO_WIDTH*2)=*(video+i);
    }
    tty_cursor-=VIDEO_WIDTH;
  }
}

void puts(const char *s)
{
  while(*s) {
    putchar(*s);
    s++;
  }
}

static void conv16(char* s, unsigned long addr)
{
  int i;
  unsigned char c;
  for (i=0; i<8; i++){
    c=(unsigned char)((addr>>(4*(7-i)))&0xf);
    if (c<10) s[i]=c+'0';
    else s[i]=c-10+'A';
    }
  s[8]=0;
}
void put16(unsigned long addr)
{
  char s[16];
  conv16(s,addr);
  puts(s);
  if (tty_cursor%80!=0) puts(" ");
}

<< к оглавлению<<


Начальный загрузчик (Boot.asm)

Следующий код используется для загрузки программы в память (по адресу 0x25000), перехода в защищенный режим и передачи управления по адресу, находящемуся в ячейке 0x25000.

Данный загрузчик имеет более широкие возможности, а для упомянутых действий можно было написать гораздо более простой код. Однако, чтобы не делать лишней работы, можно использовать предлагаемый вариант.

Следует только следить, чтобы число загружаемых секторов (старший байт слова по адресу control:) соответствовало размеру файла kernel.bin.

[BITS 16]
[ORG 0x7c00]
_start: 
 cli
 mov ax, cs
 mov ds, ax
 mov ss, ax
 mov sp, _start
 lgdt [gd_reg]
 in al, 0x92
 or al, 2
 out 0x92, al

 sti
 mov si, msg
 call kputs

 .loop:
 mov al, [control+3]
 test al,al
 jz .cont
 call read_record

 mov si, msg_read
 call kputs

 mov [proc_addr], long move_record
 call prot_call

 jmp .loop
 .cont:

 mov si, msg_jump
 call kputs

 mov [proc_addr], long prot_jump
 call prot_call

 ;; Завесим процессор
 hlt
 jmp short $

%define SIGNATURE 0xF0B0C
;; ---------------- device control ------------------- 
signature: dd SIGNATURE
control: dd 0x07000002
;;CL=$  CL - младшие шесть бит - номер сектора 
;;CH=$+1 старшие два бита CL и CH - номер цилиндра (дорожки) 
;;DH=$+2 DH - номер головки 
;;AL=$+3  AL - количество секторов (в сумме не более одного цилиндра) 
destination: dd 0x25000
;; address to place section
next_record: dd 0x0
;; ---------------------------------------------------

entry_point: dd 0x25000
device: dd 0
;;DL=$  DL - номер диска (носителя). Например 0 - дисковод A: 
;;----------------------------------------------------

read_record:
 mov [.errors_counter], byte 0
 cmp [signature], long SIGNATURE
 je .start
 mov si, msg_giving_up_sig
 call kputs
 hlt
 jmp short $
 
 .start:
 mov ax, 0x800
 mov es, ax
 mov bx, 0
 mov cx, [control]
 mov dh, [control+2]
 mov al, [control+3]
 mov ah, 0x02

 int 0x13
        jc .read_error
 ret

%define MAX_READ_ERRORS 5 
 .errors_counter: db 0 
 .read_error:
 inc byte [.errors_counter]
 cmp byte [.errors_counter], MAX_READ_ERRORS
 jl .start

 mov si, msg_giving_up
 call kputs
 hlt
 jmp short $

kputs: 
 .loop: 
 lodsb 
 test al, al 
 jz .quit 
 mov ah, 0x0E 
 int 0x10 
 jmp short .loop 
 .quit: 
 ret 

msg: db "Startup..",0x0A,0x0D,0 
msg_read: db "<=read<=",0x0A,0x0D,0 
msg_jump: db "Jump..",0x0A,0x0D,0 
msg_giving_up: db "Fatal: Too many errors",0x0A,0x0D, 0
msg_giving_up_sig: db "Fatal: Wrong record signature",0x0A,0x0D, 0

proc_addr: dd prot_jump
            ;; make a call in protected mode and return to real
prot_call:
 pushf
 cli

 mov eax, cr0 
 or al, 1 
 mov cr0, eax  

 jmp 0x8: _protected

back:
 mov ax, cs
 mov ds, ax
 mov ss, ax
 popf
 ret


[BITS 32]
_protected: 
 mov ax, 0x10
 mov ds, ax
 mov ss, ax

 call [proc_addr]

 ;; Сброс бита PE регистра CR0
 mov eax, cr0 
 and al, 11111110b
 mov cr0, eax

 jmp 0: back


prot_jump:
 mov eax, [entry_point]
 jmp [eax]

move_record:

 ; В DS - адрес исходного сегмента
        ; В ES - адрес целевого сегмента
 mov ax, ds
 mov es, ax

 mov esi, 0x8000 ; source
 mov edi, [destination]
 
 ; Копируем [control+3] секторов по 512 байт
 xor eax, eax
 mov al, [control+3]
 shl eax, 7          ; *128
 mov ecx, eax

 cld ; choose direction
 cmp esi, edi
 jz .get_next
 jg .copy
 std
 mov eax, ecx         ; backward from the top
 dec eax
 shl eax, 2
 add esi, eax
 add edi, eax

 .copy: rep movsd
 
 .get_next: 
 mov esi,[next_record]
 test esi, esi
 jnz .load_record
 mov [control+3], byte 0
 ret
 .load_record:
 mov edi, signature
 cld
 movsd ; signature
 movsd   ; control
 movsd ; destination
 movsd   ; next address
 ret

gdt:
 dw 0, 0, 0, 0 ; Нулевой дескриптор

 db 0xFF  ; Сегмент кода с DPL=0 
 db 0xFF  ; Базой=0 и Лимитом=4 Гб 
 db 0x00
 db 0x00
 db 0x00
 db 10011010b
 db 0xCF
 db 0x00
 
 db 0xFF  ; Сегмент данных с DPL=0
 db 0xFF  ; Базой=0 и Лимитом=4Гб 
 db 0x00 
 db 0x00
 db 0x00
 db 10010010b
 db 0xCF
 db 0x00

 ;; Значение, которое мы загрузим в GDTR: 
gd_reg:
 dw 8192
 dd gdt

 times 510-($-$$) db 0
 db 0x55,0xAA

 incbin 'kernel.bin'

<< к оглавлению<<


Командный файл для компиляции.

Проект собирался под Windows средствами nasmw и djgcc.

Тестирование проведено в эмуляторе Bochs.

Нужно заметить, что для Windows нет нормально работающих GNU инструментов. Так, при сборке средствами djgcc линковщик не находит глобальных символов, определенных в ассемблерных модулях. Однако данная ошибка не мешает запускать данный проект.

gcc -fno-leading-underscore -ffreestanding -c -o ktty.o ktty.c
gcc -fno-leading-underscore -ffreestanding -c -o memory.o memory.c
gcc -fno-leading-underscore -ffreestanding -c -o process.o process.c
gcc -fno-leading-underscore -ffreestanding -c -o kernel.o kernel.c
nasmw -fbin -o app.bin app.asm
nasmw -fcoff -o startup.o startup.asm
ld -Ttext 0x25000 -o kernel.bin startup.o ktty.o memory.o process.o kernel.o
objcopy kernel.bin -O binary
nasmw -fbin -o image.bin boot.asm

<< к оглавлению<<



http://www.lowlevel.ru - Сайт. Наш сайт :)
http://www.lowlevel.ru/articles/ - Архив всех выпусков рассылки (кроме того, его можно найти здесь)

http://www.lowlevel.ru/cgi-bin/yabb/YaBB.cgi - Форум
Посмотреть 10 последних сообщений форума

http://sf.net/projects/tyros/ - Открытая операционная система Tyros/Neutronix, разрабатываемая по материалам нашей рассылки




http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное