Gestión Académica · ASM 8086

Sistema de Gestión Académica en Ensamblador 8086

Una guía pensada para principiantes: aquí se explica cada pieza de código del proyecto, sin dar por sentado que ya sabes ensamblador. Vamos desde qué es un registro hasta cómo se escribe un archivo en disco.

EMU8086 Ensamblador 8086 Programa .COM INT 21h (DOS) Cifrado César

Este programa es un pequeño sistema de consola que permite a un usuario registrar estudiantes con tres notas, guardarlos en un archivo, y luego generar un reporte que cifra el nombre, calcula el promedio y clasifica a cada alumno como aprobado o reprobado.

Aunque el objetivo suena sencillo, hacerlo en ensamblador obliga a resolver a mano cosas que en otros lenguajes son automáticas: leer texto del teclado, convertir dígitos en números, separar campos de una línea, o pedirle al sistema operativo que abra un archivo. Por eso este proyecto es un buen ejercicio de Organización del Computador.

Cómo leer esta documentación

Si nunca has visto ensamblador, empieza por Conceptos básicos. Si ya conoces la teoría y quieres entender el proyecto, ve directo a Infraestructura y luego recorre los archivos en orden desde main.asm.

¿Qué hace, en una frase por opción?

  • Opción 1 – Registrar: pide un nombre y tres notas (0 a 20), las valida y las añade al archivo CALIF.TXT.
  • Opción 2 – Reporte: lee CALIF.TXT, cifra los nombres, calcula promedios y escribe FINAL.XLS con estadísticas.
  • Opción 3 – Ver reporte: muestra en pantalla el contenido de FINAL.XLS.
  • Opción 4 – Salir: termina el programa y devuelve el control a DOS.

Conceptos básicos de ensamblador

El ensamblador es el lenguaje más cercano al procesador. En lugar de escribir promedio = (a+b+c)/3, le damos al procesador instrucciones diminutas: «mueve este valor», «suma», «compara», «salta». Antes de leer el código conviene conocer cinco ideas.

1. Los registros: las «gavetas» del procesador

El procesador 8086 casi no puede trabajar directamente con la memoria; primero copia los valores a unos espacios internos súper rápidos llamados registros. Son pocos y de 16 bits (pueden guardar números de 0 a 65535). Los que verás todo el tiempo:

AX
El «acumulador». Se usa para casi todo: cálculos, resultados, y para decirle a DOS qué función queremos. Se divide en AH (parte alta) y AL (parte baja).
BX
«Base». Aquí se suele guardar el manejador (identificador) de un archivo abierto.
CX
«Contador». Instrucciones como loop lo usan para repetir; también indica «cuántos bytes» leer/escribir.
DX
«Datos». Muy usado para apuntar a la dirección de un texto que queremos imprimir.
SI / DI
Índices de origen y destino. Sirven para recorrer cadenas de caracteres, letra por letra.
DS / ES / CS
Registros de segmento: indican en qué «zona» de memoria estamos leyendo el dato, el destino o el código.

2. Las instrucciones más comunes

InstrucciónQué hace
mov a, bCopia el valor b dentro de a (mover = copiar).
add / subSuma o resta.
mul / divMultiplica o divide (usan AX y DX como pareja para números grandes).
cmp a, bCompara a con b (en realidad resta sin guardar) para decidir un salto.
jmp destinoSalto incondicional: «continúa en esta etiqueta».
je / jne / jb / jaSaltos condicionales: salta si igual / distinto / menor / mayor.
call / retLlama a un procedimiento (subrutina) y regresa al terminar.
push / popGuarda/recupera un valor en la pila, para no perder registros.
lea dx, textoCarga en DX la dirección (offset) de texto, no su contenido.
int 21hPide un servicio al sistema operativo DOS (imprimir, leer, archivos…).

3. Las «banderas», en especial el carry (CF)

Después de muchas operaciones, el procesador enciende o apaga unos interruptores llamados banderas. En este proyecto la estrella es la bandera de acarreo (Carry Flag, CF). Por convención, los procedimientos la usan como un «semáforo» de éxito o error:

  • clc apaga la bandera (CF=0) → «todo salió bien».
  • stc la enciende (CF=1) → «hubo un error».
  • Quien llamó revisa con jc (salta si hubo error) o jnc (salta si no hubo error).

4. INT 21h: hablarle a DOS

El programa no dibuja letras en la pantalla ni abre archivos por sí mismo: se lo pide a DOS. Para eso se coloca un número de función en AH y se ejecuta int 21h. Estas son las funciones que usa el proyecto:

AHServicio
02hImprime un solo carácter (el que esté en DL).
08hLee una tecla sin mostrarla (para «presione una tecla»).
09hImprime un texto que termina en el símbolo $.
0AhLee una línea del teclado dentro de un buffer preparado.
3ChCrea un archivo nuevo (o lo vacía si existía).
3DhAbre un archivo existente (lectura y/o escritura).
3EhCierra un archivo.
3FhLee bytes de un archivo abierto.
40hEscribe bytes en un archivo (o en la pantalla, que es el «archivo 1»).
4202hMueve el puntero de un archivo al final (para anexar).
4C00hTermina el programa y vuelve a DOS.

También se usa una vez int 10h con AX=0003h, que es un servicio de la BIOS para reiniciar el modo de texto 80×25 y, de paso, limpiar la pantalla.

5. Procedimientos, macros y buffers

  • Un procedimiento (PROCENDP) es una subrutina reutilizable, como una función. Se ejecuta con call y vuelve con ret.
  • Una macro (MACROENDM) es una abreviatura: su texto se «pega» tal cual donde la escribes, antes de compilar.
  • Un buffer es simplemente una zona de memoria reservada por adelantado donde se guardan datos temporales (por ejemplo, el texto que el usuario escribe).

Infraestructura del proyecto

El código no está en un solo archivo gigante: se reparte en un archivo principal (main.asm) y seis archivos .inc que se «pegan» dentro de él al compilar mediante la directiva include. Esto mantiene cada tema separado y fácil de leer.

main.asmPunto de entrada. Inicializa la memoria y controla el menú principal.
include/constantes.incNúmeros fijos: tamaños de buffers, límites y los códigos de fin de línea.
include/macros.incDos macros cortas para imprimir texto y saltos de línea.
include/datos.incTodos los mensajes, buffers y variables del programa.
include/entrada_salida.incConsola: imprimir, leer teclado, convertir números, sonidos.
include/archivos.incManejo de archivos DOS: crear, abrir, escribir, leer líneas.
include/academico.incLa lógica del negocio: registrar, cifrar, promediar y reportar.

