;       !Coproc!.asm - дополнительный файл к Главе № 25
;
;          !!! Для просмотра в TurboDebugger'е !!!

; (С) Авторские права на файлы-приложения принадлежат автору книги
; "Ассемблер? Это просто! Учимся программировать под MS-DOS"
; Автор: Калашников Олег Александрович (e-mail: Assembler@Kalashnikoff.ru)
;	 http://www.Kalashnikoff.ru

; --- Ассемблирование (получение *.com файла) ---
;При использовании MASM 6.11 - 6.13:
;ML.EXE !coproc!.asm /AT

;При использовании TASM:
;TASM.EXE !coproc!.asm
;TLINK.EXE !coproc!.obj /t/x

.386	;Будем использовать команды 80386 процессора,
.287	;А также 287 сопроцессора
CSEG segment use16
assume cs:CSEG, ds:CSEG, es:CSEG, ss:CSEG
org 100h

Start:
    mov ax,3
    int 10h      ;Чистим экран

    mov ah,2
    mov dx,40
    int 10h      ;Установим курсор на середину 0-ой строки

    call Out_dec ;Процедура вывода десятичного числа, которое в Number_dec

    ret		 ;Выход в DOS



;=== Вывод десятичного числа на экран с использованием сопроцессора ===
Out_dec proc
    finit        ;Инициализация сопроцессора

;На приведенные ниже строки не обращайте пока внимания!
;-------
    fstcw Dat    ;Получим регистр управления сопроцессором и установим RC
    or Dat,0C00h ;Округление: просто урезаем числа после запятой
    fldcw Dat    ;Загрузим изменения назад в сопроцессор
;-------

;!!! Теперь следим очень внимательно !!!

    fldz             ;Загрузим нуль (LoaD Zero)
    fild Divider     ;Загрузим делитель (10)
    fild Number_dec  ;Загрузим делимое (выводимое число)
    fst st(7)        ;Дублируем делимое ST(7)=ST(0) (STore)

Next_sym:
    fprem            ;Делим ST(0) на ST(1) и получаем остаток от деления
    fistp Dat        ;Остаток - в переменную Dat

    call Out_sym     ;Выводим остаток от деления на экран

    fld st(6)        ;ST(0)=ST(6)

    fdiv st,st(1)    ;Делим на 10   ST(0)=ST(0)/ST(1)

    frndint          ;Округляем до целого ST(0) (RouND INTeger)
    fst st(7)        ;Дублируем округленное число

    fcom st(2)       ;ST(0)=ST(2)? (COMpare - сравнение)
    fstsw ax         ;Получим состояние сопроцессора
    and ax,4500h     ;=100010100000000b - Обнулим ненужные нам биты
    cmp ax,4000h     ;=100000000000000b - Проверим...
    jnz Next_sym     ;Не нуль? Тогда обрабатываем следующую цифру... 

Finish_dec:
    ret              ;Число полностью выведено!!! Выходим из процедуры...

 ;64-х разрядная переменная для выводимого числа
Number_dec dq 1234   ;<=== Выводим это число!

 ;В данную переменную можно занести два регистра EAX или
 ;0FFFF:FFFF:FFFF:FFFFh или
 ;16^16 (16 в 16 степени) или
 ;18.446.744.073.709.551.600
 ;Однако, учитывая, что крайний левый бит сигнализирует сопроцессору о знаке
 ;числа (положительное или отрицательное), то вывести приведенное выше число
 ;не удастся. Реально же почти в два раза меньше. Но этого достаточно...


 Divider dw 10    ;Делитель всегда 10
 Dat dw ?         ;Временная переменная
Out_dec endp

;=== Вывод одного символа на экран в текущую позицию курсора ===
Out_sym proc
    push dx  ;DX содержит текущую позицию курсора, которую нужно сохранить
    mov ah,2
    mov dl,byte ptr Dat ;DL=остаток от деления на 10
    add dl,'0'          ;Добавим '0'
    int 21h  		;Выводим остаток от деления
    pop dx

    dec dx   ;Курсор назад , т.к. вывод идет в обратном порядке
    mov ah,2
    int 10h

    ret
Out_sym endp

CSEG ends
end Start