Escribir su propio sistema operativo de juguete (Parte I)
Este artículo es un tutorial práctico para la construcción de un pequeño sector de arranque. En la primera sección de la teoría detrás de lo que ocurre en el momento que se enciende el ordenador. También explica nuestro plan. La segunda sección se dice todas las cosas que usted debe tener a mano antes de seguir adelante, y la tercera sección se ocupa de los programas. Nuestro programa de inicio poco en realidad no arrancar Linux, pero va a mostrar algo en la pantalla.
1. Fondo
1.1 El disfraces
El microprocesador controla el ordenador. En el inicio, cada microprocesador es más que otro 8086. A pesar de que puede haber una nueva marca Pentium, sólo tendrá la capacidad de un 8086. Desde este punto, podemos utilizar algún procesador de software y cambiar a la infame modo protegido . Sólo entonces podemos utilizar el poder completo del procesador.
1.2 Nuestro Papel
Inicialmente, el control está en manos de la BIOS . Esto no es sino una colección de programas que se almacenan en la memoria ROM. BIOS realiza el POST (Power On Self Test). Esto comprueba la integridad de la computadora (si los periféricos están funcionando correctamente, si el teclado está conectado, etc.) Esto es cuando usted oye los sonidos de la computadora. Si todo está bien, el BIOS selecciona el dispositivo de arranque. Copia el primer sector (sector de arranque) del dispositivo, para hacer frente a la ubicación 0x7C00 . El control se transfiere a este lugar. El dispositivo de arranque puede ser un disquete, CD-ROM, disco duro u otro dispositivo de su elección. Aquí tomaremos el dispositivo de arranque a un disquete. Si hubiéramos escrito algo de código en el sector de arranque de los disquetes, el código se ejecutará ahora. Nuestro papel es claro: sólo escribe algunos programas para el sector de arranque de los disquetes.
1.3 El Plan
Primero escribe un pequeño programa en 8086 de la Asamblea (no te asustes, yo te enseñaré cómo se escribe), y copiarlo en el sector de arranque de los disquetes. Para copiar, vamos a código de un programa de C.Inicie el equipo con el disquete, y luego disfrutar.
2. Cosas Que Usted Debe Tener
as86Este es un ensamblador. El código en ensamblador que escribir se convierte en un archivo objeto con esta herramienta.
ld86Este es el enlazador. El código objeto generado por as86 se convierte en código real en lenguaje de máquina por esta herramienta. El lenguaje de máquina será en una forma que entiende 8086.
gccEl compilador de C. Por ahora tenemos que escribir un programa en C para transferir nuestro sistema operativo en el disquete.
Un disquete libreUn disquete se utiliza para almacenar el sistema operativo. Esto también es nuestro dispositivo de arranque.
Caja de Good Old Linux¿Sabes lo que es.
as86 y ld86 será en la mayoría de las distribuciones estándar. Si no, siempre puede obtener desde el sitio . Ambos se incluyen en el paquete, bin86. Una buena documentación está disponible en www.linux.org/docs/ldp/howto/Assembly-HOWTO/as86.html .3. 1, 2, 3, Start!
3.1 El sector de arranque
Coge tu editor favorito y escriba en estas líneas.
0xb800 es la dirección de la memoria de vídeo. El # es para lo que representa un valor inmediato. Después de la ejecución de
La idea de memoria de vídeo puede no ser muy claro, así que me explique con más detalle. Supongamos que asumimos la pantalla consta de 80 columnas y 25 filas. Así, por cada línea tenemos que 160 bytes, uno para cada personaje y un atributo para cada personaje. Si tenemos que escribir algunos caracteres en la columna 3, entonces tenemos que saltar bytes 0 y 1, ya que es para la 1 ª columna, 2 y 3 como lo son para la 2 ª columna, y luego escribir nuestro valor ascii para el 4 º byte y su atribuyen a la ubicación quinto en la memoria de vídeo.
3.2 Redacción del sector de arranque de disquete
Tenemos que escribir un programa en C que las copias de nuestro código (sistema operativo de código) para el primer sector del disquete. Aquí está:
Las últimas cuatro líneas de código abierto, la unidad de disquetes (que en su mayoría será / dev/fd0). Lleva la cabeza hasta el comienzo de un archivo usando [code]lseek[/code] , y luego escribe los 512 bytes del buffer al disco.
Las páginas del manual de leer, escribir, abrir y lseek (se refieren al hombre 2) le dan suficiente información sobre lo que los demás parámetros de esas funciones y cómo usarlos. Hay dos líneas en el medio, que puede ser un poco misteriosa. Las líneas:
Para que los ejecutables de este archivo tiene que escribir lo siguiente en el indicador bash de Linux.
Inserte un disquete vacío en la disquetera y el tipo
A partir de aquí, vamos a querer insertar más código en nuestro programa de sector de arranque, para que haga las cosas más complejas (como el uso de interrupciones de la BIOS, en modo protegido de conmutación, etc.) La parte final (Parte II, Parte III, etc) de este artículo le guiará en nuevas mejoras.
[size=18]Parte I[/size]
La siguiente cosa que nadie debe saber después de aprender a hacer un sector de arranque y antes de cambiar al modo protegido es, el uso de las interrupciones del BIOS. Interrupciones del BIOS son las rutinas de bajo nivel proporcionada por el BIOS para hacer la obra del creador del sistema operativo fácil. Esta parte del artículo se ocupará de las interrupciones del BIOS.
1. Teoría
1.1 ¿Por qué la BIOS?
BIOS es la copia del sector de arranque en la RAM y la ejecución de código allí. Además de esto hay muchas cosas que la BIOS no. Cuando uno inicia el sistema operativo hasta que no tiene un controlador de vídeo o una unidad de disquete o cualquier otro conductor como tal. Para incluir a cualquier conductor, en el sector de arranque es casi imposible. Así que de otra manera debería estar allí. El BIOS viene en nuestra ayuda aquí.BIOS contiene varias rutinas que podemos utilizar. Por ejemplo, hay lista de rutinas disponibles para diversos fines, como, la comprobación de los equipos instalados, el control de la impresora, etc descubrir tamaño de la memoria Estas rutinas son lo que llamamos interrupciones del BIOS.
1.2 ¿Cómo invocar interrupciones de la BIOS?
En lenguajes de programación ordinaria invocar una rutina de hacer una llamada a la rutina. Por ejemplo, en un programa en C, si no hay una rutina por el nombre de
Por ejemplo, para imprimir algo en la pantalla llamamos a la función C de esta manera:
Antes de llamar a la interrupción del BIOS, tenemos que cargar ciertos valores en el formato de pre-especificados en los registros. Supongamos que estamos utilizando 13h de interrupción del BIOS, que es para transferir los datos desde el disco a la memoria. Antes de llamar interrupción 13h tenemos que especificar la dirección del segmento en que los datos se copian. También tenemos que pasar como parámetros el número de unidad, número de pista, el número de sector, el número de sectores a ser transferidos, etc Esto lo hacemos mediante la carga de los registros predefinidos con los valores necesarios. La idea será clara después de leer la explicación en el sector de arranque que se va a construir.
Una cosa importante es que la interrupción del mismo puede ser utilizado para una variedad de propósitos. El propósito para el cual se utiliza una interrupción en particular depende del número de la función seleccionada.La elección de la función se realiza en función del valor actual en el
2. ¿Qué vamos a hacer?
Esta vez el código fuente se compone de dos programas en lenguaje ensamblador y un programa de C. Archivo de ensamblado primero es el código de sector de arranque. En el sector de arranque que hemos escrito el código para copiar el segundo sector del disco flexible en el segmento de memoria
3. El sector de arranque
Utilizando interrupción 13h, el sector de arranque carga el segundo sector del disquete en posición de memoria 0x5000 (la dirección del segmento 0x500). A continuación se realiza el código fuente usado para este propósito. Guarde el código de archivo
A continuación se carga el número de unidad en
Moviendo el valor 2 en el registro
Ahora llamamos interrupción 13h y, finalmente, pasar a 0 ª de compensación en el segmento de
4. El segundo sector
El código en el segundo sector que se indican a continuación:
5. El programa C
El código fuente del programa C es la siguiente. Guardarlo en un archivo de
Usted puede descargar el código fuente de
bsect.s sect2.s write.c Makefile Quitar la extensión txt de los archivos, y el tipo
Después de arrancar con el disquete que se puede ver la cadena que se muestra. Así, se han utilizado las interrupciones de la BIOS. En la siguiente parte de esta serie espero escribir acerca de cómo podemos cambiar el procesador a modo protegido.
En las partes I y II de esta serie, se analizó el proceso de utilización de las herramientas disponibles con Linux para construir un sector de arranque simple y acceder a la BIOS del sistema. Nuestro juguete OS estará estrechamente el modelo de un 'histórico' del kernel de Linux - lo que tenemos que cambiar a modo protegido real pronto! Esta parte muestra cómo se puede hacer.
1. ¿Cuál es el modo protegido ?
El + 80 386 proporciona muchas características nuevas para superar las deficiencias del 8086 que casi no tiene apoyo para la protección de la memoria, la memoria virtual, multitarea, o de la memoria por encima de 640K - y siguen siendo compatibles con la familia 8086. El 386 tiene todas las características de los 8086 y 286, con muchas mejoras más. Al igual que en los procesadores anteriores, no es el modo real. Al igual que el 286, el 386 puede operar en modo protegido. Sin embargo, el modo protegido en 386 es muy diferente internamente. El modo protegido en el 386 ofrece protección contra el mejor programador y más memoria que en el 286. El propósito del modo protegido no es proteger a su programa. El propósito es proteger a todos los demás (incluyendo el sistema operativo) de su programa.
1.1 Modo protegido vs Real Modo
Modo superficial protegido y modo real no parecen ser muy diferentes. Tanto la segmentación memeory uso, las interrupciones y los controladores de dispositivos para manejar el hardware. Sin embargo, existen diferencias que justifiquen la existencia de dos modos distintos. En el modo real, podemos ver la memoria como 64k 16bytes Atleast segmentos separados. La segmentación se realiza a través del uso de un mecanismo interno en relación con los registros de segmento. El contenido de estos registros de segmento (CS, DS, SS ...) forman parte de la dirección física que los lugares de la CPU en el bus de direcciones de. La dirección física es generado por la multiplicación del registro de segmento por 16 y luego agregar un poco de compensación 16. Esta es la 16 bits de desplazamiento que nos limita a 64k segmentos.
Fig. 1: modo real de direccionamiento
En modo protegido, la segmentación se define a través de un conjunto de tablas llamadas tablas de descriptores. Los registros de segmento contienen punteros en estas tablas. Hay dos tipos de tablas que se utilizan para definir la segmentación de la memoria: la tabla de descriptores globales y la tabla de descriptores locales. El GDT contiene los descriptores básicos que todas las aplicaciones pueden tener acceso. En el modo real de un segmento es grande 64k seguido por otro en una distancia de 16 bytes. En modo protegido que puede tener un segmento tan grande como el de 4 Gb y lo podemos poner donde queramos. La LDT contiene información de segmentación específica para una tarea o un programa. Un sistema operativo, por ejemplo, podría crear una GDT con los descriptores de su sistema y para cada tarea un LDT con descriptores apropiados. Cada descriptor es de 8 bytes de longitud. El formato se presenta a continuación (Fig. 3). Cada vez que se carga un registro de segmento, la dirección base se recupera de la entrada de la tabla correspondiente. El contenido del descriptor se almacena en un registro llamado programador invisible sombra registros para que las futuras referencias al mismo segmento puede utilizar esta información en lugar de referencia a la tabla cada vez. La dirección física es formada por la adición de los 16 o 32 bits de compensación a la dirección base en las diferencias de sombras register.These se ponen de manifiesto en las figuras 1 y 2.
Fig. 2: Abordar el modo protegido
Fig. 3: Formato del segmento descriptor
Tenemos otra tabla llamada tabla de descriptores de interrumpir o de la IDT. La IDT contiene los descriptores de interrupción. Estos se usan para indicar que el procesador donde encontrar el manejador de interrupciones. Contiene una entrada por cada interrupción, al igual que en modo real, pero el formato de estas entradas es totalmente diferente. No estamos utilizando la IDT en nuestro código para cambiar al modo protegido para obtener más detalles no se les da.
2. Entrar en modo protegido
El 386 tiene cuatro registros 32 bit de control llamado CR0, CR1, CR2 y CR3. CR1 se reserva para los procesadores futuros, y no está definido para el 386. CR0 contiene bits que activar y desactivar la paginación y la protección y los bits que controlan el funcionamiento del coprocesador de punto flotante. CR2 y CR3 son utilizados por el mecanismo de paginación. Estamos preocupados con el bit 0 del registro CR0 o el bit PE o la protección de los bit de habilitación. Cuando PE = 1, el procesador se dice que operan en modo protegido con el mecanismo de segmentación que hemos descrito anteriormente. Si PE = 0, el procesador funciona en modo real. El 386 también tiene la base de la mesa de segmentación registros como GDTR, LDTR y IDTR.These registros segmentos de direcciones que contienen las tablas de descriptores. Los puntos GDTR a la GDT.El GDTR de 48 bits define la base y el límite de la GDT directamente con una dirección de 32 bits lineales y un límite de 16 bits.
Cambiar al modo protegido esencialmente implica que el bit PE. Pero hay algunas otras cosas que debemos hacer. El programa debe inicializar los segmentos del sistema y registros de control. Inmediatamente después de establecer el bit PE a una que tiene que ejecutar una instrucción de salto para limpiar el canal de ejecución de las instrucciones que pueden haber sido obtenidos en el modo real. Este salto es por lo general a la siguiente instrucción. Los pasos para cambiar a modo protegido se reduce a lo siguiente:
Construcción de la GDT.Activar el modo protegido al establecer el bit PE en CR0.Saltar a borrar la cola de prefetch.Ahora vamos a dar el código para realizar este cambio.
3. Lo que necesitamos
un disquete en blancoNASM ensamblador Haga clic aquí para descargar el código. Escriba el código en un archivo por abc.asm nombre. Montarlo con el comando nasm abc.asm . Esto producirá un archivo llamado ABC. A continuación, inserte el disquete y escriba el siguiente comando dd if = abc of = / dev/fd0. Este comando escribirá el archivo abc en el primer sector del disquete. A continuación, reinicie el sistema. Usted debe ver la siguiente secuencia de mensajes.
Nuestro arranque del sistema operativo ........................A (color marrón)Cambiar al modo protegido ....A (color blanco)4. El código que hace todo!
En primer lugar, voy a dar el código para realizar el cambio. Esta es seguida por una explicación detallada.
Como se menciona en el artículo anterior (Parte 1) el BIOS selecciona el dispositivo de arranque y coloca el primer sector en la dirección 0x7C00. De esta manera empezar a writung nuestro código en 0x7c00.This es lo que está implícito en la directiva org.
FUNCIONES UTILIZADAS
print_mesg : Esta rutina utiliza la subfunción 13h de la interrupción de la BIOS 10h a escribir una cadena en los atributos screen.The se especifican mediante la colocación de los valores adecuados en los distintos registros. 10h de interrupción se utiliza para almacenar diversos manipulations.We la cadena 13h número subfn de ah que se especifica que se quiere imprimir una cadena. Bit 0 del registro al que determina la posición del cursor al lado, y si es 0 se vuelve al principio de la siguiente línea después de la llamada a la función, si es 1, el cursor se coloca inmediatamente después del último carácter impreso.
La memoria de vídeo se divide en varias páginas de visualización de vídeo llamada pages.Only una página se puede visualizar a la vez (Para más detalles sobre la memoria de vídeo se refieren Parte 1). El contenido de bh indica el número de página, bl especifica el color del carácter se va a imprimir. cx tiene la longitud de la cadena que se printed.Register dx especifica la posición del cursor. Una vez que todos los atributos se han inicializado llamamos interrupción de la BIOS 10h.
get_key : Nosotros usamos 16 horas de interrupción del BIOS cuya 00h subfunción se utiliza para obtener el siguiente carácter de la pantalla. Registro AH contiene el número subfn.
ClrScr : Esta función utiliza otra subfn de int 06h 10h es decir, para borrar la pantalla antes de imprimir un string.To indicarlo inicializamos al que 0.Registers cx y dx especificar el tamaño de la ventana que desea borrar, en este caso es todo el pantalla. Registrarse bh indica el color con el que la pantalla tiene que ser llenado, en este caso es negro.
Donde todo comienza!
La declaración de la Asamblea el primer idioma es un salto corto a la code.We begin_boot la intención de imprimir 'Ain en modo real, establecer un GDT, cambiar a modo protegido e imprimir un blanco "de un marrón A'. Estos dos modos de utilizar sus propia frente a los métodos.
En modo real :
Usamos registro de segmento gs a punto de video memory.We utilizar un adaptador CGA (dirección predeterminada de base de 0xb8000). Pero bueno tenemos un 0 falta en el code.Well la unidad de segmentación en modo real ofrece la 0.This adicionales es una cuestión de conveniencia, como el 8086 por lo general hace un manipulation.This dirección de 20bit se ha trasladado en el modo real de direccionamiento del valor 386.The ASCII para A es 0x41, 0x06 indica que necesitamos una pantalla de color marrón character.The se mantiene hasta se pulsa una key.Next vamos a mostrar un mensaje en la pantalla diciendo que vamos a proteger el mundo de la mode.So señalemos la BP (apuntador base de registrarse para el mensaje que desea imprimir).
Plataforma de lanzamiento para el modo protegido :
No necesitamos ningún interrumpe molestando, mientras que en modo protegido que hacemos? Así que vamos a desactivarlos (interrupciones que es). Eso es lo que hace de la CLI. Nosotros les permitirá later.So vamos a empezar por la creación de la GDT.We inicializar cuatro descriptores en nuestro intento de cambiar a modo protegido. Estos descriptores inicializamos nuestro segmento de código (code_gdt), los datos y los segmentos de pila (data_gdt) y el segmento de video con el fin de acceder a la memoria de vídeo. Un descriptor de maniquí también es inicializado a pesar de que nunca se usa, excepto si se quiere, por supuesto, culpa triple. Este es un descriptor nulo. Vamos a investigar algunos de los campos del segmento descriptor.
La primera palabra tiene el límite del segmento, que por simplicidad se le asigna el máximo de FFFF (4G). Para el segmento de video que establecer un valor predefinido de 3999 (80 cols * 25 * 2bytes filas - 1).La dirección base de los segmentos de código y datos se establece en 0x0000. Para el segmento de video que es 0xb8000 (memoria de vídeo dirección base).La dirección base GDT tiene que ser cargado en GDTR sistema de registro. El segmento de GDTR se carga con el tamaño de la GDT en la primera palabra y la dirección de base en el valor DWORD siguiente. La instrucción lgdt a continuación, carga el segmento de GDT en la register.Now GDTR estamos listos para cambiar realmente a pmode. Empezamos por establecer el bit menos significativo de CR0 a 1 (es decir, el bit PE). Aún no estamos en modo protegido completo!
Sección 10.3 de la Referencia del Intel 80386 Manual del Programador de 1986 establece lo siguiente: Inmediatamente después de establecer el indicador de educación física, el código de inicialización debe limpiar la cola del procesador de instrucciones de captación previa por la ejecución de un instruction.The JMP 80.386 recupera y decodifica instrucciones y direcciones antes de ser utilizados, sin embargo, después de un cambio en el modo protegido, la información de la instrucción prefetched (que se refiere al modo real-dirección) ya no es válida. Un JMP obliga al procesador para descartar la información no válida.
Estamos ahora en modo protegido. ¿Quieres comprobarlo? Vamos a nuestro A impreso en blanco. Para esto, inicializar los datos y los segmentos más con el segmento de datos de selección (datasel). Inicializar gs con el selector de segmento de video (videosel). Para mostrar un blanco 'A' mover una palabra que contiene el valor ASCII y el atributo de ubicación [GS: 0000] es decir, B8000: 0000. El bucle de giro conserva el texto en la pantalla hasta que se reinicia el sistema.
La instrucción de veces que se utiliza para rellenar 0s en los bytes no utilizada de la sector.To indican que este es un sector de arranque escribimos AA55, en bytes, 511,512. Eso es todo.
Este artículo es un tutorial práctico para la construcción de un pequeño sector de arranque. En la primera sección de la teoría detrás de lo que ocurre en el momento que se enciende el ordenador. También explica nuestro plan. La segunda sección se dice todas las cosas que usted debe tener a mano antes de seguir adelante, y la tercera sección se ocupa de los programas. Nuestro programa de inicio poco en realidad no arrancar Linux, pero va a mostrar algo en la pantalla.
1. Fondo
1.1 El disfraces
El microprocesador controla el ordenador. En el inicio, cada microprocesador es más que otro 8086. A pesar de que puede haber una nueva marca Pentium, sólo tendrá la capacidad de un 8086. Desde este punto, podemos utilizar algún procesador de software y cambiar a la infame modo protegido . Sólo entonces podemos utilizar el poder completo del procesador.
1.2 Nuestro Papel
Inicialmente, el control está en manos de la BIOS . Esto no es sino una colección de programas que se almacenan en la memoria ROM. BIOS realiza el POST (Power On Self Test). Esto comprueba la integridad de la computadora (si los periféricos están funcionando correctamente, si el teclado está conectado, etc.) Esto es cuando usted oye los sonidos de la computadora. Si todo está bien, el BIOS selecciona el dispositivo de arranque. Copia el primer sector (sector de arranque) del dispositivo, para hacer frente a la ubicación 0x7C00 . El control se transfiere a este lugar. El dispositivo de arranque puede ser un disquete, CD-ROM, disco duro u otro dispositivo de su elección. Aquí tomaremos el dispositivo de arranque a un disquete. Si hubiéramos escrito algo de código en el sector de arranque de los disquetes, el código se ejecutará ahora. Nuestro papel es claro: sólo escribe algunos programas para el sector de arranque de los disquetes.
1.3 El Plan
Primero escribe un pequeño programa en 8086 de la Asamblea (no te asustes, yo te enseñaré cómo se escribe), y copiarlo en el sector de arranque de los disquetes. Para copiar, vamos a código de un programa de C.Inicie el equipo con el disquete, y luego disfrutar.
2. Cosas Que Usted Debe Tener
as86Este es un ensamblador. El código en ensamblador que escribir se convierte en un archivo objeto con esta herramienta.
ld86Este es el enlazador. El código objeto generado por as86 se convierte en código real en lenguaje de máquina por esta herramienta. El lenguaje de máquina será en una forma que entiende 8086.
gccEl compilador de C. Por ahora tenemos que escribir un programa en C para transferir nuestro sistema operativo en el disquete.
Un disquete libreUn disquete se utiliza para almacenar el sistema operativo. Esto también es nuestro dispositivo de arranque.
Caja de Good Old Linux¿Sabes lo que es.
as86 y ld86 será en la mayoría de las distribuciones estándar. Si no, siempre puede obtener desde el sitio . Ambos se incluyen en el paquete, bin86. Una buena documentación está disponible en www.linux.org/docs/ldp/howto/Assembly-HOWTO/as86.html .3. 1, 2, 3, Start!
3.1 El sector de arranque
Coge tu editor favorito y escriba en estas líneas.
[code]inicio entrada de inicio: mov ax, # 0xb800 mov es, ax mov es seg [0], # 0x41 seg es mov [1], # 0x1f loop1: jmp loop1
[/code]Este es un lenguaje ensamblador as86 que va a entender. La primera declaración especifica el punto de entrada, donde el control debe entrar en el programa. Estamos afirmando que el control inicial debe ir a la etiqueta[code]inicio[/code] . La 2 ª línea muestra la ubicación de la etiqueta de [code]inicio[/code] (no se olvide de poner ":" después de la salida). La primera sentencia que se ejecutará en este programa es la declaración poco después de comenzar .
0xb800 es la dirección de la memoria de vídeo. El # es para lo que representa un valor inmediato. Después de la ejecución de
[code]mov ax, # 0xb800
[/code]registro AX contendrá el valor 0xb800, es decir, la dirección de la memoria de vídeo. Ahora nos trasladamos este valor en el [code]es[/code] registro. [code]es[/code] sinónimo de el segmento extra de registro. Recuerde que el 8086 tiene una arquitectura segmentada. Tiene segmentos como segmentos de código, segmentos de datos, segmentos adicionales, etc - por lo tanto, los registros de segmento CS, DS, ES. En realidad, hemos hecho nuestra la memoria de vídeo segmento extra, así que cualquier cosa escrita al segmento extra iría a la memoria de vídeo.Para mostrar cualquier carácter en la pantalla, tienes que escribir dos bytes en la memoria de vídeo. El primero es el valor ASCII que se va a mostrar. El segundo es el atributo del carácter. Atributo tiene que ver con el color que debe utilizarse como el primer plano, que para el fondo, debe abrir y cerrar char y así sucesivamente. [code]seg es[/code] en realidad es un prefijo que indica que la instrucción es que se ejecutará a continuación con referencia a [code]es[/code] sector. Por lo tanto, nos movemos 0x41 valor, que es el valor ASCII del carácter A, en el primer byte de la memoria de vídeo. Lo siguiente que necesitamos para mover el atributo del carácter al siguiente byte. Aquí entramos en 0x1f, que es el valor de representar a un personaje blanco sobre fondo azul. Así que si se ejecuta este programa, tenemos un blanco un sobre un fondo azul. Por último, existe el bucle. Tenemos que detener la ejecución después de la presentación del personaje, o tenemos un bucle que se repite siempre. Guarde el archivo como [code]boot.s[/code] .
La idea de memoria de vídeo puede no ser muy claro, así que me explique con más detalle. Supongamos que asumimos la pantalla consta de 80 columnas y 25 filas. Así, por cada línea tenemos que 160 bytes, uno para cada personaje y un atributo para cada personaje. Si tenemos que escribir algunos caracteres en la columna 3, entonces tenemos que saltar bytes 0 y 1, ya que es para la 1 ª columna, 2 y 3 como lo son para la 2 ª columna, y luego escribir nuestro valor ascii para el 4 º byte y su atribuyen a la ubicación quinto en la memoria de vídeo.
3.2 Redacción del sector de arranque de disquete
Tenemos que escribir un programa en C que las copias de nuestro código (sistema operativo de código) para el primer sector del disquete. Aquí está:
[code]# Include
[/code]En primer lugar, abrir el archivo [code]de arranque[/code] en modo de sólo lectura, y copiar el archivo descripter del archivo abierto a la variable [code]file_desc[/code] . Leer desde el archivo de 510 caracteres o hasta que termine el archivo.Aquí el código es pequeño, por lo que se produce el último caso. Ser decente, cierre el archivo.
Las últimas cuatro líneas de código abierto, la unidad de disquetes (que en su mayoría será / dev/fd0). Lleva la cabeza hasta el comienzo de un archivo usando [code]lseek[/code] , y luego escribe los 512 bytes del buffer al disco.
Las páginas del manual de leer, escribir, abrir y lseek (se refieren al hombre 2) le dan suficiente información sobre lo que los demás parámetros de esas funciones y cómo usarlos. Hay dos líneas en el medio, que puede ser un poco misteriosa. Las líneas:
[code]boot_buf [510] = 0x55; boot_buf [511] = 0xaa;
[/code]Esta información es para la BIOS. Si el BIOS es el reconocimiento de un dispositivo como un dispositivo de arranque, el dispositivo debe tener los valores de 0x55 y 0xaa en la posición 510 ª y 511 ª. Ahora hemos terminado. El programa lee el archivo [code]de arranque[/code] a un buffer llamado boot_buf. Hace los cambios necesarios en 510 ª y 511 ª bytes y luego escribe boot_buf en un disquete. Si se ejecuta el código, los primeros 512 bytes del disco contendrá el código de arranque. Guarde el archivo como [code]write.c[/code] .3.3 Vamos a hacerlo todo
Para que los ejecutables de este archivo tiene que escribir lo siguiente en el indicador bash de Linux.
[code]as86 boot.s-o boot.o ld86-d-o boot.o arranque write.c cc-o escribir
[/code]En primer lugar, nos reunimos el
[i]boot.s[/i]
para formar un archivo objeto [code]boot.o[/code] . A continuación relacionamos este archivo para obtener el archivo final [code]de arranque[/code] . El [code]d-[/code] para ld86 es para la eliminación de todas las cabeceras y la producción de binario puro. La lectura de páginas del manual de as86 y ld86 aclarará cualquier duda. A continuación, compilar el programa C para formar un ejecutable llamado [code]escribir[/code] .
Inserte un disquete vacío en la disquetera y el tipo
[code]. / Escritura
[/code]Reinicie el equipo. Acceder a la configuración del BIOS y hacer flexible la primer dispositivo de arranque. Ponga el disquete en la unidad y ver el arranque del ordenador desde el disquete de arranque.A continuación podrá ver una 'A' (con color de primer plano en blanco sobre fondo azul). Esto significa que el sistema ha arrancado desde el disquete de arranque que hemos hecho y luego ejecuta el programa del sector de arranque que escribimos. Ahora se encuentra en el bucle infinito que había escrito al final de nuestro sector de arranque. Ahora tenemos que reiniciar el equipo y retire el nuestro disco de arranque para iniciar Linux.
A partir de aquí, vamos a querer insertar más código en nuestro programa de sector de arranque, para que haga las cosas más complejas (como el uso de interrupciones de la BIOS, en modo protegido de conmutación, etc.) La parte final (Parte II, Parte III, etc) de este artículo le guiará en nuevas mejoras.
[size=18]Parte I[/size]
La siguiente cosa que nadie debe saber después de aprender a hacer un sector de arranque y antes de cambiar al modo protegido es, el uso de las interrupciones del BIOS. Interrupciones del BIOS son las rutinas de bajo nivel proporcionada por el BIOS para hacer la obra del creador del sistema operativo fácil. Esta parte del artículo se ocupará de las interrupciones del BIOS.
1. Teoría
1.1 ¿Por qué la BIOS?
BIOS es la copia del sector de arranque en la RAM y la ejecución de código allí. Además de esto hay muchas cosas que la BIOS no. Cuando uno inicia el sistema operativo hasta que no tiene un controlador de vídeo o una unidad de disquete o cualquier otro conductor como tal. Para incluir a cualquier conductor, en el sector de arranque es casi imposible. Así que de otra manera debería estar allí. El BIOS viene en nuestra ayuda aquí.BIOS contiene varias rutinas que podemos utilizar. Por ejemplo, hay lista de rutinas disponibles para diversos fines, como, la comprobación de los equipos instalados, el control de la impresora, etc descubrir tamaño de la memoria Estas rutinas son lo que llamamos interrupciones del BIOS.
1.2 ¿Cómo invocar interrupciones de la BIOS?
En lenguajes de programación ordinaria invocar una rutina de hacer una llamada a la rutina. Por ejemplo, en un programa en C, si no hay una rutina por el nombre de
pantalla
con parámetros
noofchar
- número de caracteres que se mostrará,
attr
- atributo de caracteres de la imagen es sólo para llamar a la rutina que se acaba de escribir el nombre de la rutina. Aquí hacemos uso de las interrupciones. Es decir, hacemos uso de instrucciones de montaje
int
.
Por ejemplo, para imprimir algo en la pantalla llamamos a la función C de esta manera:
[code]pantalla (noofchar, attr);
[/code]Equivalente a este, cuando el uso del BIOS, escribimos:
[code]int 0x10
[/code]1.3 Ahora bien, ¿cómo podemos pasar los parámetros?
Antes de llamar a la interrupción del BIOS, tenemos que cargar ciertos valores en el formato de pre-especificados en los registros. Supongamos que estamos utilizando 13h de interrupción del BIOS, que es para transferir los datos desde el disco a la memoria. Antes de llamar interrupción 13h tenemos que especificar la dirección del segmento en que los datos se copian. También tenemos que pasar como parámetros el número de unidad, número de pista, el número de sector, el número de sectores a ser transferidos, etc Esto lo hacemos mediante la carga de los registros predefinidos con los valores necesarios. La idea será clara después de leer la explicación en el sector de arranque que se va a construir.
Una cosa importante es que la interrupción del mismo puede ser utilizado para una variedad de propósitos. El propósito para el cual se utiliza una interrupción en particular depende del número de la función seleccionada.La elección de la función se realiza en función del valor actual en el
ah
registro. Por ejemplo, interrumpir
13h
se puede utilizar para mostrar una cadena, así como para obtener la posición del cursor. Si nos movemos con el valor
3
para registrar
ah
entonces el número de función
3
se selecciona la función que se usa para obtener la posición del cursor. Para la visualización de la cadena que nos movemos
13h
a registrar
ah
, que corresponde a la visualización de una cadena en la pantalla.
2. ¿Qué vamos a hacer?
Esta vez el código fuente se compone de dos programas en lenguaje ensamblador y un programa de C. Archivo de ensamblado primero es el código de sector de arranque. En el sector de arranque que hemos escrito el código para copiar el segundo sector del disco flexible en el segmento de memoria
0x500
(la ubicación de la dirección es 0x5000). Esto lo hacemos con interrupción de la BIOS
13h
. El código en el sector de arranque luego transfiere el control a la posición 0 del segmento de
0x500
. El código en el archivo de la segunda asamblea es para mostrar un mensaje en pantalla con interrupción de la BIOS
10h
. El programa C es para transferir el código ejecutable producido a partir de un archivo de ensamblado de sector de arranque y el código ejecutable a partir del archivo de montaje 2 para el segundo sector del disquete.
3. El sector de arranque
Utilizando interrupción 13h, el sector de arranque carga el segundo sector del disquete en posición de memoria 0x5000 (la dirección del segmento 0x500). A continuación se realiza el código fuente usado para este propósito. Guarde el código de archivo
bsect.s
.
[code]LOC1 = 0x500 entrada de inicio de inicio: mov ax, # LOC1 mov es, ax mov bx, # 0 mov dl, # 0 mov dh, # 0 mov ch, cl mov # 0, # 2 mov al, # 1 mov ah, # 2 int 0x13 jmpi 0, # LOC1
[/code]La primera línea es similar a una macro. Las siguientes dos declaraciones podría ser familiar para usted por ahora. Luego cargamos el valor 0x500 en el
es
registro. Esta es la ubicación de la dirección a la que el código en el segundo sector del disco (el primer sector es el sector de arranque) se movió. Ahora especificamos el desplazamiento dentro del segmento de cero.
A continuación se carga el número de unidad en
dl
registro, número de la cabeza en
dh
registro, número de pista en
ch
registro, número de sector en
cl
registro y el número de sectores que se transfiere al registro
al
.Así que vamos a cargar el sector 2, del número de la pista 0, 0 número de unidad de segmento de
0x500
. Todo esto corresponde a un disquete de 1,44 MB.
Moviendo el valor 2 en el registro
ah
se corresponde con la elección de un número de función. Esto es para elegir entre las funciones previstas por la interrupción 13h. Nosotros elegimos el número de función 2, que es la función que se utiliza para transferir datos desde un disquete.
Ahora llamamos interrupción 13h y, finalmente, pasar a 0 ª de compensación en el segmento de
0x500
.
4. El segundo sector
El código en el segundo sector que se indican a continuación:
[code]entrada de inicio de inicio: mov ah, # 0x03 xor bh, bh int 0x10 mov cx, # 26 mov bx, 0x0007 # mov bp, # mymsg mov ax, 0x1301 # int 0x10 loop1: jmp loop1 mymsg:. 13,10 byte ascii. "Manejo de las interrupciones del BIOS"
[/code]Este código será cargado al segmento de
0x500
y ejecutado. El código utiliza aquí interrupción
10h
para obtener la posición actual del cursor y luego para imprimir un mensaje.Las tres primeras líneas de código (a partir de la 3 ª línea) se utilizan para obtener la posición actual del cursor. Aquí el número 3 de la función de interrupción
13h
es seleccionado. Entonces claro el valor de
BH
registro.Movemos el número de caracteres en la cadena de registro
ch
. Para
bx
movemos el número de página y el atributo que se va a establecer durante la visualización. Aquí estamos pensando en mostrar los caracteres blancos sobre fondo negro. A continuación, la dirección del mensaje que se va a imprimir en mudó a registrar
pb
. El mensaje consta de dos bytes con valores de 13 y 10 que corresponden a un
escriba
, que es el retorno de carro (CR) y el salto de línea (LF) juntos. Luego hay una cadena de 24 caracteres. A continuación, se selecciona la función que corresponde a la impresión de la cadena y luego mover el cursor. Luego llega la llamada a la interrupción. Al final viene el bucle de costumbre.
5. El programa C
El código fuente del programa C es la siguiente. Guardarlo en un archivo de
write.c
.
[code]# Include
[/code]En la PARTE I de este artículo me ha dado la descripción de hacer el disco de arranque. Aquí hay ligeras diferencias. En primer lugar, copie el archivo
bsect
, el código ejecutable producido a partir de
bsect.s
para el sector de arranque. A continuación copiamos el
sect2
el ejecutable correspondiente a
sect2.s
el segundo sector del disquete. También los cambios que deben hacerse a crear el disquete de arranque también se han realizado.6. Descargas
Usted puede descargar el código fuente de
bsect.s sect2.s write.c Makefile Quitar la extensión txt de los archivos, y el tipo
[code]hacer
[/code]en la línea de comandos o puede compilar todo por separado. Tipo
[code]as86 bsect.s-o bsect.o ld86-d-o bsect.o bsect
[/code]y repita el mismo para
sect2.s
dando
sect2
. Compilar write.c y ejecutarlo después de poner el disco de arranque para la unidad, escriba:
[code]write.c cc-o escribir. / escritura
[/code]7. ¿Y ahora qué?
Después de arrancar con el disquete que se puede ver la cadena que se muestra. Así, se han utilizado las interrupciones de la BIOS. En la siguiente parte de esta serie espero escribir acerca de cómo podemos cambiar el procesador a modo protegido.
En las partes I y II de esta serie, se analizó el proceso de utilización de las herramientas disponibles con Linux para construir un sector de arranque simple y acceder a la BIOS del sistema. Nuestro juguete OS estará estrechamente el modelo de un 'histórico' del kernel de Linux - lo que tenemos que cambiar a modo protegido real pronto! Esta parte muestra cómo se puede hacer.
1. ¿Cuál es el modo protegido ?
El + 80 386 proporciona muchas características nuevas para superar las deficiencias del 8086 que casi no tiene apoyo para la protección de la memoria, la memoria virtual, multitarea, o de la memoria por encima de 640K - y siguen siendo compatibles con la familia 8086. El 386 tiene todas las características de los 8086 y 286, con muchas mejoras más. Al igual que en los procesadores anteriores, no es el modo real. Al igual que el 286, el 386 puede operar en modo protegido. Sin embargo, el modo protegido en 386 es muy diferente internamente. El modo protegido en el 386 ofrece protección contra el mejor programador y más memoria que en el 286. El propósito del modo protegido no es proteger a su programa. El propósito es proteger a todos los demás (incluyendo el sistema operativo) de su programa.
1.1 Modo protegido vs Real Modo
Modo superficial protegido y modo real no parecen ser muy diferentes. Tanto la segmentación memeory uso, las interrupciones y los controladores de dispositivos para manejar el hardware. Sin embargo, existen diferencias que justifiquen la existencia de dos modos distintos. En el modo real, podemos ver la memoria como 64k 16bytes Atleast segmentos separados. La segmentación se realiza a través del uso de un mecanismo interno en relación con los registros de segmento. El contenido de estos registros de segmento (CS, DS, SS ...) forman parte de la dirección física que los lugares de la CPU en el bus de direcciones de. La dirección física es generado por la multiplicación del registro de segmento por 16 y luego agregar un poco de compensación 16. Esta es la 16 bits de desplazamiento que nos limita a 64k segmentos.
Fig. 1: modo real de direccionamiento
En modo protegido, la segmentación se define a través de un conjunto de tablas llamadas tablas de descriptores. Los registros de segmento contienen punteros en estas tablas. Hay dos tipos de tablas que se utilizan para definir la segmentación de la memoria: la tabla de descriptores globales y la tabla de descriptores locales. El GDT contiene los descriptores básicos que todas las aplicaciones pueden tener acceso. En el modo real de un segmento es grande 64k seguido por otro en una distancia de 16 bytes. En modo protegido que puede tener un segmento tan grande como el de 4 Gb y lo podemos poner donde queramos. La LDT contiene información de segmentación específica para una tarea o un programa. Un sistema operativo, por ejemplo, podría crear una GDT con los descriptores de su sistema y para cada tarea un LDT con descriptores apropiados. Cada descriptor es de 8 bytes de longitud. El formato se presenta a continuación (Fig. 3). Cada vez que se carga un registro de segmento, la dirección base se recupera de la entrada de la tabla correspondiente. El contenido del descriptor se almacena en un registro llamado programador invisible sombra registros para que las futuras referencias al mismo segmento puede utilizar esta información en lugar de referencia a la tabla cada vez. La dirección física es formada por la adición de los 16 o 32 bits de compensación a la dirección base en las diferencias de sombras register.These se ponen de manifiesto en las figuras 1 y 2.
Fig. 2: Abordar el modo protegido
Fig. 3: Formato del segmento descriptor
Tenemos otra tabla llamada tabla de descriptores de interrumpir o de la IDT. La IDT contiene los descriptores de interrupción. Estos se usan para indicar que el procesador donde encontrar el manejador de interrupciones. Contiene una entrada por cada interrupción, al igual que en modo real, pero el formato de estas entradas es totalmente diferente. No estamos utilizando la IDT en nuestro código para cambiar al modo protegido para obtener más detalles no se les da.
2. Entrar en modo protegido
El 386 tiene cuatro registros 32 bit de control llamado CR0, CR1, CR2 y CR3. CR1 se reserva para los procesadores futuros, y no está definido para el 386. CR0 contiene bits que activar y desactivar la paginación y la protección y los bits que controlan el funcionamiento del coprocesador de punto flotante. CR2 y CR3 son utilizados por el mecanismo de paginación. Estamos preocupados con el bit 0 del registro CR0 o el bit PE o la protección de los bit de habilitación. Cuando PE = 1, el procesador se dice que operan en modo protegido con el mecanismo de segmentación que hemos descrito anteriormente. Si PE = 0, el procesador funciona en modo real. El 386 también tiene la base de la mesa de segmentación registros como GDTR, LDTR y IDTR.These registros segmentos de direcciones que contienen las tablas de descriptores. Los puntos GDTR a la GDT.El GDTR de 48 bits define la base y el límite de la GDT directamente con una dirección de 32 bits lineales y un límite de 16 bits.
Cambiar al modo protegido esencialmente implica que el bit PE. Pero hay algunas otras cosas que debemos hacer. El programa debe inicializar los segmentos del sistema y registros de control. Inmediatamente después de establecer el bit PE a una que tiene que ejecutar una instrucción de salto para limpiar el canal de ejecución de las instrucciones que pueden haber sido obtenidos en el modo real. Este salto es por lo general a la siguiente instrucción. Los pasos para cambiar a modo protegido se reduce a lo siguiente:
Construcción de la GDT.Activar el modo protegido al establecer el bit PE en CR0.Saltar a borrar la cola de prefetch.Ahora vamos a dar el código para realizar este cambio.
3. Lo que necesitamos
un disquete en blancoNASM ensamblador Haga clic aquí para descargar el código. Escriba el código en un archivo por abc.asm nombre. Montarlo con el comando nasm abc.asm . Esto producirá un archivo llamado ABC. A continuación, inserte el disquete y escriba el siguiente comando dd if = abc of = / dev/fd0. Este comando escribirá el archivo abc en el primer sector del disquete. A continuación, reinicie el sistema. Usted debe ver la siguiente secuencia de mensajes.
Nuestro arranque del sistema operativo ........................A (color marrón)Cambiar al modo protegido ....A (color blanco)4. El código que hace todo!
En primer lugar, voy a dar el código para realizar el cambio. Esta es seguida por una explicación detallada.
Como se menciona en el artículo anterior (Parte 1) el BIOS selecciona el dispositivo de arranque y coloca el primer sector en la dirección 0x7C00. De esta manera empezar a writung nuestro código en 0x7c00.This es lo que está implícito en la directiva org.
FUNCIONES UTILIZADAS
print_mesg : Esta rutina utiliza la subfunción 13h de la interrupción de la BIOS 10h a escribir una cadena en los atributos screen.The se especifican mediante la colocación de los valores adecuados en los distintos registros. 10h de interrupción se utiliza para almacenar diversos manipulations.We la cadena 13h número subfn de ah que se especifica que se quiere imprimir una cadena. Bit 0 del registro al que determina la posición del cursor al lado, y si es 0 se vuelve al principio de la siguiente línea después de la llamada a la función, si es 1, el cursor se coloca inmediatamente después del último carácter impreso.
La memoria de vídeo se divide en varias páginas de visualización de vídeo llamada pages.Only una página se puede visualizar a la vez (Para más detalles sobre la memoria de vídeo se refieren Parte 1). El contenido de bh indica el número de página, bl especifica el color del carácter se va a imprimir. cx tiene la longitud de la cadena que se printed.Register dx especifica la posición del cursor. Una vez que todos los atributos se han inicializado llamamos interrupción de la BIOS 10h.
get_key : Nosotros usamos 16 horas de interrupción del BIOS cuya 00h subfunción se utiliza para obtener el siguiente carácter de la pantalla. Registro AH contiene el número subfn.
ClrScr : Esta función utiliza otra subfn de int 06h 10h es decir, para borrar la pantalla antes de imprimir un string.To indicarlo inicializamos al que 0.Registers cx y dx especificar el tamaño de la ventana que desea borrar, en este caso es todo el pantalla. Registrarse bh indica el color con el que la pantalla tiene que ser llenado, en este caso es negro.
Donde todo comienza!
La declaración de la Asamblea el primer idioma es un salto corto a la code.We begin_boot la intención de imprimir 'Ain en modo real, establecer un GDT, cambiar a modo protegido e imprimir un blanco "de un marrón A'. Estos dos modos de utilizar sus propia frente a los métodos.
En modo real :
Usamos registro de segmento gs a punto de video memory.We utilizar un adaptador CGA (dirección predeterminada de base de 0xb8000). Pero bueno tenemos un 0 falta en el code.Well la unidad de segmentación en modo real ofrece la 0.This adicionales es una cuestión de conveniencia, como el 8086 por lo general hace un manipulation.This dirección de 20bit se ha trasladado en el modo real de direccionamiento del valor 386.The ASCII para A es 0x41, 0x06 indica que necesitamos una pantalla de color marrón character.The se mantiene hasta se pulsa una key.Next vamos a mostrar un mensaje en la pantalla diciendo que vamos a proteger el mundo de la mode.So señalemos la BP (apuntador base de registrarse para el mensaje que desea imprimir).
Plataforma de lanzamiento para el modo protegido :
No necesitamos ningún interrumpe molestando, mientras que en modo protegido que hacemos? Así que vamos a desactivarlos (interrupciones que es). Eso es lo que hace de la CLI. Nosotros les permitirá later.So vamos a empezar por la creación de la GDT.We inicializar cuatro descriptores en nuestro intento de cambiar a modo protegido. Estos descriptores inicializamos nuestro segmento de código (code_gdt), los datos y los segmentos de pila (data_gdt) y el segmento de video con el fin de acceder a la memoria de vídeo. Un descriptor de maniquí también es inicializado a pesar de que nunca se usa, excepto si se quiere, por supuesto, culpa triple. Este es un descriptor nulo. Vamos a investigar algunos de los campos del segmento descriptor.
La primera palabra tiene el límite del segmento, que por simplicidad se le asigna el máximo de FFFF (4G). Para el segmento de video que establecer un valor predefinido de 3999 (80 cols * 25 * 2bytes filas - 1).La dirección base de los segmentos de código y datos se establece en 0x0000. Para el segmento de video que es 0xb8000 (memoria de vídeo dirección base).La dirección base GDT tiene que ser cargado en GDTR sistema de registro. El segmento de GDTR se carga con el tamaño de la GDT en la primera palabra y la dirección de base en el valor DWORD siguiente. La instrucción lgdt a continuación, carga el segmento de GDT en la register.Now GDTR estamos listos para cambiar realmente a pmode. Empezamos por establecer el bit menos significativo de CR0 a 1 (es decir, el bit PE). Aún no estamos en modo protegido completo!
Sección 10.3 de la Referencia del Intel 80386 Manual del Programador de 1986 establece lo siguiente: Inmediatamente después de establecer el indicador de educación física, el código de inicialización debe limpiar la cola del procesador de instrucciones de captación previa por la ejecución de un instruction.The JMP 80.386 recupera y decodifica instrucciones y direcciones antes de ser utilizados, sin embargo, después de un cambio en el modo protegido, la información de la instrucción prefetched (que se refiere al modo real-dirección) ya no es válida. Un JMP obliga al procesador para descartar la información no válida.
Estamos ahora en modo protegido. ¿Quieres comprobarlo? Vamos a nuestro A impreso en blanco. Para esto, inicializar los datos y los segmentos más con el segmento de datos de selección (datasel). Inicializar gs con el selector de segmento de video (videosel). Para mostrar un blanco 'A' mover una palabra que contiene el valor ASCII y el atributo de ubicación [GS: 0000] es decir, B8000: 0000. El bucle de giro conserva el texto en la pantalla hasta que se reinicia el sistema.
La instrucción de veces que se utiliza para rellenar 0s en los bytes no utilizada de la sector.To indican que este es un sector de arranque escribimos AA55, en bytes, 511,512. Eso es todo.