Like this:
; Before:
; Result is in DX:AX on the form ABCD:EFGH
; EAX = ????EFGH : AX contains EFGH, upper part of EAX has unknown content
; EDX = ????ABCD : DX contains ABCD (the 16 most siginficant bits
; of the multiplication result)
; like with EAX the upper (=most siginifcant)
; 16 bits of EDX also has unknown content.
and eax, 0x0000ffff ; clear upper bits of eax
; EAX = 0000EFGH
shl edx, 16 ; shift DX into position (will just shift the upper 16 junk bits away)
; EDX = ABCD000
or eax, edx ; combine in eax
; EAX = ABCDEFGH
The reason why this works is that ax refers to the 16 least significant bits of eax. Fore more detail see this SO question and the accepted answer. This method will also work for imul, but usually you have to be careful when dealing with signed numbers in assembly code.
A complete example:
bits 32
extern printf
global main
section .text
main:
push ebx
mov ax, 0x1234
mov bx, 0x10
mul bx
and eax, 0x0000ffff ; clear upper bits of eax
shl edx, 16 ; shift DX into position
or eax, edx ; and combine
push eax
push format
call printf
add esp, 8
mov eax, 0
pop ebx
ret
section .data
format: db "result = %8.8X",10,0
Compile with:
nasm -f elf32 -g -o test.o test.asm
gcc -m32 -o test test.o
Update:
On 32-bit machines it is usually easier and preferable to deal with 32-bit values if it is reasonable in the context. For example:
movzx eax, word [input1] ; Load 16-bit value and zero-extend into eax
movzx edx, word [input2] ; Use movsx if you want to work on signed values
mul eax, edx ; eax *= edx
Which also shows the usage of one of the newer, easier to use, mul instructions. You can also do as you're doing now and mov ax, [input1] and then later extend the size with movzx eax, ax.