• Hexadecimal code. Illustrated tutorial on digital graphics

    Hexadecimal number system, is by far the most popular means of compactly recording binary numbers. Very widely used in the development and design of digital technology.

    As the name suggests, the base of this system is the number sixteen 16 or in hexadecimal 10 16. To avoid confusion, when writing numbers in number systems other than decimal, we will indicate the base of the number system at the bottom right of the main number notation. Since the base of the system is the number sixteen, it means that to represent the numbers we need sixteen digits. The first ten digits are taken from the decimal system familiar to us (0,1,..,8,9) and six letters of the Latin alphabet (a,b,c,d,e,f) are also added. For example, in the hexadecimal number 3f7c2, the letters "f" and "c" are hexadecimal digits.

    Counting in hexadecimal is similar to counting in decimal. Let's try to count and write numbers by constructing them from the available sixteen digits:

    Zero - 0 ;
    One - 1 ;
    Two - 2 ;
    ...
    and so on…
    ...
    Eight - 8 ;
    Nine - 9 ;
    Ten - a;
    Eleven - b;
    Twelve - c;
    Thirteen - d;
    Fourteen - e;
    Fifteen - f;

    What to do next? All the numbers are gone. How to depict the number Sixteen? Let's do the same as we did in decimal system. There we introduced the concept of ten, here we will introduce the concept of “sixteen” and say that sixteen is one “sixteen” and zero units. And this can already be written down - “10 16”.

    So, Sixteen - 10 16 (one "sixteen", zero ones)
    Seventeen - 11 16 (one "sixteen", one unit)
    ...
    and so on…
    ...
    Twenty five - 19 16 (one "sixteen", nine ones)
    Twenty six - 1a 16 (one "sixteen", ten units)
    Twenty seven - 1b 16 (one "sixteen", eleven ones)
    ...
    and so on…
    ...
    Thirty - 1e 16 (one "sixteen", fourteen ones)
    Thirty one - 1f 16 (one "sixteen", fifteen ones)
    Thirty two - 20 16 (two sixteens, zero ones)
    Thirty three - 21 16 (two sixteens, one one)
    ...
    and so on…
    ...
    two hundred fifty five - ff 16 (fifteen by "sixteen", fifteen ones)

    two hundred fifty six - 100 16 (one "two hundred fifty-six", zero "sixteen", zero ones)
    two hundred fifty seven - 101 16 (one "two hundred fifty-six", zero through "sixteen", one one)
    two hundred fifty eight - 102 16 (one "Two hundred fifty six", zero through "sixteen", two ones)
    ...
    and so on...
    ...

    Whenever we have exhausted the set of digits to display the next number, we enter larger units of counting (i.e. counting by “sixteen”, “two hundred and fifty-six”, etc.) and write the number with an extension of one digit .

    Consider the number 3e2c 16 written in hexadecimal number system. About it we can say that it contains: three x four thousand ninety-six, “e” (fourteen) x two hundred fifty-six, two x sixteen and “c” (twelve) ones. And you can get its value through the numbers included in it as follows.

    3e2c 16 = 3 *4096+14 *256+2 *16+12 *1, here and below the * (asterisk) sign means multiplication.

    But the series of numbers 4096, 256, 16, 1 is nothing more than integer powers of the number sixteen (the base of the number system) and therefore can be written:

    3e2c 16 = 3 *16 3 +14 *16 2 +2 *16 1 +12 *16 0

    Similarly for a hexadecimal fraction (fractional number) for example: 0.5a2 16 about it we can say that it contains: five sixteenths, “a” (ten) two hundred and fifty-sixths and two four thousand and ninety-sixths. And its value can be calculated as follows:

    0.5a2 16 = 5 *(1/16) + 10 *(1/256) + 2 *(1/4096)

    And here is a series of numbers 1/16; 1/256 and 1/4096 are nothing more than integer powers of sixteen and we can also write:

    0.5a2 16 = 5 *16 -1 + 10 *16 -2 + 2 *16 -3

    For the mixed number 7b2.1f9 we can similarly write:

    7b2.1f9 = 7 *16 2 +11 *16 1 +2 *16 0 +1 *16 -1 +15 *16 -2 +9 *16 -3

    Let's number the digits of the integer part of some hexadecimal number, from right to left, as 0,1,2...n (numbering starts from zero!). And the digits of the fractional part, from left to right, like -1,-2,-3...-m, then the value of a certain hexadecimal number can be calculated using the formula:

    N = d n 16 n +d n-1 16 n-1 +…+d 1 16 1 +d 0 16 0 +d -1 16 -1 +d -2 16 -2 +…+d -(m-1) 16 -(m-1) +d -m 16 -m

    Where: n- the number of digits in the integer part of the number minus one;
    m- the number of digits in the fractional part of the number
    d i- digit standing in i-th rank

    This formula is called the formula for the bitwise expansion of a hexadecimal number, i.e. number written in hexadecimal number system. If we replace the number sixteen in this formula with some arbitrary number q, then we obtain the expansion formula for the number written in qth number system, i.e. with base q:

    N = d n q n +d n-1 q n-1 +…+d 1 q 1 +d 0 q 0 +d -1 q -1 +d -2 q -2 +…+d -(m-1) q - (m-1) +d -m q -m

    Using this formula you can always calculate the value of a number written in any positional system notation with a base q.

    Other number systems can be found on our website using the following links.

    Now there is a very easy walk ahead associated with the hexadecimal number system. In this case, we hope you suspect, and probably rightly so, that we should now have 16 different digits.

    But, as we know, there are only ten traditional (“Arabic”) numerals. And it takes sixteen. It turns out that six characters are missing.

    Comment
    Thus, a purely design task arises on the topic “Signs” - to come up with the missing symbols for the numbers
    .

    This means that at one time specialists needed to come up with some new signs. But once upon a time, at the beginning of the computer era, there was not much choice in signs. Programmers only had numbers and letters at their disposal. Therefore, they took the elementary path: they took the first letters of the Latin alphabet as numbers, especially since historically this was not the first time (we have already mentioned that initially many peoples used letters instead of numbers).

    Comment
    We hope that everyone understands why in this case it is impossible to use, for example, the numbers “10”, “11”, “12”, etc.? Because if we are talking about the hexadecimal number system, then it should be sixteen numbers, not numbers
    .

    And the decimal number "10" began to mean Latin letter"A" (more precisely, "number A"). Accordingly, the numbers “B”, “C”, “D”, “E” and “P” come next.

    Since we intended to build a hexadecimal system, starting from zero, this is exactly 16 digits. For example, the digit "D" is the decimal number "13", and the digit "F" is the decimal number "15".

    When we add one to the hexadecimal number “F”, then, since we have run out of these digits, we put “O” in this digit, and transfer one to the next digit, so it turns out that the decimal number “16” will be represented in the hexadecimal number system by the number "10", i.e. it turns out to be a "hexadecimal ten". Let's combine decimal and hexadecimal numbers into a single table (Table 4.5).

    Table 4.5. Matching decimal and hexadecimal numbers.

    Decimal number Hexadecimal number Decimal number Hexadecimal number
    0-9 0-9 29 1D
    10 A 30 1E
    11 IN 31 1F
    12 WITH 32-41 20-29
    13 D 42-47 2A-2F
    14 E 48-255 30-FF
    15 F 256 100
    16 10 512 200
    17-25 11-19 1024 400
    26 1A 1280 500
    27 1B 4096 1000
    28 1C

    The hexadecimal system is used to write more compactly binary information. In fact, a “hex thousand”, consisting of four digits, occupies thirteen digits in binary (1000 16 = 1000000000000 2).

    When discussing number systems, “tens”, “hundreds” and “thousands” have repeatedly appeared, so it is necessary to pay attention to the so-called “round” numbers.

    To write programs in Assembly, you need to understand the hexadecimal number system. There is nothing complicated about it. We use the decimal system in life. I'm sure you all know it, so I'll try to explain the hexadecimal system using an analogy with the decimal system.

    So, in the decimal system, if we add a zero to any number on the right, then this number will increase by 10 times. For example: 1 x 10 = 10; 10 x 10 = 100; 100 x 10 = 1000, etc. In this system we use numbers from 0 to 9, i.e. ten different numbers (in fact, that’s why it’s called decimal).

    In the hexadecimal system, we use sixteen "digits". I specifically wrote the word “digits” in quotation marks, because... It doesn't just use numbers. And really, how can that be? Let me explain: from 0 to 9 we count in the same way as in decimal, but then it will be like this: A, B, C, D, E, F. The number F is not difficult count, it will be equal to 15 in the decimal system (see Table 1).

    Decimal number

    Hexadecimal number

    Table 1. Decimal and hexadecimal systems.

    Thus, if we add a zero to the right of any number in the hexadecimal system, then this number will increase by16 once.

    Example 1: 1 x 16 = 10; 10 x 16 = 100; 100 x 16 = 1000, etc.

    Were you able to distinguish hexadecimal numbers from decimal numbers in Example 1? And from this series: 10, 12, 45, 64, 12, 8, 19? These can be either hexadecimal or decimal. In order to avoid confusion and the computer to be able to clearly distinguish one number from another, in Assembly it is accepted after hexadecimal number put the symbol h or H ( H is an abbreviation for English. hexadecimal (hexadecimal). For brevity, it is sometimes simply called Hex ) . And don’t put anything after the decimal. Because numbers from 0 to 9 in both systems have the same meanings, then the numbers written as 5 and 5h are the same.

    That. Example 1 (see above) would be more correct to write like this: 1 x 16 = 10h; 10h x 16 = 100h; 100h x 16 = 1000h. Or like this: 1h x 10h = 10h; 10h x 10h = 100h; 100h x 10h = 1000h.

    We will look at why the hexadecimal system is needed in subsequent issues. And in at the moment For our example program below, we need to know about the existence of hexadecimal numbers.

    So, let's summarize. The hexadecimal number system consists of 10 digits (from 0 to 9) and 6 letters of the Latin alphabet (A, B, C, D, E, F). If we add a zero to the right of any number in the hexadecimal system, then this number will increase by16 once. It is very important to understand this topic , since we will constantly use it when writing programs.

    Now a little about how I will build examples in Assembly. It is not very convenient to present them in HTML format, so first there will be the program code itself with numbered lines, and immediately after it there will be explanations and notes.

    Something like this:

    lines Program code
    (1) mov ah,9

    Explanations:

    In line (1) we do this, and in line (15) we do that.

    Huge request: DO NOT copy programs from a page to the clipboard and then paste them into Notepad (or anywhere else)! Retype them manually in a text editor. If you have a printer, then select the program, print the selected fragment, and then transfer it into the editor from paper. All examples must be typed yourself! This will speed up the memorization of operators.

    And one more thing. There is no difference between lowercase and UPPERCASE letters in assembler. Records of the form:

    The assembler perceives them the same way. You can, of course, force the assembler to distinguish between lowercase and UPPERCASE characters, but we will not do this for now. To make the program easier to read, it is best to type operators in lowercase letters, and start the names of subroutines and labels in uppercase letters. But it depends on who will be comfortable.

    So, let's move on to our first program:

    (1) CSEG segment

    (2)org 100h

    (4)Begin:

    (6) mov ah,9

    (7) mov dx,offset Message

    (8) int 21h

    (10) int 20h

    (11)

    (12) Message db "Hello, world!$"

    (13)CSEG ends

    (14) end Begin

    In order to explain all the operators in this example, we will need several editions. Therefore, we will simply omit the description of some commands to at this stage. Just assume that this is how it should be. We will look at these operators in detail in the very near future. So, lines numbered (1), (2) and (13) you simply ignore.

    Lines (3), (5), (9) and (11) are left blank. This is done for clarity. The assembler will simply omit them.

    Now let's move on to consider the remaining operators. The program code begins with line (4). This is a mark that tells the assembler to the beginning of the code. Line (14) contains the operators end Begin ( Begin English start; end end). This is the end of the program. In general, instead of the word Begin something else could have been used. For example, Start:. In this case, we would have to end the program End Start (14).

    Lines (6) (8) display the message Hello, world!. Here we will have to briefly talk about processor registers (we will look at this topic in more detail in the next issue).

    A processor register is a specially allocated memory for storing a number.

    For example:

    If we want to add two numbers, then in mathematics we write it like this:

    A, B and C these are a kind of registers (if we talk about a computer) in which some data can be stored. A=5 can be read as: Assign A the number 5 .

    To assign a register a value, there is a mov operator in Assembler (from the English move load). Line (6) should be read like this: Loading into the register A.H.number 9 (in other words, we assign A.H.number 9). Below we will look at why this is necessary.

    In line (7) we load into the register DX message address for output (in in this example this will be a stringHello, world!$).

    Interrupts will be covered in detail in subsequent issues. Here I will say a few words.

    Interrupt MS-DOS it is a kind of subroutine (part MS-DOS), which resides permanently in memory and can be called at any time from any program.

    Let's consider the above using an example (Notes in small print):

    Program for adding two numbers

    HomePrograms

    A=5 We enter the value 5 into variable A

    B=8 into variable B the value 8

    Calling Subroutines Addition

    now C equals 13

    A=10 same thing, just different numbers

    B=25

    Calling Subroutines Addition

    now C is equal to 35

    End of the Program

    Subroutine Addition

    C=A+B

    ReturnFromSubroutine we return to the place from which we called

    EndSubroutine

    In this example, we called the subroutine twice Addition, which added two numbers passed to it in variables A and B . The result is placed in the variable C. When a subroutine is called, the computer remembers where it was called from, and then, when the subroutine has finished running, the computer returns to the place from which it was called. That. You can call subroutines an indefinite number of times from anywhere.

    When executing line (8) of an Assembly program, we call a subroutine (in this case called an interrupt), which displays the line on the screen. For this purpose, we, in fact, place the necessary values ​​in the registers. All necessary work(line output, cursor movement) is taken over by the subroutine. This line can be read like this: call the twenty-first interrupt ( int from English interrupt interrupt). Please note that after the number 21 there is a letter h . This, as we already know, is a hexadecimal number (33 in decimal). Of course, nothing prevents us from replacing the line int 21h to int 33. The program will work correctly. It's just common practice in Assembler to indicate the interrupt number in hexadecimal.

    In line (10) we, as you may have guessed, call interrupt 20 h . To call this interrupt, you do not need to specify any values ​​in the registers. It performs only one task: exiting the program (exiting to DOS). As a result of the execution of interrupt 20h, the program will return to where it was launched from (loaded, called). For example, in Norton Commander or DOS Navigator.

    Line (12) contains the message to be output. First word ( message message) message title. It can be anything (for example, mess or string, etc.). ABOUT Pay attention to line (7), in which we load into the register DX our message address.

    We can create another line, which we will call Mess2. Then, starting from line (9), insert the following commands:

    (10) mov dx,offset Mess2

    (13) Message db "Hello, world!$"

    (14) Mess2 db "It's ME! $"

    and reassemble our program. I hope you can guess what will happen

    Pay attention to the last character in the lines Message and Mess2 - $. It points to the end of the line. If we remove it, then 21 h the interrupt will continue outputting until it encounters a character somewhere in memory $. On the screen we will see garbage .

    If you have a debugger, you can see how our program will work.

    The purpose of this issue was not to understand in detail with each operator. This is impossible, because you don't have enough knowledge yet. I believe that after 3-4 releases you will understand the principle and structure of an Assembly program. Perhaps the Assembly language seemed extremely complicated to you, but believe me, this is at first glance.

    To represent numbers in a microprocessor it is used binary number system.
    Moreover, any digital signal can have two stable states: " high level" And " low level" IN binary system notation To represent any number, two digits are used, respectively: 0 and 1. Arbitrary number x=a n a n-1 ..a 1 a 0 ,a -1 a -2 …a -m will be written in binary number system as

    x = a n ·2 n +a n-1 ·2 n-1 +…+a 1 ·2 1 +a 0 ·2 0 +a -1 ·2 -1 +a -2 ·2 -2 +…+a -m ·2 -m

    Where a ibinary digits(0 or 1).

    Octal number system

    In the octal number system, the base digits are the numbers from 0 to 7. 8 low-order ones are combined into a high-order one.

    Hexadecimal number system

    In the hexadecimal number system, the base digits are the numbers from 0 to 15 inclusive. To designate base digits greater than 9 with one symbol, in addition to the Arabic numerals 0...9 in the hexadecimal number system, letters of the Latin alphabet are used:

    10 10 = A 16 12 10 = C 16 14 10 = E 16
    11 10 = B 16 13 10 = D 16 15 10 = F 16.

    For example, the number 175 10 in hexadecimal number system will be written as AF 16. Really,

    10·16 1 +15·16 0 =160+15=175

    The table shows numbers from 0 to 16 in decimal, binary, octal and hexadecimal number systems.

    Decimal Binary Octal Hexadecimal
    0 0 0 0
    1 1 1 1
    2 10 2 2
    3 11 3 3
    4 100 4 4
    5 101 5 5
    6 110 6 6
    7 111 7 7
    8 1000 10 8
    9 1001 11 9
    10 1010 12 A
    11 1011 13 B
    12 1100 14 C
    13 1101 15 D
    14 1110 16 E
    15 1111 17 F
    16 10000 20 10

    Binary-octal and binary-hexadecimal conversions

    The binary number system is convenient for performing arithmetic operations using microprocessor hardware, but is inconvenient for human perception because it requires large quantity discharges. Therefore in computer technology In addition to the binary number system, octal and hexadecimal number systems are widely used for a more compact representation of numbers.

    Three ranks octal system Numbers implement all possible combinations of octal digits in the binary number system: from 0 (000) to 7 (111). To convert binary number to octal, you need to combine binary digits into groups of 3 digits (triads) in two directions, starting from the separator of the integer and fractional parts. If necessary, you need to add insignificant zeros to the left of the original number. If a number contains a fractional part, then to the right of it you can also add insignificant zeros until all triads are filled. Each triad is then replaced by an octal digit.

    Example: Convert the number 1101110.01 2 to octal number system.

    We combine binary digits into triads from right to left. We get

    001 101 110,010 2 = 156,2 8 .

    To convert a number from octal to binary, you need to write each octal digit in binary code:

    156,2 8 = 001 101 110,010 2 .

    The four digits of the hexadecimal number system implement all possible combinations of hexadecimal digits in the binary number system: from 0 (0000) to F(1111). To convert a binary number to hexadecimal, you need to combine the binary digits into groups of 4 digits (tetrads) in two directions, starting from the decimal separator. If necessary, you need to add insignificant zeros to the left of the original number. If the number contains a fractional part, then to the right of it you also need to add insignificant zeros until all notebooks are filled. Then each tetrad is replaced hexadecimal digit.

    Example: Convert the number 1101110.11 2 to hexadecimal number system.

    We combine binary digits into tetrads from right to left. We get

    0110 1110.1100 2 = 6E,C 16 .

    To convert a number from hexadecimal to binary, you need to write each hexadecimal digit in binary code.

    1. Use this method if you are not familiar with the hexadecimal number system. The simple, intuitive method can be used by almost anyone. If you know various systems number, read about it, which is described below.

      • If you don't know anything at all about the hexadecimal system, start by learning the basic concepts.
    2. Raise 16 to the power from 1 to 5 and write down the results. The place of each digit of a hexadecimal number is the result of raising the number 16 to the power, as is the place of each digit decimal number is the result of raising the number 10 to a power. The following list of results of raising 16 to various powers will be useful in the conversion process:

      • 16 5 = 1048576
      • 16 4 = 65536
      • 16 3 = 4096
      • 16 2 = 256
      • 16 1 = 16
      • If the decimal number to convert is greater than 1048576, raise 16 to greater degree, and add the result to the list.
    3. From the list, find the largest number that is less than the given decimal number. Write down the given decimal number that you want to convert to hexadecimal. Look at the list above and find the greatest result (raising 16 to the power) that is less than the given decimal number.

      • For example, you need to convert the decimal number 495 to hexadecimal. Select the number 256 from the list.
    4. Divide the decimal number by the chosen result of raising 16 to the power. Work with integer division results - ignore the numbers after the decimal point.

      • In our example: 495 ÷ 256 = 1.93..., so work with the number 1 (this is the integer quotient of division).
      • The resulting result is the first digit of the hexadecimal number. In this case, you divided the given decimal number by 256, so the 1 is in the 256th place.
    5. Find the first remainder. That is, the remainder of dividing a given decimal number by the selected number (divisor). The remainder is calculated in the same way as for long division.

      • Multiply the resulting quotient by the divisor. In our example: 1 x 256 = 256 (that is, 1 in hexadecimal represents 256 in base 10).
      • Subtract the result of the multiplication from the given decimal number: 495 - 256 = 239 .
    6. Divide the remainder by the next (in the list) result of raising 16 to the power. Look at the list with the results of raising 16 to different powers. Find the result that is below the result you chose for the previous division. Divide the remainder by the chosen number to find the next digit of the hexadecimal number (if the remainder is less than the chosen number, the next digit is 0).

      • 239 ÷ 16 = 14 . Ignore the numbers after the decimal point.
      • This is the second digit of a hexadecimal number, which is in the 16th place. Any number from 0 to 15 can be represented by a single hexadecimal digit. The resulting numbers will be converted and placed at the end of this method.
    7. Find the second remainder. To do this, multiply the resulting quotient by the divisor, and then subtract the result of the multiplication from the first remainder. The second remainder must be converted to a hexadecimal digit.

      • 14 x 16 = 224.
      • 239 - 224 = 15, that is, the remainder is 15 .
    8. Repeat the above process until the remainder is less than 16. If the remainder is a number between 0 and 15, it can be expressed as a single hexadecimal digit. This digit will be the last digit.

      • The last digit of a hexadecimal number is 15, which is in the ones place.
    9. Convert the resulting numbers and write down the answer. You have found all the digits of a hexadecimal number. But they are written in the decimal number system. To convert each digit to base 16, use the following instructions:

      • The numbers from 0 to 9 do not change.
      • 10 = A; 11 = B; 12 = C; 13 = D; 14 = E; 15 = F
      • In our example, you got the numbers (1)(14)(15). That is, the hexadecimal number will be written like this: 1EF.
    10. Check the answer. This is easy to do if you know the basics of the hexadecimal number system. Convert each hexadecimal digit to a base 10 digit, then multiply by the result of raising 16 to the specific power that corresponds to the digit's position. In our example:

      • 1EF → (1)(14)(15)
      • Work with numbers from right to left. 15 is in the ones place: 16 0 = 1, so 15 x 1 = 15.
      • The next digit is in the 16th place: 16 1 = 16, so 14 x 16 = 224.
      • The next digit is in the 256 place: 16 2 = 256, so 1 x 256 = 256.
      • Add up the results found: 256 + 224 + 15 = 495, that is, you get the original decimal number.