¿Por qué un programa «.COM» y qué significa org 100h?

Un programa .COM es el formato más simple de DOS: código, datos y pila conviven en un único segmento de 64 KB de memoria. DOS reserva los primeros 256 bytes (256 = 100h en hexadecimal) para información interna, así que el programa comienza en la dirección 100h. La directiva org 100h le dice al ensamblador exactamente eso: «mi primer byte vive en 100h».

Los dos archivos que produce el sistema

CALIF.TXT — entrada

Datos crudos tal como los escribió el usuario. Una línea por alumno:

Juan,15,18,16
Ana,08,09,10

Formato: Nombre,Nota1,Nota2,Nota3

FINAL.XLS — salida procesada

Reporte generado: nombre cifrado, promedio y estado. Es texto CSV que Excel puede abrir:

Mxdq,15,18,16,16,APROBADO
Dqd,08,09,10,09,REPROBADO

Formato: Cifrado,N1,N2,N3,Promedio,Estado

Detalle importante

FINAL.XLS no es un archivo real de Excel: es texto plano con la extensión .XLS. Excel lo abre por compatibilidad, pero no tiene fórmulas ni formato. Es una simplificación consciente del proyecto.

Cómo compilar y ejecutar

  1. Abrir main.asm en EMU8086, manteniendo la carpeta include a su lado.
  2. Pulsar Compile: como usa org 100h, se genera un .COM.
  3. Ejecutarlo desde un directorio con permiso de escritura (ahí se crearán los archivos).

Flujo general del programa

Todo gira en torno a un bucle de menú. El usuario elige una opción, se ejecuta la acción correspondiente, se hace una pausa, y se vuelve a mostrar el menú, una y otra vez, hasta que elige salir.

Inicio · se preparan los segmentos de memoria (DS y ES = CS)
MenuPrincipal · limpia pantalla, muestra menú, lee una tecla
Según la tecla → Registrar / Reporte / Ver / Salir
PausarPantalla · «Presione una tecla…» y vuelve al menú ↺
SalirPrograma · devuelve el control a DOS (int 21h / 4C00h)

Cada acción del menú se apoya en procedimientos más pequeños definidos en los archivos .inc. Ese es el patrón central: funciones pequeñas que se llaman unas a otras, cada una con una única responsabilidad.

main.asm — el corazón del programa

Es el archivo que se compila. Hace tres cosas: reúne todos los demás archivos con include, prepara la memoria, y ejecuta el bucle del menú.

Cabecera e inclusión de archivos

main.asm
org 100h
jmp Inicio

include 'include\constantes.inc'
include 'include\macros.inc'
include 'include\datos.inc'
include 'include\entrada_salida.inc'
include 'include\archivos.inc'
include 'include\academico.inc'
1

org 100h — «este programa empieza en la dirección 100h», requisito de todo programa .COM (ver Infraestructura).

2

jmp Inicio — salta por encima de todos los datos y procedimientos incluidos, para que la ejecución empiece en el código real. Sin este salto, el procesador intentaría «ejecutar» los mensajes de texto como si fueran instrucciones.

3

Los include pegan el contenido de cada archivo aquí, en orden. El orden importa: las constantes y macros deben conocerse antes de usarlas.

Inicialización de segmentos

Inicio:
    push cs
    pop ds
    push cs
    pop es

En un .COM el código y los datos comparten segmento, pero al arrancar solo CS (segmento de código) tiene el valor correcto. Estas cuatro líneas copian CS en DS y en ES usando la pila (no existe un mov ds, cs directo). Así, cuando el programa diga «imprime el texto en tal offset», DS apuntará al lugar correcto y las variables serán legibles.

El menú y sus opciones

MenuPrincipal:
    call LimpiarPantalla
    IMPRIMIR texto_menu
    call LeerOpcionMenu

    cmp al, '1'
    je OpcionRegistrar
    cmp al, '2'
    je OpcionReporte
    cmp al, '3'
    jne VerificarOpcionSalir
    jmp OpcionVerReporte
VerificarOpcionSalir:
    cmp al, '4'
    jne OpcionPrincipalInvalida
    jmp SalirPrograma
1

Se limpia la pantalla, se imprime el menú con la macro IMPRIMIR, y LeerOpcionMenu devuelve en AL la tecla que pulsó el usuario (o 0 si escribió algo raro).

2

Se compara AL con los caracteres '1', '2', '3', '4'. Ojo: se compara con el carácter '1', no con el número 1, porque el teclado entrega caracteres.

3

Cada coincidencia salta (je) a la etiqueta de esa opción. Si no coincide con nada válido, cae en OpcionPrincipalInvalida.

¿Por qué el salto en dos pasos para el 3 y el 4?

Los saltos condicionales del 8086 solo alcanzan destinos cercanos. Como las etiquetas de las opciones están lejos, el código usa un truco: comprueba la condición con un salto corto (jne) y luego usa un jmp largo para llegar al destino real.

OpcionPrincipalInvalida:
    IMPRIMIR msg_opcion_invalida
    call SonidoError
    call PausarPantalla
    jmp MenuPrincipal

OpcionRegistrar:
    call RegistrarAlumno
    call PausarPantalla
    jmp MenuPrincipal

Todas las ramas siguen el mismo patrón: hacer la acción → pausar → volver al menú. OpcionReporte y OpcionVerReporte son idénticas cambiando solo el call. La opción inválida además emite un pitido con SonidoError.

Salir del programa

SalirPrograma:
    call LimpiarPantalla
    IMPRIMIR encabezado_salida
    IMPRIMIR msg_salida
    mov ax, 4C00h
    int 21h

mov ax, 4C00h seguido de int 21h es la forma estándar de terminar: la función 4Ch cierra el programa y el 00 es el «código de salida» (0 = sin errores). El control vuelve a DOS.

constantes.inc — números con nombre

En lugar de escribir números «mágicos» repartidos por el código, se les da un nombre con EQU («equivale a»). EQU no reserva memoria: es solo una etiqueta que el ensamblador reemplaza por su valor al compilar. Cambiar el límite en un solo sitio actualiza todo el programa.

include/constantes.inc
MAXIMO_NOMBRE        EQU 50
MAXIMO_LINEA         EQU 127
TAMANO_BUFFER_VISTA  EQU 512

