Как работают инструкции ASCII Adjust и Decimal Adjust?

Я изо всех сил пытался понять инструкции по настройке ASCII на языке ассемблера x86.

Я вижу по всему Интернету информацию, говорящую мне о разных вещах, но я думаю, что это одна и та же вещь, объясненная в другой форме, которую я до сих пор не понимаю.

Кто-нибудь может объяснить, почему в псевдокоде AAA AAS мы должны добавить, вычесть 6 из младшего полубайта в AL?

И может ли кто-нибудь объяснить AAM, AAD и псевдокод инструкций по десятичной настройке в руководствах по набору инструкций Intel Тоже почему они такие, какая в них логика?

И наконец, может ли кто-нибудь привести примеры, когда эти инструкции могут быть полезны, или, по крайней мере, в каких приложениях они были полезны в прошлом.

Я знаю, что в настоящее время эти инструкции не используются, но я все равно хочу знать, как эти инструкции работают, это полезно знать.


person emilxp    schedule 07.06.2014    source источник
comment
en.wikipedia.org/wiki/Intel_BCD_opcode   -  person phuclv    schedule 07.06.2014


Ответы (2)


почему в псевдокоде AAA, AAS приходится прибавлять, вычитать 6 из младшего полубайта в AL

Потому что в шестнадцатеричном формате каждый символ имеет 16 различных значений, а двоично-десятичный — только 10. Когда вы выполняете математику в десятичном виде, если число больше 10, вам нужно взять модуль 10 и перенести его на следующую строку. Точно так же в математике BCD, когда результат сложения больше 9, вы добавляете 6, чтобы пропустить 6 оставшихся «недопустимых» значений и перейти к следующей цифре. И наоборот, вы вычитаете 6 при вычитании.

Например: 27 + 36

  27: 0010 0111
+ 36: 0011 0110
───────────────
5_13: 0101 1101 (13 >= 10)
+  6:      0110
───────────────
  63: 0110 0011 (13 + 6 = 19 = 0x13, where 0x3 is the units digit and 0x10 is the carry)

Выполнение неупакованного сложения такое же, за исключением того, что вы переносите непосредственно от цифры единиц к цифре десятков, отбрасывая верхние полубайты каждого байта.

Для получения дополнительной информации вы можете прочитать


и может ли кто-нибудь объяснить псевдокод инструкций AAM, AAD и десятичной настройки в руководствах по набору инструкций Intel, почему они такие, какая логика стоит за ними?

AAM - это просто преобразование из двоичного кода в BCD. Обычно вы выполняете умножение в двоичном формате, затем вызов AAM делит результат на 10 и сохраняет пару частное-остаток в двух распакованных символах BCD.

Например:

13*6 = 78 = 0100 1110
78/10 = 7 remains 8 => result = 0x78

AAD — это наоборот: перед делением вы вызываете AAD, чтобы преобразовать его из BCD в двоичное и выполнить деление так же, как и другие двоичные деления.

Например: 87/5

0x8*10 + 0x7 = 0x57
0x57/5 = 0x11 remains 0x7

Причина этих инструкций в том, что в прошлом память была дорогой, и вы должны максимально сократить использование памяти. Следовательно, в ту эпоху процессоры CISC были очень распространены. Они используют множество сложных инструкций, чтобы свести к минимуму инструкции, используемые для выполнения задачи. В настоящее время память намного дешевле, а современные архитектуры почти RISCy, с компромиссом между сложностью процессора и плотностью кода.

