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

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


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

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

Выпуск 11 от 2003-06-03

Сегодня в номере:

Intro

После десятого выпуска ко мне в почтовый ящик пачками сыплются письма примерно следующего содержания: "Эй, чувак, кому нафиг нужна твоя система без многозадачности и всего остального???". В связи с этим, еще раз повторюсь, что: сейчас мы разрабатываем не столько систему, сколько фундамент для дальнейших разработок. Мне хочется, чтобы вы впервые могли "пощупать" систему не через три года после начала разработки, а через три дня. Поэтому для нашей первой (но не последней) системы мы отказываемся от тех возможностей, требуют долгого проектирования (например многозадачности и взаимодействия между процессам).

После же того, как этот "чекпойнт" будет пройден, мы углубимся в теоретические аспекты разработки ОС - перед тем, как написать уже гораздо более функциональную систему, с поддержкой всех возможностей современных ОС. А может быть даже и тех, которых еще нигде нет :)

VMware Workstation vs Bochs

Не знаю как вас, а меня сегодня VMware Workstation совершенно разочаровала. Есть, оказывается, у нее один серьезный баг - при обработке прерываний, вызываемых через шлюз задачи она некорректно ведет себя со стеком. Полдня мучался, искал несуществующую ошибку в собственном коде, пока не догадался запустить на реальной машине.

В общем, я перехожу на Bochs (http://bochs.sourceforge.net/). Во первых, он Free и Open Source, во вторых возможности отладки в нем громадные, а в третьих, надеюсь, глючит он меньше (благодаря первому пункту). Хотя, конечно, по большому счету, на эмулятор надеяться никогда нельзя - все равно на "живом" компьютере работает по другому (одна из причин - различное время отклика настоящей аппаратуры и виртуальной)

Новые идеи

Вам не кажется, что за прошедшие 20-25 лет развитие операционных систем было несколько однобоким? Принципы разработки ОС, придуманные еще в прошлом тысячелетии, до сих пор используются (и до сих пор являются основополагающими). Очевидный пример - иерархическая файловая система, которая изначально появилась в UNIX, сегодня используется всеми современными ОС. Откровенно говоря, все современные ОС (и Win в том числе) - потомки UNIX.

Лично я не хочу видеть нашу ОС (не ту, которую мы разрабатываем сейчас, а ту, которую будем делать после) похожей на систему двадцатилетней давности. Поэтому, прежде чем начать ее разработку, предлагаю устроить "копилку" оригинальных идей для нее. Если такие идеи у вас есть - присылайте: lonesome@lowlevel.ru

Все оригинальные идеи (естественно, с указанием авторов) будут опубликованы в рассылке

Разбор полетов

Опубликованный в номере 8 загрузчик начинается таким кодом:

entry:
cli
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, start
Большое спасибо Михаилу Александровичу Ананьину AKA Smile за указание на ошибку - т.к. мы помещаем в стек метку start, то прерывание таймера (если оно вдруг произойдет) при копировании загрузчика по адресу 0x9000:0 перезапишет код, находящийся перед start.

Для исправления этой проблемы необходимо поместить в SP не start, а, скажем, 0x7c00 (Мы не можем использовать entry, т.к. она равна нулю из-за директивы ORG 0)

Контроллер клавиатуры

Хотя поддержка клавиатуры имеет смехотворно низкий приоритет при разработке полнофункциональных ОС, она является краеугольной фичей и вообще, основной возможностью нашей системы :). Клавиатура занимает линию IRQ1. Следовательно, раз мы поменяли базовый вектор контроллера прерываний на 0x20, то прерывание от клавиатуры окажется под номером 0x21 (на всякий случай напомню, что на IRQ0 (а у нас - int 0x20) находится таймер, генерирующий прерывание 18.2 раза в секунду). Для управления клавиатурой предназначены порты 0x60 - 0x6f.

Нас будут интересовать порт 0x60 (из него мы будем считывать скан-код нажатой клавиши) и порт 0x61.

Порт 0x61 - нам нужен только бит 7, который указывает, заблокирована ли клавиатура. Этот бит необходимо сбрасывать в конце обработчика прерывания клавиатуры.

Примечание: при чтении из порта 0x60 мы получаем скан-код клавиатуры (не ASCII-код!), который затем необходимо будет по таблице преобразовать в ASCII. Вот таблица скан-кодов, которая подойдет для нашей системы (фактически это два массива - второй для клавиш с нажатым шифтом):