CR                 EQU 13
LF                 EQU 10
ConstanteSignificado
MAXIMO_NOMBREUn nombre puede tener como máximo 50 caracteres.
MAXIMO_LINEAUna línea leída de un archivo no puede pasar de 127 caracteres.
TAMANO_BUFFER_VISTAAl mostrar el reporte, se lee en bloques de 512 bytes.
CR (13)«Retorno de carro» (Carriage Return): mueve el cursor al inicio de la línea.
LF (10)«Avance de línea» (Line Feed): baja una línea.
CR + LF juntos = un salto de línea

En DOS y Windows, terminar una línea de texto requiere dos bytes: CR (13) y luego LF (10). Por eso a lo largo del proyecto verás CR, LF al final de cada mensaje y de cada registro escrito en los archivos.

macros.inc — abreviaturas de escritura

Una macro es texto que se «expande» (se copia tal cual) donde la invocas. Estas dos evitan repetir las mismas dos líneas cientos de veces. Fíjate que la lógica real vive en procedimientos: la macro solo prepara los registros y llama.

include/macros.inc
IMPRIMIR MACRO mensaje
    lea dx, mensaje
    call ImprimirCadena
ENDM

NUEVA_LINEA MACRO
    call ImprimirNuevaLinea
ENDM
1

IMPRIMIR texto_menu se convierte, al compilar, en lea dx, texto_menu + call ImprimirCadena. La macro recibe un parámetro (mensaje) que se sustituye por el nombre real.

2

NUEVA_LINEA no recibe parámetros: solo llama al procedimiento que imprime CR+LF.

Macro vs. procedimiento

Una macro copia su código en cada uso (más rápido, pero ocupa más espacio). Un procedimiento existe una sola vez y se salta hacia él con call (ahorra espacio). Aquí se combinan: la macro es solo la fachada corta; el trabajo pesado está en el procedimiento.

datos.inc — mensajes, buffers y variables

Aquí no hay instrucciones ejecutables: solo se reserva memoria y se le da valor inicial. La directiva db reserva bytes (define byte) y dw reserva palabras de 16 bits (define word).

Nombres de archivo terminados en cero

include/datos.inc
nombre_archivo_calif db 'CALIF.TXT', 0
nombre_archivo_final db 'FINAL.XLS', 0

Las funciones de archivo de DOS necesitan el nombre terminado en un byte 0 (así saben dónde acaba el texto). A esto se le llama «cadena ASCIIZ».

Mensajes que terminan en «$»

texto_menu          db '====================================', CR, LF
                    db ' SISTEMA DE GESTION ACADEMICA', CR, LF
                    db '====================================', CR, LF
                    db '1. Registrar Alumnos y Notas', CR, LF
                    db '2. Generar Reporte y Estadisticas', CR, LF
                    db '3. Ver Reporte Final (.XLS)', CR, LF
                    db '4. Salir', CR, LF
                    db '------------------------------------', CR, LF
                    db 'Seleccione una opcion: $'

La función de impresión de DOS (AH=09h) escribe carácter por carácter hasta encontrar el símbolo $, que actúa como «fin del mensaje». Por eso cada texto imprimible termina en '$'. El menú se arma con varias líneas db consecutivas: en memoria quedan una detrás de otra formando un único bloque.

El resto del archivo define, con el mismo patrón, los encabezados de cada pantalla y todos los mensajes de error y confirmación (msg_nota_invalida, msg_guardado, msg_falta_calif, etc.).

Cadenas SIN «$» (se escriben por longitud)

coma                db ','
bytes_nueva_linea   db CR, LF
texto_aprobado      db 'APROBADO'
LONGITUD_APROBADO   EQU ($ - texto_aprobado)
texto_reprobado     db 'REPROBADO'
LONGITUD_REPROBADO  EQU ($ - texto_reprobado)

Estos textos van a archivos, no a la pantalla, así que no pueden llevar $ (¡ensuciaría el archivo!). En su lugar, el programa indica cuántos bytes escribir. El truco EQU ($ - texto_aprobado) calcula esa longitud automáticamente: $ significa «la posición actual», así que restarle el inicio del texto da su tamaño (8 para «APROBADO»).

Buffers de entrada

buffer_entrada      db MAXIMO_NOMBRE, 0, (MAXIMO_NOMBRE + 1) dup (0)
buffer_menu         db 2, 0, 3 dup (0)

buffer_linea        db (MAXIMO_LINEA + 1) dup (0)
buffer_visualizacion db TAMANO_BUFFER_VISTA dup (0)
nombre_cifrado      db (MAXIMO_NOMBRE + 1) dup (0)
buffer_numero       db 6 dup (0)
caracter_archivo    db 0

dup significa «duplica»: 3 dup (0) reserva 3 bytes con valor 0. La función de lectura de teclado de DOS (AH=0Ah) exige un buffer con un formato muy específico de tres partes:

Byte 0 — capacidad máxima (cuántos caracteres acepta)
Byte 1 — DOS escribe aquí cuántos caracteres se escribieron realmente
Byte 2 en adelante — los caracteres tecleados

Así, buffer_menu db 2, 0, ... acepta como mucho 2 caracteres; el programa luego revisa el byte 1 para saber cuántos se escribieron. Los demás buffers son zonas de trabajo: la línea leída de un archivo, el bloque de visualización, el nombre ya cifrado, y un pequeño espacio para convertir números a texto.

Variables de trabajo

manejador_archivo   dw 0
manejador_entrada   dw 0
manejador_salida    dw 0
...
nota1                dw 0
nota2                dw 0
nota3                dw 0
promedio             dw 0
contador_total       dw 0
contador_aprobados   dw 0
contador_reprobados  dw 0
contador_invalidos   dw 0

Las variables dw guardan números de 16 bits. Los manejadores (o «handles») son los identificadores que DOS entrega al abrir un archivo; hay que guardarlos para poder escribir, leer o cerrar después. Las notas y el promedio almacenan los valores del alumno que se está procesando, y los contadores llevan las estadísticas del reporte.

entrada_salida.inc — consola, números y sonidos

Este archivo agrupa los procedimientos de más bajo nivel: cómo imprimir, cómo leer del teclado, cómo convertir dígitos en números y viceversa, y cómo emitir un pitido. Casi todo el resto del programa se apoya en estas piezas.

ImprimirCadena e ImprimirNuevaLinea

include/entrada_salida.inc
ImprimirCadena PROC NEAR
    mov ah, 09h
    int 21h
    ret
ImprimirCadena ENDP

