following code is including a hexadecimal number (relevant to ASCII code assume that it is obtained from keyboard), I want to print this hex number to the screen but in "binary" using DOS Interrupt.
NUMLOCK is 45h.
[org 0x0100]
mov AL, 45 ;moving NUMLOCK hexadecimal(ASCII code) to AX
~~How to display its binary relevant to screen using DOS Interrupt?
DOS Interrupt has only these services outputting to screen:
AH = 02h -WRITE CHARACTER TO STANDARD OUTPUT
AH = 06h - DIRECT CONSOLE OUTPUT
AH = 09h - WRITE STRING TO STANDARD OUTPUT
AH = 40h - "WRITE" - WRITE TO FILE OR DEVICE (when called with STDOUT or STDERR file handle)
All of these only copy the value from input to the output, without any conversions or formatting, so usually your input should be ASCII character or string, or binary byte array.
But you should understand, that computers work in binary (low current/high current, which is often interpreted as 0 or 1). It doesn't understand any other value.
So when you have value 69
in CPU register al
, the al
is created from 8 bit cells on the CPU chip, and those cells are set as "0 1 0 0 0 1 0 1". The cells are called bits. So when you write in Assembly source mov al,45h
, the assembler during compilation creates this binary form for you, and stores it into machine code as single byte, because that's how mov al,imm8
instruction expects the value encoded.
So all you have to do for "binary string" is to go bit by bit from left to right, 8 times, and output character '0'
or '1'
to the screen, depending whether particular bit is clear, or set.
If your task would be to display the number in any other base (for example base 10 = decimal), you would have to do complex calculation to find out particular digits '6'
and '9'
forming the decimal format "69".
Any base of power of two is a bit simpler, because you need just to extract group of bits (for example in hexadecimal base 16 every single digit is exactly 4 bits part of value, notice how 45h
is in binary 0100
(4) and 0101
(5). But you still have to calculate groups of bits.
Only the binary base 2 output is basically ready to be printed, because that's the native way how computer stores numerical integer values in registers and memory.
It's absolutely crucial you realize what are bits, and what does it mean that al
is 8 bit register, and ax
is 16 bit register, and ah
is upper 8 bits of ax
, etc... If you don't understand how binary math works, and how those values are encoded in bits, you will have hard time with very simple Assembly tasks, like this one.
That's why I'm not even adding any code here, because you need first work out your math, to fully understand what "base N" encoding of value means.
After you will understand what the computer does work with, you will be very likely able to quickly check some bit-wise instructions like shifts or test, and do the bit check + output 0/1 to screen loop. It's very simple task, when you understand it.
EDIT: I decided to give you something more visual, I wrote "display binary 8 bit value" in the "Simple 8-bit Assembler".
It's not 8086, but:
- the instructions are "same" (just very limited set, not full 8086)
- registers completely different (four 8 bit general purpose A, B, C, D and two specialized PC and SP pointers)
- memory layout completely different (only 256 bytes of total memory, last 24 bytes are for "output" display)
- the principles are same, if you understand this one, 8086 will look much more easier, just lot more powerful and easier to code (more instructions).
Put the next code into page: http://schweigi.github.io/assembler-simulator/
And use "Step" to slowly go instruction by instruction and watch the "memory" and "register" views on right to see, what the instruction does (you can use x86 instruction reference guide to read what the instruction should do, I think all the descriptions will fit also this simple Assembler (although some are not 100% correct about flags, but you will not notice and better learn 8086 instructions from start)).
; init input values
MOV A, 0x45 ; A = 45h (this ASM needs "0x" for hexa)
MOV D, 240 ; point to output display after last digit
MOV C, 8 ; 8 chars (representing bits) to output
print_loop:
; convert lowest bit of A into ASCII digit '0' or '1'
MOV B, A ; copy of A (to preserve A value)
AND B, 1 ; extract lowest bit of A (B = 0 or 1)
ADD B, '0' ; turn the value 0/1 into ASCII '0'/'1'
; display '0' or '1' to "Output" window
MOV [D], B ; write the digit to output
DEC D ; update output pointer
; shift all bits in A to right by 1 position
SHR A, 1
; create space after 4 bits displayed
CMP C, 5 ; 8-3 = 5, fourth DEC is below (in future)
JNE .no_space
DEC D ; skip one position in output
.no_space:
; loop until 8 bits are displayed
DEC C ; counter of bits
JNZ print_loop ; repeat until C becomes 0
; stop simulator
HLT
Then you can think how you can turn this into 8086 version.
I would suggest to turn the loop logic to go from top-most bit, it's easier with 8086, as you can use many more instructions, like adc
(for example you can try to figure out, what is result of mov dl,24
adc dl,dl
... it may be quite interesting after shl
). Then you can output the digits directly by int 21h,2
(if you reverse the logic to test bits from left to right).
Or you can keep the same logic, and store the ASCII digits into memory buffer from end (make sure you have big enough buffer), and the print it with single int 21h, 9
.
Or ... there are million other ways how to calculate the same result (outputting to screen "0100 0101" ASCII digits from original value 45h
). It's only about outputting correct number at correct place and time, how you calculate it doesn't matter, as long as your result is correct.