Скачиваний:
27
Добавлен:
15.09.2014
Размер:
42.71 Кб
Скачать

ПРИМЕР Файл lst_2_01.inc Подпрограмма, устанавливающая режим линейной адресации данных.

; Автор текста программы Кулаков Владимир Геннадьевич. ; Порт, управляющий запретом немаскируемых прерываний CMOS_ADDR equ 0070h CMOS_DATA equ 0071h ; Селекторы сегментов SYS_PROT_CS equ 0008h SYS_REAL_SEG equ 0010h SYS_MONDO_SEG equ 0018h CODESEG ;********************************************** ;* ВКЛЮЧЕНИЕ РЕЖИМА ЛИНЕЙНОЙ АДРЕСАЦИИ ПАМЯТИ * ;* (процедура параметров не имеет) * ;********************************************** PROC Initialization NEAR pushad ; Сохранить значения сегментных регистров в ; реальном режиме (кроме GS) mov [CS:Save_SP],SP mov AX,SS mov [CS:Save_SS],AX mov AX,DS mov [CS:Save_DS],AX mov AX,CS mov [word ptr CS:Self_Mod_CS],AX mov DS,AX cli mov SS,AX mov SP,offset Local_Stk_Top sti ; Установить режим линейной адресации call SetLAddrModeForGS ; Восстановить значения сегментных регистров cli mov SP,[CS:Save_SP] mov AX,[CS:Save_SS] mov SS,AX mov AX,[CS:Save_DS] mov DS,AX sti ; Разрешить работу линии A20 call Enable_A20 popad ret ENDP Initialization ; Область сохранения значений сегментных регистров Save_SP DW ? Save_SS DW ? Save_DS DW ? ; Указатель на GDT GDTPtr DQ ? ; Таблица дескрипторов сегментов для ; входа в защищенный режим GDT DW 00000h,00000h,00000h,00000h ;не используется DW 0FFFFh,00000h,09A00h,00000h ;сегмент кода CS DW 0FFFFh,00000h,09200h,00000h ;сегмент данных DS DW 0FFFFh,00000h,09200h,0008Fh ;сегмент GS ; Локальный стек для защищенного режима ; (организован внутри кодового сегмента) label GDTEnd word DB 255 DUP(0FFh) Local_Stk_Top DB (0FFh) ; ОТМЕНИТЬ ПРЕДЕЛ СЕГМЕНТА GS ; Процедура изменяет содержимое теневого ; регистра GS таким образом, что становится ; возможной линейная адресация через него ; 4 Gb памяти в реальном режиме PROC SetLAddrModeForGS near ; Вычислить линейный адрес кодового сегмента mov AX,CS movzx EAX,AX shl EAX,4 ;умножить номер параграфа на 16 mov EBX,EAX ;сохранить линейный адрес в EBX mov [word ptr CS:GDT+10],AX mov [word ptr CS:GDT+18],AX ; Переставить местами старшее и младшее слова ror EAX,16 ; Занести биты 16-23 линейного адреса в дескрипторы ; сегментов кода и данных mov [byte ptr CS:GDT+12],AL mov [byte ptr CS:GDT+20],AL ; Установить предел (Limit) и базу (Base) для GDTR add EBX, offset GDT mov [word ptr CS:GDTPtr],(offset GDTEnd-GDT-1) mov [dword ptr CS:GDTPtr+2],EBX ; Сохранить регистр флагов pushf ; Запретить прерывания, так как таблица прерываний IDT ; не сформирована для защищенного режима cli ; Запретить немаскируемые прерывания NMI in AL,CMOS_ADDR mov AH,AL or AL,080h ;установить старший разряд out CMOS_ADDR,AL ;не затрагивая остальные and AH,080h ; Запомнить старое состояние маски NMI mov CH,AH ; Перейти в защищенный режим lgdt [fword ptr CS:GDTPtr] mov BX,CS ;запомнить сегмент кода mov EAX,CR0 or AL,01b ;установить бит PE mov CR0,EAX ;защита разрешена ; Безусловный дальний переход на метку SetPMode ; (очистить очередь команд и перезагрузить CS) DB 0EAh DW (offset SetPMode) DW SYS_PROT_CS SetPMode: ; Подготовить границы сегментов mov AX,SYS_REAL_SEG mov SS,AX mov DS,AX mov ES,AX mov FS,AX ; Снять ограничения с сегмента GS mov AX,SYS_MONDO_SEG mov GS,AX ; Вернуться в реальный режим mov EAX,CR0 and AL,11111110b ;сбросить бит PE mov CR0,EAX ;защита отключена DB 0EAh DW (offset SetRMode) Self_Mod_CS DW ? SetRMode: ; Регистры стека и данных ; настроить на сегмент кода mov SS,BX mov DS,BX ; Обнулить дополнительные сегментные ; регистры данных (GS не трогать!) xor AX,AX mov ES,AX mov FS,AX ; Возврат в реальный режим, ; прерывания снова разрешены in AL,CMOS_ADDR and AL,07Fh or AL,CH out CMOS_ADDR,AL popf ret ENDP SetLAddrModeForGS ;**************************************** ;* Разрешить работу с памятью выше 1 Мб * ;**************************************** PROC Enable_A20 near call Wait8042BufferEmpty mov AL,0D1h ;команда управления линий A20 out 64h,AL call Wait8042BufferEmpty mov AL,0DFh ;разрешить работу линии out 60h,AL call Wait8042BufferEmpty ret ENDP Enable_A20 ;*********************************************** ;* ОЖИДАНИЕ ОЧИСТКИ ВХОДНОГО БУФЕРА I8042 * ;* При выходе из процедуры: * ;* флаг ZF установлен - нормальное завершение, * ;* флаг ZF сброшен - ошибка тайм-аута. * ;*********************************************** proc Wait8042BufferEmpty near push CX mov CX,0FFFFh ;задать число циклов @@kb: in AL,64h ;получить статус test AL,10b ;буфер i8042 свободен? loopnz @@kb ;если нет, то цикл pop CX ; (если при выходе сброшен флаг ZF - ошибка) ret endp Wait8042BufferEmpty ENDS Файл lst_2_02.inc Отладочная подпрограмма, предназначенная для отображения на экран содержимого регистров процессора

