Представление целых чисел в GAS


  
 
 
  К оглавлению
 

1.Целочисленные константы

1.1. Порядок записи

Целочисленные константы могут быть представлены в виде бинарного числа, записанного в двоичной системе счисления или числа, записанного в системе счисления десятичной, восьмеричной или шестнадцатеричной .
Бинарным числом, представленным в двоичной системе счисления, предваряет ‘0b’ или ‘0B’, и следующие за ними цифры ‘0’ или ‘1’.
Числом, представленным в восьмеричной системе счисления, относятся константы, имеющие впереди ‘0’ одно и более допустимых восьмеричных чисел ‘01234567’
К числам, представленным в десятичной системе счисления, относятся константы, имеющие впереди первую ненулевую цифру и/или следуемые за ней одно или более десятичных цифр (‘0123456789’).
К числам, представленным в шестнадцатеричной системе счисления, относятся константы, начинающиеся с ‘0x’ или ‘0X’ и следуемых за ними шестнадцатеричных цифр ‘0123456789abcdefABCDEF’.
При этом вне зависимости от используемой системы счисления, саму запись константного числа в непосредственном операнде необходимо предварять префиксом ‘$’. Иначе, оно будет определено как прямой адрес памяти, который тоже можно отнести к константному значению.

2. Использование систем счисления

В таблице 1 приводится соотношение перечисленных выше систем счисления, применяемых для представления целых чисел в константных выражениях и операциях вывода целых чисел.
Таблица 1
3 бит 2 бит 1 бит 0 бит DEC BIN OCT HEX
0 0 0 0 0 0b0000 00 0x00
0 0 0 1 1 0b0001 01 0x01
0 0 1 0 2 0b0010 02 0x02
0 0 1 1 3 0b0011 03 0x03
0 1 0 0 4 0b0100 04 0x04
0 1 0 1 5 0b0101 05 0x05
0 1 1 0 6 0b0110 06 0x06
0 1 1 1 7 0b0111 07 0x07
1 0 0 0 8 0b1000 10 0x08
1 0 0 1 9 0b1001 11 0x09
1 0 1 0 10 0b1010 12 0x0A
1 0 1 1 11 0b1011 13 0x0B
1 1 0 0 12 0b1100 14 0x0С
1 1 0 1 13 0b1101 15 0x0D
1 1 1 0 14 1110B 16 0x0E
1 1 1 1 15 0b1111 17 0x0F
Начиная со школы мы привыкли оперировать с числами в десятичной системе счисления, а вычислительная техника в двоичной, программисты же пишущие на языке GNU С/Assembler для удобства в шестнадцатеричной, потому что она позволяет легко разделить байты пополам на 4-ре бита. Восьмеричная же система счисления используется в основном для задания системными администраторами прав доступа к объектам и устройствам файловой системы, потому что позволяет разбивать данные на три бита.
Для перевода из одной системы счисления в другую можно использовать специальные калькуляторы или на основе таблицы 1, простым пересчетом выполнить расчет следующим образом.
К примеру число в двоичной системе счисления 0b1000, которое соответствует 8 в десятичной системе счисления, добавляем впереди ещё пару бит 1 и 0, получив таким образом бинарное число 0b101000. Для перевода в шестнадцатеричную систему счисления согласно таблице 1 необходимо выбрать в соответствие с состояниями единичных битов из столбца HEX для старших двух битов ‘2’, а младших четырех – 8, что вместе составит 0x28. Для бинарного числа 0b11101000 по той же таблице получим для старших четырех битов из столбца HEX ‘E’, а младших – 8, что вместе составит 0xE2. Как видно из приведенных примеров числа четные, в то время как нечетное число будет иметь в младшем разряде всегда 1.
Теперь на основе той же таблицы произведем перевод десятичного числа 255 в двоичное. Пользуясь нехитрым правилом, что нечетное число в нулевом, младшем бите равно 1, записываем начальное константное выражение ‘0b1’, а само десятичное число 255 уменьшая на один получая тем самым значение 254. Для определение остальных битов нам нужно, основываясь на таблице 1, построить дискретный ряд соответствующий их порядку следования в двоичной системе. Если посмотреть внимательно на десятичные значения в строках таблицы 1, имеющие одиночное значение в битах с нулевого по четвертый, они меняются строго по формуле – 2N.
Соответственно, Бит 4 = 24 = 16, Бит 5 = 32, Бит 6 = 64, Бит 7 = 128. На этом остановимся, потому что единичное значение в восьмом бите равняется 256. Тем самым мы определили старший, значащий бит 7 разбираемого десятичного числа в двоичной системе отсчета и можем записать как 0b10000001.
Затем, отнимаем 128 от 254 получаем 126 и определяем для него старший бит 6, который равняется десятичному значению 64 и записываем в двоичное выражение 0b11000001. Далее, таким же образом получаем двоичные значения:
0b11100001 – 62 от 32 получим единичное значение в бите 5
0b11110001 – 30 от 16 получим единичное значение в бите 4
0b11111001 – 14 от 8 получим единичное значение в бите 3
0b11111101 – 6 от 4 получим единичное значение в бите 2
0b11111111 – 2 от 2 получим единичное значение в бите 1
В остатке мы получим нуль, подтверждая тем самым, что данный способ работает.
Теперь в соответствие с таблицей 1 переводим полученное двоичное число 0b11111111 в восьмеричную систему отсчета, аккуратно разделив его по три бита 0b11 = 03 0b111=07 0b111=07, которое в итоге равно восьмеричному значению числа 0377 и равное десятичному значению числа 255.
Для перевода в шестнадцатеричную систему счисления разделим двоичное число 0b11111111 по четыре бита – 0b1111=0xF и 0b1111=0xF и с помощью той же таблицы 1 приводим его к шестнадцатеричному значению 0xFF.

