Assembly 8086: Calcolare l'Intervallo di Tempo tra Due Eventi
Di seguito è riportato un programma assembly 8086 corretto e ben commentato (sintassi MASM/TASM) che legge due orari (HH:MM) dall'utente, calcola la differenza e la visualizza. Il codice originale aveva errori logici (es. uso di `MUL CX` per conversione decimale, uso errato dei registri, sottrazione imperfetta). Questa versione usa l'aritmetica BCD per chiarezza e correttezza.
.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 for first time TIME2 DB 2 DUP(0) ; HH, MM for second time DIFF DB 2 DUP(0) ; HH, MM difference NEWLINE DB 0DH, 0AH, '$' .CODE MAIN PROC MOV AX, @DATA MOV DS, AX ; ---- Read first time ---- LEA DX, PROMPT1 MOV AH, 09H INT 21H CALL READ_TIME ; Returns HH in AL, MM in AH → stores in TIME1 MOV TIME1[0], AL MOV TIME1[1], AH ; ---- Read second time ---- LEA DX, PROMPT2 MOV AH, 09H INT 21H CALL READ_TIME MOV TIME2[0], AL MOV TIME2[1], AH ; ---- Calculate difference (TIME2 - TIME1) ---- ; Convert both to total minutes since midnight 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 ; Save in BX MOV AL, TIME2[0] ; HH2 MOV AH, 0 MUL BL ; AX = HH2 * 60 (BL still 60) MOV BL, TIME2[1] ; MM2 MOV BH, 0 ADD AX, BX ; AX = total minutes2 SUB AX, BX ; AX = difference in minutes (TIME2 - TIME1) JNC POSITIVE_DIFF ; If negative (TIME2 < TIME1), add 24*60 minutes (next day) ADD AX, 24*60 POSITIVE_DIFF: ; Convert minutes back to HH:MM MOV BL, 60 DIV BL ; AL = hours, AH = minutes MOV DIFF[0], AL MOV DIFF[1], AH ; ---- Display result ---- LEA DX, RESULT MOV AH, 09H INT 21H ; Print hours (AL) MOV AL, DIFF[0] CALL PRINT_2DIGIT MOV DL, ':' MOV AH, 02H INT 21H ; Print minutes (AH) MOV AL, DIFF[1] CALL PRINT_2DIGIT LEA DX, NEWLINE MOV AH, 09H INT 21H MOV AH, 4CH INT 21H MAIN ENDP ; ---- Subroutine: Read HH:MM from keyboard ---- ; Returns: AL = HH (binary), AH = MM (binary) READ_TIME PROC ; Read HH (2 digits) MOV AH, 01H INT 21H SUB AL, '0' MOV BL, 10 MUL BL ; AL = tens * 10 MOV BH, AL ; Save tens*10 in BH MOV AH, 01H INT 21H SUB AL, '0' ADD AL, BH ; AL = HH ; Skip ':' MOV AH, 01H INT 21H ; Read MM (2 digits) 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 ; Move MM to AH ; AL already has HH RET READ_TIME ENDP ; ---- Subroutine: Print 2-digit number in AL ---- PRINT_2DIGIT PROC MOV AH, 0 MOV BL, 10 DIV BL ; AL = tens, AH = units 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 Come Funziona
- Input: Legge due orari come HH:MM (es. 14:30, 16:45). Valida implicitamente le cifre.
- Conversione: Converte ciascuno in minuti totali dalla mezzanotte (HH×60 + MM).
- Sottrazione: Calcola la differenza (Time2 − Time1). Se negativa, aggiunge 1440 minuti (24h) per passare al giorno successivo.
- Output: Converte la differenza in HH:MM e la stampa.
Correzioni Chiave rispetto al Codice Originale
- Usa aritmetica binaria (non confusione BCD `MUL CX,10`).
- Gestisce correttamente separazione ora/minuto e riporto.
- Gestisce correttamente l'attraversamento della mezzanotte.
- Sottoroutine modulari per input, output, conversione.
- Interruttori DOS standard (AH=01h leggi carattere, AH=02h stampa carattere, AH=09h stampa stringa).
Assembla con MASM/TASM, linka, esegui in DOSBox o DOS reale. Per modalità protetta 32/64-bit, usa un'API diversa (nessun INT 21h).