El más simple de todos. Espera que DX ya apunte a un texto terminado en $ (la macro IMPRIMIR se encarga de eso). Pone 09h en AH y ejecuta int 21h: DOS imprime el texto. NEAR significa que el procedimiento vive en el mismo segmento que quien lo llama.

ImprimirNuevaLinea PROC NEAR
    push ax
    push dx
    mov ah, 02h
    mov dl, CR
    int 21h
    mov dl, LF
    int 21h
    pop dx
    pop ax
    ret
ImprimirNuevaLinea ENDP

Imprime un salto de línea enviando dos caracteres sueltos (CR y luego LF) con la función 02h. Fíjate en el patrón push al entrar y pop al salir: guarda AX y DX para devolverlos intactos. Así quien lo llamó no pierde lo que tenía en esos registros. Este cuidado se repite en casi todos los procedimientos.

LimpiarPantalla

LimpiarPantalla PROC NEAR
    push ax
    mov ax, 0003h
    int 10h
    pop ax
    ret
LimpiarPantalla ENDP

Usa la interrupción de la BIOS int 10h (no la de DOS) con AX=0003h para reiniciar el modo de texto a 80 columnas × 25 filas en color. Como efecto secundario, esto borra la pantalla, que es justo lo que queremos entre pantalla y pantalla.

LeerEntradaBuffer

LeerEntradaBuffer PROC NEAR
    push ax
    push bx
    mov bx, dx
    mov byte ptr [bx + 1], 0
    mov ah, 0Ah
    int 21h
    pop bx
    pop ax
    ret
LeerEntradaBuffer ENDP

Lee una línea del teclado dentro del buffer al que apunta DX. Antes de leer pone a cero el byte 1 (la cuenta de caracteres) para poder reutilizar el mismo buffer varias veces sin arrastrar la longitud anterior. byte ptr [bx + 1] significa «el byte que está una posición después de donde apunta BX».

LeerOpcionMenu

LeerOpcionMenu PROC NEAR
    push bx
    push dx
    lea dx, buffer_menu
    call LeerEntradaBuffer
    lea bx, buffer_menu
    cmp byte ptr [bx + 1], 1
    jne EntradaMenuInvalida
    mov al, [bx + 2]
    jmp FinEntradaMenu

EntradaMenuInvalida:
    xor al, al

FinEntradaMenu:
    pop dx
    pop bx
    ret
LeerOpcionMenu ENDP
1

Lee la entrada en buffer_menu.

2

Comprueba que el usuario escribiera exactamente un carácter (byte 1 == 1). Si escribió cero o dos, no es una opción válida.

3

Si es válido, copia ese carácter (byte 2) en AL. Si no, xor al, al pone AL en 0 (una forma rápida y clásica de «AL = 0»). El menú principal interpretará ese 0 como «opción inválida».

LeerNumeroBuffer — convertir texto en número

Cuando el usuario teclea «15», en realidad escribe los caracteres '1' y '5', no el número quince. Este procedimiento hace la conversión y, de paso, valida que el resultado esté entre 0 y 20.

LeerNumeroBuffer PROC NEAR
    push bx
    push cx
    push dx
    push si
    push di

    xor ax, ax
    xor cx, cx
    mov cl, byte ptr buffer_entrada[1]
    cmp cx, 0
    jne NumeroTieneEntrada
    jmp NumeroSinSignoInvalido
NumeroTieneEntrada:
    lea si, buffer_entrada[2]

BucleNumeroSinSigno:
    mov dl, [si]
    cmp dl, '0'
    jb NumeroSinSignoInvalido
    cmp dl, '9'
    ja NumeroSinSignoInvalido
    sub dl, '0'
    xor dh, dh
    mov di, dx
    mov bx, 10
    mul bx
    or dx, dx
    jnz NumeroSinSignoInvalido
    add ax, di
    cmp ax, 20
    ja NumeroSinSignoInvalido
    inc si
    loop BucleNumeroSinSigno
    clc
    jmp FinNumeroSinSigno

NumeroSinSignoInvalido:
    stc

FinNumeroSinSigno:
    pop di
    pop si
    pop dx
    pop cx
    pop bx
    ret
LeerNumeroBuffer ENDP

La idea es el algoritmo clásico de conversión decimal: por cada dígito, se multiplica el acumulado por 10 y se le suma el nuevo dígito. Por ejemplo, para «15»: empezamos en 0 → 0×10+1 = 1 → 1×10+5 = 15.

1

CL recibe cuántos caracteres se escribieron. Si es 0 (línea vacía), va directo a «inválido».

2

SI apunta al primer dígito. En el bucle, cada carácter se compara: si es menor que '0' o mayor que '9', no es un dígito → inválido.

3

sub dl, '0' convierte el carácter en su valor numérico ('7' menos '0' = 7). Se guarda en DI temporalmente, porque la multiplicación va a usar DX.

4

mul bx (con BX=10) multiplica AX por 10. Se comprueba or dx, dx / jnz: si el resultado se desbordó a DX, el número es demasiado grande → inválido.

5

Se suma el dígito y se verifica que no pase de 20. loop repite mientras queden caracteres. Al final, clc marca éxito (CF=0) o stc marca error (CF=1).

LeerNota — insiste hasta que la nota sea válida

LeerNota PROC NEAR
    push bx
    push dx
    mov bx, dx

LeerNotaNuevamente:
    mov dx, bx
    call ImprimirCadena
    lea dx, buffer_entrada
    call LeerEntradaBuffer
    call LeerNumeroBuffer
    jnc FinLecturaNota
    IMPRIMIR msg_nota_invalida
    call SonidoError
    jmp LeerNotaNuevamente

FinLecturaNota:
    pop dx
    pop bx
    ret
LeerNota ENDP

Une las piezas anteriores en un bucle de validación. Guarda en BX el mensaje de solicitud (por ejemplo «Ingrese la nota 1:») para poder reimprimirlo. Muestra el mensaje, lee, convierte. Si LeerNumeroBuffer devolvió éxito (jnc), termina. Si no, muestra el error, pita, y vuelve a empezar. No sale hasta obtener una nota válida entre 0 y 20.

NumeroATexto — el camino inverso

NumeroATexto PROC NEAR
    push ax
    push bx
    push di

    lea di, buffer_numero[6]
    xor cx, cx
    mov bx, 10
    cmp ax, 0
    jne BucleConversionNumero
    dec di
    mov byte ptr [di], '0'
    inc cx
    jmp FinConversionNumero

