;Hacer un programa en modo protegido que utilice paginación.
;los primeros 20 MB deben tener "identity mapping" usando paginas
;grandes. El buffer de video debe poder ser accesible desde la
;direccion lineal 0x80000000.
;Las tablas de paginacion deben comenzar a partir de la direccion 0x00500000.
;Se utiliza el "modo flat" para segmentos, esto es, un segmento de codigo de
;4 GB y otro de datos del mismo tamaño.
;
; Directorio de paginas
;   indice  contenido
;   0x3FF  |00000000|   No presente
;    ...      ...    
;   0x200  |00501003|   Tabla de paginas comienza en 0x00501000
;    ...      ...
;   0x004  |01000083|   Pagina de 4 MB supervisor R/W comienza en 16 MB
;   0x003  |00C00083|   Pagina de 4 MB supervisor R/W comienza en 12 MB
;   0x002  |00800083|   Pagina de 4 MB supervisor R/W comienza en 8 MB
;   0x001  |00400083|   Pagina de 4 MB supervisor R/W comienza en 4 MB
;   0x000  |00000083|   Pagina de 4 MB supervisor R/W comienza en 0 MB
;
; Tabla de paginacion
;   indice  contenido
;   0x3FF  |00000000|   No presente
;    ...      ...    
;   0x000  |000B8003|   Pagina de 4 KB supervisor R/W comienza en 0x000B8000
;
; Hecho por Dario Alpern el 1 de mayo de 2013
;
INICIO_TABLAS_PAGINACION EQU 0x00500000     ;5MB.
INICIO_DIR_PAGINAS       EQU INICIO_TABLAS_PAGINACION
INICIO_TABLA_PAGINAS     EQU INICIO_DIR_PAGINAS + 0x1000
DIR_LINEAL_VIDEO         EQU 0x80000000
     ORG 0x8000
     jmp inicio
gdt: dd 0,0
SEL_CODIGO EQU $-gdt
     dw 0xFFFF         ;Bits 15-0 del limite.
     dw 0              ;Bits 15-0 de la base.
     db 0              ;Bits 23-16 de la base.
     db 0x9A           ;Segmento de codigo con DPL=0.
     db 0xCF           ;Bits G=D=1 y bits 19-16 del limite.
     db 0              ;Bits 31-24 de la base.
SEL_DATOS EQU $-gdt
     dw 0xFFFF         ;Bits 15-0 del limite.
     dw 0              ;Bits 15-0 de la base.
     db 0              ;Bits 23-16 de la base.
     db 0x92           ;Segmento de datos con DPL=0.
     db 0xCF           ;Bits G=D=1 y bits 19-16 del limite.
     db 0              ;Bits 31-24 de la base.
longitud_gdt EQU $-gdt
imagen_gdtr:
     dw longitud_gdt-1 ;Limite de la GDT.
     dd gdt            ;Base de la GDT.
texto db "Esto es una prueba.",0

inicio:
     cli                        ;Deshabilitar interrupciones antes de
                                ;entrar a modo protegido.
     cld                        ;Poner a cero flag de direccion para que
                                ;las instrucciones de cadena operen con
                                ;direcciones crecientes.
     lgdt [imagen_gdtr]         ;Cargar base y limite de GDT en el reg. GDTR.
     mov eax,cr0                ;Pasar a modo protegido encendiendo
     or al,1                    ;el bit 0 de CR0.
     mov cr0,eax
     jmp SEL_CODIGO:modo_protegido  ;Saltar al segmento de 32 bits.

     BITS 32
modo_protegido:
     mov ax,SEL_DATOS
     mov ds,ax                  ;Segmento de datos de 4 GB.
     mov es,ax
     mov ss,ax
     mov esp,0x8000             ;Poner la pila antes del programa.
     call activar_gate_a20      ;Se van a usar direcciones arriba de 1 MB.
     mov edi,INICIO_TABLAS_PAGINACION ;Apuntar al inicio de la 1ra tabla.
     mov ecx,2*0x0400           ;Cantidad de entradas del directorio y tabla
     xor eax,eax                ;Poner a cero esas entradas.
     rep stosd
     mov esi,INICIO_DIR_PAGINAS ;Apuntar al inicio del directorio de paginas.
     mov ecx,5                  ;Cantidad de entradas a inicializar.
     mov eax,0x00000083         ;Valor de la primera entrada del directorio.
ciclo:
     mov [esi],eax              ;Inicializar entrada del directorio.
     add esi,4                  ;Apuntar a la siguiente entrada.
     add eax,0x00400000         ;Calcular siguiente valor.
     loop ciclo
     mov dword [INICIO_DIR_PAGINAS+0x800],0x00501003;Apuntar a tabla de pags.
     mov dword [INICIO_TABLA_PAGINAS],0x000B8003 ;Apuntar a buffer de video.
     mov eax,INICIO_TABLAS_PAGINACION
     mov cr3,eax                ;Apuntar a directorio de paginas.
     mov eax,cr4                ;Activar el bit Page Size Enable (bit 4
     or al,0x10                 ;de CR4) para habilitar las paginas grandes.
     mov cr4,eax
     mov eax,cr0                ;Activar paginacion encendiendo el
     or eax,0x80000000          ;bit 31 de CR0.
     mov cr0,eax
     mov edi,DIR_LINEAL_VIDEO   ;Apuntar a buffer de video
     mov ecx,25*80              ;Cantidad de caracteres en el buffer.
     mov ax,0x0720              ;Espacio con atributo blanco sobre negro.
     rep stosw                  ;Limpiar la pantalla.
     mov esi,texto              ;Apuntar al texto a mostrar.
     mov edi,DIR_LINEAL_VIDEO   ;Apuntar a buffer de video
ciclo_mostrar_caracteres:
     movsb                      ;Copiar el caracter en pantalla.
     inc edi                    ;Saltear atributo
     cmp byte [esi], 0          ;Verificar si se termino el string.
     jnz ciclo_mostrar_caracteres ;Saltar si no es asi.
modo_halted:
     hlt                        ;Pasar a modo halted.
     jmp modo_halted

activar_gate_a20:
     call empty_8042            ;Esperar hasta que se pueda acceder al 8042.
     mov al,0xD1                ;Comando de escritura al controlador.
     out 0x64,al
     call empty_8042            ;Esperar hasta que se pueda acceder al 8042.
     mov al,0xDF                ;Encender bit Gate A20.
     out 0x64,al
     call empty_8042            ;Esperar hasta que se pueda acceder al 8042.
     ret

empty_8042:
     in al,0x64
     test al,2
     jnz empty_8042
     ret