3. Размерность целых чисел без учета знака

Размерность целых чисел без учета знака определяется их размером, используемым в процессе вычислительных операций с ними в зависимости от выбранного типа данных. GNU Assembler оперирует целочисленными данными в соответствие с фундаментальными типами используемыми в архитектуре Intel x86, как показано на рис. 1
 
                1 байт
              70  
            Ст.байт Мл.байт 2 байта
            158 70  
        Ст. слово Мл. слово 4 байта
        3116 150  
Ст. сдвоенное слово Мл. сдвоенное слово 8 байт
6332 310  
 
 
Рис. 1
Согласно с рис.1 целые числа могут быть представлены четырьмя базовыми типами с размерностью в байт (восемь бит), слово (16-ть бит), двойное слово (32 бита), четверное слово (64 бита).
Таблица 2
Тип данных GNU C Диапазон значений Размер в x86-32, байт Размер в x86-64, байт Объявление в GNU Assembler
unsigned char 0, … ,255 1 1 .byte значение [, … ,значение N]
unsigned short 0, … ,65535 2 2 .hword значение [, … ,значение N]
.short значение [, … ,значение N]
unsigned int 0, … ,4294967295 4 4 .int значение [, … ,значение N]
unsigned long 0, … ,4294967295 (18446744073709551615 в х86-64) 4 8 .long значение [, … ,значение N]

4. Порядок следования байтов

Вопрос порядка следования байт возникает при оперировании c данными, имеющий размер от двух байт и более, потому что архитектура x86 использует порядок следования отличный от того, чем мы привыкли пользоваться. В листинге 1, показан пример записи кода ассемблера в мнемоническом представлении программистом (справа) и в машинных кодах (слева) результат работы GNU Assembler AS(1).
Листинг 1
                 . . .
  11:	05 67 45 23 01       	add    $0x1234567,%eax
  16:	66 05 ab 89          	add    $0x89ab,%ax
  1a:	04 cd                	add    $0xcd,%al
  1c:	80 c4 ef             	add    $0xef,%ah
                 . . .                