BucleConversionNumero:
    xor dx, dx
    div bx
    add dl, '0'
    dec di
    mov [di], dl
    inc cx
    cmp ax, 0
    jne BucleConversionNumero

FinConversionNumero:
    mov dx, di
    pop di
    pop bx
    pop ax
    ret
NumeroATexto ENDP

Convierte un número (en AX) al texto de sus dígitos, necesario para imprimirlo o escribirlo en un archivo. Se dividen los dígitos de derecha a izquierda usando el resto de dividir entre 10:

1

Se apunta con DI al final de buffer_numero y se va retrocediendo, porque los dígitos salen del último al primero.

2

Caso especial: si el número es 0, se escribe directamente el carácter '0' (el bucle normal no produciría ningún dígito).

3

En el bucle, div bx (BX=10) deja el cociente en AX y el resto en DX. Ese resto es el último dígito; add dl, '0' lo convierte en carácter y se guarda retrocediendo.

4

Se repite hasta que AX llega a 0. Al final, DX apunta al primer dígito y CX dice cuántos dígitos hay: justo lo que necesitan las funciones de escritura.

ImprimirSinSigno

ImprimirSinSigno PROC NEAR
    push ax
    push bx
    push cx
    push dx
    call NumeroATexto
    mov bx, 1
    mov ah, 40h
    int 21h
    pop dx
    pop cx
    pop bx
    pop ax
    ret
ImprimirSinSigno ENDP

Imprime un número en pantalla. Primero lo convierte a texto con NumeroATexto, y luego usa la función de escritura 40h con BX=1. Ese «1» es el manejador de la salida estándar: en DOS la pantalla se trata como si fuera un archivo abierto con el número 1. Así se reutiliza la misma función que escribe en archivos. Se emplea para mostrar las estadísticas del reporte.

PausarPantalla

PausarPantalla PROC NEAR
    push ax
    IMPRIMIR msg_pausa
    mov ah, 08h
    int 21h
    pop ax
    ret
PausarPantalla ENDP

Muestra «Presione una tecla para continuar…» y con la función 08h espera a que el usuario pulse una tecla (sin mostrarla en pantalla). Es lo que permite leer el resultado antes de volver al menú.

SonidoError y SonidoExito

SonidoError PROC NEAR
    push ax
    push dx
    mov ah, 02h
    mov dl, 07h
    int 21h
    pop dx
    pop ax
    ret
SonidoError ENDP

Ambos procedimientos son idénticos: imprimen el carácter 07h, conocido como BEL (bell/campana). Ese carácter no dibuja nada; en su lugar hace sonar un pitido en el altavoz. Se usan para dar retroalimentación audible: un pitido al equivocarse y otro al guardar con éxito.

archivos.inc — trabajar con archivos de disco

Aquí están los procedimientos que crean, abren, escriben, leen y cierran archivos usando int 21h. Es donde el programa vuelve persistentes los datos.

EscribirBytes — el ladrillo de toda escritura

include/archivos.inc
EscribirBytes PROC NEAR
    push ax
    mov ah, 40h
    int 21h
    jc ErrorEscribirBytes
    cmp ax, cx
    jne ErrorEscribirBytes
    clc
    jmp FinEscribirBytes

ErrorEscribirBytes:
    stc

FinEscribirBytes:
    pop ax
    ret
EscribirBytes ENDP

Recibe tres cosas: BX = manejador del archivo, DX = dónde están los datos, CX = cuántos bytes escribir. Llama a la función 40h de DOS y comprueba dos posibles fallos: que DOS reporte error (jc) o que haya escrito menos bytes de los pedidos (cmp ax, cx; DOS deja en AX cuántos escribió realmente). Si algo falla, enciende CF; si todo va bien, lo apaga. Todas las escrituras del proyecto pasan por aquí, lo que centraliza la detección de errores.

EscribirNumeroArchivo

EscribirNumeroArchivo PROC NEAR
    push ax
    push cx
    push dx
    call NumeroATexto
    call EscribirBytes
    pop dx
    pop cx
    pop ax
    ret
EscribirNumeroArchivo ENDP

Escribe un número en un archivo como texto legible. Combina dos piezas ya vistas: NumeroATexto lo convierte a dígitos (dejando DX y CX listos) y EscribirBytes los graba. Así el CSV contiene «16» y no un byte binario ilegible.

AnexarAlumno — añadir un registro a CALIF.TXT

Este procedimiento guarda una línea nueva sin borrar las anteriores. La estrategia: intentar abrir el archivo; si existe, mover el puntero al final; si no existe, crearlo.

AnexarAlumno PROC NEAR
    push ax
    push bx
    push cx
    push dx

    lea dx, nombre_archivo_calif
    mov ax, 3D02h
    int 21h
    jnc ArchivoAnexoAbierto

    xor cx, cx
    lea dx, nombre_archivo_calif
    mov ah, 3Ch
    int 21h
    jnc ArchivoAnexoCreado
    jmp ErrorAbrirAnexo
ArchivoAnexoCreado:
    mov manejador_archivo, ax
    jmp ArchivoAnexoListo

ArchivoAnexoAbierto:
    mov manejador_archivo, ax
    mov bx, ax
    mov ax, 4202h
    xor cx, cx
    xor dx, dx
    int 21h
    jnc ArchivoAnexoListo
    jmp ErrorCerrarAnexo
1

mov ax, 3D02h abre el archivo en modo lectura/escritura. Si jnc (no hubo error), el archivo existía → saltamos a mover el puntero al final.

2

Si falló, el archivo no existe: mov ah, 3Ch con CX=0 lo crea vacío. Si tampoco se puede crear, error total.

3

Cuando el archivo ya existía, mov ax, 4202h mueve el puntero de escritura al final (offset 0 desde el final). Así lo nuevo se agrega sin pisar lo anterior. En todos los casos se guarda el manejador devuelto por DOS.

ArchivoAnexoListo:
    mov bx, manejador_archivo
    xor cx, cx
    mov cl, byte ptr buffer_entrada[1]
    lea dx, buffer_entrada[2]
    call EscribirBytes
    jnc NombreAnexoEscrito
    jmp ErrorCerrarAnexo
NombreAnexoEscrito:

    mov cx, 1
    lea dx, coma
    call EscribirBytes
    ...
    mov ax, nota1
    call EscribirNumeroArchivo
    ...
    mov cx, 2
    lea dx, bytes_nueva_linea
    call EscribirBytes
    ...
    mov bx, manejador_archivo
    mov ah, 3Eh
    int 21h
    clc
    jmp FinAnexarAlumno

