Este artículo lo escribí para el blog en español DesdeLinux el 1 de enero de 2014 y ahora lo dejo aquí, en mi blog personal. El artículo está tal cual, sin ninguna modificación desde aquella fecha.
Continuamos esta serie de posts sobre cómo crear nuestro sistema operativo. Hoy no nos vamos a centrar en un tema sino que vamos a definir algunas funciones útiles de ahora en adelante. En primer lugar vamos a definir 3 funciones que cumplan la función de memcpy, memset y memcmp:
void*ND::Memory::Set(void*buf,intc,size_tlen){unsignedchar*tmp=(unsignedchar*)buf;while(len--){*tmp++=c;}returnbuf;}void*ND::Memory::Copy(void*dest,constvoid*src,size_tlen){constunsignedchar*sp=(constunsignedchar*)src;unsignedchar*dp=(unsignedchar*)dest;for(;len!=0;len--)*dp++=*sp++;returndest;}intND::Memory::Compare(constvoid*p1,constvoid*p2,size_tlen){constchar*a=(constchar*)p1;constchar*b=(constchar*)p2;size_ti=0;for(;i<len;i++){if(a[i]<b[i])return-1;elseif(a[i]>b[i])return1;}return0;}
Todas ellas se auto-implementan. Estas funciones yo las he sacado de una pequeña librería del C, la implementación suele ser parecida en todos los sistemas operativos. Ahora vamos a hacer 3 funciones simulares pero para manipular strings. Cumplirían la función de strcpy, strcat y strcmp.
size_tND::String::Length(constchar*src){size_ti=0;while(*src--)i++;returni;}intND::String::Copy(char*dest,constchar*src){intn=0;while(*src){*dest++=*src++;n++;}*dest='\0';returnn;}intND::String::Compare(constchar*p1,constchar*p2){inti=0;intfailed=0;while(p1[i]!='\0'&&p2[i]!='\0'){if(p1[i]!=p2[i]){failed=1;break;}i++;}if((p1[i]=='\0'&&p2[i]!='\0')||(p1[i]!='\0'&&p2[i]=='\0'))failed=1;returnfailed;}char*ND::String::Concatenate(char*dest,constchar*src){intdi=ND::String::Length(dest);intsi=0;while(src[si])dest[di++]=src[si++];dest[di]='\0';returndest;}
Vamos ahora con unas funciones bastante interesantes. Con estas funciones podremos leer y escribir en los puertos del hardware. Esto normalmente se hace con ASM y corresponde (en x86) a las instrucciones in y out. Para llamar de una manera fácil a ASM desde C se usa la instrucción asm, con el peligro que conlleva de que no es portable. A esta sentencia le añadimos el volatile para que GCC no intente optimizar ese texto. Por otra parte la instrucción asm tiene una forma curiosa de aceptar parámetros, pero eso creo que se entiende mejor viendo los ejemplos.
uint8_tND::Ports::InputB(uint16_t_port){unsignedcharrv;asmvolatile("inb %1, %0":"=a"(rv):"dN"(_port));returnrv;}uint16_tND::Ports::InputW(uint16_tport){uint16_trv;asmvolatile("inw %1, %0":"=a"(rv):"dN"(port));}voidND::Ports::OutputB(uint16_tport,uint8_tvalue){asmvolatile("outb %1, %0"::"dN"(port),"a"(value));}
Y hasta aquí el post 3, hoy no hemos hecho nada vistoso pero sí hemos definido una funciones que nos vendrán bien de cara a un futuro. Aviso a los usuarios de 64 bits que estoy trabajando en solucionar un bug que impide compilar correctamente en 64 bits. En el siguiente post veremos un componente importante de la arquitectura x86, la GDT.