/* scancode.h */
char scancodes[] = {
  0,
  0, //ESC
  '1','2', '3', '4', '5', '6', '7', '8', '9', '0',
  '-', '=',
  8, //BACKSPACE
  '\t',//TAB
  'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']',
  '\n', //ENTER
  0, //CTRL
  'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '',
  0, //LEFT SHIFT,
  '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
  0, //RIGHT SHIFT,
  '*', //NUMPAD
  0, //ALT
  ' ', //SPACE
  0, //CAPSLOCK
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //F1 - F10
  0, //NUMLOCK
  0, //SCROLLOCK
  0, //HOME
  0,
  0, //PAGE UP
  '-', //NUMPAD
  0, 0,
  0, //(r)
  '+', //NUMPAD
  0, //END
  0,
  0, //PAGE DOWN
  0, //INS
  0, //DEL
  0, //SYS RQ
  0,
  0, 0, //F11-F12
  0,
  0, 0, 0, //F13-F15
  0, 0, 0, 0, 0, 0, 0, 0, 0, //F16-F24
  0, 0, 0, 0, 0, 0, 0, 0
};
char scancodes_shifted[] = {
  0,
  0, //ESC
  '!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
  '_', '+',
  8, //BACKSPACE
  '\t',//TAB
  'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}',
  '\n', //ENTER
  0, //CTRL
  'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',
  0, //LEFT SHIFT,
  '|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
  0, //RIGHT SHIFT,
  '*', //NUMPAD
  0, //ALT
  ' ', //SPACE
  0, //CAPSLOCK
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //F1 - F10
  0, //NUMLOCK
  0, //SCROLLOCK
  0, //HOME
  0,
  0, //PAGE UP
  '-', //NUMPAD
  0, 0,
  0, //(r)
  '+', //NUMPAD
  0, //END
  0,
  0, //PAGE DOWN
  0, //INS
  0, //DEL
  0, //SYS RQ
  0,
  0, 0, //F11-F12
  0,
  0, 0, 0, //F13-F15
  0, 0, 0, 0, 0, 0, 0, 0, 0, //F16-F24
  0, 0, 0, 0, 0, 0, 0, 0
};

Примечание 2: прерывание вызывается как при нажатии, так и при отпускании клавиши, и скан-код отпущенной клавиши равен скан-коду нажатой плюс 0x80.

Вы уже догадались, как определить, была ли нажата клавиша 'Shift'?

А теперь реализуем на практике взаимодействие с контроллером клавиатуры:

Первое, что нам нужно - создать функции-обработчики прерываний для таймера (без него никак) и клавиатуры. Прежде чем выполнить свою работу, обработчик аппаратного прерывания должен сохранить регистры, а по завершении обработки - послать байт 0x20 в порт 0x20 (сигнал "конец прерывания" контроллеру прерываний), чтобы разрешить дальнейшую обработку аппаратных прерываний. Ну и восстановить регистры. Вот макрос, который сделает всю черную работу:

#define IRQ_HANDLER(func) void func (void);\
 asm(#func ": pusha \n call _" #func " \n movb $0x20, %al \n outb %al, $0x20 \n popa \n iret \n");\
 void _ ## func(void)

Выглядит он достаточно коряво, но в этом вина не моя, а разработчиков CPP - препроцессора Си.

Теперь можно создавать обработчики аппаратных прерываний вот так:

IRQ_HANDLER(имя_функции)
{
}

Обработчик таймера нам нужен только как "заглушка", поэтому внутри у него ничего не будет:

IRQ_HANDLER(irq_timer)
{

}

А вот обработчик IRQ1 (клавиатуры) уже выполняет кое-какую работу, а именно - выводит на экран нажатую клавишу.

//Здесь мы храним состояние шифта
char shift = 0;
IRQ_HANDLER(irq_keyboard)
{
  unsigned char scancode, ascii;
  unsigned char creg;
  //Прочитаем скан-код из порта 0x60
  scancode = inportb(0x60);

  switch(scancode) {

    //Скан-коды нажатого шифта
  case 0x36:
  case 0x2A:
    shift = 1;
    break;
    //Скан-коды отпущенного шифта
  case 0x36 + 0x80:
  case 0x2A + 0x80:
    shift = 0;
    break;

  default:
  //Если клавиша отпущена...
    if(scancode >= 0x80) {
    //То ничего не делать

    } else {
    //А если нажата...

      if(shift){
      //Если шифт нажат, но преобразовать скан-код в "шифтнутое" ASCII
ascii = scancodes_shifted[scancode];
      } else {
      //А если не нажат - то в обычное
ascii = scancodes[scancode];
      }

      //Если в результате преобразования нажата клавиша с каким-либо
      //символом, то вывести его на экран
      if(ascii != 0) {
putchar(ascii);
      }
    }
    break;
  }
  //Считаем байт состояния клавиатуры
  creg = inportb(0x61);

  //Установим в нем старший бит
  creg |= 1;
  //И запишем обратно
  outportb(0x61, creg);
}

Ну и наконец - функция, которая установит все обработчики прерываний и разрешит их обработку:

void init_interrupts()
{
  i_install(0x20, &irq_timer, 0x8e);
  i_install(0x21, &irq_keyboard, 0x8e);
  i_setup();
  i_enable();
}

Что нам осталось? Сохранить вышенаписанные функции как файл handlers.c (не забыв сделать в начале #include "scancode.h"), подключить его к нашей системе, и вызвать из функции kernel_main() функцию init_interrupts(). Все! Можно нажимать на клавиши - если все в порядке, то соответствующие символы отобразятся на экране монитора

Outro

На сегодня все, уважаемые подписчики.
Как всегда, мой почтовый ящик открыт для вас:
lonesome@lowlevel.ru
Также вы можете задавать интересующие вас вопросы в форуме lowlevel.ru
Предыдущие выпуски рассылки вы можете найти по этому адресу:
http://subscribe.ru/archive/comp.soft.prog.osdev
А все, исходники, опубликованые в рассылке, располагаются здесь:
http://www.lowlevel.ru/osdev/sources.htm
Всего наилучшего!
Lonesome



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

В избранное