Con el archivo listo, se escribe el registro campo por campo, en el orden exacto del formato Nombre,Nota1,Nota2,Nota3: el nombre (tomando su longitud del byte 1 del buffer), una coma, la nota 1, otra coma, la nota 2, etc., y al final el salto de línea (CR+LF). Tras cada escritura se comprueba el carry; si algo falla, se salta a cerrar el archivo y reportar error. Al terminar bien, se cierra con la función 3Eh y se marca éxito con clc. El bloque final de etiquetas de error (ErrorCerrarAnexo, ErrorAbrirAnexo) se asegura de cerrar el archivo si estaba abierto antes de devolver CF=1.

Idea clave de diseño

Las notas se validan antes de abrir el archivo (en RegistrarAlumno) y se guardan en variables. Así, cuando se llega aquí, ya se sabe que todo es correcto y nunca se escribe un registro a medias.

LeerLineaArchivo — leer un registro a la vez

Para procesar el reporte hay que leer CALIF.TXT línea por línea. Este procedimiento lee carácter por carácter hasta encontrar un fin de línea, sin cargar todo el archivo en memoria.

LeerLineaArchivo PROC NEAR
    push bx
    ... (guarda registros)

ReiniciarLecturaLinea:
    lea si, buffer_linea
    xor di, di
    mov linea_desbordada, 0

LeerCaracterLinea:
    mov bx, manejador_entrada
    mov ah, 3Fh
    mov cx, 1
    lea dx, caracter_archivo
    int 21h
    jnc CaracterLeido
    jmp ErrorLeerLinea
CaracterLeido:
    cmp ax, 0
    jne CaracterDisponible
    jmp FinArchivoEnLinea
CaracterDisponible:
    mov al, caracter_archivo
    cmp al, CR
    je LeerCaracterLinea
    cmp al, LF
    je LineaLeidaCompleta
    cmp di, MAXIMO_LINEA
    jae MarcarDesbordeLinea
    mov [si], al
    inc si
    inc di
    jmp LeerCaracterLinea
1

Con la función 3Fh y CX=1 se lee un solo byte del archivo. DOS deja en AX cuántos bytes leyó: si es 0, se acabó el archivo.

2

Los caracteres CR se ignoran; un LF marca el fin de la línea. Cualquier otro carácter se guarda en buffer_linea y se incrementan los índices (SI = dónde escribir, DI = cuántos llevamos).

3

Si la línea supera MAXIMO_LINEA, se marca linea_desbordada y se siguen consumiendo caracteres (sin guardarlos) hasta el fin de línea, para que la siguiente lectura empiece limpia en el próximo registro.

El procedimiento devuelve en AX un código con tres significados posibles, que el generador de reportes sabrá interpretar:

Valor de AXSignificado
0Se llegó al fin del archivo.
FFFFhLa línea era demasiado larga (registro inválido a omitir).
otro númeroLongitud de la línea leída correctamente.

Además, se saltan las líneas totalmente vacías (ReiniciarLecturaLinea) para no procesar renglones en blanco.

VerReporteFinal — mostrar FINAL.XLS en pantalla

VerReporteFinal PROC NEAR
    call LimpiarPantalla
    IMPRIMIR encabezado_visualizacion

    lea dx, nombre_archivo_final
    mov ax, 3D00h
    int 21h
    jnc ArchivoVistaAbierto
    jmp FaltaArchivoVista
ArchivoVistaAbierto:
    mov manejador_entrada, ax

BucleVisualizacion:
    mov bx, manejador_entrada
    mov ah, 3Fh
    mov cx, TAMANO_BUFFER_VISTA
    lea dx, buffer_visualizacion
    int 21h
    jc ErrorLecturaVista
    cmp ax, 0
    je CerrarVista

    mov cx, ax
    mov bx, 1
    mov ah, 40h
    int 21h
    jc ErrorLecturaVista
    jmp BucleVisualizacion
1

Abre FINAL.XLS solo para lectura (3D00h). Si no existe, muestra el error msg_falta_final y pita.

2

Lee el archivo en bloques de 512 bytes (más eficiente que byte a byte). DOS indica en AX cuántos bytes leyó realmente; si son 0, terminó.

3

Ese mismo bloque se escribe en pantalla con la función 40h y BX=1 (salida estándar). Leer en bloques permite mostrar reportes más grandes que la memoria reservada. Al terminar, cierra el archivo con 3Eh.

academico.inc — la lógica del negocio

Este archivo contiene lo que hace «académico» al sistema: validar nombres, registrar alumnos, separar los campos de una línea (parsing CSV), aplicar el cifrado César, calcular el promedio y generar el reporte con estadísticas.

ValidarNombre

include/academico.inc
ValidarNombre PROC NEAR
    push bx
    push cx
    push si

    xor cx, cx
    mov cl, byte ptr buffer_entrada[1]
    jcxz NombreValidadoVacio
    lea si, buffer_entrada[2]
    xor bl, bl

BucleValidarNombre:
    cmp byte ptr [si], ','
    je NombreValidadoConComa
    cmp byte ptr [si], ' '
    je SiguienteCaracterNombre
    mov bl, 1
SiguienteCaracterNombre:
    inc si
    loop BucleValidarNombre
    cmp bl, 0
    je NombreValidadoVacio
    clc
    jmp FinValidarNombre

NombreValidadoVacio:
    mov al, 1
    stc
    jmp FinValidarNombre

NombreValidadoConComa:
    xor al, al
    stc

Rechaza tres tipos de nombre inválido: vacíos, formados solo por espacios, y los que contienen comas (que romperían el formato CSV). Usa BL como bandera propia: empieza en 0 y se pone en 1 si aparece al menos un carácter que no sea espacio. Al final, si BL sigue en 0, el nombre estaba «vacío de contenido». Cuando falla, además pone en AL un código (1 = vacío, 0 = tenía coma) para que quien llama sepa qué mensaje mostrar. jcxz significa «salta si CX es cero», es decir, si no se escribió nada.

RegistrarAlumno — la opción 1 completa

RegistrarAlumno PROC NEAR
    call LimpiarPantalla
    IMPRIMIR encabezado_registro

LeerNombreAlumno:
    IMPRIMIR solicitud_nombre
    lea dx, buffer_entrada
    call LeerEntradaBuffer
    cmp byte ptr buffer_entrada[1], 0
    jne VerificarNombreAlumno
    IMPRIMIR msg_nombre_vacio
    call SonidoError
    jmp LeerNombreAlumno

