мат. Пример на рис.13.2 демонстрирует умножение четырех- байтового множимого на одно-байтовый множитель. Так как команда AAM может иметь дело только с однобайтовыми числами, то в программе организован цикл, который обрабатывает байты справа налево. Окончательный результат умножения в данном примере - 0108090105. Если множитель больше одного байта, то необходимо обеспечить еще один цикл, который обрабатывает множитель. В этом случае проще будет преобразовать число из ASCII-формата в двоичный формат (см. следующий раздел "Преобразование ASCII-формата в двоичный формат"). ------------------------------------------------------------ ------------------------------------------------------------ Рис.13.2. Умножение в ASCII-формате. Деление в ASCII-формате Команда AAD (ASCII Adjust for Division - коррекция для деления ASCII-кодов) выполняет корректировку ASCII кода делимого до непосредственного деления. Однако, прежде необходимо очистить левые тройки ASCII-кодов для получения распакованного десятичного формата. Команда AAD может оперировать с двухбайтовыми делимыми в регистре AX. Предположим, что регистр AX содержит делимое 3238 в ASCII- формате и регистр CL содержит делитель 37 также в ASCII- формате. Следующие команды выполняют коррекцию для последую щего деления: AX: AND CL,0FH ;Преобразовать CL в распак.дес. AND AX,0F0FH ;Преобразовать AX в распак.дес. 0208 AAD ;Преобразовать в двоичный 001C DIV CL ;Разделить на 7 0004 Команда AAD умножает содержимое AH на 10 (шест.0A), прибавляет pезультат 20 (шест.14) к регистру AL и очищает регистр AH. Значение 001C есть шест. представление десятич ного числа 28. Делитель может быть только однобайтовый от 01 до 09. Пример на рис. 13.3. выполняет деление четырехбайтового делимого на однобайтовый делитель. В программе организован цикл обработки делимого справа налево. Остатки от деления находятся в регистре AH и команда AAD корректирует их в регистре AL. Окончательный pезультат: частное 00090204 и в регистре AH остаток 02. Ассемблер для IBM PC. Глава 13 6 Если делитель больше одного байта, то необходимо постро ить другой цикл для обработки делителя, но лучше воспользо ваться следующим разделом "Преобразование ASCII-формата в двоичный формат." ДВОИЧНО-ДЕСЯТИЧНЫЙ ФОРМАТ (BCD) ------------------------------------------------------------ В предыдущем примере деления в ASCII-формате было получено частное 00090204. Если сжать это значение, сохраняя только правые цифры каждого байта, то получим 0924. Такой формат называется двоично-десятичным (BCD - Binary Coded Decimal) (или упакованным). Он содержит только десятичные цифры от 0 до 9. Длина двоично-десятичного представления в два раза меньше ASCII-представления. ------------------------------------------------------------ ------------------------------------------------------------ Рис.13.3. Деление в ASCII-формате. Заметим, однако, что десятичное число 0924 имеет основание 10 и, будучи преобразованным в основание 16 (т.е. в шест. представление), даст шест.039C. Можно выполнять сложение и вычитание чисел в двоично-десятичном представлении (BCD-формате). Для этих целей имеются две корректиpующих команды: DAA (Decimal Adjustment for Addition - десятичная коррекция для сложения) DAS (Decimal Adjustment for Subtraction - десятичная коррекция для вычитания) Обработка полей также осуществляется по одному байту за одно выполнение. В примере программы, приведенном на рис. 13.4, выполняется преобразование чисел из ASCII-формата в BCD-формат и сложение их. Процедура B10CONV преобразует ASCII в BCD. Обработка чисел может выполняться как справа налево, так и слева направо. Кроме того, обработка слов проще, чем обработка байтов, так как для генерации одного байта BCD-кода требуется два байта ASCII-кода. Ориентация на обработку слов требует четного количества байтов в ASCII- поле. Процедура C10ADD выполняет сложение чисел в BCD-формате. Окончательный результат - 127263. ПРЕОБРАЗОВАНИЕ ASCII-ФОРМАТА В ДВОИЧНЫЙ ФОРМАТ ------------------------------------------------------------ bыполнение арифметических операций над числами в ASCII или BCD форматах удобно лишь для коротких полей. В боль шинстве случаев для арифметических операций используется преобразование в двоичный формат. Практически проще Ассемблер для IBM PC. Глава 13 7 преобразование из ASCII-формата непосредственно в двоичный формат, чем преобразование из ASCII- в BCD-формат и, затем, в двоичный формат: Метод преобразования базируется на том, что ASCII-формат имеет основание 10, а компьютер выполняет арифметические операции только над числами с основанием 2. Процедура преобразования заключается в следующем: 1. Начинают с самого правого байта числа в ASCII-формате и обрабатывают справа налево. 2. Удаляют тройки из левых шест.цифр каждого ASCII-байта. 3. Умножают ASCII-цифры на 1, 10, 100 (шест.1, A, 64) и т.д. и складывают результаты. Для примера рассмотрим преобразование числа 1234 из ASCII-формата в двоичный формат: Десятичное Шестнадцатиричное 4 х 1 = 4 4 3 х 10 = 30 1E 2 х 100 = 200 C8 1 х 1000 = 1000 3E8 Результат: 04D2 ------------------------------------------------------------ ------------------------------------------------------------ Рис. 13.4. BCD-преобразование и арифметика. Проверьте, что шест.04D2 действительно соответствует десятичному 1234. На рис. 13.5. в процедуре B10ASBI выполняется преобразоние ASCII-числа 1234 в двоичный формат. В примере предполагается, что длина ASCII-числа равна 4 и она записана в поле ASCLEN. Для инициализации адрес ASCII- поля ASCVAL-1 заносится в регистр SI, а длина - в регистр BX. Команда по метке B20 пересылает ASCII-байт в регистр AL: MOV AL,[SI+BX] Здесь используется адрес ASCVAL-1 плюс содержимое регистра BX (4), т.е. получается адрес ASCVAL+3 (самый правый байт поля ASCVAL). В каждом цикле содержимое регистра BX уменьшается на 1, что приводит к обращению к следующему слева байту. Для данной адресации можно использовать регистр BX, но не CX, и, следовательно, нельзя применять команду LOOP. В каждом цикле происходит также умножение поля MULT10 на 10, что дает в результате множители 1,10,100 и т.д. Такой прием применен для большей ясности, однако, для большей производительности множитель можно хранить в регистре SI или DI. ПРЕОБРАЗОВАНИЕ ДВОИЧНОГО ФОРМАТА В ASCII-ФОРМАТ ------------------------------------------------------------ Ассемблер для IBM PC. Глава 13 8 Для того, чтобы напечатать или отобразить на экране арифметический pезультат, необходимо преобразовать его в ASCII-формат. Данная операция включает в себя процесс обратный предыдущему. Вместо умножения используется деление двоичного числа на 10 (шест. 0A) пока результат не будет меньше 10. Остатки, которые лежат в границах от 0 до 9, образуют число в ASCII-формате. В качестве примера рассмотрим преобразование щест.4D2 обратно в десятичный формат: Частное Остаток 4D2 : A 7B 4 7B : A C 3 C : A 1 2 Так как последнее частное 1 меньше, чем шест.A, то операция завершена. Остатки вместе с последним частным образуют результат в ASCII-формате, записываемый справа налево 1234. Все остатки и последнее частное должны записываться в память с тройками, т.е. 31323334. На рис. 13.5. процедура C10BIAS преобразует шест. 4D2 (результат вычисления в процедуре B10ASBI) в ASCII-число 1234. Полезно переписать всю программу (рис.13.5.) в компьютер и выполнить трассиpовку ее выполнения по шагам. ------------------------------------------------------------ ------------------------------------------------------------ Рис.13.5. Преобразование ASCII и двоичного форматов. СДВИГ И ОКРУГЛЕНИЕ ------------------------------------------------------------ Рассмотрим процесс округления числа до двух десятичных знаков после запятой. Если число равно 12,345, то необходимо прибавить 5 к отбрасываемому разряду и сдвинуть число вправо на один десятичный разряд: Число: 12,345 Плюс 5: +5 Округленное число: 12,350 = 12,35 Если округляемое число равно 12,3455, то необходимо прибавить 50 и сдвинуть на два десятичных разряда. Для 12,34555 необходимо прибавить 500 и сдвинуть на три десятичных разряда: 12,3455 12,34555 +50 +500 12,3505 = 12,35 12,35055 = 12,35 К числу, имеющему шесть знаков после запятой, необходимо прибавить 5000 и сдвинуть на четыре десятичных разряда и т.д. Поскольку данные представляются в компьютере в двоичном Ассемблер для IBM PC. Глава 13 9 виде, то 12345 выглядит как шест.3039. Прибавляя 5 к 3039, получим 303E, что соответствует числу 12350 в десятичном представлении. Пока все хорошо. Но вот сдвиг на одну двоич ную цифру дает в результате шест.181F, или 1675 - т.е. сдвиг на одну двоичную цифру просто делит число пополам. Но нам необходим такой сдвиг, который эквивалентен сдвигу вправо на одну десятичную цифру. Такой сдвиг можно осуществить делением на 10 (шест.A): Шест.303E : Шест.A = 4D3 или дес.1235 Преобразование шест.4D3 в ASCII-формат дает число 1235. Теперь oстается лишь вставить запятую в правильную позицию числа 12,35, и можно выдать на экран округленное и сдвинутое значение. Таким образом можно округлять и сдвигать любые двоичные числа. Для трех знаков после запятой необходимо прибавить 5 и разделить на 10, для четырех знаков после запятой: прибавить 50 и pазделить на 100. Возможно вы заметили модель: фактор округления (5, 50, 500 и т.д.) всегда составляет половину фактора сдвига (10, 100, 1000 и т.д.). Конечно, десятичная запятая в двоичном числе только подpазумевается. ПРОГРАММА: ПРЕОБРАЗОВАНИЕ ВРЕМЕНИ И РАСЦЕНКИ РАБОТ ДЛЯ РАСЧЕТА ЗАРПЛАТЫ ------------------------------------------------------------ Программа, приведенная на рис.13.6, позволяет вводить с клавиатуры значения продолжительности и расценки работ и отображать на экран pасчитанную величину заработанной платы. Для краткости в программе опущены некоторые проверки на ошибку. Программа содержит следующие процедуры: B10INPT Вводит значения времени работы на ее расценку с клавиатуры. Эти значения могут содержать десятич ную запятую. D10HOUR Выполняет преобразование значения времени из ASCII в двоичный формат. E10RATE Выполняет преобразование значения расценки из ASCII в двоичный формат. F10MULT Выполняет умножение, округление и сдвиг. Величина зарплаты без дробной части или с одним или двумя знаками после запятой не требует округления и сдви га. Данная процедура ограничена тем, что позволяет обрабатывать величину зарплаты с точностью до шести десятичных знаков, что, конечно, больше, чем требуется. G10WAGE Вставляет десятичную запятую, определяет правую позицию для начала записи ASCII символов и преобразует двоичное значение зарплаты в ASCII-формат. Ассемблер для IBM PC. Глава 13 10 K10DISP Заменяет лидирующие нули на пробелы и выводит результат на экран. M10ASBI Преобразует ASCII в двоичный формат (общая процедура для времени и расценки) и определяет число цифр после запятой в введенном значении. ------------------------------------------------------------ ------------------------------------------------------------ Рис.13.6. Расчет заработной платы. Ограничения. Первое ограничение в программе, приведенной на рис.13.6, cостоит в том, что допускает не более шести десятичных знаков после запятой. Другое ограничение - размер самой зарплаты и тот факт, что сдвиг включает деление на число, кратное 10, a преобразование в ASCII-формат включает деление на 10. Если значение времени или расценки содержит больше шести десятичных знаков или зарплата превы шает величину около 655350, то программа выдает нулевой результат. На практике программа может предусмотреть в данном случае вывод предупреждающего сообщения или иметь подпрограммы для исключения таких ограничений. Контроль ошибок. Программа, разработанная для пользовате лей, не являющихся программистами, должна не только выдавать предупреждающие сообщения, но также проверять корректность вводимых значений. Правильными символами при вводе числовых значений являются цифры от 0 до 9 и символ десятичной запятой. Для любых других символов программа должна выдать предупреждающее сообщение и вновь повторить запрос на ввод. Полезной командой для проверки корректности вводимых символов является XLAT (см. главу 14). Тщательно проверяйте программы для любых возможных состояний: нулевое значение, максимально большие и малые значения, отрицательные значения. Отрицательные величины Некоторые применения программ допускают наличие отрицательных величин. Знак минус может устанавливаться после числа, например, 12,34-, или перед числом -12,34. Программа может проверять наличие минуса при преобразовании в двоичный формат. Можно оставить двоичное число положительным, но установить соответствующий индикатор исходной отрицательной величины. После завершения арифметических операций знак минус при необходимости может быть вставлен в ASCII поле. Если необходимо, чтобы двоичное число было также отрицательным, то можно преобразовать, как обычно, ASCII-формат в двоичный, а для изменения знака двоичного числа воспользоваться командами, описанными в главе 12 "Преобразование знака". Будьте внимательны при использовании Ассемблер для IBM PC. Глава 13 11 команд IMUL и IDIV для обработки знаковых данных. Для округления отрицательных чисел следует не прибавлять, а вычитать фактор 5. ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------ ъ ASCII-формат требует один байт на каждый символ. Если поле содержит только цифры от 0 до 9, то замена старших троек в каждом байте на нули создает распакованный десятичный формат. Сжатие числа до двух цифр в байте создает упакованный десятичный формат. ъ После ASCII-сложения необходимо выполнить коррекцию с помощью команды AAA; после ASCII-вычитания - коррекция с помощью команды AAS. ъ Прежде чем выполнить ASCII-умножение, необходимо преобразовать множимое и множитель в "распакованный десятичный" формат, обнулив в каждом байте левые тройки. После умножения необходимо выполнить коррекцию результата с помощью команды AAM. ъ Прежде чем выполнить ASCII-деление, необходимо: 1) преобразовать делимое и делитель в "распакованный десятичный" формат, обнулив в каждом байте левые тройки и 2) выполнить коррекцию делимого с помощью команды AAD. ъ Для большинства арифметических операций используйте преобразование чисел из ASCII-формата в двоичной формат. В процессе такого преобразования проверяйте на корректность ASCII-символы: они должны быть от шест.30 до шест.39, могут содержать десятичную запятую (точку) и, возможно, знак минус. ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------ 13.1. Предположим, что регистр AX содержит 9 в ASCII коде, а регистр BX -7 также в ASCII коде. Объясните и дайте точный результат для следующих несвязанных операций: а) ADD AX,33H б) ADD AX,BX AAA AAA в) SUB AX,BX г) SUB AX,0DH AAS AAS 13.2. Поле UNPAK содержит шест. 01040705 в распаковочном десятичном формате. Напишите цикл, который преобразует это содержимое в ASCII-формат, т.е. 31343735. Ассемблер для IBM PC. Глава 13 12 13.3. Поле ASCA содержит значение 313733 в ASCII-формате, а другое поле ASCB содержит 35. Напишите команды для умножения этих чисел в ASCII-формате и записи произведения в поле ASCPRO. 13.4. Используя данные из вопроса 13.3, разделите ASCA на ASCB и запишите частное в поле ASCQUO. 13.5. Выполните следующие вычисления вручную: а) преобразо вать ASCII 46328 в двоичный формат и показать результат в шест.виде; б) преобразовать полученное шест.значение обратно в ASCII-формат. 13.6. Напишите и выполните программу, которая определяет размер памяти компьютера (INT 12H - см.гл.2), преобразует полученное значение в ASCII-формат и выводит результат на экран в следующем виде: Размер памяти nnn байтов. Ассемблер для IBM PC. Глава 14 24 ГЛАВА 14. Обработка таблиц ------------------------------------------------------------ Обработка таблиц Цель: Раскрыть требования для определения таблиц, организа ции поиска в таблицах и сортировки элементов таблицы. ВВЕДЕНИЕ ------------------------------------------------------------ Многие программные применения используют табличную организацию таких данных, как имена, описания, размеры, цены. Определение и использование таблиц включает одну новую команду ассемблера - XLAT. Таким образом, использова ние таблиц - это лишь дело техники и применения знаний, полученных из предыдущих глав. Данная глава начинается определением некоторых общепринятых таблиц. Организация поиска в таблице зависит от способа ее определения. Существует много различных вариантов определения таблиц и алгоритмов поиска. ОПРЕДЕЛЕНИЕ ТАБЛИЦ ------------------------------------------------------------ Для облегчения табличного поиска большинство таблиц определяются систематично, т.е. элементы таблицы имеют одина ковый формат (символьный или числовой), одинаковую длину и восходящую или нисходящую последовательность элементов. Таблица, которой уже приходилось пользоваться в данной книге - это стек, представляющий собой таблицу из 64-х неинициализированных слов: STACK DW 64 DUP(?) Следующие две таблицы инициализированы символьными и числовыми значениями: MONTAB DB 'JAN','FEB','MAR', ... ,'DEC' COSTAB DB 205,208,209,212,215,224,... Таблица MONTAB определяет алфавитные аббревиатуры месяцев, а COSTAB - определяет таблицу номеров служащих. Таблица может также содепжать смешанные данные (регулярно чередующиеся числовые и символьные поля). В следующей ассортиментной таблице каждый числовой элемент (инвентарный номер) имеет две цифры (один байт), а каждый символьный элемент (наименование) имеет девять байтов. Точки, показанные в наименовании "Paper" дополняют длину этого поля до 9 байт. Точки показывают, что недостающее пространство должно присутствовать. Вводить точки необязательно. Ассемблер для IBM PC. Глава 14 25 STOKTBL DB 12,'Computers',14,'Paper....',17,'Diskettes' Для ясности можно закодировать элементы таблицы вертикально: STOKTBL DB 12, 'Computers' DB 14, 'Paper....' DB 17, 'Diskettes' Рассмотрим теперь различные способы использования таблиц в программах. ПРЯМОЙ ТАБЛИЧНЫЙ ДОСТУП ------------------------------------------------------------ Предположим, что пользователь ввел номер месяца - 03 и программа должна преобразовать этот номер в алфавитное значение March. Программа для выполнения такого преобразова ния включает определение таблицы алфавитных названий месяцев, имеющих одинаковую длину. Так как самое длинное название - September, то таблица имеет следующий вид: MONTBL DB 'January..' DB 'February.' DB 'March....' Каждый элемент таблицы имеет длину 9 байт. Адрес элемента 'January' - MONTBL+0, 'February' - MONTBL+9, 'March' - MONTBL+18. Для локализации месяца 03, программа должна выполнить следующее: 1. Преобразовать введенный номер месяца из ASCII 33 в двоичное 03. 2. Вычесть единицу из номера месяца: 03 - 1 = 02 3. Умножить результат на длину элемента (9): 02 х 9 = 18 4. Прибавить произведение (18) к адресу MONTBL; в результате получится адрес требуемого названия месяца: MONTBL+18. ------------------------------------------------------------ ------------------------------------------------------------ Рис. 14.1. Прямая табличная адресация. На рис.14.1 приведен пример прямого доступа к таблице названий месяцев. Для краткости в программе используются вместо девятисимвольных названий - трехсимвольные. Введенный номер месяца определен в поле MONIN. Предположим, что некоторая подпрограмма формирует запрос на ввод номера месяца в ASCII-формате в поле MONIN. Описанная техника работы с таблицей называется прямым табличным доступом. Поскольку данный алглритм непосредствен но вычисляет адpес необходимого элемента в таблице, то в программе не требуется выполнять операции поиска. Ассемблер для IBM PC. Глава 14 26 Хотя прямая табличная адресация очень эффективна, она возможна только при последовательной организации. То есть можно использовать такие таблицы, если элементы располагаются в регулярной последовательности: 1, 2, 3,... или 106, 107, 108,... или даже 5, 10, 15. Однако, не всегда таблицы построены таким образом. В следующем разделе рассматриваются таблицы, имеющие нерегулярную организацию. ТАБЛИЧНЫЙ ПОИСК ------------------------------------------------------------ Некоторые таблицы состоят из чисел, не имеющих видимой закономерности. Характерный пример - таблица инвентарных номеров с последовательными номерами, например, 134, 138, 141, 239 и 245. Другой тип таблиц состоит из распределенных по ранжиру величин, таких как подоходный налог. В следующих разделах рассмотрим эти типы таблиц и организацию табличного поиска. Таблицы с уникальными элементами Инвентарные номера большинства фирм часто не имеют последовательного порядка. Номера, обычно, группируются по категориям, первые цифры указывают на мебель или приборы, или номер отдела. Кроме того время от времени номера удаляются, а новые добавляются. В таблице необходимо связать инвентарные номера и их конкретные наименования (и, если требуется, включить стоимость). Инвернтарные номера и наименования могут быть определены в различных таблицах, например: STOKNOS DB '101','107','109',... STOKDCR DB 'Excavators','Processors','Assemblers',... или в одной таблице, например: STOKTAB DB '101','Excavators' DB '107','Processors' DB '109','Assemblers' ... Программа на рис.14.2 определяет инвентарную таблицу и выполняет табличный поиск. Таблица содержит шесть пар номеров и наименований. Цикл поиска начмнается со сравнения введенного инвентарного номера в поле STOKNIN с первым номером в таблице. Если номера различные, то адрес в таблице увеличивается для сравнения со следующим инвентарным номером. Если номера равны, то программа (A30) выделяет наименование из таблицы и записывает его в поле DESCRN. Поиск выполняет максимум шесть сравнений и если требуемый номер в таблице отсутствует, то происходит переход на программу обработки ошибки, которая выводит на экран соответствующее сообщение. Ассемблер для IBM PC. Глава 14 27 Обратите внимание, что в начале программы имеется команда, которая пересылает содержимое поля STOKNIN в регистр AX. Хотя STOKNIN определенно как 3233, команда MOV загрузит в регистр AX это значение в обратной последователь ности байтов 3332. Так как элементы таблицы имеют прямую последовательность байтов, то после команды MOV имеется команда XCHG, которая меняет местами байты в регистре AX, возвращая им прямую последовательность, т.е. 3233. Команда CMP, предполагая обратную последовательность, сравнивает сначала правые байты, а затем - левые. Следовательно, проверка на pавенство будет корректной, но проверки на больше или меньше дадут неправильные результаты. Для сравнения на больше или меньше следует опустить команду XCHG, переслать элемент таблицы командой MOV, скажем, в регистр BX и затем сравнить содержимое регистров AX и BX следующим образом: MOV AX,STOKNIN LEA SI,STOKTAB C20: MOV BX,[SI] CMP AX,BX JA или JB ... В программе такого типа другая таблица может определять стоимость единицы товара. Программа может локализовать элемент таблицы, вычислить продажную стоимость (количество товара умножить на стоимость единицы товара) и выдать на экран наименование и прадажную стоимость товара. В примере на рис. 14.2 таблица содержит двухбайтовые номера и десятибайтовые наименования. Детальное программиро вание будет oтличаться для различного числа и длины элементов. Например, для сравнения трехбайтовых полей можно использовать команду REPE CMPSB, хотя эта команда также включает использование pегистра CX. Таблицы с ранжированием Подохожный налог дает характерный пример таблицы с ранжированными значениями. Представим себе таблицу, содержащую размеры доходов oблагаемых налогами, процент налога и поправочный коэффициент: Размер дохода Процент налога Поправочный к-нт 0-1000.00 10 0,00 1000,01-2500,00 15 050,00 2500,01-4250,00 18 125,00 4250,01-6000,00 20 260,00 6000,01 и более 23 390,00 Ассемблер для IBM PC. Глава 14 28 В налоговой таблице процент увеличивается в соответствии с увеличением налогооблагаемого дохода. Элементы таблицы доходов содержат максимальные величины для каждого шага: TAXTBL DD 100000,250000,425000,600000,999999 для организации поиска в такой таблице, программа сравнивает доxод налогоплатильщика с табличным значением дохода: если меньше или равно, то использовать соответствующий процент и поправку; если больше, то перейти к следующему элементу таблицы. Величина налога рассчитывается по формуле: Доход х Процент налога : 100 - поправочный к-нт Табличный поиск с использованием сравнения строк Если элемент таблицы превышает длину в два байта, то для операции сравнения можно использовать команду REPE CMPS. Предположим, что таблица инвентарных номеров (рис.14.2) переделана для трехбайтовых номеров. Если STOKNIN является первым полем в области данных, а STOKTAB - вторым, то они могут выглядеть cледующим образом: Данные: |123|035Excavators|038Lifters |049Presses | ... | | | | | | | Адрес: 00 03 06 16 19 29 32 Программа на рис.14.3 определяет таблицу STOKTAB, включая последний элемент '999' для индикации конца таблицы при поиске. Программа поиска сравнивает содержимое каждого элемента таблицы с содержимым поля STOKNIN: Элемент таблицы STOKNIN Результат сравнения 035 123 Меньше: проверить след.эл-т 038 123 Меньше: проверить след.эл-т 049 123 Меньше: проверить след.эл-т 102 123 Меньше: проверить след.эл-т 123 123 Равно: элемент найден Заметим, что команда CMPSB на рис.14.3 сравнивает байт за байтом, пока байты не будут равны и автоматически увеличива ет регистpы SI и DI. ------------------------------------------------------------ ------------------------------------------------------------ Рис.14.3. Табличный поиск с использованием команды CMPSB Ассемблер для IBM PC. Глава 14 29 Регистр CX инициализируется значением 03, а начальные относительные адреса в регистрах SI и DI устанавливаются равными 03 и 00 соответственно. Сравнение с первым элементом таблицы (035:123) завершается на первом байте, после этого регистр SI содержит 04, DI: 01, CX: 02. Для следующего сравнения регистр SI должен иметь значение 16, а DI: 00. Корректировка регистра DI сводится к простой перезагрузке адреса STOKNIN. Увеличение адреса следующего элемента таблицы, который должен быть в регистре SI, зависит от того, на каком байте (первом, втором или третьем) закончилось предыдущее сравнение. Регистр CX содержит число байт, не участвующих в сравнении, в данном случае - 02. Прибавив к содержимому регистра SI значение в регистре CX и длину наименования, получим относительный адрес следующего элемента: Адрес в SI после CMPSB 04 Прибавить CX 02 Прибавить длину наименования 10 Относительный адрес след.элемента 16 Так как регистр CX всегда содержит число байт, не участвующих в сравнении (если такие есть), то расчет справедлив для всех случаев: прекращение сравнения после 1, 2 или 3 байта. Если сравниваются одинаковые элементы, то регистр CX получит значение 00, а адрес в pегистре SI укажет на требуемое наименование. Таблицы с элементами переменной длины Существуют таблицы, в которых элементы имеют переменную длину. Каждый элемент такой таблицы может завершаться специальным символом ограничителем, например, шест.00; конец таблицы можно обозначить огpаничителем шест.FF. В этом случае необходимо гарантировать, чтобы внутри элементов таблицы не встречались указанные ограничители. Помните, что двоичные числа могут выражаться любыми битовыми комбинация ми. Для поиска можно использовать команду SCAS. ТРАНСЛИРУЮЩАЯ КОМАНДА XLAT ------------------------------------------------------------ Команда XLAT транслирует содержимое одного байта в другое предопределенное значение. С помощью команды XLAT можно проверить корректность содержимого элементов данных. При передаче данных между персональным компьютером и ЕС ЭВМ (IBM) с помощью команды XLAT можно выполнить перекодировку данных между форматами ASCII и EBCDIC. В следующем примере происходит преобразование цифр от 0 до 9 из кода ASCII в код EBCDIC. Так как представление цифр в ASCII выглядит как шест.30-39, а в EBCDIC - шест.F0-F9, то замену можно выполнить командой OR. Однако, дополнительно преобразуем все остальные коды ASCII в пробел (шест.40) в Ассемблер для IBM PC. Глава 14 30 коде EBCDIC. Для команды XLAT необходимо определить таблицу перекодировки, которая учитывает все 256 возможных символов, с кодами EBCDIC в ASCII позициях: XLTBL DB 47 DUP(40H) ;Пробелы в коде EBCDIC DB 0F0H,0F1H,0F2H,0F3H,...,0F9H ;0-9 (EBCDIC) DB 199 DUP(40H) ;Пробелы в коде EBCDIC Команда XLAT предполагает адрес таблицы в регистре BX, а транслируемый байт (например, поля ASCNO) в регистре AL. Следующие команды выполняют подготовку и трансляцию байта: LEA BX,XLTBL MOV AL,ASCNO XLAT Команда XLAT использует значение в регистре AL в качестве относительного aдреса в таблице, т.е. складывает адрес в BX и смещение в AL. Если, например, ASCNO содержит 00, то адрес байта в таблице будет XLTBL+00 и команда XLAT заменит 00 на шест.40 из таблицы. Если поле ASCNO cодержит шест.32, то адрес соответствующего байта в таблице будет XLTBL+50. Этот байт содержит шест.F2 (2 в коде EBCDIC), который команда XLAT загружает в регистр AL. ------------------------------------------------------------ ------------------------------------------------------------ Рис.14.4. Преобразование ASCII в EBCDIC. В программе на рис.14.4 добавлено преобразование десятичной точки (2E) и знака минус (2D) из кода ASCII в код EBCDIC (4B и 60 соответственно). В программе организован цикл для обработки шестибайтового поля. Поле ASCNO в начале выполнения программы содержит значение 31.5 с последующим пробелом, или шест.2D33312E3520. В конце выполнения програм мы в поле EBCNO должно быть шест. 60F3F14BF540. ПРОГРАММА: ОТОБРАЖЕНИЕ ШЕСТ. И ASCII-КОДОВ ------------------------------------------------------------ Программа, приведенная на рис.14.5, отображает на экране почти все ASCII-символы, а также их шест.значения. Например, ASCII-символ для шест.53 - это буква S, эти данные программа выводит в виде 53 S. Полное изображение на экране выглядит в виде матрицы 16х16: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 FA FB FC FD FE FF ------------------------------------------------------------ Ассемблер для IBM PC. Глава 14 31 ------------------------------------------------------------ Рис.14.5. Отображение шест. и ASCII-кодов Как было показано еще на рис.8.1, отображение ASCII- символов, oсобых проблем не вызывает. Что же касается отображения шест.значений в символах ASCII, то этот процесс более сложный. Например, для вывода на экран в коде ASCII шест. 00, 01 и т.д. необходимо преобразовать шест.00 в шест. 3030, шест.01 в шест.3031 и т.д. В программе начальное значение поля HEXCTR равно 00. Это значение последовательно увеличивается на 1. Процедура C10HEX расщепляет байт HEXCTR на две щест.цифры. Предположим, что байт HEXCTR содержит шест. 4F. Процедура сначала выделяет шест.цифру 4 и использует это значение для перекодировки по таблице XLATAB. В регистре AL устанавливает ся в результате значение шест.34. Затем процедура выделяет вторую шест.цифру F и перекодирует ее в шест.46. В результате oбработки получается шест.3446, что отображается на экране как 4F. Так как функция DOS для вывода на экран (шест.40) рассматривает шест.1A как конец файла, то в программе это значение заменяется на пробел. Программа, использующая для вывода на экран функцию DOS (шест.09), дожна заменять символ ограничитель '$' на пробел. Существует много различных способов преобразования шест.цифр в ASCII-символы. Можно поэкспериментировать с операциями сдвига и сравнения. ПРОГРАММА: СОРТИРОВКА ЭЛЕМЕНТОВ ТАБЛИЦЫ ------------------------------------------------------------ Часто возникает необходимость сортировки элементов таблицы в восходящем или нисходящем порядке. Например, пользователю может потребоваться список наименований товара в алфавитном порядке или список общих цен в нисходящей последовательности. Обычно, табличные данные не определяются как в предыдущей программе, а загружаются с клавиатуры или с диска. Данный раздел посвящен сортировке элементов таблицы, что касается различных применений, включающих сортировку записей на дисках, то здесь возможны более сложные программы. Существует несколько алгоритмов сортировки таблиц от неэффективных, но понятных, до эффективных и непонятных. Программа сортировки, предлагаемая в данном разделе, весьма эффективна и может применяться для большенства табличных сортировок. Конечно, если не проверить различные алгоритмы сортировок, то даже самая неэффективная программа может показаться работающей со скоростью света. Но цель данной книги - показать технику ассемблера, а не сортировки. Основной подход заключается в сравнении соседних элементов таблицы. Если первый элемент больше второго, то элементы меняются местами. Таким образом выполняется сравнение элементов 1 со 2, 2 с 3 и т.д. до конца таблицы с Ассемблер для IBM PC. Глава 14 32 перестановкой элементов там, где это необходимо. Если в проходе были сделаны перестановки, то весь процесс повторяет ся с начала таблицы т.е. сравниваются снова элементы 1-2, 2-3 и т.д. Если в проходе не было перестановок, то таблица отсортирована и можно прекратить процесс. Ниже приведен алгоритм, в котором переменная SWAP является индикатором: была перестановка элементов (YES) или нет (NO): G10: Определить адрес последнего элемента G20: Установить SWAP=NO Определить адрес первого элемента G30: Элемент > следующего элемента? Да: Представить элементы Установить SWAP=YES Перейти к следующему элементу Конец таблицы? Нет: Перейти на G30 Да: SWAP=YES? Да: Перейти на G20 (повторить сорт.) Нет: Конец сортировки Программа, показанная на рис.14.6, обеспечивает ввод с клавиатуры до 30 имен, сортировку введенных имен в алфавит ном порядке и вывод на экран отсортированного списка имен. ------------------------------------------------------------ ------------------------------------------------------------ Рис.14.6. Сортировка таблицы имен. ОПЕРАТОРЫ ТИПА, ДЛИНА И РАЗМЕРА ------------------------------------------------------------ Ассемблер содержит ряд специальных операторов, которые могут оказаться полезными при программировании. Например, при изменении длины таблицы придется модифицировать программу (для нового определения таблицы) и процедуры, проверяющие конец таблицы. В этом случае использование операторов TYPE (тип), LENGTH (длина) и SIZE (размер) позволяют уменьшить число модифицируемых команд. Рассмотрим определение следующей таблицы из десяти слов: TABLEX DW 10 DUP(?) ;Таблица из 10 слов Программа может использовать оператор TYPE для определения типа (DW в данном случае), оператор LENGTH для определения DUP-фактора (10) и оператор SIZE для определения числа байтов (10 х 2 = 20). Следующие команды иллюстрируют три таких применения: MOV AX,TYPE TABLEX ;AX=0002 MOV BX,LENGTH TABLEX ;BX=000A (10) MOV CX,SIZE TABLEX ;CX=0014 (20) Ассемблер для IBM PC. Глава 14 33 Значения LENGTH и SIZE можно использлвать для окончания табличного поиска или сортировки. Например, если регистр SI содержит продвинутый адрес таблицы при осуществлении поиска, то проверка на конец таблицы может быть следующий: CMP SI,SIZE TABLEX В главе 23 "Справочник по директивам ассемблера" дается детальное описание операторов TYPE, LENGTH и SIZE. ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------ ъ Для большинства применений, определяйте таблицы, имеющие родственные элементы одной длины и формата данных. ъ Стройте таблицы на основе форматов данных. Например, элементы могут быть символьные или числовые длиной один, два и более байтов каждый. Может оказаться более практичным определение двух таблиц: одна, например, для трехсимвольных значений номеpов, а другая для двухбайтовых значений цен единиц т