; Автор текста программы Кулаков Владимир Геннадьевич. DATASEG label REGROW_386 byte DB 0,0,'EAX =',0 DB 1,0,'EBX =',0 DB 2,0,'ECX =',0 DB 3,0,'EDX =',0 DB 4,0,'ESI =',0 DB 5,0,'EDI =',0 DB 6,0,'EBP =',0 DB 7,0,'ESP =',0 DB 8,0,'IP =',0 DB 9,0,'CS =',0 DB 10,0,'DS =',0 DB 11,0,'ES =',0 DB 12,0,'FS =',0 DB 13,0,'GS =',0 DB 14,0,'SS =',0 DB 16,8,' AVR NIOODIT SZ A P C',0 DB 17,8,' CMF TPLFFFF FF F F F',0 DB 18,0,'Флаги:',0 DB 20,8,'PCN A V NETEMP',0 DB 21,8,'GDW M P ETSMPE',0 DB 22,0,'CR0:',0 DB 24,15 DB 'Для продолжения работы нажмите любую клавишу',0 CODESEG ;********************************************** ;* ВЫВЕСТИ НА ЭКРАН ДАМП РЕГИСТРОВ ПРОЦЕССОРА * ;* (процедура параметров не имеет) * ;********************************************** PROC ShowRegs FAR pushad pushfd push DS mov BP,SP mov AX,DGROUP mov DS,AX ; Сохраняем глобальные переменные mov AL,[TextColorAndBackground] push AX push [ScreenString] push [ScreenColumn] ; Очищаем экран call ClearScreen ; Вывести 21 строку текста mov [TextColorAndBackground],YELLOW mov SI, offset REGROW_386 mov CX,22 @@GLB: call ShowString loop @@GLB mov [TextColorAndBackground],WHITE mov EAX,[BP+34] ;Показать EAX mov [ScreenString],0 mov [ScreenColumn],6 call ShowHexDWord mov EAX,[BP+22] ;Показать EBX inc [ScreenString] mov [ScreenColumn],6 call ShowHexDWord mov EAX,[BP+30] ;Показать ECX inc [ScreenString] mov [ScreenColumn],6 call ShowHexDWord mov EAX,[BP+26] ;Показать EDX inc [ScreenString] mov [ScreenColumn],6 call ShowHexDWord mov EAX,[BP+10] ;Показать ESI inc [ScreenString] mov [ScreenColumn],6 call ShowHexDWord mov EAX,[BP+6] ;Показать EDI inc [ScreenString] mov [ScreenColumn],6 call ShowHexDWord mov EAX,[BP+14] ;Показать EBP inc [ScreenString] mov [ScreenColumn],6 call ShowHexDWord mov EAX,[BP+18] ;Показать ESP inc [ScreenString] mov [ScreenColumn],6 call ShowHexDWord mov AX,[BP+38] ;Показать IP inc [ScreenString] mov [ScreenColumn],6 call ShowHexWord mov AX,[BP+40] ;Показать CS inc [ScreenString] mov [ScreenColumn],6 call ShowHexWord mov AX,[BP] ;Показать DS inc [ScreenString] mov [ScreenColumn],6 call ShowHexWord mov AX,ES ;Показать ES inc [ScreenString] mov [ScreenColumn],6 call ShowHexWord mov AX,FS ;Показать FS inc [ScreenString] mov [ScreenColumn],6 call ShowHexWord mov AX,GS ;Показать GS inc [ScreenString] mov [ScreenColumn],6 call ShowHexWord mov AX,SS ;Показать SS inc [ScreenString] mov [ScreenColumn],6 call ShowHexWord add [ScreenString],4 mov [ScreenColumn],8 mov EAX,[BP+2] call ShowBinDWord add [ScreenString],4 mov [ScreenColumn],8 mov EAX,CR0 call ShowBinDWord ; Ожидаем нажатия любого символа на клавиатуре call GetChar ; Очищаем экран call ClearScreen ; Восстановить глобальные переменные pop [ScreenColumn] pop [ScreenString] pop AX mov [TextColorAndBackground],AL pop DS popfd popad ret ENDP ShowRegs ENDS Файл lst_2_05.inc Набор подпрограмм, предназначенных для перевода целых и вещественных чисел из двоичной системы счисления в десятичную и наоборот.

