Emulando a Linus Torvalds: Crea tu propio sistema operativo desde 0 (III)

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, int c, size_t len)
{
unsigned char* tmp=(unsigned char*)buf;
while(len--)
{
*tmp++=c;
}
return buf;
}
void* ND::Memory::Copy(void* dest,const void* src, size_t len)
{
const unsigned char* sp=(const unsigned char*)src;
unsigned char* dp=(unsigned char*)dest;
for(;len!=0;len--) *dp++=*sp++;
return dest;
}
int ND::Memory::Compare(const void* p1, const void* p2, size_t len)
{
const char* a=(const char*)p1;
const char* b=(const char*)p2;
size_t i=0;
for(;i<len;i++)
{
if(a[i] < b[i]) return -1; else if(a[i] > b[i])
return 1;
}
return 0;
}

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_t ND::String::Length(const char* src)
{
size_t i=0;
while(*src--)
i++;
return i;
}
int ND::String::Copy(char* dest, const char* src)
{
int n = 0;
while (*src)
{
*dest++ = *src++;
n++;
}
*dest = '';
return n;
}
int ND::String::Compare(const char *p1, const char *p2)
{
int i = 0;
int failed = 0;
while(p1[i] != '' && p2[i] != '')
{
if(p1[i] != p2[i])
{
failed = 1;
break;
}
i++;
}
if( (p1[i] == '' && p2[i] != '') || (p1[i] != '' && p2[i] == '') )
failed = 1;
return failed;
}
char *ND::String::Concatenate(char *dest, const char *src)
{
int di = ND::String::Length(dest);
int si = 0;
while (src[si])
dest[di++] = src[si++];
dest[di] = '';
return dest;
}

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.

void ND::Ports::OutputB(uint16_t port, uint8_t value)
{
asm volatile("outb %1, %0" : : "dN"(port), "a"(value));
}
uint8_t ND::Ports::InputB(uint16_t _port)
{
unsigned char rv;
asm volatile("inb %1, %0" : "=a"(rv) : "dN"(_port));
return rv;
}

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.


Deja tu comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*

*

  1. Responsable de los datos: Miguel Ángel Gatón
  2. Finalidad de los datos: Controlar el SPAM, gestión de comentarios.
  3. Legitimación: Tu consentimiento
  4. Comunicación de los datos: No se comunicarán los datos a terceros salvo por obligación legal.
  5. Almacenamiento de los datos: Base de datos alojada en Occentus Networks (UE)
  6. Derechos: En cualquier momento puedes limitar, recuperar y borrar tu información.

  1.   Nombre dijo

    A ver si comentas eso de como solucionar el bug, porque no avanzo desde la primera parte.

    1.    O_Pixote_O dijo

      Creo que comentaron el error en los comentarios del post 2. Creian que era algo del grub si no recuerdo mal

  2.   BlueSkull dijo

    Copiar bye a bye?

    1.    BlueSkull dijo

      Byte a byte evidentemente…, 2 fallos consecutivos no hacen un acierto, confirmado.

  3.   ruby232 dijo

    Muchas gracias por el post muy bueno y lo estoy siguiendo, tengo algunas prguntas:
    1. Cuando dices ‘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 que te refieres cuando dices ‘con el peligro que conlleva de que no es portable’?

    2. Si se fuera a hacer un sistema operativo ‘profesional'(por decirlo de alguna manera) esta parte de acceder al Hardware se haría en Ensamblador.

    3. Como se haría en Ensamblador.?

    1.    AdrianArroyoCalle dijo

      Gracias a ti por seguirlo, voy a contestarte a las preguntas de una en una:
      1- Bien, el problema de la instrucción asm es que no existe en ningún estándar C, por ello cada compilador lo implementa a su manera (si es que lo implementa). En este caso se puede compilar con GCC y Clang(se parece mucho a GCC en este aspecto) pero no podrás en otros compiladores como Intel C (este usa la Intel Syntax en el ASM).
      2- Profesionalmente se debería separar claramente una parte y otra por arquitecturas y otra parte común. No es necesario hacerlo en ensamblador (Linux lo tiene dentro de C)
      3- En ensamblador es muy sencillo también, pero lo tienes en un archivo aparte. Por otro lado al tener argumentos primero debemos pasarlo a los registros y eso puede liar un poco más, después se invoca a outb o inb y se declara la función como visible globalmente. Luego desde C deberás hacer un header que declare una función «extern». En NASM (Intel Syntax) sería algo así:
      outb:
      push ebp
      mov ebp, esp

      mov eax, [ebp + 12]
      mov edx, [ebp + 8]

      out dx, al

      mov esp, ebp
      pop ebp
      ret

  4.   Max dijo

    Pregunta, la sintaxis de que asm usas? No entiendo porque tantos asm distintos xD MASM, FASM, NASM, AT&T…
    Y si tenes tiempo, podrias explicar la linea:
    asm volatile(«outb %1, %0» : : «dN»(port), «a»(value));

    Hasta «asm volatile» entendi xD «outbyte 1,0?»
    O es 1–>»dN»(port), 0 –> «a»(value)?
    Si es esta ultima, no entiendo que es «dn» y que es «a»…

    Muchas gracias por tus aportess! Increibless!!

    1.    AdrianArroyoCalle dijo

      La sintaxis que uso es AT&T que es la que usa GCC internamente aunque se puede hacer sin problemas en NASM (adaptando la sintaxis). Respecto a esa sentencia compleja de asm volatile solo puedo decirte que es así por el GCC ya que usa unos parámetros para saber como debe pasar el dato. Por ejemplo «a» es un operando especial para x86 que se usa para representar el registro a. La lista entera está aquí: http://gcc.gnu.org/onlinedocs/gcc/Constraints.html#Constraints

  5.   charizardfuego52 dijo

    bien, realmente necesitare ayuda, soy nuevo en esto y no tengo ni la mas minima idea de que hacer con lo escrito en el terminal ¿alguien me ayuda?