Build time display

Hi, I am trying to display the actual hours / minutes / seconds, this is my sample code:

MOV AH, 2Ch INT 21h MOV AH, 0Eh MOV AL, CH INT 10h MOV AL, 3Ah INT 10h MOV AL, CL INT 10h MOV AL, 3Ah INT 10h MOV AL, DH INT 10h ret 

Here you can see what the console shows.

enter image description here

+4
source share
3 answers

See the 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 #: print single character int 0x10 ; print high digit first. Doesn't clobber anything, so AH still holds 0x0E after mov al, dl int 0x10 ; print the low digit 2nd ret 

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 #: print single character int 0x10 ; print first digit. Doesn't clobber anything, so AH still holds 0x0E after mov al, dl int 0x10 ; print second digit ret 

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!

+4
source

You need a printing procedure to print bytes as numbers, instead of writing them directly to the screen as characters. Fortunately, since you only need to deal with values ​​between 0 and 59, and since you need leading zeros, the problem is pretty simple. Assuming the value will be printed in AX:

 print2Digits: ;; input in AX (0-99) ;; clobbers AX and DX, save them if needed MOV DL, 0Ah ; divide by: 10 DIV DL ; first digit in AL (quotient), second digit in AH (remainder) MOV DX, AX ; save the digits ADD AL, 30h ; ASCII '0' MOV AH, 0Eh ; set up print INT 10h ; print first digit. MOV AL, DH ; retrieve second digit ADD AL, 30h INT 10h ; print it RET 
+4
source

You are trying to map values ​​(time) in CH , CL and DH registers as aschi characters.

Suppose your CH contains a value of 15h.

Now when you put this value in AL and call int 10h , it displays the aschi character that matches the value of 15h. If you want to display decimal digits within 15 hours, you will have to call a display procedure that violates the value in the register and crosses out the digits displayed in it, and not just the representation of aschi in that value.

Suppose you have a decimal 85 in the register. Now you need to print on the screen "8" and "5". Thus, you need to convert the value 85 to to aschi-56 ("8") and aschii-53 ("5"). Then put them into the registers one by one and call int 10 twice to display "85".

hobbs answer shows how to do this.

Here is another tutorial

+2
source

Source: https://habr.com/ru/post/1436198/


All Articles