Rambler's
Top100

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

Автор: lonesome [TSH/Digital Daemons]
Дата: 7.06.2003
Раздел: Разработка ОС

ПРЕДЫДУЩИЙ ВЫПУСК      СЛЕДУЮЩИЙ ВЫПУСК

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

Выпуск 12 от [SUBSCRIBE issue_date]

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

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

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

Для начала определимся с функциями, которые нам понадобятся:
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


ПРЕДЫДУЩИЙ ВЫПУСК      СЛЕДУЮЩИЙ ВЫПУСК

Rambler's Top100