VerificarNombreAlumno:
    call ValidarNombre
    jnc LeerNotasAlumno
    cmp al, 1
    je NombreAlumnoVacio
    IMPRIMIR msg_nombre_invalido
    call SonidoError
    jmp LeerNombreAlumno
    ...
LeerNotasAlumno:
    lea dx, solicitud_nota1
    call LeerNota
    mov nota1, ax
    lea dx, solicitud_nota2
    call LeerNota
    mov nota2, ax
    lea dx, solicitud_nota3
    call LeerNota
    mov nota3, ax

    call AnexarAlumno
    jc ErrorGuardarRegistro
    IMPRIMIR msg_guardado
    call SonidoExito
    ret

Es el «director de orquesta» de la opción Registrar. Coordina piezas ya explicadas:

1

Pide el nombre y lo valida con ValidarNombre. Si es inválido, muestra el mensaje adecuado (vacío o con coma) y vuelve a pedirlo.

2

Pide las tres notas con LeerNota (que ya insiste hasta que sean válidas) y las guarda en nota1, nota2, nota3.

3

Llama a AnexarAlumno para grabar la línea. Si todo salió bien, muestra «Datos guardados» y hace sonar el pitido de éxito; si falló, muestra el error de escritura.

AnalizarNumeroCsv — leer una nota de la línea

AnalizarNumeroCsv PROC NEAR
    push bx
    push cx
    xor bx, bx
    xor cx, cx

AnalizarCaracterNumero:
    cmp si, fin_analisis
    jb CaracterNumeroDisponible
    jmp NumeroTerminaEnLinea
CaracterNumeroDisponible:
    mov al, [si]
    cmp al, ','
    jne CaracterNumeroNoEsComa
    jmp NumeroTerminaEnComa
CaracterNumeroNoEsComa:
    cmp al, '0'
    jae LimiteInferiorNumeroValido
    jmp NumeroCsvInvalido
    ...
CaracterNumeroEsDigito:
    sub al, '0'
    xor ah, ah
    push ax
    mov ax, bx
    mov dx, 10
    mul dx
    mov bx, ax
    pop ax
    add bx, ax
    cmp bx, 20
    jbe NumeroAnalizadoEnRango
    jmp NumeroCsvInvalido

Es el hermano de LeerNumeroBuffer, pero para leer números dentro de una línea de archivo. SI recorre la línea y se detiene al llegar a una coma o al final (fin_analisis). Acumula el número en BX con el mismo algoritmo «×10 + dígito» y verifica el rango 0–20. Al terminar, devuelve el valor en AX y en DL qué lo detuvo: 1 si fue una coma (hay más campos), 0 si fue el fin de línea (era el último campo). Esto permite al generador comprobar que la estructura del registro es correcta.

AnalizarRegistro — cifrado César y promedio

Este es el procedimiento más rico. Recibe una línea completa y la descompone: cifra el nombre, extrae las tres notas y calcula el promedio. Empecemos por el cifrado.

¿Qué es el cifrado César?

Es el cifrado más antiguo que se conoce: cada letra se reemplaza por la que está 3 posiciones más adelante en el alfabeto. Así A→D, B→E, C→F… Al llegar al final se «da la vuelta»: X→A, Y→B, Z→C. En este proyecto se aplica con desplazamiento 3 al nombre del alumno antes de guardarlo en el reporte.

AnalizarCaracterNombre:
    cmp si, bp
    jb CaracterNombreDisponible
    jmp RegistroAnalizadoInvalido
CaracterNombreDisponible:
    lodsb
    cmp al, ','
    jne NombreNoTerminado
    jmp FinAnalizarNombre
NombreNoTerminado:
    cmp bx, MAXIMO_NOMBRE
    jb HayEspacioParaNombre
    jmp RegistroAnalizadoInvalido
HayEspacioParaNombre:
    cmp al, 'a'
    jb IntentarMayuscula
    cmp al, 'z'
    ja IntentarMayuscula
    add al, 3
    cmp al, 'z'
    jbe GuardarCaracterCifrado
    sub al, 26
    jmp GuardarCaracterCifrado

IntentarMayuscula:
    cmp al, 'A'
    jb GuardarCaracterCifrado
    cmp al, 'Z'
    ja GuardarCaracterCifrado
    add al, 3
    cmp al, 'Z'
    jbe GuardarCaracterCifrado
    sub al, 26

GuardarCaracterCifrado:
    stosb
    inc bx
    jmp AnalizarCaracterNombre
1

lodsb carga en AL el carácter al que apunta SI y avanza SI automáticamente. Si es una coma, el nombre terminó.

2

Se comprueba si el carácter es minúscula (entre 'a' y 'z') o mayúscula (entre 'A' y 'Z'). Si no es una letra (por ejemplo un espacio), se deja igual.

3

Si es letra, add al, 3 la desplaza 3 posiciones. Si se pasa del final del alfabeto (mayor que 'z' o 'Z'), sub al, 26 «da la vuelta» restando el tamaño del alfabeto. Esto convierte x→a, y→b, z→c.

4

stosb guarda AL en la posición a la que apunta DI (el buffer nombre_cifrado) y avanza DI. BX cuenta cuántas letras llevamos, para no pasar de MAXIMO_NOMBRE.

Tras cifrar el nombre, se leen las tres notas y se calcula el promedio:

    call AnalizarNumeroCsv        ; nota 1 (debe terminar en coma)
    ...
    mov nota1, ax
    call AnalizarNumeroCsv        ; nota 2 (debe terminar en coma)
    ...
    mov nota2, ax
    call AnalizarNumeroCsv        ; nota 3 (debe terminar en fin de linea)
    ...
    mov nota3, ax

    mov ax, nota1
    add ax, nota2
    add ax, nota3
    xor dx, dx
    mov bx, 3
    div bx
    mov promedio, ax
    clc

Se llama tres veces a AnalizarNumeroCsv, comprobando que las dos primeras notas terminen en coma (DL=1) y la tercera en fin de línea (DL=0). Si la estructura no cuadra, el registro se marca inválido con stc. Para el promedio se suman las tres notas y se dividen entre 3 con div bx. Como es división entera, la parte decimal se descarta (13.6 se convierte en 13): esto es un requisito del proyecto. La bandera CF distingue registro válido (clc) de inválido (stc), lo que permite saltar líneas mal formadas sin detener el reporte.

