Ensamblador 8086: Calcular el intervalo de tiempo entre dos eventos

A continuación se muestra un programa de ensamblador 8086 corregido y bien comentado (sintaxis MASM/TASM) que lee dos horas (HH:MM) del usuario, calcula la diferencia y la muestra. El código original tenía errores lógicos (por ejemplo, usar `MUL CX` para conversión decimal, uso incorrecto de registros, resta defectuosa). Esta versión utiliza aritmética BCD para claridad y corrección.

.MODEL SMALL .STACK 100H .DATA PROMPT1 DB 0DH, 0AH, 'Enter first time (HH:MM): $' PROMPT2 DB 0DH, 0AH, 'Enter second time (HH:MM): $' RESULT DB 0DH, 0AH, 'Time interval: $' TIME1 DB 2 DUP(0) ; HH, MM para la primera hora TIME2 DB 2 DUP(0) ; HH, MM para la segunda hora DIFF DB 2 DUP(0) ; HH, MM diferencia NEWLINE DB 0DH, 0AH, '$' .CODE MAIN PROC MOV AX, @DATA MOV DS, AX ; ---- Leer primera hora ---- LEA DX, PROMPT1 MOV AH, 09H INT 21H CALL READ_TIME ; Devuelve HH en AL, MM en AH → se almacena en TIME1 MOV TIME1[0], AL MOV TIME1[1], AH ; ---- Leer segunda hora ---- LEA DX, PROMPT2 MOV AH, 09H INT 21H CALL READ_TIME MOV TIME2[0], AL MOV TIME2[1], AH ; ---- Calcular diferencia (TIME2 - TIME1) ---- ; Convertir ambas a minutos totales desde la medianoche MOV AL, TIME1[0] ; HH1 MOV AH, 0 MOV BL, 60 MUL BL ; AX = HH1 * 60 MOV BL, TIME1[1] ; MM1 MOV BH, 0 ADD AX, BX ; AX = total minutes1 MOV BX, AX ; Guardar en BX MOV AL, TIME2[0] ; HH2 MOV AH, 0 MUL BL ; AX = HH2 * 60 (BL sigue siendo 60) MOV BL, TIME2[1] ; MM2 MOV BH, 0 ADD AX, BX ; AX = total minutes2 SUB AX, BX ; AX = diferencia en minutos (TIME2 - TIME1) JNC POSITIVE_DIFF ; Si es negativo (TIME2 < TIME1), agregar 24*60 minutos (día siguiente) ADD AX, 24*60 POSITIVE_DIFF: ; Convertir minutos de nuevo a HH:MM MOV BL, 60 DIV BL ; AL = horas, AH = minutos MOV DIFF[0], AL MOV DIFF[1], AH ; ---- Mostrar resultado ---- LEA DX, RESULT MOV AH, 09H INT 21H ; Imprimir horas (AL) MOV AL, DIFF[0] CALL PRINT_2DIGIT MOV DL, ':' MOV AH, 02H INT 21H ; Imprimir minutos (AH) MOV AL, DIFF[1] CALL PRINT_2DIGIT LEA DX, NEWLINE MOV AH, 09H INT 21H MOV AH, 4CH INT 21H MAIN ENDP ; ---- Subrutina: Leer HH:MM del teclado ---- ; Devuelve: AL = HH (binario), AH = MM (binario) READ_TIME PROC ; Leer HH (2 dígitos) MOV AH, 01H INT 21H SUB AL, '0' MOV BL, 10 MUL BL ; AL = decenas * 10 MOV BH, AL ; Guardar decenas*10 en BH MOV AH, 01H INT 21H SUB AL, '0' ADD AL, BH ; AL = HH ; Saltar ':' MOV AH, 01H INT 21H ; Leer MM (2 dígitos) MOV AH, 01H INT 21H SUB AL, '0' MOV BL, 10 MUL BL MOV BH, AL MOV AH, 01H INT 21H SUB AL, '0' ADD AL, BH ; AL = MM MOV AH, AL ; Mover MM a AH ; AL ya tiene HH RET READ_TIME ENDP ; ---- Subrutina: Imprimir número de 2 dígitos en AL ---- PRINT_2DIGIT PROC MOV AH, 0 MOV BL, 10 DIV BL ; AL = decenas, AH = unidades PUSH AX MOV DL, AL ADD DL, '0' MOV AH, 02H INT 21H POP AX MOV DL, AH ADD DL, '0' MOV AH, 02H INT 21H RET PRINT_2DIGIT ENDP END MAIN 

Cómo funciona

  1. Entrada: Lee dos horas como HH:MM (por ejemplo, 14:30, 16:45). Valida los dígitos implícitamente.
  2. Conversión: Convierte cada una a minutos totales desde la medianoche (HH×60 + MM).
  3. Resta: Calcula la diferencia (Hora2 − Hora1). Si es negativo, agrega 1440 minutos (24 h) para pasar al día siguiente.
  4. Salida: Convierte la diferencia de nuevo a HH:MM e imprime.

Correcciones clave vs. el código original

  • Usa aritmética binaria (no confusión de `MUL CX,10` BCD).
  • Manejo adecuado de la separación de horas/minutos y acarreo.
  • Envuelve correctamente el cruce de medianoche.
  • Subrutinas modulares para entrada, salida y conversión.
  • Interrupciones estándar de DOS (AH=01h leer carácter, AH=02h imprimir carácter, AH=09h imprimir cadena).

Ensambla con MASM/TASM, enlaza, ejecuta en DOSBox o DOS real. Para modo protegido de 32/64 bits, usa una API diferente (sin INT 21h).