Как следует из листинга 1, 32-х разрядное целочисленное значение (без учета знака) по смещению 0x11 в машинных кодах содержит, как и полагает её типу, четыре следующих друг за другом байтов от старшего к младшему (big-endian), комбинируемые как сдвоенные слова. Такую же картину можно наблюдать с 16-ти разрядным словом по смещению 0x16, а вот данные по смещению 0x1a и 0x1с, имеющие размерность в один байт, не подвержены таким метаморфозам.
В том же листинге, слева приводится порядок запись данных – следующих друг за другом байтов от младшему к старшему (little-endian), который мы привыкли использовать со школьной скамьи.
Для конвертирования данных из формата "big-endian" в формат "little-endian" и обратно в архитектуре х86 предусмотрена инструкция BSWAP, которую так же можно использовать для повышения быстродействия вычислительных операций десятичной арифметики.
Инструкция BSWAP (от англ. byte swap – байтовая перестановка) изменяет порядок следования байтов в 32-х разрядном операнде следующим образом:
биты с 0 по 7 в нулевом байте меняются местами с битами в четвертом байте (биты 24, … ,31);
биты с 8 по 15 во втором меняются местами с битами в третьем байте (биты 6, … ,21).
В листинге 2 приводится пример использование инструкции BSWAP.
Листинг 2
                 . . .
imm32_value:
        .int    0xD4C3B2A1 
                 . . .
        movl   imm32_value,%edx         

        bswapl %edx,%eax
                 . . .
В листинге 3 приводится результат выполнения BSWAP
Листинг 3
user@debian:~/gasrunparts-0.2$ src/gasrunparts bswap_intops
Result:  0xA1B2C3D4
Для конвертирования 16-ти разрядных данных из формата "big-endian" в формат "little-endian" вместо инструкции BSWAP следует использовать XCHG, как показано в листинге 4.
Листинг 4
                 . . .
imm16_value:        
        .hword    0xF2E1 
                 . . .
         movw   imm16_value, %ax
    
         xchgb  %al, %ah           
                 . . .
В листинге 5 приводится результат выполнения XCHG
Листинг 5
user@debian:~/gasrunparts-0.2$ src/gasrunparts xchgw_intops
Result:  0xE1F2
На рис. 2 показан пример конвертирования 32-х и 16-ти разрядных данных из формата "big-endian" в формат "little-endian" в соответствие с кодом ассемблера, приводимом в листингах 2 и 3.
 
$0xD4C3B2A1 bswap $0xA1B2C3D4
D416 C316 B216 A116 A116 B216 C316 D416
 
  $0xF2E1 xchg $0xE1F2  
    F216 E116 E116 F216    
 
Рис. 2

5. Целые числа с учетом знака

5.1. Их представление в машинном коде

