repeat:
daa
calling convention – arguments in EAX and EDX, result in AL. The multiplication function begins at the start of the code, and the addition function begins after the first four bytes.
32-битный машинный код x86,
16 байт
gf9_multiply
-5 с другим, более коротким способом уменьшения коэффициентов по модулю 3.
gf9_add
Использует полиномы по модулю \$X^2 + 1\$, представленные целыми числами путем замены \$ X = 16 \$. Выбранные значения соответствуют каноническим формам \$aX + b\$ для \$a,b \in \{0,1,2\}\$ – шестнадцатеричные 00, 01, 02, 10, 11, 12, 20, 21. , 22.
gf9_add:
add eax, edx
Использует
Попробуйте онлайн! sub al, ah
, this adds EDX and EAX (the arguments), so that AH represents a polynomial equivalent to the result modulo 3, of the form \$ aX+b \$ for \$ 0 \le a \le 4, 0 \le b \le 4 \$.
Объяснение gf9_multiply:
mul edx
, EDX will be 0 when we get here, and this instruction leaves EAX unchanged.
regparm(2)
Умножьте заданные полиномы (которые есть в EAX и EDX). Поместите продукт в EDX:EAX — значение достаточно мало, чтобы поместиться в EAX, поэтому EDX становится равным 0. Вычтите AH (второй младший байт EAX) из AL (самый младший байт EAX). Это уменьшает полином по модулю \$ X^2 + 1 \$: AL теперь представляет полином, эквивалентный результату по модулю 3, вида \$ aX+b \$ для \$ 0 \le a \le 8, -4 \le b \le 4 \$..
f7 e2 28 e0 01 d0 27 2c 03 a8 08 75 f9 d4 30 c3
stands for Decimal Adjust (AL) after Addition. "Decimal" in these instruction names refers to packed BCD, which puts one decimal digit in each Здесь две функции сливаются.Для f(a)=a%3+a\3*I
g(a)=[real(a),imag(a)]%3*[1,3]~
s(a,b)=g(f(a)+f(b))
p(a,b)=g(f(a)*f(b))
or similar instruction, to correct the result for BCD.
Для
Это одна из нескольких инструкций x86, предназначенных для обработки чисел в
двоично-десятичный код f(a)=a%3+a\3*x
g(a)=Vecrev(a%(x^2+1),2)%3*[1,3]~
s(a,b)=g(f(a)+f(b))
p(a,b)=g(f(a)*f(b))
does.
грызть
. Как следует из названия инструкции, ее предназначение — использовать после регулярного e=@(x)mod(x,3)+i*round(x/3)
d=@(x)mod(real(x)^3+3*imag(x),9)
a=@(x,y)d(e(x)+e(y))
m=@(x,y)d(e(x)*e(y))
instruction based on whether it results in a carry across the centre of the byte, which covers the cases where the low nybble reaches 16 or higher and wraps back around to a valid BCD digit. The instruction e=@(x)mod(x,3)+i*fix(x/3)
d=@(x)[1 3]*mod([real(x);imag(x)],3)
a=@(x,y)d(e(x)+e(y))
m=@(x,y)d(e(x)*e(y))
Так какая коррекция необходима?
T`3-8`012012
also makes a similar adjustment for the high nybble of AL: if the high nybble's original value is 10 to 15, or the carry flag (CF) is 1 (which is based on carries off the top of the number), it adds 6 to the high nybble.
Рассмотрим простой пример сложения 2 (двоичный код 00000010) и 8 (двоичный код 00001000). M`1
, which always sets them to 0 here. The low nybble is 10 to 15 iff, in the current polynomial \$ aX+b \$ (with \$ 0 \le a \le 8, -4 \le b \le 4 \$), \$ b \$ is -4 to -1, in which case adding 6 results in \$ b \$ being 2 to 5. The high nybble is 10 to 15 only if \$ a \$ is 0 in addition to \$ b \$ being negative, in which case adding 6 to the high nybble makes \$ a \$ 6.
Результатом обычного сложения является обычная цифра 10 (двоичный код 00001010), но сумма в формате BCD должна быть равна 10 в формате BCD (двоичный код 00010000). Таким образом, результат необходимо увеличить на 6, чтобы изменить 10 на 16. Это одна из вещей, которые 1{300}|1{98}|1{30}
, AL still represents a polynomial equivalent to the result modulo 3 of the form \$ aX+b \$, but now with \$ 0 \le a \le 8, 0 \le b \le 5 \$.
1(?=1*;(1*))|.
$1
Но как он узнает, следует ли применять этот +6? В данном случае это очевидно из самого значения, поскольку двоичное число 1010 не является допустимой цифрой BCD. Но если бы вместо этого вычисление было 8 плюс 8, результатом было бы двоичное число 00010000, которое является действительным BCD для 10, но правильная сумма BCD равна 16.
\d+
$*
Здесь на помощь приходит «флаг вспомогательного переноса» (AF). Он устанавливается
\d+
$*
1(?=1*;(1*))|.
$1
1{300}|1{98}|1{30}
M`1
T`3-8`012012
добавляет 6 к AL, если младший полубайт AL равен от 10 до 15 или AF равен 1. 3$
0
T`34`_1
adds 6 to \$ b \$ while possibly changing \$ a \$ from 0 to 6, similarly to before, and then M`1
Здесь флаги AF и CF взяты из предыдущего \d+
$*
sees that the eights bit is 0, and the jump does not happen again.
Таким образом, после
Вычтите 3. Снова AL представляет собой полином, эквивалентный результату по модулю 3 в виде \$ aX+b \$, и на этот раз \$ 0 \le a \le 8, -3 \le b \le 2 \$. \d+
$*
M`1
3$
0
T`34`_1
could have been omitted for -2 bytes.
0-8
Посмотрите на восьмой бит AL, который равен 1, если \$ b \$ отрицательное значение, и 0 в противном случае. i x=[div x 3,x] --conert integer to polynomial/list representation
j[a,b]=mod(a*3+mod b 3)9 --inverse of i
s=(.i).(j.).zipWith(+).i --"sum"
m[a,b][c,d]=j[a*d+b*c,b*d-a*c] --core function of "product"
p=(.i).m.i --"product"
converts a regular number in AL to an unpacked BCD (one byte per decimal digit) number in AH and AL, which is done by setting AH and AL to the quotient and remainder, respectively, of AL divided by 10. However, the instruction allows that number 10 to be changed. We set it to 0x30; the important part is that AL is reduced modulo 0x30, which means \$ a \$ is reduced modulo 3.
Перейти назад, если \$ b \$ отрицательно (от -3 до -1). Если это так, то дальше
P # product of the inputs
ƵÈ% # mod 300
98% # mod 98
S3%J # split, mod 3 each digit, join
берет 3 из \$b\$, оставляя его с конечным результатом +3, что помещает его в диапазон от 0 до 2, а затем
В любом случае AL теперь представляет собой полином, эквивалентный результату по модулю 3 в форме \$ aX+b \$ с \$ 0 \le a \le 8, 0 \le b \le 2 \$.
PƵÈ%98%S3%J
Если бы можно было условно прыгать по АФ, то
Это еще одна инструкция BCD. В форме по умолчанию
O # sum of the inputs
S # split to a list of digits
3% # mod 3 each digit
J # join
||answer||
Предыдущее решение, также 16 байт 05AB1EСборка:
OS3%J