1 2 3 4 5 6 7 Ім'я файлу: Арх_комп_КСД12_Гайдук_КР.pdf 6.2 Сложение Основная идея сложения в следующем вначале выравниваем порядки. Что это значит, покажем на примере десятичных чисел. Пусть, дано два числа 125 и 4539, и их нужно сложить. Представим их в экспоненциальной форме, нормализовав мантиссы 1.25*10^2 и 4.539*10^3. Были бы у этих чисел порядки одинаковые, то мы могли бы просто сложить мантиссы, оставить показатель степени неизменными на том сложение закончить например, Розширення: pdf Розмір: 997кб. Дата: 13.02.2022 скачати Пов'язані файли: пояснительная_записка.doc 2.3*10^2+1.3*10^2 = (2.3+1.3)*10^2 = 3.6*10^2 = 230+130 = 360. Однако, если мы просто сложим мантиссы 1.25 и 4.539, то мы будем складывать сотни с тысячами, десятки с сотнями и т.д., что полностью абсурдно. Поэтому, предварительно выравнивают порядки. Для приведенных примеров числа после выравнивания порядков будут выглядеть таки, или таки. Очевидно, что нормализация мантиссы одного из слагаемых нарушается. В отношении сложения вещественных чисел в машинных кодах логика остается той же. Подпрограмма сложения чисел с плавающей запятой описана в файле fadd.asm, а ее код приведен ниже ;******************************************************************************* ; ; Модуль сложения чисел с плавающей точкой. ; ;******************************************************************************* ; Подпрограмма сложения чисел с плавающей точкой. AddF: mov acc, pA ; eor acc, pB ; Знаки совпадают brpl AddF1 ; Да, выполняем сложение. ldi temp, 0x80 ; B <- (-B) add pB, temp 22 rcall SubF ; Знаки разные. Переходим к вычитанию. rjmp EndAdd ; Проверка операндов на равенство нулю. AddF1: rcall cp_B_0 ; B = 0? breq EndAdd ; A+0 = A. rcall cp_A_0 ; A = 0? brne AddF2 ; A != 0. Продолжаем. rcall swapAB ; A <-> B rjmp EndAdd ; 0+B = B. A <- B; ; Проверка порядков. Больший операнд будет первым. AddF2: bst pB, 7 ; Запоминаем знак. rcall rec ; Восстановление А. rcall swapAB rcall rec ; Восстановление B. mov acc, pA sub acc, pB ; acc <- pA-pB brpl AddF3 ; Порядок первого > порядка второго || dp = 0. rcall swapAB mov acc, pA sub acc, pB ; Выравнивание порядков. AddF3: breq AddF6 ; dp = 0. Переход к сложению мантисс. cpi acc, 24 ; Есть ли смысл двигать, или A >> B (A << B)? brmi AddF4 ; if res < 0. if dp < 24. rjmp AddF7 ; В В очень маленькое знач. Выходим. ; AddF4: mov pA, acc ; Разность порядков в pA rcall swapAB ; Теперь разность в pB ; Движение "верхнего" операнда. AddF5: rcall rshift inc pA dec pB ; Разность уменьшается. brne AddF5 ; Пока не сравняем. ; Сложение мантисс. AddF6: add mAL, mBL adc mAM, mBM adc mAH, mBH brcc AddF7 ; Ничего не вылезло. Пакуем. inc pA ; Коррекция порядка. cpi pA, 0xff ; Проверка на переполнение. breq EndAdd rcall rshift ; Сдвиг мантиссы. ; Упаковка результата. AddF7: rcall pack ; Окончание сложения. EndAdd: ret 23 Пример A B C=A+B A in HEX B in HEX C (теор) in HEX C (практ.) in HEX 1.365 368.2 369.565 3faeb852 43b8199a 43b8c852 43b8c852 6.3 Вычитание Логика алгоритма вычитания, в общем-то, такая же, как и алгоритма сложения вначале выравниваем порядки, затем выполняем вычитание мантисс. Плюс, всякие частности вроде проверки на равенство нулю уменьшаемого или вычитаемого (аналогичные проверки есть и при сложении, проверки на антипереполнение (порядок становится равным 0xFF (в стандарте IEEE-754 порядок, равный 0xFF, используется для обозначения бесконечности или неопределенности, в зависимости от значения битв мантиссе, ноне вследствие последовательного увеличения порядка и перехода от 0xFE ка вследствие последовательного уменьшения порядка, и перехода от 0x00 к 0xFF). Подпрограмма вычитания чисел с плавающей запятой описана в файле fsub.asm, а ее код приведен ниже ;******************************************************************************* ; ; Модуль вычитания чисел с плавающей точкой. ; ;******************************************************************************* ; Операция вычитания вещественных чисел. SubF: mov acc, pA eor acc, pB brpl SubF1 ; Знаки одинаковые. ldi temp, 0x80 add pB, temp rcall AddF ; Меняем знаки, переходим к сложению. rjmp EndSub ; Проверка операндов на равенство нулю. SubF1: rcall cp_B_0 ; A-0 = A breq EndSub rcall cp_A_0 brne SubF2 ; Продолжаем алгоритм. rcall swapAB ldi temp, 0x80 add pA, temp ; 0-B = -B. rjmp EndSub ; Сравниваем порядки и мантиссы. SubF2: bst pA, 7 ; T <- sign rcall rec ; Восстановление операндов. rcall swapAB rcall rec mov acc, pB sub acc, pA ; dp = pB-pA brne SubF3 ; Продолжаем алгоритм. cp mBH, mAH ; mB-mA Сравниваем мантиссы. brne SubF3 24 cp mBM, mAM brne SubF3 cp mBL, mAL brne SubF3 clr pA ; Числа равны. return 0 clr mAH clr maM clr mAL rjmp EndSub ; Первым операндом ставит большее число. SubF3: brcc SubF4 ; if B > A rcall swapAB ; Обмен и инверсия знака. brbc 6, SubF3_m1 ; Проверка го бита. rjmp SubF3_m2 SubF3_m1: set ; T = 1 rjmp SubF4 SubF3_m2: clt ; T = 0 ; Проверка равенства порядков, а также потери значности. SubF4: mov acc, pB ; Проверка равенства порядков. sub acc, pA ; dp = pB-pA breq SubF7 ; Переход к вычитанию мантисс. cpi acc, 24 ; Нет ли потери значности. brmi SubF6 ; Переходим к выравниванию порядков. rcall swapAB ; rjmp SubF9 ; Упаковка результата. ; Выравнивание порядка. SubF6: rcall rshift dec acc brne SubF6 ; Вычитание мантисс. SubF7: sub mBL, mAL mov mAL, mBL sbc mBM, mAM mov mAM, mBM sbc mBH, mAH mov mAH, mBH mov pA, pB ; Проверка нормализации. SubF8: sbrc mAH, 7 ; Если нарушена, переходим сразу к декременту. rjmp SubF9 ; Иначе, пакуем. dec pA cpi pA, 0xff ; Проверка антипереполнения. sec ; C = 1 breq EndSub ; ret NaN lsl mAL rol mAM rol mAH clc rjmp SubF8 ; Повтор проверки. ; Упаковка результата. 25 SubF9: rcall pack ; Завершение вычитания. EndSub: ret Пример A B C=A-B A in HEX B in HEX C (теор) in HEX C (практ.) in HEX 1.365 368.2 -366,835 3faeb852 43b8199a c3b76ae1 c3b76ae2 6.4 Умножение Всем нам хорошо известен алгоритм умножения "в столбик. Если речь о десятичных числах, то умножение мы начинаем с единиц умножаем единицы второго сомножителя на все разряды первого сомножителя – так получаем первое частичное произведение. Затем перемножаем десятки второго сомножителя на все разряды первого сомножителя – получаем второе частичное произведение, и сдвигаем его на один разряд влево и т.д. Затем суммируем все частичные произведения. Сдвиг выполняется, опять же, затем, чтоб не складывать слонов с обезьянами, образно говоря – те, чтоб при сложении частичных произведений складывать единицы с единицами, десятки с десятками и т.д. К сожалению, очень многие выполняют операции "в столбик" "на автомате, без понимания сути того, что они делают. Немного поразмыслив с карандашом в руках илистом бумаги, несложно убедиться, что возможно всего 4 алгоритма умножения 1 - начиная с мл. разрядов, со сдвигом частичного произведения влево, 2 - начиная с мл. разрядов, со сдвигом частичного произведения вправо, 3 - начиная сост. разрядов, со сдвигом частичного произведения влево, 4 - начиная сост. разрядов, со сдвигом частичного произведения вправо. В данной работе мантиссы чисел перемножаются с помощью второго алгоритма. Т.к. числа у нас в экспоненциальной форме, то порядки просто складываются. Знак произведения определяется вначале операции, и затем учитывается при формировании конечного результата. Код подпрограммы ;******************************************************************************* ; ; Модуль умножения чисел с плавающей точкой. ; ;******************************************************************************* ; Умножение чисел с плавающей запятой. MulF: clr _pC clr mCL clr mCM clr mCH rcall cp_A_0 breq EndMul rcall cp_B_0 breq EndMulB0 26 mov acc, pA eor acc, pB bst acc, 7 rcall rec rcall swapAB rcall rec mov _pC, pB add _pC, pA brcs MulF1 subi _pC, 127 brcc MulF2 rjmp EndMul ; MulF1: ldi temp, 129 add _pC, temp brcs EndMul ; MulF2: ldi cnt, 24 MulF3: ror mBH ror mBM ror mBL brcc MulF4 add mCL, mAL adc mCM, mAM adc mCH, mAH MulF4: dec cnt brne MulF5 rjmp MulF6 MulF5: ror mCH ror mCM ror mCL rjmp MulF3 ; MulF6: brcc MulF7 ror mCH ror mCM ror mCL inc _pC cpi _pc, 0xFF sec breq EndMul rjmp MulF7 EndMulB0: mov pA, _pC mov mAL, mCL mov mAM, mCM mov mAH, mCH rjmp EndMul ; MulF7: mov pA, _pC 27 mov mAL, mCL mov mAM, mCM mov mAH, mCH rcall pack ; Умножение завершено. EndMul: ret Пример A B C=A*B A in HEX B in HEX C (теор) in HEX C (практ.) in HEX 1.365 368.2 502.593 3faeb852 43b8199a 43fb4be7 43fb4be8 6.5 Деление В данной работе использован алгоритм деления чисел с восстановлением остатка. Как и при делении десятичных чисел "в столбик, разряды частного получаются по одному. Код подпрограммы ;******************************************************************************* ; ; Модуль деления чисел с плавающей точкой. ; ;******************************************************************************* ; Деление вещественных чисел. DivF: clr _pC clr mCL clr mCM clr mCH rcall cp_B_0 sec breq EndDiv rcall cp_A_0 breq EndDiv mov acc, pA eor acc, pB bst acc, 7 rcall rec rcall swapAB rcall rec rcall swapAB mov _pC, pA sub _pC, pB brcc DivF1 ldi temp, 127 add _pC, temp brcs DivF2 sec rjmp EndDiv ; 28 DivF1: ldi temp, 127 add _pC, temp brcs EndDiv cpi _pC, 0xFF sec breq EndDiv ; DivF2: ldi cnt, 24 lsr mAH ror mAM ror mAL lsr mBH ror mBM ror mBL ; DivF3: sub mAL, mBL sbc mAM, mBM sbc mAH, mBH brcc DivF4 add mAL, mBL adc mAM, mBM adc mAH, mBH clc rjmp DivF5 DivF4: sec DivF5: rol mCL rol mCM rol mCH lsl mAL rol mAM rol mAH dec cnt brne DivF3 sbrc mCH, 7 rjmp DivF6 rol mCL rol mCM rol mCH dec _pC cpi _pC, 0xFF sec breq EndDiv ; DivF6: mov pA, _pC mov mAL, mCL mov mAM, mCM mov mAH, mCH rcall pack ; Деление завершено. EndDiv: ret 29 Пример A B C=A/B A in HEX B in HEX C (теор) in HEX C (практ.) in HEX 1.365 368.2 0.003707224 3faeb852 43b8199a 3b72f4e6 3b72f4e6 7. Некоторые стандартные математические функции К данным функциям отнесена подпрограмма взятия модуля числа и подпрограмма взятия целой части от числа. 7.1 Взятие модуля от числа Для взятия модуля достаточно сбросить знаковый бит числа. Подпрограмма описана в файле fabs.asm. ;******************************************************************************* ; Взятие модуля числа. A <- Abs(A). ;******************************************************************************* AbsF: andi pA, 0x7F ; Сброс знакового разряда. ret 7.2 Взятие целой части от числа Взятие целой части сводится к "затиранию" дробной части мантиссы вначале она на нужное количество разрядов логически смещается вправо, а затем возвращается на место. Количетство разрядов определяется исходя из порядка числа. Код подпрограммы описан в файле fintof.asm. ;******************************************************************************* ; ; Взятие целой части от числа. A <- INT(A) ; Дробная часть просто отбрасывается. ; ;******************************************************************************* IntOf: rcall cp_A_0 ; A = 0? breq EndIntOf ; Z = 1 bst pA, 7 ; Запоминаем знак. rcall rec ; Восстановление из базового формата. ldi temp, 127 sub pA, temp ; Истинный порядок. brpl IntOf1 ; if |A| >= 1 rcall ClrA ; A = 0 rjmp EndIntOf IntOf1: cpi pA, 0 breq IntOf2 ; else, A != 0 && A != 1 && A != -1 ldi cnt, 24 sub cnt, pA dec cnt ; Мантиссу двигаем вправо, чтоб 30 ; затереть дробную часть. IntOf3: rcall rshift dec cnt brne IntOf3 ; while cnt > 0 ldi cnt, 24 sub cnt, pA dec cnt IntOf4: ; Теперь возвращаем мантиссу на место. rcall lshift dec cnt brne IntOf4 ; while cnt > 0 ldi temp, 127 add pA, temp rcall pack rjmp EndIntOf IntOf2: ; +1 Or -1 clr mAH clr mAM clr mAL ldi temp, 127 add pA, temp ori mAH, 0x80 rcall pack EndIntOf: ret Пример A INT(A) A in HEX INT(A) (теор) in HEX INT(A) (практ.) in HEX 368.2 368 43b8199a 43b80000 43b80000 8. Некоторые стандартные функции для работы с числами К числу данных функций отнесены подпрограмма преобразования целого числа в вещественное и подпрограмма преобразование вещественного числа в целое. Подпрограммы описаны в файле stdlib.asm. 8.1 Преобразование целого числа в вещественное Подпрограмма реализует обычный алгоритм представления вещественного числа в машинном формате. Исходное целое число (от -128 до +127) помещается в регистра выходное вещественное число возвращается в составной регистр A. ;******************************************************************************* ; ; Стандартные функции. ; ;******************************************************************************* 31 ;******************************************************************************* ; Приведение целочисленного типа к вещественному. ; Входное число в диапазоне от -128 до 127. ;******************************************************************************* IntToFloat: cpi mAL, 0x00 ; Проверка на равенство нулю. brne SignByteToFloat1 ; if A != 0 clr pA ; return 0x00000000 clr mAH clr mAM clr mAL rjmp EndSignByteToFloat ; Начинаем преобразование. SignByteToFloat1: ori mAL, 0x00 ; Проверка знака. clt ; T = 0. brpl SignByteToFloat2 ; if A >= 0 set ; Иначе, берем модуль. Знак в T. dec mAL ; A = A-1. ldi temp, 0xFF eor mAL, temp ; A = not A ; Очищаем счетчик сдвигов. SignByteToFloat2: clr cnt ; cnt = 0 ; Выравниваем число полевому краю, считая сдвиги. SignByteToFloat3: ori mAL, 0x00 brpl SignByteToFloat4 ; В м бите еще 0? rjmp SignByteToFloat5 ; Число уже "уперлось" в левый край. SignByteToFloat4: lsl mAL inc cnt rjmp SignByteToFloat3 ; Получаем порядок. SignByteToFloat5: ldi temp, 8 sub temp, cnt dec temp ; Порядок. p = 8-cnt-1 clr pA ; pA = 0 mov pA, temp ldi temp, 127 add pA, temp ; pA - Машинный порядок. mov mAH, mAL ; Восстановленная мантисса. clr mAM clr mAL ; Упаковка результата. mov pB, pA ; pB <- pA. clc ; C = 0. По умолчанию положительное. brtc SignByteToFloat6 ; if T = 0, пропускаем. sec ; C = 1. SignByteToFloat6: ror pA ; Что в С положили, то и втянем. clc ; То, что вытолкнули, нам не надо. bst pB, 0 ; T <- pB(0). Мл. разряд порядка. bld mAH, 7 ; mAH(7) <- T. Скрытую единицу затерли. ; Приведение завершено. EndSignByteToFloat: 32 ret 8.2 Преобразование вещественного числа в целое Вначале выполняется проверка на попадание в диапазон от -128 до 127. Если проверка пройдена, то мантисса сдвигается вправо на 23-p разрядов, где p – истинный порядок числа. В конце выполняется коррекция знака и копирование результата в регистр pA. Использованные подпрограммы AToBuf и BufToA будут рассмотрены в разделе 10 – первая из них помещает содержимое регистра A в буферную область памяти, а вторая выполняет обратную операцию. Код подпрограммы ;******************************************************************************* ; Преобразование вещественного числа в целое. ; Применим только к числам, для которых справедливо ; неравенство -128 <= Int(A) <= 127. ;******************************************************************************* FloatToInt: rcall AToBuf ori pA, 0x00 brpl FloatToInt1 ldi pB, 0x43 ; 128 ldi mBH, 0x00 ldi mBM, 0x00 ldi mBL, 0x00 rcall AddF ori pA, 0x00 brpl FloatToInt2 rjmp EndFloatToInt FloatToInt1: ldi pB, 0x42 ; 127 ldi mBH, 0xfe ldi mBM, 0x00 ldi mBL, 0x00 rcall SubF ori pA, 0x00 brpl EndFloatToInt rjmp FloatToInt2 FloatToInt2: rcall BufToA rcall rec ldi temp, 127 sub pA, temp ldi temp, 23 sub temp, pA 33 mov cnt, temp FloatToInt3: rcall rshift dec cnt brne FloatToInt3 rcall BufToB andi pB, 0x80 brpl FloatToInt4 neg mAL FloatToInt4: mov pA, mAL EndFloatToInt: ret 9. Определение констант Определение констант выполнено в файле def_const.inc, содержимое которого представлено ниже ;******************************************************************************* ; ; Определение констант. ; ;******************************************************************************* .equ buff = 0x60 ; Буфер на 4 байта. 0x60-0x63 .equ hours = 0x64 ; Часы. .equ minut = 0x65 ; Минуты. .equ secnd = 0x66 ; Секунды. .equ day = 0x67 ; День. .equ month = 0x68 ; Месяц. .equ year = 0x69 ; Год. 0x69-0x6c .equ l_deg = 0x6d ; Долгота. Градусы. .equ l_min = 0x6e ; Минуты. .equ l_sec = 0x6f ; Секунды. .equ longt = 0x70 ; Долгота. As Float. 0x70-0x73 .equ s_deg = 0x74 ; Широта. Градусы. .equ s_min = 0x75 ; Минуты. .equ s_sec = 0x76 ; Секунды. .equ lattd = 0x77 ; Широта. As Float. 0x77-0x7a .equ timep = 0x7b ; Номер часового пояса. .equ sumtm = 0x7c ; Признак перехода на летнее время. .equ time = 0x7d ; Время в формате HH,hh. 0x7d-0x80 .equ dddd = 0x81 ; День в формате DD,dd. 0x81-0x84 .equ n_jd = 0x85 ; n. 0x85-0x88 Вспомогательные величины. .equ m_jd = 0x89 ; m. 0x89-0x8c .equ p_jd = 0x8d ; p. 0x8d-0x90 .equ jdate = 0x91 ; JD. 0x91-0x94 .equ jcent = 0x95 ; Юлианские столетия T. 0x95-0x98 .equ star0 = 0x99 ; Звездное время по Гринвичу. 0x99-0x9c .equ ptime = 0x9d ; Поясное время (часы и доли часа. 0x9d-0xa0 .equ tstar = 0xa1 ; Звездное время. 0xa1-0xa4 .equ decln = 0xa5 ; Склонение. 0xa5-0xa8 .equ rgsun = 0xa9 ; Прямое восхождение. 0xa9-0xac .equ tmang = 0xad ; Часовой угол. 0xad-0xb0 34 .equ powx = 0xb1 ; Степень х (в триг. операциях. 0xb1-0xb4 .equ fact = 0xb5 ; Факториал (в триг. операциях. 0xb5-0xb8 .equ sumr = 0xb9 ; Накопитель (в триг. операциях. 0xb9-0xbc .equ acc_x = 0xbd ; Вспомогательная величина X. 0xbd-0xc0 .equ acc_y = 0xc1 ; Вспомогательная величина Y. 0xc1-0xc4 .equ acc_z = 0xc5 ; Вспомогательная величина Z. 0xc5-0xc8 .equ azim = 0xc9 ; Азимут в радианах. 0xc9-0xcc .equ heig = 0xcd ; Высота в радианах. 0xcd-0xd0 Указанные в файле константы определяют начальные адреса соответствующих областей памяти. Например, мы видим, что географическая долгота как вещественно число хранится в памяти по адресу 0x70-0x73, и константа longt указывает на адрес первого байта. Для удобства, младшие байты чисел размещались по старшим адресам, а не так, как это принято в архитектуре Intel. Данный файл можно использовать как "карту памяти" – исходя из него мы можем видеть, что и по каким адресам будет располагаться в памяти. 1 2 3 4 5 6 7 |