Here is the code I am working on! The follow line is used to skip paging:
; TEMPORAL
jmp inicializar_AT
; TEMPORAL
CODE:
; ------------------------------------------
; ------------------------------------------
; Kernel MISIS 1.0. Multitarea. JAER Mex2003
; ------------------------------------------
; ------------------------------------------
; ---------------------------
; ---------------------------
; Objetos globales y externos
; ---------------------------
; ---------------------------
; ----------------------------------
; Constantes utilizadas en el kernel
; ----------------------------------
dir_GDT equ 0x3800 ; Dirección de la GDT (múltiplo
dir_LDT_ini equ 0x34800 ; LDT de las tareas
dir_TSS_ini equ 0x35800 ; TSS de las tareas
dir_KERNEL equ 0x50000 ; Dirección al kernel
tam_KERNEL equ 0x50000 ; Tamaño del kernel
desc_lineal equ 8 ; Descriptor lineal
desc_kernel_c equ 16 ; Descriptor del kernel (código)
desc_kernel_d equ 24 ; Descriptor del kernel (datos)
desc_LDT_ini equ 32 ; 128 descriptores de LDT
desc_TSS_ini equ 1056 ; 128 descriptores de TSS
fin_GDT equ 2080 ; Final de la GDT
; --------------------------
; --------------------------
; Punto de entrada al kernel
; --------------------------
; --------------------------
[BITS 16] ; Inicialmente instrucciones de 16 bits
inicio: ; ------------------------------------
; Deshabilitar interrupciones externas
; ------------------------------------
cli
; ---------------------------------
; Actualizar registros de segmentos
; ---------------------------------
mov ax, cs ; Mover 'cs' a 'ax'
mov ds, ax ; Actualizar el segmento de datos
; --------------------------------
; Crear una pila de datos temporal
; --------------------------------
mov ax, 0x9F00
mov ss, ax
mov sp, 0x1000
; ------------------------------------
; ------------------------------------
; Crear tabla de descriptores globales
; ------------------------------------
; ------------------------------------
; escribir una rutina 'agregar_entrada_GDT' (de cualquier tipo)
crear_GDT: mov si, mensaje_paso_1
call mensaje ; Desplegar mensaje de aviso
; ------------------------------
; Dirección a la GDT del sistema
; ------------------------------
mov ax, 0x380 ; Dirección a la GDT en 0x380(0)
mov gs, ax ; Copiarlo a 'gs'
; ---------------
; Descriptor nulo
; ---------------
mov [gs:0], WORD 0x0000 ; Limite 0-15
mov [gs:2], WORD 0x0000 ; Base 0-15
mov [gs:4], BYTE 0x00 ; Base 16-23
mov [gs:5], BYTE 0x00 ; Tipo, DPL...
mov [gs:6], BYTE 0x00 ; G, Tam-op, Limite 16-19.
mov [gs:7], BYTE 0x00 ; Base 31-24
; -----------------
; Descriptor lineal
; -----------------
mov [gs:0 + desc_lineal], WORD 0xFFFF
mov [gs:2 + desc_lineal], WORD 0x0000
mov [gs:4 + desc_lineal], BYTE 0x00
mov [gs:5 + desc_lineal], BYTE 0x92
mov [gs:6 + desc_lineal], BYTE 0xCF
mov [gs:7 + desc_lineal], BYTE 0x00
; -------------------------------
; Descriptor de código del kernel
; -------------------------------
mov eax, DWORD dir_KERNEL
mov ebx, DWORD tam_KERNEL - 1
mov [gs:0 + desc_kernel_c], bx
mov [gs:2 + desc_kernel_c], ax
shr eax, 16
shr ebx, 16
mov [gs:4 + desc_kernel_c], al
mov [gs:5 + desc_kernel_c], BYTE 0x9A
mov [gs:6 + desc_kernel_c], bl
or [gs:6 + desc_kernel_c], BYTE 0x40 ; G=0, 32Bits
mov [gs:7 + desc_kernel_c], ah
; ------------------------------
; Descriptor de datos del kernel
; ------------------------------
mov eax, DWORD dir_KERNEL
mov ebx, DWORD tam_KERNEL - 1
mov [gs:0 + desc_kernel_d], bx
mov [gs:2 + desc_kernel_d], ax
shr eax, 16
shr ebx, 16
mov [gs:4 + desc_kernel_d], al
mov [gs:5 + desc_kernel_d], BYTE 0x92
mov [gs:6 + desc_kernel_d], bl
or [gs:6 + desc_kernel_d], BYTE 0x40 ; G=0, 32Bits
mov [gs:7 + desc_kernel_d], ah
; -----------------------
; 128 descriptores de LDT
; -----------------------
mov ebx, DWORD desc_LDT_ini ; Primer descriptor LDT
mov ecx, DWORD dir_LDT_ini ; Primer de LDT
.agregar_ldtds mov eax, ecx
mov [gs:0 + ebx], WORD 0x1F ; LDT de 4 descriptores (?)
mov [gs:2 + ebx], ax
shr eax, 16
mov [gs:4 + ebx], al
;mov [gs:5 + ebx], BYTE 0xE2 ; Presente, DPL=3, LDT
mov [gs:5 + ebx], BYTE 0x82 ; Presente, DPL=0, LDT
mov [gs:6 + ebx], BYTE 0x40 ; G=0, TO=32
mov [gs:7 + ebx], ah
add ecx, 32 ; Siguiente LDT
add ebx, 8 ; Siguinete descriptor LDT
cmp ebx, desc_LDT_ini + 0x400 ; = desc_TSS_ini
jb .agregar_ldtds
; -----------------------
; 128 descriptores de TSS
; -----------------------
; ebx = desc_TSS_ini
mov ebx, DWORD desc_TSS_ini ; Primer descriptor TSS
mov ecx, DWORD dir_TSS_ini ; Primer TSS
.agregar_tssds mov eax, ecx
mov [gs:0 + ebx], WORD 0x67 ; Tamaño del TSS
mov [gs:2 + ebx], ax ; Base 0-15
shr eax, 16
mov [gs:4 + ebx], al ; Base 16-23
;mov [gs:5 + ebx], BYTE 0xE9 ; Presente (?), DPL=3, TSS32
mov [gs:5 + ebx], BYTE 0x89 ; Presente (?), DPL=0, TSS32
mov [gs:6 + ebx], BYTE 0x00 ; G=0
mov [gs:7 + ebx], ah ; Base 24-31
add ecx, 104 ; Siguiente TSS
add ebx, 8 ; Siguinete descriptor TSS
cmp ebx, desc_TSS_ini + 0x400 ; = fin_GDT
jb .agregar_tssds
mov si, mensaje_OK
call mensaje
; -----------------------------------
; -----------------------------------
; Iniciar operación en Modo Protegido
; -----------------------------------
; -----------------------------------
activar_MP: mov si, mensaje_paso_2
call mensaje ; Desplegar mensaje de aviso
; ---------------------------------------------------------
; Actualizar el campo 'base' de la imagen del registro GDTR
; ---------------------------------------------------------
mov [gdtr], WORD fin_GDT - 1 ; Tamaño de la GDT
mov [gdtr + 2], DWORD dir_GDT ; Dirección real de la GDT
; -------------------------
; Cargar los registros GDTR
; -------------------------
lgdt [gdtr] ; Cargar el registro GDTR
; ------------------------------------------------------
; Establecer el bit PE (modo protegido) del registro cr0
; ------------------------------------------------------
mov eax, cr0
or al, 1 ; Establacer el bit PE del registro CR0
mov cr0, eax ; Actualizar cr0
; -----------------------------------------------
; Forzar al procesador a cambiar a modo protegido
; -----------------------------------------------
jmp desc_kernel_c:inicio_MP
[BITS 32] ; Inicio de modo protegido en 32 bits!
inicio_MP: ; ------------------------------------------
; Actualizar todos los registros de segmento
; ------------------------------------------
mov ax, desc_kernel_d
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
; --------------------------------------------------
; Actualizar el apuntador 'esp' a la cima de la pila
; --------------------------------------------------
; Limite de desc_kernel_d (!)
mov esp, 0x50000
mov esi, mensaje_OK
call mensaje_mp
; TEMPORAL
jmp inicializar_AT
; TEMPORAL
; -----------------------------
; -----------------------------
; Activar paginación de memoria
; -----------------------------
; -----------------------------
activar_PAG: mov esi, mensaje_paso_2a
call mensaje_mp
; ------------------------------------
; Cargar 'gs' con el descriptor lineal
; ------------------------------------
mov ax, desc_lineal ; Selector lineal
mov gs, ax ; Copiarlo a 'gs'
; -------------------------------------------------
; Primera entrada del directorio de páginas: Kernel
; -------------------------------------------------
mov eax, 0x2000 ; Dirección de la primera tabla de páginas
or eax, 0x0007 ; Nivel usuario, lectura/escritura, presente
mov ebx, 0x1000 ; Desplazamiento a la primera entrada
mov [gs:ebx], eax ; Insertar la entrada
; --------------------
; 1022 entradas libres
; --------------------
add ebx, 4 ; Avanzar a la segunda entrada
mov ecx, 0x3FE ; Establecer un contador en 1022 entradas
.llenar_dp mov [gs:ebx], DWORD 0 ; Insertar entrada nula
add ebx, 4 ; Siguiente entrada
loop .llenar_dp
; --------------------------------------------------------
; Última entada: Utilizada por el administardor de memoria
; --------------------------------------------------------
mov eax, 0x1000 ; Dirección del mismo directorio!
or eax, 0x0007 ; Nivel usuario, lectura/escritura, presente
mov [gs:ebx], eax ; Insertar la entrada
; ------------------------------------------
; Inicializar la tabla de páginas del kernel
; ------------------------------------------
mov ebx, 0x2000 ; Cargar 'ebx' con el desplazamiento a la tabla
mov ecx, 0x400 ; Establecer un contador en 1024 entradas
.llenar_tp_1 mov [gs:ebx], DWORD 0 ; Insertar entrada NULA
add ebx, 4 ; Siguiente entrada
loop .llenar_tp_1
; ---------------------------------------------------
; Mapear el primer MB (+1) del espacio de direcciones
; ---------------------------------------------------
mov ebx, 0x2000 ; Cargar 'ebx' con el desplazamiento a la tabla
mov edx, 0 ; Cargar 'edx' con la primera dirección
mov ecx, 0x101 ; Establecer un contador en 267 entradas (!)
.llenar_tp_2
or edx, 0x0007 ; Nivel usuario, lectura/escritura, presente
mov [gs:ebx], edx ; Insertar entrada
add ebx, 4 ; Siguiente entrada
add edx, 0x1000 ; Siguiente dirección
loop .llenar_tp_2
; -----------------------------------------------------
; Cargar CR3 con la dirección del directorio de páginas
; -----------------------------------------------------
xor eax, eax
mov eax, 0x1000
mov cr3, eax
; -----------------------------------------------
; Establecer el bit PG (paginar) del registro cr0
; -----------------------------------------------
mov eax, cr0
or eax, 0x80000000 ; Establacer el bit PG del registro CR0
mov cr0, eax ; Actualizar cr0
; ----------------------------------------------
; Forzar al procesador a cambiar a modo paginado
; ----------------------------------------------
jmp activar_A20
; ---------------------------------------------------------------------------
; ---------------------------------------------------------------------------
; Activar la linea A20 para tener acceso a toda la memoria de la computadora.
; ---------------------------------------------------------------------------
; ---------------------------------------------------------------------------
activar_A20: mov esi, mensaje_OK
call mensaje_mp
; ...
; ---------------------------
; ---------------------------
; Inicializar área de trabajo
; ---------------------------
; ---------------------------
inicializar_AT: mov esi, mensaje_paso_3
call mensaje_mp
; --------------------------------------------------
; Inicializar en ceros el área de las LDT de usuario
; --------------------------------------------------
mov ebx, DWORD dir_LDT_ini ; Inicio del área de LTD(s)
mov ecx, 0x400 ; Establecer contador
.ini_1 mov [gs:ebx], DWORD 0x00000000 ; Inicializar 4 bytes
add ebx, 4
loop .ini_1
; --------------------------------------------------
; Inicializar en ceros el área de los TSS de usuario
; --------------------------------------------------
mov ebx, DWORD dir_TSS_ini ; Inicio del área de TSS(s)
mov ecx, 0xD00 ; Establecer contador
.ini_2 mov [gs:ebx], DWORD 0x00000000 ; Inicializar 4 bytes
add ebx, 4
loop .ini_2
mov esi, mensaje_OK
call mensaje_mp
; ------------------------------
; ------------------------------
; Iniciar el ambiente multitarea
; ------------------------------
; ------------------------------
primer_tarea: mov esi, mensaje_paso_4
call mensaje_mp
; ------------------------------------
; Cargar 'gs' con el descriptor lineal
; ------------------------------------
mov ax, desc_lineal ; Selector lineal
mov gs, ax ; Copiarlo a 'gs'
; --------------------------------------------------------
; Actualizar los DPL de los descriptor del kernel a ring 0
; --------------------------------------------------------
mov ebx, DWORD dir_GDT + desc_LDT_ini
mov [gs:ebx + 5], BYTE 0x82 ; Presente, DPL=0, LDT
mov ebx, DWORD dir_GDT + desc_TSS_ini
mov [gs:ebx + 5], BYTE 0x89 ; Presente, DPL=0, TSS32
; ---------------------------------------------------
; Cargar el registro de tareas con el TSS del sistema
; ---------------------------------------------------
mov ax, desc_TSS_ini ; 1er descriptor TSS
ltr ax
; ---------------------------------------------------
; Cargar el registro de la LDT con el descriptor nulo
; ---------------------------------------------------
mov ax, 0
lldt ax
mov esi, mensaje_OK
call mensaje_mp
; --------------------------------
; --------------------------------
; Fin de rutinas de inicialización
; --------------------------------
; --------------------------------
; ------------------------------------
; Re-habilitar interrupciones externas (ERROR)
; ------------------------------------
;sti ; Error (!)(?)
; -------------------------
; -------------------------
; Cargar e iniciar el shell
; -------------------------
; -------------------------
otra_tarea: ; ---------------------------------------
; Crear tarea de usuario y brincar a ella
; ---------------------------------------
mov ax, desc_lineal ; Selector lineal
mov gs, ax ; Copiarlo a 'gs'
mov ebx, DWORD dir_TSS_ini ; Dirección a los TSS(s)
add ebx, 104 ; Primer TSS de usuario
mov [gs:ebx + 0x00], DWORD 0
mov [gs:ebx + 0x04], DWORD 0
mov [gs:ebx + 0x08], DWORD 0
mov [gs:ebx + 0x0C], DWORD 0
mov [gs:ebx + 0x10], DWORD 0
mov [gs:ebx + 0x14], DWORD 0
mov [gs:ebx + 0x18], DWORD 0
mov [gs:ebx + 0x1C], DWORD 0x1000 ; PDBR (cr3)
lea eax, [tarea_usuario] ; Punto de entrada
mov [gs:ebx + 0x20], eax ; 'eip'
mov [gs:ebx + 0x24], DWORD 0
mov [gs:ebx + 0x28], DWORD 0
mov [gs:ebx + 0x2C], DWORD 0
mov [gs:ebx + 0x30], DWORD 0
mov [gs:ebx + 0x34], DWORD 0
lea eax, [esp-512] ; Apuntador de la pila
mov [gs:ebx + 0x38], eax ; 'esp' (?)
mov [gs:ebx + 0x3C], DWORD 0
mov [gs:ebx + 0x40], DWORD 0
mov [gs:ebx + 0x44], DWORD 0
mov [gs:ebx + 0x48], WORD desc_kernel_d ; 'es'
mov [gs:ebx + 0x4C], WORD desc_kernel_c ; 'cs'
mov [gs:ebx + 0x50], WORD desc_kernel_d ; 'ss'
mov [gs:ebx + 0x54], WORD desc_kernel_d ; 'ds'
mov [gs:ebx + 0x58], WORD desc_kernel_d ; 'fs'
mov [gs:ebx + 0x5C], WORD desc_kernel_d ; 'gs'
mov [gs:ebx + 0x60], DWORD 0
mov [gs:ebx + 0x64], DWORD 0
; -------------
; Iniciar tarea
; -------------
mov ecx, 0xFF ; Solo funciona 2 veces, ¿por que?
.ciclo jmp desc_TSS_ini+8:0
; Continua aquí después de ir a la tarea de usuario...
loop .ciclo
; ---------------
; Fin del sistema
; ---------------
cli
hlt
; -------------------------------------------------------------------------------------
; TEMPORAL - TEMPORAL - TEMPORAL - TEMPORAL - TEMPORAL - TEMPORAL - TEMPORAL - TEMPORAL
; -----------------
; -----------------
; Tarea del usuario
; -----------------
; -----------------
tarea_usuario: mov esi, mensaje_usuario
call mensaje_mp
jmp desc_TSS_ini:0
; Continua aquí después de ir al kernel...
jmp tarea_usuario ; IMPORTANTE: Repetir la tarea!
; TEMPORAL - TEMPORAL - TEMPORAL - TEMPORAL - TEMPORAL - TEMPORAL - TEMPORAL - TEMPORAL
; -------------------------------------------------------------------------------------
; -------------------
; -------------------
; Estructura de datos
; -------------------
; -------------------
; -----------------------------------
; Imagen de los registros GDTR e IDTR
; -----------------------------------
gdtr dw 0
dd 0
; -------------------------------------------------------
; Mensajes de aviso de paso del proceso de inicialización
; -------------------------------------------------------
mensaje_paso_1 db "Crear tabla GDT ", 0x00
mensaje_paso_2 db "Activar modo protegido ", 0x00
mensaje_paso_2a db "Paginar memoria ", 0x00
mensaje_paso_3 db "Inicializar area de trabajo ", 0x00
mensaje_paso_4 db "Ambiente multitarea ", 0x00
mensaje_usuario db "1 2 3 4 5 6 7 8 9 0 1 ", 0x00
mensaje_OK db "[OK]", 0x0D, 0x0A, 0x00
; -----------------------
; -----------------------
; Rutinas complementarias
; -----------------------
; -----------------------
; ----------------------------------------------------------
; mensaje: Despliega el primer mensaje de aviso en modo real (?)
; ----------------------------------------------------------
mensaje: cld
lodsb ; Cargar 'ax' con 'ds:si' e incrementar 'si' en 1
or al, al ; Verificar si 'al' es cero (fin de la cadena ASCIIZ)
jz .listo ; Si 'al' es cero salir de la rutina
mov ah, 0x0E ; Servicio 0Eh (escribe en un teletipo)
int 0x10 ; Exhibición de video
jmp mensaje ; Siguiente caracter
.listo: ret
; ---------------------------------------------------------
; mensaje_mp: Desplaza el texto de la pantalla hacia arriba
; ---------------------------------------------------------
mensaje_mp: push eax
push ebx
push cx
push dx
push esi
push gs
; ---------------------------------------------
; Cargar 'gs' con el selector de memoria lineal
; ---------------------------------------------
mov ax, desc_lineal
mov gs, ax
; ---------------------------------------------------------
; Calcular la posición del cursor sobre la memoria de video
; ---------------------------------------------------------
xor eax, eax ; Limpiar 'eax'
call posicion_cursor ; Leer la posición actual del cursor
mov cx, ax ; Guardar la posición en 'cx'
shl eax, 1 ; Multiplicar todo por 2
lea ebx, [eax + 0xB8000] ; Cargar la posición en 'ebx'
.leer_desplegar: ; ----------------
; Leer el caracter
; ----------------
cld ; Procesar la cadena de izquierda a derecha (¿Donde va?)
lodsb ; Cargar 'al' con 'ds:esi' e incrementar 'esi'
or al, al ; Verificar si se llegó al fin de la cadena ASCIIZ
jz .listo ; Si 'al' es cero salir de la rutina
; ----------------------------------------------
; Verificar si se llegó al limite de la pantalla
; ----------------------------------------------
cmp cx, 0x7D0 ; Se excedio el límite de la pantalla?
jb .continuar ; Antes se utilizaba 'jne'
call desplazar_texto
push ebx
mov bx, 0x0780 ; Nueva posición del cursor
call mover_cursor ; Mover el cursor
pop ebx
sub ebx, 0x0A0 ; Decrementar el apuntador de memoria
sub cx, 0x050 ; Decrementar el apuntador de pantalla
; ---------------------
; Caracteres especiales
; ---------------------
.continuar: cmp al, 0x0A
je .nueva_linea
cmp al, 0x0D
je .retorno_carro
; --------------------
; Imprimir el caracter
; --------------------
mov byte [gs:ebx], al ; Desplegar el caracter
inc ebx ; Siguiente caracter
mov byte [gs:ebx], 0x007 ; Aplicar atributos de texto
inc ebx ; Siguientes atributos
inc cx ; Contador de caracteres
jmp .leer_desplegar
.nueva_linea: add ebx, 0xA0 ; 160 decimal
add cx, 0x50 ; 80 decimal
jmp .leer_desplegar
.retorno_carro: mov ax, cx ; Posición actual
mov dl, 0x50 ; 80 decimal
div dl ; Dividir entre 80
shr ax, 8 ; Mover 'ah' a 'al'
sub cx, ax ; Restar el residuo a 'cx'
shl ax, 1 ; Multiplicar por 2
sub bx, ax ; Si se puede hacer así (!)
jmp .leer_desplegar
.listo: ; ---------------------------------------
; Mover el caracter al final de la cadena
; ---------------------------------------
mov bx, cx ; Nueva posición del cursor
call mover_cursor ; Mover el cursor
pop gs
pop esi
pop dx
pop cx
pop ebx
pop eax
ret
posicion_cursor: push bx
push dx
xor bx, bx
mov dx, word 0x3D4 ; Numero de puerto (VGA)
mov al, byte 0x0F ; Indice: Byte bajo posición del cursor
out dx, al ; Enviar al puerto
mov dx, word 0x3D5 ; Numero de puerto (VGA datos)
in al, dx ; Leer del puerto
mov bl, al ; Guardar el valor en 'bx'
mov dx, word 0x3D4 ; Numero de puerto (VGA)
mov al, byte 0x0E ; Indice: Byte alto posición del cursor
out dx, al ; Enviar al puerto
mov dx, word 0x3D5 ; Numero de puerto (VGA datos)
in al, dx ; Leer del puerto
mov bh, al ; Guardar el valor en 'bx'
mov ax, bx ; Mover el resultado a 'ax'
pop dx
pop bx
ret
desplazar_texto: push ax
push ebx
push ecx
push dx
push gs
; ---------------------------------------------
; Cargar 'gs' con el selector de memoria lineal
; ---------------------------------------------
mov ax, desc_lineal
mov gs, ax
; --------------------------------------
; Desplazar el texto utilizando un ciclo
; --------------------------------------
mov ebx, 0x0B8000 ; Inicio de la memoria de video
mov ecx, 0x0780 ; Establecer contador en 80*24
; --------------------------------------------------------------
; Copiar cada caracter/atributo de la pantalla un renglón arriba
; --------------------------------------------------------------
.desplazar: mov dx, word [gs:ebx+0x0A0] ; Copiar en 'dx' el caracter/atributo que está 160 casillas adelante
mov [gs:ebx], dx ; Mover el caracter/atributo a la posición apuntada por 'gs:ebx'
inc ebx ; Incrementar 'ebx'
inc ebx ; Incrementar 'ebx'
loop .desplazar
; ----------------------
; Borrar la última línea
; ----------------------
mov ecx, 0x50 ; Establecer contador en 80
.borrar: mov [gs:ebx], word 0x0720 ; Escribir un espacio en la posición apuntada por 'gs:ebx'
inc ebx ; Incrementar 'ebx'
inc ebx ; Incrementar 'ebx'
loop .borrar
pop gs
pop dx
pop ecx
pop ebx
pop ax
ret
mover_cursor: push ax
push dx
mov dx, word 0x3D4 ; Numero de puerto (VGA)
mov al, byte 0x0F ; Indice: Byte bajo posición del cursor
out dx, al ; Enviar al puerto
mov dx, word 0x3D5 ; Numero de puerto (VGA datos)
mov al, bl ; Cargar 'al' con byte bajo
out dx, al ; Enviar al puerto
mov dx, word 0x3D4 ; Numero de puerto (VGA)
mov al, byte 0x0E ; Indice: Byte alto posición del cursor
out dx, al ; Enviar al puerto
mov dx, word 0x3D5 ; Numero de puerto (VGA datos)
mov al, bh ; Cargar 'al' con byte alto
out dx, al ; Enviar al puerto
pop dx
pop ax
ret
verificar_A20: ; ------------------------------------------------------
; Cargar 'fs' con el selector lineal que apunta a 0x0000
; ------------------------------------------------------
mov ax, desc_lineal
mov fs, ax ; Cargar 'fs' con el selector lineal
; --------------------------------------------------------
; Girar mientras no se agote el contador y, fs:0200 apunte
; a la misma dirección de memoria física que fs:100200
; --------------------------------------------------------
mov ax, 0 ; Inicializar 'ax' en cero
.verifiar: inc ax
mov word [fs:0x200], ax ; Mover un dato a fs:0x0200
cmp ax, [fs:0x100200] ; Comparar con fs:0x100200
loope .verifiar ; Girar mientras sean iguales
; ZF -> Línea A20 desactivada
; !ZF -> Línea A20 activa
ret