person phuclv    schedule 07.06.2014
comment
Идеальное объяснение! Спасибо, теперь я вижу, насколько это интуитивно понятно, и как я толком не читал, что делают aad и aam, я думал, что они делают еще более сложные вещи..... - person emilxp; 07.06.2014
comment
Учитывая, что AAD и AAM принимают непосредственный байтовый аргумент и либо делят, либо умножают аккумулятор на это значение, мне интересно, почему Intel не указала их в таких терминах? - person supercat; 09.06.2014
comment
@supercat после прочтения функции AAD и AAM тоже задался этим вопросом - person phuclv; 10.06.2014
comment
еще более интересная вещь, которую я обнаружил, заключается в том, что AAM и AAD фактически работают с любыми непосредственными байтами, что делает их настоящим базовым преобразователем. AAD похож на mul imm8, а AAM — на аналогичный способ достижения div imm8 hugi.scene.org/online/coding/hugi%2017%20-%20coaax.htm rcollins.org/secrets/opcodes/AAD.html code.google.com/p/corkami/wiki/x86oddities#aad - person phuclv; 11.06.2014
comment
Руководства Intel по ISA в настоящее время документируют форму произвольного делителя AAM, и это всего лишь синтаксис ассемблера. что версия без аргументов использует 0xa. Разве это не было хорошо задокументировано в 2014 году или хорошо известно? Во всяком случае, для дальнейшего использования, вот некоторый фактический рабочий код для числовой > 2-значной строки, один раз с использованием DIV и снова с использованием AAM (менее удобно, потому что байты в AX не в порядке печати). Отображение времени в сборке - person Peter Cordes; 05.07.2018

Я написал одну программу, которая поможет разобраться в AAA After Addition.

.model small
.data
a db '1234'
len1 db $-a
b db '9876'
len2 db $-b
result db 05 dup(?)
len3 db $-result  

.code
main proc near
mov ax,@data
mov ds,ax
                    
lea bx,a
add bl,len1
mov si,bx

lea bx,b
add bl,len2
mov di,bx

dec si
dec di
dec len3
           
lea bx,result
add bl,len3
             
mov cl,len1  
mov ax,0h

l1:                            
mov al,[si]
mov dl,[di]
cmp ah,00h
je skip 
mov ah,0h
inc al                
skip:
    add al,dl
    aaa    
    or al,30h
    mov [bx],al
    dec bx 
    dec si
    dec di
    loop l1 
cmp ah,00h
je over
mov [bx],31h
jmp finish
over:
mov [bx],30h

finish:
        
mov ax,04ch
int 21h
endp 
end

Теперь, как вы можете видеть в программе, после инструкции добавления мы используем aaa, который преобразует число в ASCII (30-39 соответствует 0-9). Таким образом, при записи реального вывода нам действительно нужно вернуть его в шестнадцатеричном виде для того, что мы берем, или для ответа. Теперь с помощью si и di мы загружаем цифры одну за другой и проверяем, есть ли перенос, как когда мы делаем aaa, мы узнаем, потому что, когда цифра больше 9, она генерирует цифру ah, и поэтому мы будем добавлять по одному. Посмотрите, как aaa работает ниже.

  AAA (ASCII Adjust after Addition)
  if low nibble of AL > 9 or AF = 1 then:
  AL = AL + 6  
  AH = AH + 1  
  AF = 1  
  CF = 1  
  else 
  AF = 0  
  CF = 0  
  in both cases: 
  clear the high nibble of AL. 

Для получения дополнительных программ, связанных с сложением, вычитанием, умножением и делением ASCII, перейдите по этой ссылке. GitHub

person Vraj Rana    schedule 08.10.2020
comment
Если вам нужен указатель на конец строки цифр b, просто поместите туда метку и mov bx, OFFSET b_end. Или, по крайней мере, сделайте len2 постоянной времени сборки (len2 equ $-b), чтобы вы могли делать mov bx, OFFSET b + len2. (Или lea, если вы хотите потратить впустую байт кода.) То же самое для a, так что вы можете просто сделать mov si, OFFSET a_end - 1 или что-то в этом роде, вместо всех этих вычислений времени выполнения для констант времени сборки. Или поместите инструкции pointer-dec в цикл до доступа к памяти. - person Peter Cordes; 09.10.2020
comment
Вывод не шестнадцатеричныйдесятичный, он просто десятичный. Вот почему or al, '0' работает, не требуя обработки случая a..f, потому что aaa разбивает сумму на десятичные цифры. (Не ASCII, вы делаете это вручную после того, как AAA предоставит вам распакованный BCD) - person Peter Cordes; 09.10.2020
comment
Кроме того, ваша обработка переноса выглядит неэффективной, и за ней не очень легко следовать. Вместо того, чтобы сравнивать ah с нулем, вы можете просто сделать add al, ah / mov ah, 0 - person Peter Cordes; 09.10.2020