Знаковые числа могут быть представлены нулем, положительным и отрицательным числом. Существуют три схемы для представления знаковых целых чисел в:
– прямом коде со знаком в старшем разряде (Sign-Magnitude);
– обратном коде (Оne's complement);
– дополнительным коде (Two's complement).
Из всех выше перечисленных схем представления целых чисел с учетом знака в архитектуре x86 используется последняя, о которой поговорим здесь более подробно.
На рис. 3 показано представление в дополнительном коде числа с учетом знака.
 
Отриц. значения | Полож. значения
 
-128
-127
……
……
……
  -2
  -1
   0
   1
   2
………
………
………
 126
 127
 
1000 0000
1000 0001
………
………
………
1111 1110
1111 1111
0000 0000
0000 0001
0000 0010
………
………
………
0111 1110
0111 1111
 
Рис. 3
Если посмотреть на рис. 3, на первый взгляд в представлении отрицательных целых чисел отсутствует какая-либо логика, но это не так. Поэтому давайте отсортируем десятичный ряд чисел со знаком, представленных на этом рисунке, по их двоичной величине начиная с нулевого отсчета.
 
| Полож. значения Отриц. значения
 
0
1
   2
……
 126
 127
 -128
 -127
 -126
………
………
 -2
 -1
 
0000 0000
0000 0001
0000 0010
………
0111 1110
0111 1111
1000 0000
1000 0001
1000 0010
………
………
1111 1110
1111 1111
 
Рис. 4
Как показано на рис. 4, целые числа с учетом знака представлены в дополнительном коде с обратным отсчетом: отрицательные – от –128 до –1, а положительные числа – от нуля до 127. Последнее значение является максимальным для положительных целых, потому что его двоичная величина равна 011111112, а старший значащий разряд равен нулю.
Поэтому, при увеличении на единицу положительного целого в значении 127, как показано на рис. 4, мы переходим границу там, где заканчивается область действия для положительных значений и начинается для отрицательных с равенством двоичной величины целого с учетом знака 100000002 и с появлением у него в старшем значащем разряде единицы.
Отсюда следует, что целые числа с учетом знака имеют представление в дополнительном коде и кодируются в двоичном виде для отрицательных и положительных значений.
При этом, вес каждого разряда равен 2N, исключая старший разряд, являющейся наиболее значимым и содержащий знак, вес которого равен отрицательному из степени два. Таким образом, десятичная величина целого числа с учетом знака может быть представлена как 2N-1, а их диапазон значений составляет –(2N–1), … ,2N–1–1.
Для кодирования десятичного целого к его двоичному виду с учетом знака в диапазоне – минус 128, …, плюс 127 вновь обратим свой взгляд на рис. 4.
Как на нем показано, перевод имеющего положительный знак десятичного целого к двоичному виду осуществляется в соответствие с методикой перевода целых чисел исходя из данной размерности N–1 разряд от десятичной системы счисления к двоичной. А вот для отрицательного целого нам придется данную методику не много изменить. Впрочем как и таблицу 1, в которой приводится соотношение десятичной, двоичной, восьмеричной и шестнадцатеричной систем счисления к целым числам без учета знака.
Поэтому ниже приводится таблица 4, которую будем использовать при переводе десятичного числа к его двоичной величине для отрицательных чисел от десятичной к двоичной и шестнадцатеричной системе счисления. Восьмеричная система счисления в этой таблице не приводится, т.к. она редко используется для кодировки такого рода чисел в архитектуре x86.
Таблица 4
7 бит 6 бит 5 бит 4 бит 3 бит 2 бит 1 бит 0 бит DEC BIN HEX
1 0 0 0 0 0 0 0 –128 0b10000000 0x08
1 1 0 0 0 0 0 0 –64 0b11011111 0xC0
1 1 1 0 0 0 0 0 –32 0b11100000 0xE0
1 1 1 1 0 0 0 0 –16 0b11110000 0xF0
1 1 1 1 0 0 0 1 –15 0b11110001 0xF1
1 1 1 1 0 0 1 0 –14 0b11110010 0xF2
1 1 1 1 0 0 1 1 –13 0b11110011 0xF3
1 1 1 1 0 1 0 0 –12 0b11110100 0xF4
1 1 1 1 0 1 0 1 –11 0b11110101 0xF5
1 1 1 1 0 1 1 0 –10 0b11110110 0xF6
1 1 1 1 0 1 1 1 –9 0b11110111 0xF7
1 1 1 1 1 0 0 0 –8 0b11111000 0xF8
1 1 1 1 1 0 0 1 –7 0b11111001 0xF9
1 1 1 1 1 0 0 1 –6 0b11111010 0xFA
1 1 1 1 1 0 0 1 –5 0b11111011 0xFB
1 1 1 1 1 1 0 0 –4 0b11111100 0xFС
1 1 1 1 1 1 0 0 –3 0b11111101 0xFD
1 1 1 1 1 1 1 0 –2 0b11111110 0xFE
1 1 1 1 1 1 1 1 –1 0b11111111 0xFF
Теперь можно осуществить перевод отрицательного числа –113, сначала из десятичной системы в двоичную, а затем в шестнадцатеричную.
Первое, что сделаем, как и в ранее приводимой методики перевода целых чисел из десятичной в шестнадцатеричную систему отсчета, определим старший и младший разряд, которые равны единице. Потому что во-первых, число 113 имеет отрицательный знак, а во-вторых, оно является нечетным. Поэтому можем записать их в двоичной нотации, как 0b10000001.
Осталось определить остальные разряды обратно тому, как это делали ранее при переводе целых чисел без учета знака, а именно операцией вычитания в соответствии с десятичной величиной разряда.
Как следует из таблицы 4, число –112 больше числа –128, поэтому отнимаем от него число –64 и получаем – не единицу, а нуль в шестом разряде, и зеркально стоящую от него единицу в втором разряде. При этом остаток десятичного числа будет равен –48, не забываем обновить двоичную нотацию 0b10000011. Далее, повторяя операцию вычитания по цене разряда получим следующие значения:
0b10000111 отнимая от –49 число –32 – нуль в пятом разряде и единицу в втором.
0b10001111 отнимая от –16 число –16 – нуль в четвертом разряде и единицу в третьем.
При этом на последней операции в остатке остается нуль, тем самым приведение отрицательного десятичного целого –113 к его двоичному виду закончена и равна 0b10001111, а шестнадцатеричное представление согласно той же таблицы 4 будет равной 0x8F, что подтверждается листингами 4 и 5.
Листинг 4
.section .data
imm8_svalue:        
        .byte   0b10001111 

   …

.section .text
.globl trans_negative
.type  trans_negative, @function
trans_negative:           
                           
           pushl   %ebp         
           movl	   %esp, %ebp 

           xorl    %eax,%eax
    
           movb   imm8_svalue, %al      
    
           leave                   
           ret				    

Листинг 5
user@debian:~/gasrunparts-0.3$ src/gasrunparts trans_negative
Result:  0xFFFFFF8F(HEX), -113(DEC)
Как следует из листинга 5 отрицательное значение числа 113 является его шестнадцатеричное представление 0xFFFFFF8F, с автоматическим формированием (дополнением) единиц в старших 24-х разрядах, потому что согласно листингу 4 размерность данных, записываемых в регистр EAX, равно 8-мь разрядов со значением 0x8F в то время как сам регистр 32-х разрядный.
Для контраста в листингах 6 и 7 приводится пример представления того же числа в шестнадцатеричной и бинарной (двоичной) форме записи, но без учета знака
Листинг 6
.section .data
   …
imm8_value:        
        .byte   0b01110001 

   …

.section .text
.globl trans_positive
.type  trans_positive, @function
trans_positive:           
                           
           pushl   %ebp         
           movl	   %esp, %ebp 

           xorl    %eax,%eax
    
           movb   imm8_value, %al      
    
           leave                   
           ret				    

Листинг 7
user@debian:~/gasrunparts-0.3$ src/gasrunparts trans_positive
Result:  0x71(HEX), 113(DEC)
Таким образом, отрицательные числа могут быть представлены размером в один байт, слово, двойное слово или четверное слово. Все операции над данным видом целых чисел осуществляется с приведением их из десятичной системы счисления к двоичной (или шестнадцатеричной) в дополнительном коде. При этом, при объявлении констант следует руководствоваться типами и их значениями, приведенными в таблице 5.
Таблица 5
Тип данных GNU C Диапазон значений Размер в x86-32, байт Размер в x86-64, байт Объявление в GNU Assembler
signed char –128, … ,127 1 1 .byte значение [, … ,значение N]
signed short int –32768, … ,32767 2 2 .hword значение [, … ,значение N]
.short значение [, … ,значение N]
signed int –2147483648, … ,2147483647 4 4 .int значение [, … ,значение N]
signed long int –2147483648, … ,2147483647 (–9223372036854775808, … , 9223372036854775807 в х86-64) 4 8 .long значение [, … ,значение N]
  
 К оглавлению

Сайт создан в системе uCoz