Binary Coded Decimal and Assembly

I’ve been learning a little bit of assembly over the last few days. Why in the world would I want to learn assembly? I don’t know. Why not?

The code I’ve been writing has been a very simple boot loader. I followed another tutorial on how to get it to load and write some text. After that I thought it would be nice to see the system time. How hard could it be? Famous last words right? I’m using NASM on windows to compile the asm and then qemu to run the image. You can figure that part out with google if you want.

To start with I found a table with the different interrupts available on x86. As an example here is one https://stanislavs.org/helppc/int_table.html. INT 0X1A is the real time clock interrupt. To control what the interrupt does you must set the AH register. For example 0x02 will read the clock. The code looks like this

mov ah, 0x02
int 0x1A

This will set the AH register to 0x02 and then call the 0x1A interrupt. This will set a few other registers on return. CH will be set to hours and CL will be set to minutes. These values will be in BCD (binary coded decimal). What this means is the first 8 bits of the register will be the first number and the second 8 bits will be the second number. For example is the time is 12:34 CH will be set to 00010010. When you split the register into nibbles you will get 0001 and 0010. 0001 in binary is 1 in decimal. 0010 in binary is 2 in decimal. CL will be set to 00110100. This breaks out to 0011 and 0100 or 3 and 4. But how do you decode that in assembly? The first number is pretty easy. A shift right will move the bits over and drop the bits that aren’t important.

;move ch into al (al is used for tty output)
mov al, ch
;shift right 4 bits
shr al, 0x04

This will take 00010010 and move everything right 4 bits. AL will be 000000001 afterwards. Then just print the value.

;set ah to 0x0e to set int 0x10 as character tty output
move ah, 0x0e
;add 30H to al to start at the 0 character in the ascii table.
add al, 0x30
;call int 0x10 to output character
int 0x10

This will output “1” to the screen. The next number in the sequence is a little different. By using an and operation you can mask bits. By anding 0x0F to 00010010 you will make the top 4 bits. In binary this will look like :

Value00010010
Mask00001111
AND00000010

After the AND operation the value is correct and can be printed.

;move ch back into al
mov al, ch
;and to mask upper half
and al, 0x0F

;add 30H to al to start at the 0 character in the ascii table.
add al, 0x30
;call int 0x10 to output character ah is already set
int 0x10

The screen will now have 12 on it. Next is a colon.

;set al to 0x3A the hex code for ascii :
move al, 0x3A
;cal int 10 to print
int 0x10

Now to print the minutes. It’s the same thing as the hours but starting with CL instead of CH.

;move cl into al (al is used for tty output)
mov al, cl
;shift right 4 bits
shr al, 0x04
;add 30H to al to start at the 0 character in the ascii table.
add al, 0x30
;call int 0x10 to output character
int 0x10
;move cl back into al
mov al, cl
;and to mask upper half
and al, 0x0F
;add 30H to al to start at the 0 character in the ascii table.
add al, 0x30
;call int 0x10 to output character
int 0x10

And that’s that. Now the screen will read 12:34. The thing that really messed me up while doing that was that the clock in qemu is set to UTC. It was very hard to debug when i kept getting 14 for hours instead of 09. I’m sure their is a better way to do this but it works and that’s all I wanted.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: