К оглавлению
1.Целочисленные константы1.1. Порядок записиЦелочисленные константы могут быть представлены в виде бинарного числа, записанного в двоичной системе счисления или числа, записанного в системе счисления десятичной, восьмеричной или шестнадцатеричной .
Бинарным числом, представленным в двоичной системе счисления, предваряет 0b или 0B, и следующие за ними цифры 0 или 1.
Числом, представленным в восьмеричной системе счисления, относятся константы, имеющие впереди 0 одно и более допустимых восьмеричных чисел 01234567
К числам, представленным в десятичной системе счисления, относятся константы, имеющие впереди первую ненулевую цифру и/или следуемые за ней одно или более десятичных цифр (0123456789).
К числам, представленным в шестнадцатеричной системе счисления, относятся константы, начинающиеся с 0x или ‘0X и следуемых за ними шестнадцатеричных цифр 0123456789abcdefABCDEF.
При этом вне зависимости от используемой системы счисления, саму запись константного числа в непосредственном операнде необходимо предварять префиксом $. Иначе, оно будет определено как прямой адрес памяти, который тоже можно отнести к константному значению.
2. Использование систем счисленияВ таблице 1 приводится соотношение перечисленных выше систем счисления, применяемых для представления целых чисел в константных выражениях и операциях вывода целых чисел.
Таблица 1
Начиная со школы мы привыкли оперировать с числами в десятичной системе счисления, а вычислительная техника в двоичной, программисты же пишущие на языке 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
Согласно с рис.1 целые числа могут быть представлены четырьмя базовыми типами с размерностью в байт (восемь бит), слово (16-ть бит), двойное слово (32 бита), четверное слово (64 бита).
Таблица 2
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.
Рис. 2
5. Целые числа с учетом знака5.1. Их представление в машинном кодеЗнаковые числа могут быть представлены нулем, положительным и отрицательным числом. Существуют три схемы для представления знаковых целых чисел в:
– прямом коде со знаком в старшем разряде (Sign-Magnitude);
– обратном коде (Оne's complement);
– дополнительным коде (Two's complement).
Из всех выше перечисленных схем представления целых чисел с учетом знака в архитектуре x86 используется последняя, о которой поговорим здесь более подробно.
На рис. 3 показано представление в дополнительном коде числа с учетом знака.
Рис. 3
Если посмотреть на рис. 3, на первый взгляд в представлении отрицательных целых чисел отсутствует какая-либо логика, но
это не так. Поэтому давайте отсортируем десятичный ряд чисел со знаком, представленных на этом рисунке, по их двоичной величине начиная с нулевого отсчета.
Рис. 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
Теперь можно осуществить перевод отрицательного числа –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
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
К оглавлению |