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

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


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

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

Выпуск 12 от 2003-06-07

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

Стандартная библиотека

Сегодня мы разработаем стандартную библиотеку функций для нашей системы, которая будет использоваться как ядром, так и приложениями.

Для начала определимся с функциями, которые нам понадобятся:
printf - форматированный вывод
strcmp - сравнение строк
strcpy - копирование строки
memcpy - копирование памяти
getsn - ввод строки пользователем

Две из этих функций - printf() и getsn() мы сегодня подробно разберем, а разработка оставшихся не должна доставить вам никаких проблем. Но на всякий случай их исходный текст будет опубликован в следующем выпуске

printf

Наш printf будет представлять собой сильно урезанный (и немного измененный) аналог printf стандартной библиотеки Си. Будут поддерживаться форматы:
%i - беззнаковое целое десятичное число
%s - строка
%x - беззнаковое целое шестнадцатеричное число от 0 до 0xFF
%X - беззнаковое целое шестнадцатеричное число от 0 до 0xFFFFFFFF
%c - ASCII-символ
%z - установка цвета терминала

Мы не будем поддерживать другие форматы, а также флаги, точность и пр. Толку от них (в нашей системе) будет немного, а лишней работы - навалом.

Для printf понадобятся некоторые вспомогательные функции. В первую очередь - это vprintf, которая и будет выполнять всю работу по расшифровке и выводу форматов. По сути дела, задача printf сводится только к "упаковке" переданных параметров (количество которых, как известно, варьируется) в структуру типа va_list и вызову vprintf.

Другие вспомогательные функции будут выполнять работу по выводу на экран чисел в текстовом виде. Это:
putdec - вывод десятичного числа
puthexd - вывод шестнадцатеричной или десятичной цифры
puthex - вывод шестандцатеричного числа от 0 до 0xFF
puthexi - вывод шестнадцатеричного числа от 0 до 0xFFFFFFFF

Текст функции printf и всех вспомогательных функций приведен ниже. Как и для всех публикуемых в рассылке исходников, он не претендует на оптимизированность (т.к. пытается претендовать на понятность :))

#include <stdarg.h>
void putdec(unsigned int byte)
{
  unsigned char b1;
  int b[30];
  signed int nb;
  int i=0;

  while(1){
    b1=byte%10;
    b[i]=b1;
    nb=byte/10;
    if(nb<=0){
      break;
    }
    i++;
    byte=nb;
  }

  for(nb=i+1;nb>0;nb--){
    puthexd(b[nb-1]);
  }
}
void puthexi(unsigned int dword)
{
  puthex( (dword & 0xFF000000) >>24);
  puthex( (dword & 0x00FF0000) >>16);
  puthex( (dword & 0x0000FF00) >>8);
  puthex( (dword & 0x000000FF));
}
void puthex(unsigned char byte)
{
 unsigned  char lb, rb;
  lb=byte >> 4;

  rb=byte & 0x0F;


  puthexd(lb);
  puthexd(rb);
}
void puthexd(unsigned char digit)
{
  char table[]="0123456789ABCDEF";
   putchar(table[digit]);
}
void printf(const char *fmt, ...)
{
  va_list args;
  va_start(args, fmt);
  textcolor(7);
  vprintf(fmt, args);
  va_end(args);
}
void vprintf(const char *fmt, va_list args)
{
  while (*fmt) {

    switch (*fmt) {
    case '%':
      fmt++;
      switch (*fmt) {
      case 's':
puts(va_arg(args, char *));
break;
      case 'c':
putchar(va_arg(args, unsigned int));
break;
      case 'i':
putdec(va_arg(args, unsigned int));
break;
      case 'x':
puthex(va_arg(args, unsigned int));
break;
      case 'X':
puthexi(va_arg(args, unsigned int));
break;
      case 'z':
textcolor(va_arg(args,unsigned int));
break;
      }

      break;

    default:
      putchar(*fmt);
      break;
    }
    fmt++;
  }
}

getsn

