Автор: lonesome [TSH/Digital Daemons]
Дата: 7.06.2003
Раздел: Разработка ОС
Сегодня в номере:
Сегодня мы разработаем стандартную библиотеку функций для нашей системы, которая будет использоваться как ядром, так и приложениями.
Для начала определимся с функциями, которые нам понадобятся:
printf - форматированный вывод
strcmp - сравнение строк
strcpy - копирование строки
memcpy - копирование памяти
getsn - ввод строки пользователем
Две из этих функций - printf() и getsn() мы сегодня подробно разберем, а разработка оставшихся не должна доставить вам никаких проблем. Но на всякий случай их исходный текст будет опубликован в следующем выпуске
Наш 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 в стандартной библиотеке Си нет, но по названию можно догадаться, что это тот же самый 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):
> А идея была как раз по поводу файловых систем. Честно говоря, не особо > в курсе всего их разнообразия, но, насколько мне известно, для обычных > человеческих дискет не существует ничего, что позволило бы шифровать > хранящуюся на них информацию. Или дублировать. Потому что дискеты - > вешь такая ненадежная, что я привык по два раза записывать на них одну > и ту же информацию. > > Поэтому было бы совсем неплохо создать что-то, позволяющее шифровать > информацию на дискетах(путь для начала хоть по алгоритму Цезаря), или > дублировать ее... И совсем не обязательно делать ее файлововой. > Возможен вед и принцип "одна дискета - один файл". Наверняка, это даже > проще в реализации.
На сегодня все, уважаемые подписчики.
Как всегда, мой почтовый ящик открыт для вас: lonesome@lowlevel.ru
Также вы можете задавать интересующие вас вопросы в форуме lowlevel.ru
Предыдущие выпуски рассылки вы можете найти по этому адресу:
http://subscribe.ru/archive/comp.soft.prog.osdev
А все, исходники, опубликованые в рассылке, располагаются здесь:
http://www.lowlevel.ru/osdev/sources.htm
Всего наилучшего!
Lonesome