See the x86 wiki tag for a tutorial reference guide and lots of good links to help materials and tutorials.
It takes enough code to break an integer into ASCII digits so you can allocate it to a function.
This is an optimized and fixed version of @hobbs print2Digits. (I also fixed the version in his answer, so this is also correct, but left optimizations for this).
print2Digits: ;; input in AL (0-99). (Or preferably already zero-extended to AX so we can omit CBW) ;; clobbers AX and DX cbw ; zero AH. Sign-extending AL does the job because AL is only allowed to be 0-99. mov dl, 10 div dl ; quotient in AL(first (high) digit), remainder in AH(second (low) digit) add ax, 0x3030 ; add '0' to al and ah at the same time. mov dl, ah ; save the 2nd digit mov ah, 0x0E ; BIOS call
Since we used the div to split the integer into two base10 digits, we need ah be zero. that is, that the dividends be in AX, and not just in AL with possible garbage in AH. We could save cbw or mov ah,0 if the caller did movzx ax, ch or something to zero ah .
(Except that 8086 does not have movzx , so you really would like xor ax,ax / mov al, ch .)
There is a system call DOS to print the entire string, so you can store the characters in a small buffer and print them all at once, as I do in this on AMD64 machines the Linux FizzBuzz . See also How to print an integer in Assembly level programming without printf from c library? for a more general int-> line in a buffer function or other multi-valued digital references in the x86 tag wiki
You can also use aam to divide AL (instead of AX) by 10 , avoiding the need to reset AH first. This is slightly faster than div r8 on modern Intel and AMD processors. However, the results are placed in registers opposite the div , which means additional instructions after aam . This balances the savings on mov dl, 10 and cbw .
print2Digits: ;; input in AL (0-99). (Ignores AH because we use AAM instead of div) ;; clobbers AX and DX aam ; like 'div' by 10, but with the outputs reversed, and input from AL only ;; quotient in AH (high digit), remainder in AL(low digit). (Opposite to div) add ax, 0x3030 ; add '0' to al and ah at the same time. mov dl, al ; save the low digit mov al, ah ; print high digit first mov ah, 0x0E ; BIOS call
Even if we want to save to a string (and make one call to the string function pr int- or system call), we will have to swap al and ah before storing AX in memory (for example, xchg al,ah , or more efficiently on modern equipment, but Requires 186: rol ax,8 ). div produces them in the correct order inside AX.
For the 386, where you have a 32-bit address size, we can save one instruction:
lea dx, [eax + 0x3030] ; need a 32bit addressing mode to use eax as a source reg. Adds '0' to both digits at once, with a different destination. mov al, dh ; then get ready to print the high byte first
lea requires an address size prefix, a 2-byte mod / rm and a 32-bit offset, so it loses a lot in code size, but it saves one instruction.
Using lea to read from eax after writing the div ax is likely to be faster on Sandybridge processors, especially Haswell and later, but on Intel pre-SnB partial register stop will allow better use of a clean 16-bit version with separate add and mov instructions .
Of course, if you really care about performance, you would use the multiplicative inverse instead of dividing by 10. And you usually donβt write 16-bit code that makes legacy BIOS calls!