Аналога нашей функции getsn в стандартной библиотеке Си нет, но по названию можно догадаться, что это тот же самый gets, но с указанием максимального количества считываемых символов (именно из-за отсутствия этой возможности gets уязвим к переполнению буфера и рекомендуется никогда его не использовать)

Для реализации getsn нам понадобится вспомогательная функция getc, которая считывает введенный символ (если символа в буфере нет - то ждет, пока он появится). А функция ungetc() будет использоваться в обработчике прерывания клавиатуры для помещения в буфер нажатой клавиши. Примечание: принцип работы нашей ungetc() отличается от стандартной!

struct {
  int pointer;
  char buff[0x100];
} tty_buffer;
//Синхронный getc (возвращает символ; если буфер пуст -
//ждет пока символ появится)
char getc(void)
{
  char symbol;
  int i;
  while (tty_buffer.pointer == 0) {
    asm("hlt");
  }

  symbol = tty_buffer.buff[0];

  for (i = 0; i < tty_buffer.pointer; i++) {
    tty_buffer.buff[i] = tty_buffer.buff[i-1];
  }
  tty_buffer.pointer--;

  return symbol;
}
void ungetc(char c)
{
  tty_buffer.buff[tty_buffer.pointer] = c;
  tty_buffer.pointer++;
}
void getsn(const char *s, int num)
{
  char c = 0;
  char *start;

  start = s;
  while ( (c = getc()) != '\n') {

    switch(c) {
    case 8:

      if( start < s) {
s--;
*s = 0;
putchar(c);
      }
      break;

    default:
      if ( (s - start) <= num ) {
*s = c;
putchar(c);
s++;
*s = 0;
      }
      break;
    }
  }

  putchar('\n');
}

В качестве буфера введенных символов мы создаем структуру tty_buffer. Теперь осталось только изменить строчку putchar(ascii) в обработчике клавиатуры на ungetc(ascii)

Все! Можно прямо сейчас внести изменения в главный файл ядра kernel.c и опробовать наши замечательные функции.

void kernel_main()
{
    init_tty();
    clear();
    init_interrupts();

    printf("Decimal %i\n"
   "Hexadecimal short: %x\n"
   "Hexadecimal long: %X\n"
   "String: %s\n",
   12345, 0x77, 0x12345678, __DATE__);

    getsn_test();

    for(;;);
}
void getsn_test()
{
  char buffer[0x100];

  printf("Enter something:");

  getsn(buffer, 0x100);

  printf("You entered: %s\n", buffer);
}

Чего-то не хватает, не так ли? Мы забыли добавить специальный обработчик случая нажатия Backspace (ascii-код - 8) в функцию putchar(), поэтому нажатие Backspace выводит на экран какую-то абракадабру. Впрочем нет ничего проще, чем исправить эту ситуацию - достаточно добавить в искомый switch вот такой обработчик:

  case 8: //backspace
    tty_cursor--;
    *(video + tty_cursor*2) = ' ';
    break;

Идеи

Опубликованное в прошлом номере предложение собирать для системы оригинальные идеи не вызвало особого энтузиазма и за три дня откликнулись только два человека. Тем не менее, кампания по сбору идей продолжается! Пишите: lonesome@lowlevel.ru (и не забудьте указать, если вы хотите, чтобы ваш e-mail был опубликован)

А пока - первая идея (автор: Иманкулов Роман aka Ramzes):

> А идея была как раз по поводу файловых систем. Честно говоря, не особо
> в курсе всего их разнообразия, но, насколько мне известно, для обычных
> человеческих  дискет  не существует ничего, что позволило бы шифровать
> хранящуюся  на  них  информацию. Или дублировать. Потому что дискеты -
> вешь такая ненадежная, что я привык по два раза записывать на них одну
> и ту же информацию.
>
> Поэтому  было  бы совсем неплохо создать что-то, позволяющее шифровать
> информацию  на дискетах(путь для начала хоть по алгоритму Цезаря), или
> дублировать  ее...  И  совсем  не  обязательно  делать  ее файлововой.
> Возможен вед и принцип "одна дискета - один файл". Наверняка, это даже
> проще в реализации.

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
Отписаться
Убрать рекламу

В избранное