[Назад] [Далее]

4.3.3. Прямая работа с видеопамятью

Все, что изображено на мониторе — и графика, и текст, одновременно присутствует в памяти, встроенной в видеоадаптер. Для того чтобы изображение появилось на мониторе, оно должно быть записано в память видеоадаптера. Для этого отводится специальная область памяти, начинающаяся с абсолютного адреса B800h:0000h (для текстовых режимов) и заканчивающаяся на B800h:FFFFh. Все, что программы пишут в эту область памяти, немедленно пересылается в память видеоадаптера. В текстовых режимах для хранения каждого изображенного символа используются два байта: байт с ASCII-кодом символа и байт с его атрибутом, так что по адресу B800h:0000h лежит байт с кодом символа, находящимся в верхнем левом углу экрана; по адресу B800h:0001h лежит атрибут этого символа; по адресу B800h:0002h лежит код второго символа в верхней строке экрана и т.д.

Таким образом, любая программа может вывести текст на экран простой командой пересылки данных, не прибегая ни к каким специальным функциям DOS или BIOS.

; dirout.asm
; Выводит на экран все ASCII-символы без исключения,
; используя прямой вывод на экран
        .model  tiny
        .code
        .386                  ; будет использоваться регистр ЕАХ
                              ; и команда STOSD
        org     100h          ; начало СОМ-файла
start:
        mov     ax,0003h
        int     10h           ; видеорежим 3 (очистка экрана)
        cld                   ; обработка строк в прямом направлении
                              ; подготовка данных для вывода на экран
        mov     еах,1F201F00h ; первый символ 00 с атрибутом 1Fh,
                              ; затем пробел (20h) с атрибутом 1Fh
        mov     bx,0F20h      ; пробел с атрибутом 0Fh
        mov     cx,255        ; число символов минус 1
        mov     di,offset ctable ; ES:DI - начало таблицы
cloop:
        stosd                 ; записать символ и пробел в таблицу ctable
        inc     al            ; AL содержит следующий символ
        test    cx,0Fh        ; если СХ не кратен 16,
        jnz     continue_loop ; продолжить цикл,
        push    cx            ; иначе: сохранить значение счетчика
        mov     cx,80-32      ; число оставшихся до конца строки символов
        xchg    ax,bx
        rep     stosw         ; заполнить остаток строки пробелами
                              ; с атрибутом 0F
        xchg    bx.ax         ; восстановить значение ЕАХ
        pop     cx            ; восстановить значение счетчика
continue_loop:
        loop    cloop

        stosd                 ; записать последний (256-й) символ и пробел

; собственно вывод на экран
        mov     ax,0B800h     ; сегментный адрес видеопамяти
        mov     es,ax
        xor     di,di         ; DI = 0, адрес начала видеопамяти в ES:DI
        mov     si,offset ctable ; адрес таблицы в DS:SI
        mov     cx,15*80+32   ; 15 строк по 80 символов, последняя строка - 32
        rep     movsw         ; скопировать таблицу ctable в видеопамять
        ret                   ; завершение СОМ-файла
ctable:                       ; Данные для вывода на экран начинаются сразу
                              ; за концом файла. В ЕХЕ-файле такие данные
                              ; определяют в сегменте .data?
        end     start

При подготовке данных для копирования в видеопамять в этой программе использовался тот факт, что в архитектуре Intel при записи слова (или двойного слова) в память старший байт располагается по старшему адресу. Так что при записи в память двойного слова 1F201F00h сначала записывается самый младший байт 00h (ASCII-код текущего символа), потом 1Fh, используемый в этом примере атрибут, потом 20h (код пробела) и потом, по самому старшему адресу, — самый старший байт, 1Fh, атрибут для этого пробела. Кроме того, в этом примере использовались некоторые 32-битные команды (MOV и STOSD). Этими командами можно пользоваться из 16-битной программы (разумеется, если процессор 80386 и выше), но не стоит этим злоупотреблять, так как каждая такая команда оказывается длиннее на 1 байт и выполняется дольше на 1 такт.


п»ї
"target=_blank><\/a>") //-->