2016-05-10 5 views
3

Привет им пытаются отобразить текущее время часы/минуты/секунды это мой пример кода:Отображение времени на ассемблере

MOV AH, 2Ch 
INT 21h 

MOV AH, 0Eh 

MOV AL, CH 
INT 10h 

MOV AL, 3Ah 
INT 10h 

MOV AL, CL 
INT 10h 

MOV AL, 3Ah 
INT 10h 

MOV AL, DH 
INT 10h 

ret 

Здесь вы можете себе, что консоль отображает

enter image description here

+0

было ли это 2:14:02? :) – hobbs

+0

да как я могу получить результат в цифрах? : s –

+0

Я добавил emu8086 в теги, учитывая, что выведенный вами вывод экрана для этого эмулятора. –

ответ

2

Смотрите тегов вику для набора инструкций справочного руководства, и много хороших ссылок на справочные материалы и учебники.


Требуется достаточно кода для разбиения целого числа на цифры ASCII, которые необходимо учитывать в функции.

Это оптимизированная и исправленная версия функции print2Digits @ hobbs. (Я также испробовал версию в своем ответе, так что это тоже правильно, но оставил оптимизацию для этого).

print2Digits: 
    ;; input in AL (0-99). 
    ;; clobbers AX and DX 
    cbw    ; zero AH. Sign-extending AL does the job because AL is only allowed to be 0-99. 
        ; omit this step if you can easily have the caller use the full AX, i.e. zero AH for us 

    mov dl, 10 
    div dl  ; quotient in AL(first (high) digit), remainder in AH(second (low) digit) 

    add ax, 0x3030 ; add '0' to al and ah at the same time. 
    mov dl, ah  ; save the 2nd digit 

    mov ah, 0x0E ; DOS system call number for printing a single character 
    int 0x10  ; print high digit first. Doesn't clobber anything, so AH still holds 0x0E after 
    mov al, dl 
    int 0x10  ; print the low digit 2nd 
    ret 

Так как мы использовали div разделить целое на две base10 цифры, нам нужно ah равным нулю. Мы могли бы сэкономить cbw, если вызывающий абонент сделал movzx ax, ch или что-то, до нуля ah.

(За исключением того, что у 8086 не было movzx, так что вы должны были бы на самом деле xor ax,ax/mov al, ch.)

Там в системном вызове DOS для печати целой строки, чтобы вы могли хранить символы в небольшой буфер и печатать их все сразу, как я в этом AMD64 Linux FizzBuzz


Это также возможного используйте aam, чтобы разделить AL (вместо AX) на 10, избегая при этом необходимости нуля AH. Это немного быстрее, чем div r8 на современных процессорах Intel и AMD. Однако он помещает результаты в противоположные регистры от div, что означает дополнительные инструкции после aam. Это балансирует экономию на mov dl, 10 и cbw

print2Digits: 
    ;; input in AL (0-99). (Ignores AH because we use AAM instead of div) 
    ;; clobbers AX and DX 
     aam   ; like `div` by 10, but with the outputs reversed, and input from AL only 
         ; quotient in AH (high digit), remainder in AL(low digit). (Opposite to div) 

    add ax, 0x3030 ; add '0' to al and ah at the same time. 
    mov dl, al  ; save the low digit 
    mov al, ah  ; print high digit first 

    mov ah, 0x0E ; DOS system call number for printing a single character 
    int 0x10  ; print first digit. Doesn't clobber anything, so AH still holds 0x0E after 
    mov al, dl 
    int 0x10  ; print second digit 
    ret 

Даже если мы хотели сохранить в строку (и сделать один вызов функции печати строки или системного вызова), мы должны были бы поменять местами Аль и ah перед хранением AX в памяти (например, xchg al,ah, или более эффективно на современном оборудовании, rol ax,8). div производит их в правильном порядке.


Для 386, где 32-битный адрес, размер доступен, мы можем сохранить одну команду:

