ПЕРЕЛІК ДИСЦИПЛІН:
  • Адміністративне право
  • Арбітражний процес
  • Архітектура
  • Астрологія
  • Астрономія
  • Банківська справа
  • Безпека життєдіяльності
  • Біографії
  • Біологія
  • Біологія і хімія
  • Ботаніка та сільське гос-во
  • Бухгалтерський облік і аудит
  • Валютні відносини
  • Ветеринарія
  • Військова кафедра
  • Географія
  • Геодезія
  • Геологія
  • Етика
  • Держава і право
  • Цивільне право і процес
  • Діловодство
  • Гроші та кредит
  • Природничі науки
  • Журналістика
  • Екологія
  • Видавнича справа та поліграфія
  • Інвестиції
  • Іноземна мова
  • Інформатика
  • Інформатика, програмування
  • Юрист по наследству
  • Історичні особистості
  • Історія
  • Історія техніки
  • Кибернетика
  • Комунікації і зв'язок
  • Комп'ютерні науки
  • Косметологія
  • Короткий зміст творів
  • Криміналістика
  • Кримінологія
  • Криптология
  • Кулінарія
  • Культура і мистецтво
  • Культурологія
  • Російська література
  • Література і російська мова
  • Логіка
  • Логістика
  • Маркетинг
  • Математика
  • Медицина, здоров'я
  • Медичні науки
  • Міжнародне публічне право
  • Міжнародне приватне право
  • Міжнародні відносини
  • Менеджмент
  • Металургія
  • Москвоведение
  • Мовознавство
  • Музика
  • Муніципальне право
  • Податки, оподаткування
  •  
    Бесплатные рефераты
     

     

     

     

     

     

         
     
    Справжній "Hello World "
         

     

    Інформатика, програмування

    Справжній "Hello World"

    Станіслав Иевлев

    З чого починається вивчення нової мови (або середовища) програмування? З написання простенькій програми, що виводить на екран коротке привітання типу "Hello World! ". Наприклад, для C це буде виглядати приблизно так:

    main () (

    printf ( "Hello World! N ");

    )

    Показово, але зовсім нецікаво. Програма, звичайно, працює, привітання своє пише; але ж для цього потрібна ціла операційна система! А що якщо хочеться написати програмку, для якої нічого не треба? Вставляємо дискетки в комп'ютер, завантажується з неї і ... "Hello World"! Можна навіть прокричати це вітання із захищеного режиму ... Сказано - зроблено. З чого б почати? .. Набратися знань, звичайно. Для цього дуже добре полазити в исходники Linux і Thix. Перша система всім добре знайома, другий менш відома, але не менш корисна.

    Підучившись? Тепер займемося. Зрозуміло, що насамперед треба написати завантажувальний сектор для нашої міні-операційки (а це ж буде саме міні-операційка!). Оскільки процесор вантажиться в 16-розрядному режимі, то для створення завантажувального сектора використовується асемблер і лінковщік з пакету bin86. Можна, звичайно, пошукати ще що-небудь, але обидва наших прикладу використовують саме його, і ми теж підемо стопах вчителів. Синтаксис цього асемблера трохи дивакуватий, що суміщає риси, характерні і для Intel і для AT & T, але після кількох тижнів мук можна звикнути.

    Завантажувальний сектор (boot.S)

    Свідомо не буду приводити повних лістингів програм. Так стануть зрозуміліше основні ідеї, та і вам буде набагато приємніше, якщо все напишете своїми руками. Для початку визначимося з основними константами.

    START_HEAD = 0 - Головка приводу, яку будемо використовувати.

    START_TRACK = 0 - Доріжка, звідки почнемо читання.

    START_SECTOR = 2 - Сектор, починаючи з якого будемо зчитувати наше ядерце.

    SYSSIZE = 10 -- Розмір ядра в секторах (кожен сектор містить 512 байт)

    FLOPPY_ID = 0 -- Ідентифікатор приводу. 0 - для першого, 1 - для друга

    HEADS = 2 -- Кількість головок приводу.

    SECTORS = 18 -- Кількість доріжок на дискеті. Для формату 1.44 МБ це кількість дорівнює 18.

    У процесі завантаження буде відбуватися наступне. Завантажувач BIOS вважає перший сектор дискети, покладе його за адресою 0000:0 x7c00 і передасть туди управління. Ми його отримаємо і - для початку - перемістити себе нижче за адресою 0000:0 x600, перейдемо туди і спокійно продовжимо роботу. Власне вся наша робота буде складатися з завантаження ядра (сектори 2 - 12 першим доріжки дискети) за адресою 0x100: 0000, переходу в захищений режим і стрибка на перші рядки ядра. У зв'язку з цим ще кілька констант:

    BOOTSEG = 0x7c00 - Сюди помістить завантажувальний сектор BIOS.

    INITSEG = 0x600 - Сюди його перемістив ми.

    SYSSEG = 0x100 - А тут приємно розташується наше ядро.

    DATA_ARB = 0x92 - Визначник сегмента даних для дескриптора

    CODE_ARB = 0x9A - Визначник сегменту коду для дескриптора.

    Насамперед зробимо переміщення самих себе в більш прийнятне місце.

    cli

    xor ax, ax

    mov ss, ax

    mov sp, # BOOTSEG

    mov si, sp

    mov ds, ax

    mov es, ax

    sti

    cld

    mov di, # INITSEG

    mov cx, # 0x100

    repnz

    movsw

    jmpi go, # 0

    Тепер необхідно налаштувати як слід сегменти для даних (es, ds) і для стека. Неприємно, звичайно, що все доводиться робити вручну, але що поробиш - адже крім нас і BIOS в пам'яті комп'ютера нікого немає.

    go:

    mov ax, # 0xF0

    mov ss, ax

    mov sp, ax

    ; Стек розмістимо як 0xF0: 0xF0 = 0xFF0

    mov ax, # 0x60

    ; Сегменти для даних ES і DS поставимо в 0x60

    mov ds, ax

    mov es, ax

    Нарешті, можна вивести переможний привітання. Нехай світ дізнається, що ми змогли завантажитися! Оскільки у нас є все-таки цілий BIOS, скористаємося готової функцією 0x13 переривання 0x10. Можна, звичайно, його знехтувати і написати прямо в відеопам'ять, але у нас кожен байт команди на рахунку, а байт таких всього 512. Витратимо їх краще на щось більш корисне.

    mov cx, # 18

    mov bp, # boot_msg

    call write_message

    Функція write_message виглядає наступним чином

    write_message:

    push bx

    push ax

    push cx

    push dx

    push cx

    mov ah, # 0x03

    ; прочитаємо поточне положення курсору,

    ; щоб не виводити повідомлення де попало.

    xor bh, bh

    int 0x10

    pop cx

    mov bx, # 0x0007

    ; Параметри символів, що виводяться:

    ; відеосторінок 0, атрибут 7 (сірий на чорному)

    mov ax, # 0x1301

    ; Виводимо рядок і Зрушуємо курсор

    int 0x10

    pop dx

    pop cx

    pop ax

    pop bx

    ret

    ; А повідомлення так

    boot_msg:

    . byte 13,10

    . ascii "Booting data ..."

    . byte 0

    До цього часу на дисплеї комп'ютера з'явиться скромне "Booting data ...". Це в принципі не гірше, ніж "Hello World", але давайте доб'ємося трохи більшого. Перейдемо в захищений режим і виведемо цей "Hello" вже з програми, написаної на C. Ядро 32-розрядне. Воно буде у нас розміщуватися окремо від завантажувального сектора і збиратися вже за допомогою gcc і gas. Синтаксис асемблера gas відповідає вимогам AT & T, так що тут все буде простіше. Але для Спершу нам потрібно прочитати ядро. Знову скористаємося готової функцією 0x2 переривання 0x13.

    recalibrate:

    mov ah, # 0

    mov dl, # FLOPPY_ID

    int 0x13

    ; проведемо Реініціалізація дисководу.

    jc recalibrate

    call read_track

    ; виклик функції читання ядра

    jnc next_work

    ; якщо під час читання не відбулося

    ; нічого поганого, то працюємо далі

    bad_read:

    ; якщо читання відбулося невдало -

    ; виводимо повідомлення про помилку

    mov bp, # error_read_msg

    mov cx, 7

    call write_message

    inf1: jmp inf1

    ; і йдемо в нескінченний цикл. Тепер

    ; нас врятує тільки ручна перезавантаження

    Сама функція читання гранично проста: довго і нудно заповнюємо параметри, а потім одним махом зчитуємо ядро. Труднощі почнуться, коли ядро перестане міститися в 17 секторах (тобто 8.5КБ); але це поки що в майбутньому, а зараз цілком достатньо такого блискавичного читання

    read_track:

    pusha

    push es

    push ds

    mov di, # SYSSEG

    ; Визначаємо

    mov es, di

    ; адреса буфера для даних

    xor bx, bx

    mov ch, # START_TRACK

    ; доріжка 0

    mov cl, # START_SECTOR

    ; починаючи з сектора 2

    mov dl, # FLOPPY_ID

    mov dh, # START_HEAD

    mov ah, # 2

    mov al, # SYSSIZE

    ; вважати 10 секторів

    int 0x13

    pop ds

    pop es

    popa

    ret

    ; Ось і все. Ядро успішно прочитано,

    ; і можна вивести ще одне радісне

    ; повідомлення на екран.

    next_work:

    call kill_motor

    ; зупиняємо привід дисководу

    mov bp, # load_msg

    ; виводимо сполучення

    mov cx, # 4

    call write_message

    ; Ось вміст повідомлення

    load_msg:

    . ascii "done"

    . byte 0

    ; А ось функція зупинки двигуна приводу.

    kill_motor:

    push dx

    push ax

    mov dx, # 0x3f2

    xor al, al

    out dx, al

    pop ax

    pop dx

    ret

    На даний момент на екрані виведено "Booting data ... done" і лампочка приводу флоппі-дисків погашена. Всі затихли і готові до смертельного номеру - стрибка в захищений режим. Для початку треба включити адресну лінію A20. Це в точності означає, що ми будемо використовувати 32-розрядну адресацію до даних.

    mov al, # 0xD1

    ; команда запису для 8042

    out # 0x64, al

    mov al, # 0xDF

    ; включити A20

    out # 0x60, al

    Виведемо попередження - про те, що переходимо в захищений режим. Нехай все знають, які ми важливі.

    protected_mode:

    mov bp, # loadp_msg

    mov cx, # 25

    call write_message

    Повідомлення:

    loadp_msg:

    . byte 13,10

    . ascii "Go to protected mode ..."

    . byte 0

    Поки у нас ще живий BIOS, запам'ятаємо позицію курсору і збережемо її у відомому місці (0000:0 x8000 ). Ядро пізніше забере всі дані і буде їх використовувати для виведення на екран переможного повідомлення.

    save_cursor:

    mov ah, # 0x03

    ; читаємо поточну позицію курсору

    xor bh, bh

    int 0x10

    seg cs

    mov [0x8000], dx

    ; зберігаємо в спеціальному тайнику

    Тепер увагу, забороняємо переривання (нема чого відволікатися під час такої роботи) і завантажуємо таблицю дескрипторів

    cli

    lgdt GDT_DESCRIPTOR

    ; завантажуємо описувач таблиці дескрипторів.

    У нас таблиця дескрипторів складається з трьох описувачів: нульовий (завжди повинен бути присутнім), сегменту коду і сегменту даних.

    align 4

    . word 0

    GDT_DESCRIPTOR:. word 3 * 8 - 1;

    ; розмір таблиці дескрипторів

    . long 0x600 + GDT

    ; місце розташування таблиці дескрипторів

    . align 2

    GDT:

    . long 0, 0

    ; Номер 0: порожній дескриптор

    . word 0xFFFF, 0

    ; Номер 8: дескриптор коду

    . byte 0, CODE_ARB, 0xC0, 0

    . word 0xFFFF, 0

    ; Номер 0x10: дескриптор даних

    . byte 0, DATA_ARB, 0xCF, 0

    Перехід в захищений режим може відбуватися мінімум двома способами, але обидві ОС, обрані нами для прикладу (Linux і Thix) використовують для сумісності з 286 процесором команду lmsw. Ми будемо діяти тим же способом

    mov ax, # 1

    lmsw ax

    ; прощай реальний режим. Ми тепер

    ; знаходимося в захищеному режимі.

    jmpi 0x1000, 8

    ; Затяжний стрибок на 32-розрядне ядро.

    Ось і вся робота завантажувального сектора - не мало, але й не багато. Тепер з ним ми попрощаємося і попрямуємо до ядра. Наприкінці асемблерні файлу корисно додати наступну інструкцію.

    org 511

    end_boot:. byte 0

    У результаті скомпільований код буде займати рівно 512 байт, що дуже зручно для підготовки образу завантажувального диска.

    Перші зітхання ядра (head.S)

    Ядро, до жаль, знову почнеться з асемблерні коду. Але тепер його буде зовсім небагато. Ми власне задамо правильні значення сегментів для даних (ES, DS, FS, GS). Записав туди значення відповідного дескриптора даних.

    cld

    cli

    movl $ (__KERNEL_DS),% eax

    movl% ax,% ds

    movl% ax,% es

    movl% ax,% fs

    movl% ax,% gs

    Перевіримо, чи нормально включилася адресна лінія A20 - простим тестом запису. Обнулив для чистоти експерименту регістр прапорів.

    xorl% eax,% eax

    1: incl% eax

    movl% eax, 0x000000

    cmpl% eax, 0x100000

    je 1b

    pushl $ 0

    popfl

    викличемо довгоочікувану функцію, вже написану на С: call SYMBOL_NAME (start_my_kernel). І більше нам тут робити нічого.

    Поговоримо на мові високого рівня (start.c)

    Ось тепер ми повернулися до того, з чого починали розповідь. Майже повернулися, тому що printf () тепер треба робити вручну. Оскільки готових переривань вже немає, то будемо використовувати прямі запис у відеопам'ять. Для цікавих - майже весь код цієї частини, з незначними змінами, запозичений з частини ядра Linux, здійснює розпакування (/ arch/i386/boot/compressed/*). Для збирання вам буде потрібно додатково визначити такі макроси як inb (), outb (), inb_p (), outb_p (). Готові визначення найпростіше позичити з будь-якої версії Linux.

    Тепер, щоб не плутатися з вбудованими в glibc функціями, скасуємо їх визначення

    # undef memcpy

    // Задамо декілька своїх:

    static void puts (const char *);

    static char * vidmem = (char *) 0xb8000;/* адреса відеопам'яті */

    static int vidport;/* відеопортів */

    static int lines, cols;/* кількість ліній і рядків на екран */

    static int curr_x, curr_y;/* поточне положення курсору */

    І почнемо, нарешті, писати код на мові високого рівня ... правда, з невеликими асемблерні вставками.

    /* функція перекладу курсору в положення (x, y).

    Робота ведеться через введення/виведення в відеопортів */

    void gotoxy (int x, int y)

    (

    int pos;

    pos = (x + cols * y) * 2;

    outb_p (14, vidport);

    outb_p (0xff & (pos>> 9), vidport +1);

    outb_p (15, vidport);

    outb_p (0xff & (pos>> 1), vidport +1);

    )

    /* функція прокручування екрану. Працює,

    використовуючи пряму запис у відеопам'ять */

    static void scroll ()

    (

    int i;

    memcpy (vidmem, vidmem + cols * 2, (lines - 1) * cols * 2);

    for (i = (lines - 1) * cols * 2; i

    vidmem [i] = '';

    )

    /* функція виводу рядка на екран */

    static void puts (const char * s)

    (

    int x, y;

    char c;

    x = curr_x;

    y = curr_y;

    while ((c = * s + +)! = '

         
     
         
    Реферат Банк
     
    Рефераты
     
    Бесплатные рефераты
     

     

     

     

     

     

     

     
     
     
      Все права защищены. Reff.net.ua - українські реферати ! DMCA.com Protection Status