
;        ===== DISPLAY.ASM - процедуры работы с экраном ======

; === Рисуем рамку заданного размера в центре экрана ===
Draw_frame proc
     push es ;Сохраним ES

     push 0B800h ;ES должен указывать на сегмент 0-ой видеостраницы
     pop es      ;это сработает только на 286+ процессоре!!!

;________________________________________
;
; Производим вычисления для того, чтобы
; разместить ЛЮБУЮ рамку в цетре экрана!
;
; Нужно следить НУ ОЧЕНЬ внимательно.
; Без калькулятора не обойтись...
;________________________________________

     mov ax,Height_X ;AX = высота нашей рамки
     shr al,1        ;делим высоту на 2 путем смещения битов вправо на 1
     mov dh,11       ;Середина
     sub dh,al       ;DH (строка) ГОТОВА!!!!!

     mov ax,Width_Y  ;AX = ширина нашей рамки
     shr al,1        ;делим ее на 2
     mov dl,39       ;Середина
     sub dl,al       ;DL (колонка) ГОТОВА!!!!!
;Теперь DH содержит центрированный ряд (строку),
;а DL - колонку относительно размеров рамки (окошка)...
;_________________________________________

     call Get_linear	;Получаем линейный адрес в видеобуфере из DX,
     push di		;который и сохраним...

     mov ah,Num_attr	;В AH - атрибуты цвета
     mov al,'╔'		;Верхний левый угол...
     stosw		;Заносим два байта (атрибут в AH / символ в AL)

     mov al,'═'		;Далее...
     mov cx,Width_Y	;В CX - ширина рамки
     rep stosw		;Поехали...

     mov al,'╗'		;Завершаем верхний ряд
     stosw

     pop di		;Восстановим DI + 160 (следующий ряд)
     add di,160

;Теперь у нас примерно такая ситуация на экране:
;╔═══════════════════════════╗
;_ - где _, там у нас DI


     mov cx,Height_X	;CX - кол-во повторов (высота)

Next_lined:
     push cx		;Сохраним счетчик
     push di		;Сохраним DI

     mov al,'║'		;Вывели этот символ (он один)
     stosw

     mov al,32		;32 - пробел (или 20h или ' ')
     mov cx,Width_Y	;CX = ширина
     rep stosw		;Поехали!

     mov al,'║'		;Завершаем ряд...
     stosw

     pop di
     add di,160		;Переводим DI на следующий ряд
     pop cx		;Восстановим счетчик
     loop Next_lined	;Следующий ряд...

;Теперь у нас примерно такая ситуация на экране:
;╔═══════════════════════════╗
;║                           ║
;║                           ║
;║                           ║
;_ - тут DI


     mov al,'╚'		;Нижняя часть рамки...
     stosw

     mov al,'═'
     mov cx,Width_Y
     rep stosw

     mov al,'╝'
     stosw

     pop es		;Восстановим ES

;Теперь у нас примерно такая ситуация на экране:
;╔═══════════════════════════╗
;║                           ║
;║                           ║
;║                           ║
;╚═══════════════════════════╝

     ret


;Вот мы и написали одну очень мощную процедуру, которой можно пользоваться
;в любое время и вставлять в любую программу. Хотя, мы еще будем много чего
;добавлять в нее: усовершенствовать, автоматизировать, ускорять и пр.
;Как я уже говорил: стоит один раз написать, а потом...
Draw_frame endp


; === Преобразование DH:DL в линейный массив ===
Get_linear proc
    push ax	;Сохраним все используемые регистры
    push bx
    push dx

    shl dl,1	;Математика: умножаем DL на 2 (DL=DL*2)...

    mov al,dh	;В AL - ряд,
    mov bl,160	;который нужно умножить на 160
    mul bl	;Умножаем: AL(ряд)*160; результат --- в AX

;Почему на 160?
;Дело в том, что видеобуфер устроен как линейный (прямой) массив.
;Один символ занимает 2 байта: атрибут/код символ.
;Т.к. в режиме 3 в одном ряду 80 символов, то нам нужно умножить на 160!

    mov di,ax	;Результат умножения в DI
    xor dh,dh	;Аннулируем DH
    add di,dx	;Теперь в DI линейный адрес в видеобуфере...

    pop dx	;Восстанавливаем регистры...
    pop bx
    pop ax
    ret
;С моей точки зрения - это довольно-таки оптимальный алгоритм...
Get_linear endp


; === Проверяем видеорежим монитора и текущую видеостраницу ===
Check_video proc
     mov ah,0Fh
     int 10h
     cmp al,3	;Текстовый режим?
     je Ok_video

     mov ax,3	;Если нет - делаем текстовый
     int 10h

Ok_video:
     or bh,bh	;Нулевая страница?
     jz Ok_page

     mov ax,0500h ;Если нет - делаем нулевую текущей
     int 10h

Ok_page:
     ret
Check_video endp

; === Сохраним экран ===
Save_mainscr proc
    pusha	;Сохраним ВСЕ регистры (кроме сегментных) в стеке
    push es	;Сегментные придется сохранить самому...
    push ds

    push 0B800h	;С нулевой страницы
    pop ds
    xor si,si	;Первого символа

    push 0B900h	;В первую страницу...
    pop es
    xor di,di	;Первый символ...

    mov cx,2000	;4000 байт
    rep movsw	;Делаем копию...

    pop ds
    pop es
    popa	;Восстановим регистры (POP All - вытолкнуть все)
    ret
Save_mainscr endp

; === Восстановим экран ===
Restore_mainscr proc
    pusha
    push es
    push ds

    push 0B900h	;С первой видеостраницы
    pop ds
    xor si,si

    push 0B800h	;В нулевую...
    pop es
    xor di,di

    mov cx,2000
    rep movsw

    pop ds
    pop es
    popa
    ret
Restore_mainscr endp


; === Прячем курсор, сохранив предварительно его текущую позицию ===
Hide_cursor proc
    mov ah,3		;Получаем текущую позицию курсора
    mov bh,Video_page
    int 10h
    mov Pos_cursor,dx	;Теперь она в DX (отсчет с нуля!)

    mov ah,2
    mov bh,Video_page	;Прячем курсор (установим его на 25 строку (19h))
    mov dx,1900h
    int 10h
    ret
Hide_cursor endp

; === Восстановим курсор ===
Restore_cursor proc
    mov ah,2
    mov bh,Video_page	;Видеостраница
    mov dx,Pos_cursor	;Сохраненная позиция курсора
    int 10h		;Установим (позиционируем) курсор
    ret
Restore_cursor endp