HALICERY

free-time coding, hardware dev, articles

Top
Home 8042 Blogs About
Home IntelEssential 16/32-bit Instructions AAM

Last modified: Thu Jun 18 16:56:42 UTC+0200 2026 © A. Tarpai


Decimal stuff: AAM

8086 introduced unpacked (ASCII) BCD along with AAM/AAD. All these instructions are invalid in 64-bit mode.

AAM

Originally made for 10-base unpacked ASCII adjustment after multiply.

F. ex. multiplying 9 x 9 = 81, AL contains 51h. AAM makes 2 x 10-base digits from AL into AH/AL:

  AH   AL                      AH   AL
+----+----+                  +----+----+
| xx | 51 |  ---- AAM ---->  | 08 | 01 |
+----+----+     (D4 0A)      +----+----+

But the exact operation is an unsigned divide, with the 8-bit DIVISOR following the opcode. It works with any other imm8 value (except that zero makes exception). AH gets the QUOTIENT, AL the REMAINDER.

Compared to insigned DIV:

      DIV r/m8                                 AAM imm8

   +-----------+                                +----+
   |   WORD    |   AX                           |BYTE|   AL
   +-----------+                                +----+

          +----+                                +----+
          |BYTE|   R/M                          |BYTE|   imm8
          +----+                                +----+
_______________________________       _______________________________
          +----+                                +----+
          |BYTE|   AL=QUOTIENT                  |BYTE|   AL=REMAINDER!
          +----+                                +----+

          +----+                                +----+
          |BYTE|   AH=REMAINDER                 |BYTE|   AH=QUOTIENT!
          +----+                                +----+

NB:

AAD

Originally made for 10-base ASCII adjustment before divide.

F. ex. lets say we have a 2-digit unpacked decimal number in AX as 35 and we try to divide by 7. Before executing DIV, AAD can convert this number to its binary value in AL:

  AH   AL                      AH   AL
+----+----+                  +----+----+
| 03 | 05 |  ---- AAD ---->  | 00 | 23 |   23H = 35
+----+----+     (D5 0A)      +----+----+

But the exact operation is an unsigned multiple, with the 8-bit multiplier following the opcode. It works with any other imm8 value. AH is zeroed out (so AX is ready to divide), while AL = AH * imm8 + AL.

Equivalent to this pseudo-code (CPU cannot multiply AH only):

AAD i8 =

MUL AH, i8
ADD AL, AH
XOR AH, AH

Some usage of AAM and AAD

These can be actually handy, when crunching bits.

AAM 2n

AAM can separate an 8-bit value in AL into higher- and lower n-bits into AH/AL, leaving zero in all other bits.

  7   6   5   4   3   2   1   0      7   6   5   4   3   2   1   0
 _______________________________    _______________________________
| . | . | . | . | . | . | . | . |  |_7_|_6_|_5_|_4_|_3_|_2_|_1_|_0_|

               AH                                 AL
 _______________________________    _______________________________
| 0   0   0   0   0   0   0 |_7_|  | 0 |_6_|_5_|_4_|_3_|_2_|_1_|_0_|   AAM 128      AAM 0x80
 _______________________________    _______________________________
| 0   0   0   0   0   0 |_7_|_6_|  | 0   0 |_5_|_4_|_3_|_2_|_1_|_0_|   AAM 64       AAM 0x40
 _______________________________    _______________________________
| 0   0   0   0   0 |_7_|_6_|_5_|  | 0   0   0 |_4_|_3_|_2_|_1_|_0_|   AAM 32       AAM 0x20
 _______________________________    _______________________________
| 0   0   0   0 |_7_|_6_|_5_|_4_|  | 0   0   0   0 |_3_|_2_|_1_|_0_|   AAM 16       AAM 0x10
 _______________________________    _______________________________
| 0   0   0 |_7_|_6_|_5_|_4_|_3_|  | 0   0   0   0   0 |_2_|_1_|_0_|   AAM 8        AAM 0x08
 _______________________________    _______________________________
| 0   0 |_7_|_6_|_5_|_4_|_3_|_2_|  | 0   0   0   0   0   0 |_1_|_0_|   AAM 4        AAM 0x04
 _______________________________    _______________________________
| 0 |_7_|_6_|_5_|_4_|_3_|_2_|_1_|  | 0   0   0   0   0   0   0 |_0_|   AAM 2        AAM 0x02
 _______________________________    _______________________________
|_7_|_6_|_5_|_4_|_3_|_2_|_1_|_0_|  | 0   0   0   0   0   0   0   0 |   AAM 1        AAM 0x01

AAM 1

Zeroes AL and saves AL into AH.

AAM 16 for print hex

Use AMM for a hex writer with imm8 = 16, if don't want to bother masking and shifting nibbles.

F. ex. to print out the byte value EAh could be easier using aam 16 (codebytes D4 10):

  AH   AL                      AH   AL
+----+----+                  +----+----+
| xx | EA |  --- AAM 16 -->  | 0E | 0A |
+----+----+                  +----+----+

Then print value in AH followed by AL.

AAM/AAD 16 for packed/unpacked BCD conversion

AAM/AAD 16 also converts between packed/unpacked BCD:

+----+----+  <--- AAM 16 ------  +----+----+
| 08 | 09 |                      | 00 | 89 |
+----+----+  ------ AAD 16 --->  +----+----+

AAM 128

Simple test for Scan-1 Break Code read from 8042. Break codes have bit7 set. Separate msb, sign (0 or 1) into AH from AL using aam 128 (codebytes D4 80):

  AH   AL                       AH   AL
+----+----+                   +----+----+
| xx | FF |  --- AAM 128 -->  | 01 | 7F |
+----+----+                   +----+----+