Как конфертировать из BCD в ASCII и распечатать результат?
Я хочу вывести дату RTC.
Я понял, что для этого мне нужно преобразовать BCD в ASCII, но я не знаю, что делать.
Для преобразования я использую эту страницу: преобразование BCD в ASCII, но оно использует прерывание 21h, а на этой странице: https://en.wikipedia.org/wiki/BIOS_interrupt_call прерывание 21h не существует.
Как я могу конвертировать и печатать BCD?
DAY:
mov ah,0x04
int 0x1a
mov al,dl
aam ;to convert.
mov bx,ax
mov cx,ax
add dl,ch
mov ah,02h
int 21h
mov dl,cl
int 21h
Я проверил код в Ubuntu. Я думаю, что я должен использовать прерывание 10h для вывода, а не 21h. (int 21h
не активен).
Как конвертировать BCD в ASCII и как напечатать его, используя прерывание 10h?
Просто использовать следующий стиль?
mov al, 'h'
int 10h
1 ответ
Что такое BCD (двоично-десятичная дробь)?
В случае результата прерывания по времени BIOS используется "упакованный BCD". Каждый байт (8 битов) разбивается на две половины по 4 бита (полубайта), каждая из которых содержит одно десятичное значение (4 бита допускают значения от 0 до 15, но только значения от 0 до 9 используются, чтобы оставаться "десятичными").
Так, например, если время заканчивается "37" секунд, байт секунд будет содержать значение 0x37 = 55 = 0b00110111
, Обратите внимание, что как в шестнадцатеричном, так и в двоичном виде 4-битные половины видны / читаются даже человеком после непродолжительной практики (в десятичном не нужно вычислять деление / остаток в голове на 16, чтобы разделить его на 3 и 7: 55 / 16 = 3
, 55 % 16 = 7
).
Теперь сборка довольно проста с немного битой, так что даже если вы не хотите углубляться в специализированные инструкции BCD, такие как AAM
Вы можете использовать обычную битовую маскировку + сдвиг, чтобы извлечь два значения.
Допустим, у вас есть ценность 0x37
в BL
и вы хотите извлечь его в BH
(0x03
) а также BL
(0x07
), то, например, этот код будет вычислять, что:
mov bh, bl ; copy the packed value also into BH
and bl, 0x0F ; keep only lower 4 bits in BL (0x37 & 0x0F = 0x07)
shr bh, 4 ; shift right by 4 bits BH (unsigned(0x37) >> 4 = 0x03)
; and that's it, it's that simple to manipulate bits in assembly
Как это отобразить?
Это зависит от вашей целевой платформы, исходя из вопросов и комментариев, которые вы хотите использовать в режиме x86 16b в режиме реального времени на ПК-совместимом компьютере с доступным только BIOS, поэтому одним из самых простых способов является преобразование этих десятичных значений 0–9 в значения ASCII из цифр (добавив 48 работ, или многие ассемблеры сделают преобразование из символов для вас, например, add bl,'0'
работает, и вы можете думать об этом как "добавить символ шрифта от 0 до 0-9 значение", и как следующие цифры в шрифте определяются в +1 от '0'
, он рассчитает правильное значение символа).
А затем используйте один из int 10h
службы для отображения символа ASCII, такие как AH=0x0A
оказание услуг. Пример отображения символа ASCII из BL
:
mov ah, 0x0A ; write ASCII character
mov al, bl ; character value to write
xor bx, bx ; bh = bl = 0 in text mode (adjust in gfx mode as needed)
mov cx, 1 ; write it only once
int 0x10 ; call the BIOS service
Поскольку вы хотите отобразить время, и у вас есть значения в нескольких регистрах, вам, вероятно, потребуется сохранить их между печатью, например, если бы я использовал int 0x1A,02
Служба для чтения RTC, я бы использовал эту "архитектуру" для ее отображения (не писать полную реализацию, просто верхнюю логику, чтобы показать вам, как вы можете использовать push/pop
инструкции стека для сохранения значений для последующего использования):
read_and_display_rtc_time:
mov ah,2
int 0x1A ; call service 00 of RTC BIOS interrupt
push dx ; preserve DH (seconds)
push cx ; preserve CL (minutes)
mov al,ch ; AL = CH (hours)
call display_bcd ; display AL as BCD-packed decimal value
call display_colon ; display ":"
pop ax ; restore minutes (push cx) into AL
call display_bcd ; display AL as BCD-packed decimal value
call display_colon ; display ":"
pop ax
mov al,ah ; restore seconds into AL
call display_bcd ; display AL as BCD-packed decimal value
ret
display_bcd:
; AL contains BCD-packed decimal value (4:4 bits)
; TODO code to split it into two values, convert to ASCII and display
ret
display_colon:
; TODO code to display colon on screen
ret
Теперь я прокомментирую некоторые ваши проблемы в комментариях:
mov cl,ax
Да, это не законно, потому что ax
16-битный регистр, и cl
является 8-битным регистром (псевдоним для нижней 8 b части cx
), поэтому вы потеряете 8 бит в такой операции.
Чтобы показать программисту, что вы действительно хотите потерять старшие 8 бит, вы можете написать mov cl,al
(al
псевдоним младших 8 бит ax
).
Обратное преобразование типа mov ax,cl
снова потерпит неудачу, но снова как программист вы можете точно определить, как 8-битное значение должно быть преобразовано в 16-битное значение.
Например, чтобы выполнить "беззнаковое" преобразование, вы можете использовать инструкцию 386+:
movzx ax,cl
Или 8086-80286 способ расчета:
xor ax,ax ; set all bits of AX to zero first
mov al,cl ; copy the CL into lower 8 bits of AX
И для "подписанного" преобразования снова есть специализированный 386+ способ:
movsx ax,cl
; or before 80386 times there are two common options
mov al,cl ; if "ax" is target, then there's special instr.
cbw ; sign extend AL into AX
; or when non-ax register is target, like DX
mov dh,cl ; copy CL into upper 8 bits first
sar dx,8 ; now do sign-right-shift by 8 bits
; dx = sign extended CL
Просто имейте в виду, что компьютер - немного более сложный калькулятор, и ничего более, поэтому вы должны попытаться преобразовать слово "задача" в размышление о том, какие числовые значения имеются на входной стороне, какие числовые значения представляют на выходной стороне (даже "символы" являются просто числовые значения в компьютере), а затем вы просто вычисляете математические формулы для преобразования входных чисел в выходные числа.
Если у вас есть bochs, закомментируйте код, который еще не компилируется, и начните с небольшого фрагмента кода, который работает, проверьте в отладчике, что они делают, чтобы вы могли визуально увидеть, как происходит вычисление, а затем медленно добавляйте новые части код с помощью 2-3 инструкций и продолжайте их выполнять в отладчике, чтобы увидеть, рассчитывают ли они, что вы хотите. (злоупотребление словом "вычислить" в последних параграфах является преднамеренным. если вы знаете, как работать с калькулятором, вы довольно близки к тому, чтобы знать, как кодировать на ассемблере, хотя ваш исходный код, вероятно, будет уродливым, так как для этого требуется время Выберите также "стиль" и эффективность - просто проверьте набор инструкций, чтобы понять, какие вычисления возможны, и лучше понять, что означает, что AX
16-битный регистр, и AL
8 бит, и как числовые значения кодируются в битах (ток электричества).
"проверено в Ubuntu"
Не совсем, ты просто побежал nasm
в Ubuntu для создания 16-битного двоичного кода машинного кода x86 (кросс-компиляция, вы можете создать такой двоичный файл на любом другом компьютере, на котором есть ассемблер x86, который не имеет отношения к вашим вопросам, за исключением того, что вы должны указать NASM, так что люди, пытающиеся ответить вам, будут знать какой синтаксис сборки x86 использовать, у каждого ассемблера есть небольшие различия). Затем вы попробовали этот двоичный файл на виртуальной машине (BOCHS). Если вы попытаетесь запустить его непосредственно под Ubuntu, он полностью потерпит неудачу, так как ОС Ubuntu не поддерживает среду выполнения для машинного кода реального режима 16b.
Если вы не уверены, что имеет значение, просто опишите все ваши инструменты и командные строки / опции, которые вы используете.