EscribirRegistroReporte

EscribirRegistroReporte PROC NEAR
    ...
    mov bx, manejador_salida
    mov cx, longitud_cifrado
    lea dx, nombre_cifrado
    call EscribirBytes           ; nombre cifrado
    ...
    mov ax, nota1
    call EscribirNumeroArchivo   ; ,nota1
    ...  (nota2, nota3, promedio, todas separadas por comas)
    cmp promedio, 10
    jb EscribirEstadoReprobado
    mov cx, LONGITUD_APROBADO
    lea dx, texto_aprobado
    call EscribirBytes
    ...
EscribirEstadoReprobado:
    mov cx, LONGITUD_REPROBADO
    lea dx, texto_reprobado
    call EscribirBytes
    ...
FinEscribirEstado:
    mov cx, 2
    lea dx, bytes_nueva_linea
    call EscribirBytes

Escribe en FINAL.XLS el registro ya procesado, en el formato Cifrado,N1,N2,N3,Promedio,Estado. Va campo por campo intercalando comas, igual que AnexarAlumno. La decisión clave está al final: cmp promedio, 10 — si el promedio es menor que 10, escribe «REPROBADO»; si es 10 o más, «APROBADO». Cada escritura verifica el carry, y una única línea termina con CR+LF.

GenerarReporte — la opción 2 completa

GenerarReporte PROC NEAR
    call LimpiarPantalla
    IMPRIMIR encabezado_reporte

    lea dx, nombre_archivo_calif
    mov ax, 3D00h
    int 21h
    jnc EntradaReporteAbierta
    jmp FaltaEntradaReporte
EntradaReporteAbierta:
    mov manejador_entrada, ax

    lea dx, nombre_archivo_final
    xor cx, cx
    mov ah, 3Ch
    int 21h
    jnc SalidaReporteCreada
    jmp ErrorCrearReporte
SalidaReporteCreada:
    mov manejador_salida, ax

    mov contador_total, 0
    mov contador_aprobados, 0
    mov contador_reprobados, 0
    mov contador_invalidos, 0
    IMPRIMIR msg_procesando
1

Abre CALIF.TXT para leer (3D00h). Si no existe, muestra «No existe CALIF.TXT» y termina.

2

Crea FINAL.XLS desde cero con 3Ch. Ojo: crear vacía el archivo si ya existía, para que el reporte refleje exactamente el contenido actual y no acumule resultados viejos.

3

Pone los cuatro contadores a cero (total, aprobados, reprobados, inválidos) antes de empezar.

BucleGenerarReporte:
    call LeerLineaArchivo
    jnc LineaReporteLeida
    jmp ErrorEntradaSalidaReporte
LineaReporteLeida:
    cmp ax, 0
    jne ReporteTieneLinea
    jmp ReporteGenerado
ReporteTieneLinea:
    cmp ax, 0FFFFh
    je ContarRegistroInvalido
    call AnalizarRegistro
    jnc RegistroReporteAnalizado
    jmp ContarRegistroInvalido
RegistroReporteAnalizado:
    call EscribirRegistroReporte
    ...
    inc contador_total
    cmp promedio, 10
    jb ContarAlumnoReprobado
    inc contador_aprobados
    jmp BucleGenerarReporte

ContarAlumnoReprobado:
    inc contador_reprobados
    jmp BucleGenerarReporte

ContarRegistroInvalido:
    inc contador_invalidos
    jmp BucleGenerarReporte

Este es el bucle principal del reporte, que une todo lo anterior:

LeerLineaArchivo → ¿AX=0? Fin del archivo → salir del bucle
¿AX=FFFFh? Línea demasiado larga → contar como inválida
AnalizarRegistro → si CF=1 (mal formado) → contar inválida
EscribirRegistroReporte → grabar la línea procesada
Sumar +1 al total, y a aprobados o reprobados según el promedio ↺
ReporteGenerado:
    mov bx, manejador_entrada
    mov ah, 3Eh
    int 21h
    mov bx, manejador_salida
    mov ah, 3Eh
    int 21h

    IMPRIMIR msg_reporte_correcto
    call SonidoExito
    IMPRIMIR titulo_estadisticas
    IMPRIMIR msg_total
    mov ax, contador_total
    call ImprimirSinSigno
    NUEVA_LINEA
    ...
    cmp contador_invalidos, 0
    je FinGenerarReporte
    IMPRIMIR msg_registros_invalidos
    mov ax, contador_invalidos
    call ImprimirSinSigno
    NUEVA_LINEA

Cuando el archivo se acaba, se cierran ambos archivos (entrada y salida) con la función 3Eh. Luego se muestran las estadísticas: total de alumnos, aprobados y reprobados, cada número impreso con ImprimirSinSigno. Solo si hubo registros inválidos se muestra esa cuarta línea (cmp contador_invalidos, 0 / je). Las etiquetas finales de error se encargan de cerrar los archivos abiertos si algo falla a mitad de camino, evitando dejar manejadores «colgando».

Resumen del recorrido completo

El usuario registra alumnos (opción 1) que se acumulan en CALIF.TXT. Al generar el reporte (opción 2), cada línea se lee, se valida, se cifra el nombre, se promedia y se clasifica, escribiendo el resultado en FINAL.XLS junto con estadísticas. La opción 3 muestra ese reporte. Cada bloque de código que has visto encaja en esa historia.

Glosario rápido

Registro
Pequeño almacén interno del procesador (AX, BX, CX, DX…).
Buffer
Zona de memoria reservada para guardar datos temporales.
Manejador
Número que DOS asigna a un archivo abierto para poder usarlo.
Interrupción
Petición de un servicio al sistema (DOS con int 21h, BIOS con int 10h).
Carry (CF)
Bandera usada como semáforo de éxito (0) / error (1).
Procedimiento
Subrutina reutilizable (PROC/ENDP) que se llama con call.
Macro
Abreviatura de código que se expande al compilar.
ASCIIZ
Cadena de texto terminada en un byte 0.
db / dw
Reservar un byte / una palabra de 16 bits en memoria.
EQU
Dar nombre a un valor constante (no ocupa memoria).
lea
Cargar la dirección (offset) de una variable en un registro.
CR / LF
Los dos bytes (13 y 10) que juntos forman un salto de línea en DOS.
Cifrado César
Sustituir cada letra por otra 3 posiciones más adelante.
CSV
Texto con valores separados por comas (Comma-Separated Values).
.COM
Formato de programa DOS con un único segmento de 64 KB.