lea dx, [eax + 0x3030] ; need a 32bit addressing mode to use eax as a source reg. Adds '0' to both digits at once, with a different destination. 
mov al, dh 

lea нужен префикс адреса размера и 2-байтовый мод/гт, и 32-битное смещение, поэтому он плохо проигрывает по размеру кода, но сохраняет одну инструкцию.

Использование lea для чтения из eax после div пишет ax, вероятно, будет быстрее на SandyBridge семейства процессоров, особ. Хасуэлл и позже, но на Intel pre-SnB, частичный регистрационный стойло позволит лучше использовать чистую 16-битную версию с отдельными инструкциями add и mov.

+0

Учитывая, что это вопрос с отметкой 8086 и что эмулятор (emu8086) ограничен 8086 плюс несколько инструкций 80186, я рекомендую хотя бы раскомментировать 16-разрядный код и закомментировать 32-битный код. emu8086 и процессор 8086 не имеют 32-разрядных регистров общего назначения. –

+0

@MichaelPetch: спасибо, я не читал теги, только перевернулся, чтобы прочитать ответы, увидев, что вопрос тривиален. Исправлена. –

+0

Спасибо большое! –

3

Вам нужна процедура печати, чтобы печатать байты в виде чисел, вместо того, чтобы записывать их непосредственно на экран в виде символов. К счастью, поскольку вам нужно иметь дело только с значениями между 0 и 59, и поскольку вам нужны ведущие нули, проблема довольно проста. Предполагая, что значение, которое будет напечатано в AX:

print2Digits: 
    ;; input in AX (0-99) 
    ;; clobbers AX and DX, save them if needed 
    MOV DL, 0Ah ; divide by: 10 
    DIV DL  ; first digit in AL (quotient), second digit in AH (remainder) 
    MOV DX, AX ; save the digits 
    ADD AL, 30h ; ASCII '0' 
    MOV AH, 0Eh ; set up print 
    INT 10h  ; print first digit. 
    MOV AL, DH ; retrieve second digit 
    ADD AL, 30h 
    INT 10h  ; print it 
    RET 
+0

Я не очень понимаю, в каких строках мне нужно вызвать funciont print2digits? (также, когда я пишу move AX, CH i получает неправильные параметры.) –

+0

Удовлетворительный факт, устаревшая инструкция AAM является ярлыком для деления 'al' на 10. Это похоже на' div' с помощью прямого делителя, только с входным из 'al' вместо' ax'. Это на самом деле немного быстрее, чем 'div r8' на Haswell. Вы можете оптимизировать свой код, выполнив 'add ax, 0x3030' после div, чтобы преобразовать обе цифры в ASCII с помощью insn. Однако есть проблема: 'DI' - это 16-битный регистр, поэтому вы фактически делаете' div r16', деля 'dx: ax' на' di'. И 'mov di, ah' не собирается. –

+0

'edx' или' ecx' являются хорошим выбором для регистров царапин, которые вызывают вызовы в большинстве ABI. Они имеют байтовые компоненты, доступные за пределами 64-битного режима. (В 64-битном режиме 'DIL' - это низкий 8' RDI', но для доступа требуется префикс REX). –

2

Вы пытаетесь отобразить значение (время) в CH, CL и DH регистров как aschi символы.

Допустим, ваш CH содержит значение 15h.

Теперь, когда вы ставите это значение в AL и звоните int 10h, он отображает символ aschi, который соответствует значению 15h. Если вы хотите показать десятичные цифры в течение 15 часов, вам придется вызвать процедуру отображения, которая нарушит значение в регистре и вычеркивает цифры, отображаемые в нем, а не только представление aschi в этом значении.

Допустим, у вас есть десятичный знак 85 в регистре. Теперь вам нужно напечатать на экране «8» и «5». Таким образом, вам нужно преобразовать значение 85 в to aschi-56 («8») и aschii-53 («5»). Затем поместите их в регистры один за другим и дважды вызовите int 10, чтобы отобразить «85».

hobbs answer показывает, как это сделать.

Here это еще один учебник

Смежные вопросы