Что такое 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-битной части 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
Просто имейте в виду, что компьютер — это немного более сложный калькулятор, не более того, поэтому вы должны попытаться преобразовать слово «задача» в размышления о том, какие числовые значения у вас есть на входной стороне, какие числовые значения представляют выходную сторону (даже «символы» просто числовые значения в компьютере), а затем вы просто вычисляете математические формулы для преобразования входных чисел в выходные числа.
Если у вас есть бохи, закомментируйте код, который еще не компилируется, и начните с небольшого фрагмента кода, который работает, проверьте в отладчике, что они делают, чтобы вы могли визуально увидеть, как происходит вычисление, затем постепенно добавляйте новые части кода. код с помощью 2-3 инструкций и продолжайте запускать их в отладчике, чтобы увидеть, вычисляют ли они то, что вы хотите. (чрезмерное использование слова «вычислить» в последних абзацах является преднамеренным.. если вы знаете, как работать с калькулятором, вы довольно близко знаете, как кодировать на ассемблере, хотя ваш исходный код, вероятно, будет уродливым, так как требуется время для подобрать также «стиль» и эффективность — просто проверьте набор инструкций, чтобы понять, какие вычисления возможны, и лучше понять, что означает, что AX
— это 16-битный регистр, а AL
— 8-битный, и как числовые значения кодируются в бит (электрический ток)).
"проверено в убунту"
Не совсем так, вы запустили только nasm
в Ubuntu для создания 16-битного двоичного машинного кода x86 (кросс-компиляция, вы можете создать такой двоичный файл на любой другой машине, на которой есть ассемблер x86, на самом деле не имеет отношения к вашим вопросам, за исключением того, что вы должны указать NASM, поэтому люди, пытающиеся ответить вам, будут знать, какой синтаксис сборки x86 использовать, каждый ассемблер имеет небольшие различия). Затем вы попробовали этот двоичный файл на виртуальной машине (BOCHS). Если вы попытаетесь запустить его непосредственно под Ubuntu, это полностью потерпит неудачу, поскольку ОС Ubuntu не поддерживает среду выполнения для машинного кода реального режима 16b.
Если вы не уверены, что важно, просто опишите все ваши инструменты и командные строки/параметры, которые вы используете.
person
Ped7g
schedule
10.09.2017
call
с nasm здесь. Что касается того, как его использовать, представьте что-то вродеmov al, ch; call writeint; mov al, cl; call writeint; mov al, dh; call writeint
. - person David Wohlferd   schedule 10.09.2017mov ah,04h; int 1Ah, mov ax,[dl](http://vitaly_filatov.tripod.com/ng/asm/asm_029.5.html); mov cl,ax;
(Я получил код печати, используя cl :mov cl,'a'; mov ch, (color code); mov bx, (postion); mov [bx], cx )
Но mov ax, dl & mov cl, ax line неверный код операции и ошибка операндов. В чем проблема с использованием прерывания 1Ah? - person LocketGoma   schedule 10.09.2017org 100h; mov ax, 0b800h ; mov ds, ax; mov cl, 'A'; mov ch, 11011111b; mov bx, 15eh; mov [bx], cx; ret;
- person LocketGoma   schedule 10.09.2017BOCHS
имеет отладчик (сам я не пробовал, просто постоянно вижу его здесь в вопросах о разработке загрузчиков и ядер). Потратив полдня на его настройку и изучение того, как его контролировать и отлаживать, позже вы сможете получить огромное преимущество. И у вас неправильные теги в этом вопросе. сборка ubuntu + полностью не соответствует вашей реальной целевой платформе. - person Ped7g   schedule 10.09.2017AAM
используется неправильно. - person Sep Roland   schedule 10.09.2017