; Автор текста программы Кулаков Владимир Геннадьевич. DATASEG ; Количество разрядов мантиссы (1-18) MaxPositions DW 10 ; Количество знаков числа после запятой (1-17) NumberSymbolsAD DW 5 ; Машинный ноль Data_Minimum DQ 1.E-110 ; Константы (10 в степени N) MConst DQ 1.0E1,1.0E2,1.0E3,1.0E4,1.0E5 DQ 1.0E6,1.0E7,1.0E8,1.0E9,1.0E10 DQ 1.0E11,1.0E12,1.0E13,1.0E14,1.0E15 DQ 1.0E16,1.0E17,1.0E18,1.0E19,1.0E20 DQ 1.0E21,1.0E22,1.0E23,1.0E24,1.0E25 DQ 1.0E26,1.0E27,1.0E28,1.0E29,1.0E30 DQ 1.0E31,1.0E32,1.0E33,1.0E34,1.0E35 DQ 1.0E36,1.0E37,1.0E38,1.0E39,1.0E40 DQ 1.0E41,1.0E42,1.0E43,1.0E44,1.0E45 DQ 1.0E46,1.0E47,1.0E48,1.0E49,1.0E50 DQ 1.0E51,1.0E52,1.0E53,1.0E54,1.0E55 DQ 1.0E56,1.0E57,1.0E58,1.0E59,1.0E60 DQ 1.0E61,1.0E62,1.0E63,1.0E64,1.0E65 DQ 1.0E66,1.0E67,1.0E68,1.0E69,1.0E70 DQ 1.0E71,1.0E72,1.0E73,1.0E74,1.0E75 DQ 1.0E76,1.0E77,1.0E78,1.0E79,1.0E80 DQ 1.0E81,1.0E82,1.0E83,1.0E84,1.0E85 DQ 1.0E86,1.0E87,1.0E88,1.0E89,1.0E90 DQ 1.0E91,1.0E92,1.0E93,1.0E94,1.0E95 DQ 1.0E96,1.0E97,1.0E98,1.0E99,1.0E100 DQ 1.0E101,1.0E102,1.0E103,1.0E104,1.0E105 DQ 1.0E106,1.0E107,1.0E108,1.0E109,1.0E110 DQ 1.0E111,1.0E112,1.0E113,1.0E114,1.0E115 DQ 1.0E116,1.0E117,1.0E118,1.0E119,1.0E120 DQ 1.0E121,1.0E122,1.0E123,1.0E124,1.0E125 DQ 1.0E126,1.0E127,1.0E128 ; Данные передаются в математические процедуры ; через общую область памяти (главный сегмент данных) ; 32-разрядное целое число Data_Int32 DD ? ; Число с плавающей запятой двойной точности Data_Double DQ ? ; Модуль числа с плавающей запятой Data_Abs DQ ? ; Число в BCD-формате Data_BCD DT ? ; Управляющее слово сопроцессора Data_Control DW ? ; Вспомогательный флаг Data_Flag DB ? ; Целая часть десятичного логарифма числа Data_Log10 DW ? ; Знак результата (если не 0 - отрицательное число) Data_Sign DB ? ; Строка для хранения числа в коде ASCII Data_String DB 32 DUP (?) ; Структура для вывода результата OutD_String DB 34 DUP (?) EVEN CODESEG ;*************************************************** ;* ПРЕОБРАЗОВАТЬ 32-РАЗРЯДНОЕ ЦЕЛОЕ ЧИСЛО В СТРОКУ * ;* Входные параметры: * ;* Data_Int32 - 32-разрядное число. * ;* Выходные параметры: * ;* Data_String - строка-результат. * ;*************************************************** PROC Int32_to_String near pushad push DS push ES mov AX,DGROUP mov DS,AX mov ES,AX cld ; Перевести число из двоичного кода в код BCD fninit ;сброс сопроцессора ; Загрузить число в двоичном коде fild [Data_Int32] ; Извлечь число в коде BCD fbstp [Data_BCD] ; Результат записывать в строку Data_String mov DI,offset Data_String call BCD_to_ASCII ; Записать признак конца строки (код 0) xor AL,AL stosb pop ES pop DS popad ret ENDP Int32_to_String ;***************************************************** ;* ПРЕОБРАЗОВАТЬ СТРОКУ В 32-РАЗРЯДНОЕ ЦЕЛОЕ ЧИСЛО * ;* Входные параметры: * ;* Data_String - число в коде ASCII. * ;* Выходные параметры: * ;* Data_Int32 - 32-разрядное число в двоичном коде. * ;***************************************************** PROC String_to_Int32 near pushad push DS mov AX,DGROUP mov DS,AX cld ; Очищаем Data_BCD mov [dword ptr Data_BCD],0 mov [dword ptr Data_BCD+4],0 mov [word ptr Data_BCD+8],0 ; Очищаем байт знака mov [Data_Sign],0 ; Заносим в SI указатель на строку mov SI,offset Data_String ; Пропускаем пробелы перед числом mov CX,64 ;защита от зацикливания @@ShiftIgnore: lodsb cmp AL,' ' jne @@ShiftIgnoreEnd loop @@ShiftIgnore jmp short @@Error @@ShiftIgnoreEnd: ; Проверяем знак числа cmp AL,'-' jne @@Positive mov [Data_Sign],80h lodsb @@Positive: ; 32-битное двоичное число соответствует ; десятичному, имеющему до 9 разрядов mov CX,9 @@ASCIItoBCDConversion: ; Символы числа должны быть цифрами cmp AL,'0' jb @@Error cmp AL,'9' ja @@Error ; Пишем очередную цифру в младшую тетраду BCD and AL,0Fh or [byte ptr Data_BCD],AL ; Проверка на конец строки cmp [byte ptr SI],0 je @@ASCIItoBCDConversionEnd ; Сдвигаем BCD на 4 разряда влево ; (сдвигаем старшие 2 байта) mov AX,[word ptr Data_BCD+6] shld [word ptr Data_BCD+8],AX,4 ; (сдвигаем средние 4 байта) mov EAX,[dword ptr Data_BCD] shld [dword ptr Data_BCD+4],EAX,4 ; (сдвигаем младшие 4 байта) shl [dword ptr Data_BCD],4 ; Загружаем следующий символ в AL lodsb loop @@ASCIItoBCDConversion cmp AL,0 jne @@Error ;переполнение разрядной сетки ;ПРЕОБРАЗОВАТЬ ЧИСЛО ИЗ КОДА BCD В ЦЕЛОЕ ЧИСЛО @@ASCIItoBCDConversionEnd: ; Вписать знак в старший байт mov AL,[Data_Sign] mov [byte ptr Data_BCD+9],AL ; Сбросить регистры сопроцессора fninit ; Загрузить в сопроцессор число в BCD-формате fbld [Data_BCD] ; Выгрузить число в двоичном формате fistp [Data_Int32] jmp short @@End @@Error:; При любой ошибке обнулить результат mov [Data_Int32],0 @@End: pop DS popad ret ENDP String_to_Int32 ;******************************************************* ;* ВЫВОД БАЙТА НА ЭКРАН В ДЕСЯТИЧНОМ КОДЕ * ;* Подпрограмма выводит содержимое регистра AL в * ;* шестнадцатеричном коде в указанную позицию экрана. * ;* Координаты позиции передаются через глобальные * ;* переменные ScreenString и ScreenColumn. После * ;* выполнения операции вывода байта происходит * ;* автоматическое приращение значений этих переменных. * ;******************************************************* PROC ShowDecByte NEAR push EAX and EAX,0FFh call ShowDecDWord pop EAX ret ENDP ShowDecByte ;***************************************************** ;* ВЫВОД 16-РАЗРЯДНОГО СЛОВА НА ЭКРАН * ;* В ДЕСЯТИЧНОМ КОДЕ * ;* Параметры: * ;* AX - число, которое будет выведено на экран. * ;* Номер строки передается через глобальную * ;* переменную ScreenString, номер столбца - через * ;* переменную ScreenColumn, цвет текста определяется * ;* глобальной переменой TextColorAndBackground. * ;***************************************************** PROC ShowDecWord NEAR push EAX and EAX,0FFFFh call ShowDecDWord pop EAX ret ENDP ShowDecWord ;***************************************************** ;* ВЫВОД 32-РАЗРЯДНОГО СЛОВА НА ЭКРАН * ;* В ДЕСЯТИЧНОМ КОДЕ * ;* Параметры: * ;* EAX - число, которое будет выведено на экран. * ;* Номер строки передается через глобальную * ;* переменную ScreenString, номер столбца - через * ;* переменную ScreenColumn, цвет текста определяется * ;* глобальной переменой TextColorAndBackground. * ;***************************************************** PROC ShowDecDWord NEAR pushad push DS push ES ; Настроить регистр DS на глобальный сегмент данных mov DI,DGROUP mov DS,DI ; Перевести число в десятичный код mov [Data_Int32],EAX call Int32_to_String ; Настроить регистры ES:DI для ; прямого вывода в видеопамять ; Загрузить адрес сегмента видеоданных в ES mov AX,0B800h mov ES,AX ; Умножить номер строки на длину ; строки в байтах mov AX,[ScreenString] mov DX,160 mul DX ; Прибавить к полученному произведению номер ; колонки (дважды) add AX,[ScreenColumn] add AX,[ScreenColumn] ; Переписать результат в индексный регистр mov DI,AX cld ; Использовать цвет символов, заданный по умолчанию mov AH,[TextColorAndBackground] ; Вывести число на экран mov CX,10 mov SI,offset Data_String @@NextChar: lodsb ;загрузить цифру в AL and AL,AL ;проверка на 0 (конец строки) jz @@EndOfString stosw ;вывести цифру на экран loop @@NextChar @@EndOfString: pop ES pop DS popad ret ENDP ShowDecDWord ;******************************************************* ;* ПРЕОБРАЗОВАНИЕ ЧИСЛА С ПЛАВАЮЩЕЙ ЗАПЯТОЙ В СТРОКУ * ;* Число имеет формат с удвоенной точностью, результат * ;* выдается в десятичном коде, в "бытовом" формате с * ;* фиксированным количеством знаков после запятой. * ;* Входные параметры: * ;* Data_Double - преобразуемое число; * ;* NumberSymbolsAD - количество знаков после * ;* запятой (0-17). * ;* Выходные параметры: * ;* Data_String - строка-результат. * ;******************************************************* PROC DoubleFloat_to_String near pushad push DS push ES mov AX,DGROUP mov DS,AX mov ES,AX ; Результат записывать в строку Data_String mov DI,offset Data_String ; Сдвигаем число влево на NumberSymbolsAD ; десятичных разрядов fninit ;сброс сопроцессора fld [Data_Double] ;загрузить число mov BX,[NumberSymbolsAD] cmp BX,0 je @@NoShifts ;нет цифр после запятой jl @@Error ;ошибка dec BX shl BX,3 ;умножаем на 8 add BX,offset MConst fmul [qword ptr BX] ;умножить на константу @@NoShifts: ; Извлечь число в коде BCD fbstp [Data_BCD] ; Проверить результат на переполнение mov AX,[offset Data_BCD + 8] cmp AX,0FFFFh ;"десятичное" переполнение? je @@Overflow ; Выделить знак числа и записать его в ASCII-коде mov AL,[offset Data_BCD + 9] and AL,AL jz @@NoSign mov AL,'-' stosb @@NoSign: ; Распаковать число в код ASCII mov BX,8 ;смещение последней пары цифр mov CX,9 ;счетчик пар цифр ; Определить позицию десятичной точки в числе mov DX,18 sub DX,[NumberSymbolsAD] js @@Error ;ошибка, если отрицательная jz @@Error ;или нулевая позиция @@NextPair: ; Загрузить очередную пару разрядов mov AL,[BX + offset Data_BCD] mov AH,AL ; Выделить, перевести в ASCII и ; сохранить старшую тетраду shr AL,4 add AL,'0' stosb dec DX jnz @@N0 mov AL,'.' stosb @@N0: ; Выделить, перевести в ASCII и ; сохранить младшую тетраду mov AL,AH and AL,0Fh add AL,'0' stosb dec DX jnz @@N1 mov AL,'.' stosb @@N1: dec BX loop @@NextPair mov AL,0 stosb ; Убрать незначащие нули слева mov DI,offset Data_String mov SI,offset Data_String ; Пропустить знак числа, если он есть cmp [byte ptr SI],'-' jne @@N2 inc SI inc DI @@N2: ; Загрузить в счетчик цикла количество разрядов ; числа плюс 1 (байт десятичной точки) mov CX,18+1+1 ; Пропустить незначащие нули @@N3: cmp [byte ptr SI],'0' jne @@N4 cmp [byte ptr SI+1],'.' je @@N4 inc SI loop @@N3 ; Ошибка - нет значащих цифр jmp short @@Error ; Скопировать значащую часть числа в начало строки @@N4: rep movsb jmp short @@End ; Ошибка @@Error: mov AL,'E' stosb mov AL,'R' stosb mov AL,'R' stosb xor AL,AL stosb jmp short @@End ; Переполнение разрядной сетки @@Overflow: mov AL,'#' stosb xor AL,AL stosb ; Конец процедуры @@End: pop ES pop DS popad ret ENDP DoubleFloat_to_String ;******************************************************* ;* ПРЕОБРАЗОВАТЬ ЧИСЛО С ПЛАВАЮЩЕЙ ЗАПЯТОЙ В СТРОКУ * ;* Число имеет формат с удвоенной точностью, результат * ;* выдается в десятичном коде в научном формате, т.е. * ;* с нормализованной мантиссой и порядком. * ;* Входные параметры: * ;* Data_Double - преобразуемое число; * ;* MaxPositions - количество разрядов мантиссы (1-18). * ;* Выходные параметры: * ;* Data_String - строка-результат. * ;******************************************************* PROC DoubleFloat_to_ExpForm near pushad push DS push ES mov AX,DGROUP mov DS,AX mov ES,AX ; Результат записывать в строку Data_String mov DI,offset Data_String ; Устанавить режим округления "вниз" (01) fninit ;сброс сопроцессора fstcw [Data_Control] ;считать слово состояния ; Изменить режим округления and [Data_Control],0F7FFh or [Data_Control],0400h fldcw [Data_Control] ; Определить знак числа fld [Data_Double] ;загрузить число fxam ;протестировать число fstsw AX test ah,10b ;проверить знак jz @@z0 ; Число отрицательное mov AL,'-' ;записать знак "минус" stosb ; Запомнить модуль числа fabs @@z0: fst [Data_Abs] ; Произвести проверку на "машинный ноль" fcom [Data_Minimum] fstsw AX and AH,1000001b jnz @@Zero ; Вычислить десятичный логарифм числа fldlg2 fxch ST(1) fyl2x ; Записать десятичный порядок fistp [Data_Log10] ; Нормализовать десятичную мантиссу fninit ;сброс сопроцессора fld [Data_Abs] ;загрузить модуль числа mov BX,[MaxPositions] sub BX,[Data_Log10] cmp BX,0 je @@z2 ;сдвиги не нужны jl @@z1 ;нужен сдвиг вправо ; Сдвинуть число влево ; на [BX] десятичных разрядов dec BX cmp BX,127 ja @@Zero ;машинный ноль shl BX,3 ;умножаем на 8 add BX,offset MConst fmul [qword ptr BX] ;умножить на константу jmp short @@z2 ; Сдвинуть число вправо ; на [BX] десятичных разрядов @@z1: neg BX dec BX cmp BX,127 ja @@Overflow ;переполнение shl BX,3 ;умножаем на 8 add BX,offset MConst fdiv [qword ptr BX] ;разделить на константу @@z2: fbstp [Data_BCD] ;извлечь число в коде BCD ; Записать в точку перед мантиссой mov SI,DI ;запомнить позицию точки mov AL,'.' stosb ; Преобразовать мантиссу в код ASCII call BCD_to_ASCII ; Помнять местами точку и первую цифру мантиссы mov AX,[SI] xchg AL,AH mov [SI],AX ; Перевести порядок результата в код BCD ; Проверить значение порядка cmp [Data_Log10],0 je @@End ; Записать признак порядка mov AL,'e' stosb fninit ;сброс сопроцессора ; Загрузить десятичный порядок в двоичном коде fild [Data_Log10] ; Извлечь порядок в коде BCD fbstp [Data_BCD] ; Результат записать в строку Data_String call BCD_to_ASCII jmp short @@End @@Zero: ; Машинный ноль mov AL,'0' stosb jmp short @@End @@Overflow: ; Переполнение разрядной сетки mov AL,'#' stosb @@End: ; Записать признак конца строки (код 0) xor AL,AL stosb pop ES pop DS popad ret ENDP DoubleFloat_to_ExpForm ;**************************************************** ;* ПРЕОБРАЗОВАТЬ СТРОКУ В ЧИСЛО С ПЛАВАЮЩЕЙ ЗАПЯТОЙ * ;* (число имеет обычный, "бытовой" формат) * ;* Входные параметры: * ;* Data_String - число в коде ASCII. * ;* Выходные параметры: * ;* Data_Double - число в двоичном коде. * ;**************************************************** PROC String_to_DoubleFloat near pushad push DS mov AX,DGROUP mov DS,AX cld ; Очищаем Data_BCD mov [dword ptr Data_BCD],0 mov [dword ptr Data_BCD+4],0 mov [word ptr Data_BCD+8],0 ; Очищаем байт знака mov [Data_Sign],0 ; Заносим в SI указатель на строку mov SI,offset Data_String ; Пропускаем пробелы перед числом mov CX,64 ;защита от зацикливания @@ShiftIgnore: lodsb cmp AL,' ' jne @@ShiftIgnoreEnd loop @@ShiftIgnore jmp @@Error @@ShiftIgnoreEnd: ; Проверяем знак числа cmp AL,'-' jne @@Positive mov [Data_Sign],80h lodsb @@Positive: mov [Data_Flag],0 ;признак наличия точки mov DX,0 ;позиция точки mov CX,18 ;макс. число разрядов @@ASCIItoBCDConversion: cmp AL,'.' ;точка? jne @@NotDot cmp [Data_Flag],0 ;точка не встречалась? jne @@Error mov [Data_Flag],1 lodsb cmp AL,0 ;конец строки? jne @@NotDot inc CX jmp @@ASCIItoBCDConversionEnd @@NotDot: ; Увеличить на 1 значение позиции точки, ; если она еще не встречалась cmp [Data_Flag],0 jnz @@Figures inc DX @@Figures: ; Символы числа должны быть цифрами cmp AL,'0' jb @@Error cmp AL,'9' ja @@Error ; Пишем очередную цифру в младшую тетраду BCD and AL,0Fh or [byte ptr Data_BCD],AL ; Проверка на конец строки cmp [byte ptr SI],0 je @@ASCIItoBCDConversionEnd ; Сдвигаем BCD на 4 разряда влево ; (сдвигаем старшие 2 байта) mov AX,[word ptr Data_BCD+6] shld [word ptr Data_BCD+8],AX,4 ; (сдвигаем средние 4 байта) mov EAX,[dword ptr Data_BCD] shld [dword ptr Data_BCD+4],EAX,4 ; (сдвигаем младшие 4 байта) shl [dword ptr Data_BCD],4 ; Загружаем следующий символ в AL lodsb loop @@ASCIItoBCDConversion ; Если 19-й символ не 0 и не точка, ; то ошибка переполнения cmp AL,'.' jne @@NotDot2 inc CX lodsb @@NotDot2: cmp AL,0 jne @@Error ;переполнение разрядной сетки ; ПРЕОБРАЗОВАТЬ ЧИСЛО ИЗ КОДА BCD В ВЕЩЕСТВЕННОЕ ЧИСЛО @@ASCIItoBCDConversionEnd: ; Вписать знак в старший байт mov AL,[Data_Sign] mov [byte ptr Data_BCD+9],AL ; Сбросить регистры сопроцессора fninit ; Загрузить в сопроцессор число в BCD-формате fbld [Data_BCD] ; Вычислить номер делителя mov BX,18+1 sub BX,CX sub BX,DX cmp BX,0 je @@NoDiv dec BX shl BX,3 ;умножаем на 8 add BX,offset MConst fdiv [qword ptr BX] ;разделить на константу @@NoDiv:; Выгрузить число в двоичном формате fstp [Data_Double] jmp short @@End @@Error:; При любой ошибке обнулить результат fldz ;занести ноль с стек сопроцессора fstp [Data_Double] @@End: pop DS popad ret ENDP String_to_DoubleFloat ;**************************************************** ;* ПРЕОБРАЗОВАТЬ ЧИСЛО ИЗ КОДА BCD В КОД ASCII * ;* (вспомогательная функция, регистры не сохраняет) * ;* Входные параметры: * ;* Data_BCD - число в BCD-формате. * ;* Регистр DI - указатель на строку результата. * ;**************************************************** PROC BCD_to_ASCII near ; Выделить знак числа и записать его в ASCII-коде mov AL,[offset Data_BCD + 9] and AL,AL jz @@n0 mov AL,'-' stosb ; Пропустить незначащие (нулевые) разряды слева @@n0: mov BX,8 mov CX,9 @@n1: ;проверяем на 0 очередную пару разрядов cmp [byte ptr BX+offset Data_BCD],0 jne @@n2 dec BX loop @@n1 ; Если значение числа равно нулю, записать символ ; нуля в строку результата и выйти из программы mov AL,'0' stosb jmp short @@End ; Пропустить незначащий ноль в старшей ; тетраде (если он есть) @@n2: ; Загрузить первую значащую пару разрядов mov AL,[BX + offset Data_BCD] mov AH,AL ; Выделить, перевести в ASCII и ; сохранить старшую тетраду shr AL,4 cmp AL,0 ; Если 0 - пропустить старшую тетраду je @@n3 add AL,'0' stosb ; Выделить, перевести в ASCII и ; сохранить младшую тетраду @@n3: mov AL,AH and AL,0Fh add AL,'0' stosb dec BX dec CX jz @@End ;выход, если это последний разряд ; Распаковать остальные разряды числа (если они есть) @@n4: ; Загрузить очередную пару разрядов mov AL,[BX + offset Data_BCD] mov AH,AL ; Выделить, перевести в ASCII и ; сохранить старшую тетраду shr AL,4 add AL,'0' stosb ; Выделить, перевести в ASCII и ; сохранить младшую тетраду mov AL,AH and AL,0Fh add AL,'0' stosb dec BX loop @@n4 @@End: ret ENDP BCD_to_ASCII ;************************************************** ;* ОТОБРАЗИТЬ ЧИСЛО В КОДЕ ASCII НА ЭКРАН * ;* Подпрограмма отображает Data_String на экран. * ;* Координаты позиции передаются через глобальные * ;* переменные ScreenString и ScreenColumn. После * ;* выполнеия операции происходит автоматическое * ;* приращение значений этих переменных. * ;************************************************** PROC ShowDataString near pusha push ES mov AX,DGROUP mov ES,AX cld ; Занести координаты сторки mov DI,offset OutD_String mov AX,[ScreenString] stosb mov AX,[ScreenColumn] stosb ; Копировать Data_String в OutD_String mov SI,offset Data_String mov CX,31 rep movsb ; Поставить символ-ограничитель mov AL,0 stosb mov SI,offset OutD_String call ShowString pop ES popa ret ENDP ShowDataString ;*************************************************** ;* ВВЕСТИ ЦЕЛОЕ ЧИСЛО С КЛАВИАТУРЫ * ;* Подпрограмма обеспечивает ввод 9-разрядного * ;* десятичного целого числа со знаком. * ;* Координаты поля ввода задаются через переменные * ;* ScreenString и ScreenColumn. Цвет определяется * ;* переменной TextColorAndBackground. * ;*************************************************** PROC GetInteger near pushad ; Инициализировать переменные @@ClearField: ; Обнулить счетчик цифр mov BX,0 ; Установить координаты поля ввода mov DI,offset OutD_String mov AX,[ScreenString] mov [DI],AL inc DI mov AX,[ScreenColumn] mov [DI],AL inc DI ; Очистить строку (заполнить пробелами) mov [dword ptr DI],20202020h mov [dword ptr DI+4],20202020h mov [word ptr DI+8],2020h mov [byte ptr DI+10],0 ;конец строки ; Цикл ввода @@GetNextChar: ; Отобразить число на экране mov SI,offset OutD_String call ShowString ; Установить курсор в позицию ввода push [ScreenColumn] add [ScreenColumn],BX call SetCursorPosition pop [ScreenColumn] ; Принять байт с клавиатуры call GetChar ; Цифра или команда? cmp AL,0 jz @@Command ;если ноль - введена команда ; Проверка на специальные символы cmp AL,'-' je @@M ; Проверка на диапазон '0'-'9' cmp AL,'0' jb @@Error cmp AL,'9' ja @@Error ; Проверяем количество символов (не более 10) @@M: cmp BX,10 jae @@Error inc BX ; Записываем символ в число mov [DS:DI],AL inc DI jmp short @@GetNextChar ; Проанализировать код команды @@Command: cmp AH,B_Esc ;очистить поле je @@ClearField @@TestRubout: cmp AH,B_RUBOUT ;"Забой" jne @@TestEnter cmp BX,0 je @@Error ; Передвинуть признак конца строки ; на разряд влево dec DI dec BX mov [byte ptr DS:DI],' ' jmp short @@GetNextChar @@TestEnter: cmp AH,B_Enter ;завершение ввода числа jne @@Error ; Перевести число в двоичный код mov SI,offset Data_String mov DI,offset OutD_String add DI,2 mov CX,10 @@i1: mov AL,[DI] cmp AL,' ' ;конец числа? jbe @@EndOfString mov [SI],AL inc SI inc DI loop @@i1 @@EndOfString: mov [byte ptr SI],0 ;конец строки call String_to_Int32 jmp short @@End ; Ошибка при вводе @@Error: call Beep jmp @@GetNextChar @@End: popad ret ENDP GetInteger ;*************************************************** ;* ВВЕСТИ ВЕЩЕСТВЕННОЕ ЧИСЛО С КЛАВИАТУРЫ * ;* Подпрограмма обеспечивает ввод вещественного * ;* числа в обычном формате (до 18 цифр). * ;* Координаты поля ввода задаются через переменные * ;* ScreenString и ScreenColumn. Цвет определяется * ;* переменной TextColorAndBackground. * ;*************************************************** PROC GetFloat near pushad ; Инициализировать переменные @@ClearField: ; Обнулить счетчик цифр mov BX,0 ; Установить координаты поля ввода mov DI,offset OutD_String mov AX,[ScreenString] mov [DI],AL inc DI mov AX,[ScreenColumn] mov [DI],AL inc DI ; Очистить строку (заполнить пробелами) mov [dword ptr DI],20202020h mov [dword ptr DI+4],20202020h mov [dword ptr DI+8],20202020h mov [dword ptr DI+12],20202020h mov [dword ptr DI+16],20202020h mov [byte ptr DI+20],0 ;конец строки ; Цикл ввода @@GetNextChar: ; Отобразить число на экране mov SI,offset OutD_String call ShowString ; Установить курсор в позицию ввода push [ScreenColumn] add [ScreenColumn],BX call SetCursorPosition pop [ScreenColumn] ; Принять байт с клавиатуры call GetChar ; Цифра или команда? cmp AL,0 jz @@Command ;если ноль - введена команда ; Проверка на специальные символы cmp AL,'-' je @@M cmp AL,'.' je @@M ; Проверка на диапазон '0'-'9' cmp AL,'0' jb @@Error cmp AL,'9' ja @@Error ; Проверяем количество символов (не более 20) @@M: cmp BX,20 jae @@Error inc BX ; Записываем символ в число mov [DS:DI],AL inc DI jmp short @@GetNextChar ; Проанализировать код команды @@Command: cmp AH,B_Esc ;очистить поле je @@ClearField @@TestRubout: cmp AH,B_RUBOUT ;"Забой" jne @@TestEnter cmp BX,0 je @@Error ; Передвинуть признак конца строки ; на разряд влево dec DI dec BX mov [byte ptr DS:DI],' ' jmp short @@GetNextChar @@TestEnter: cmp AH,B_Enter ;завершение ввода числа jne @@Error ; Перевести число в двоичный код mov SI,offset Data_String mov DI,offset OutD_String add DI,2 mov CX,20 @@i1: mov AL,[DI] cmp AL,' ' ;конец числа? jbe @@EndOfString mov [SI],AL inc SI inc DI loop @@i1 @@EndOfString: mov [byte ptr SI],0 ;конец строки call String_to_DoubleFloat jmp short @@End ; Ошибка при вводе @@Error: call Beep jmp @@GetNextChar @@End: popad ret ENDP GetFloat ENDS

Соседние файлы в папке Учебник