Как написать на экран с адресом видеопамяти 0xb8000 из реального режима?

Я создал простой код для загрузки второго сектора с жесткого диска, а потом писать на весь экран, с пробелами, с красным фоном. Проблема в том, что всегда вместо пробелов я @ знаки. Это код:

org 0x7C00
bits 16

xor ax,ax
mov ds,ax
mov es,ax

mov bx,0x8000
cli
mov ss,bx
mov sp,ax
sti

cld
clc

xor ah,ah
int 0x13
mov bx,0x07E0
mov es,bx
xor bx,bx
mov ah,0x2 ;function
mov al,0x5 ;sectors to read
mov ch,0x0 ;track
mov cl,0x2 ;sector
mov dh,0x0 ;head
int 0x13
;jc error
;mov ah, [0x7E00]
;cmp ah,0x0
;je error
jmp error
cli
hlt
jmp 0x07E0:0x0000

error:
    xor bx,bx
    mov ax,0xb800
    mov es,ax
    mov al,0x40 ;colour
    mov ah,' ' ;character
    .red:
        cmp bx,0x0FA0
        je .end
        mov WORD [es:bx], ax
        inc bx
        jmp .red
    .end:
        cli
        hlt

times 0x1FE - ($ - $$) db 0x0
db 0x55
db 0xAA

в соответствии с этим кодом экран должен быть заполнен пробелами, но это не так.

1 ответов


при записи в видеопамять (начиная @ 0xb8000) есть 2 байта для каждой ячейки на экране. Символ для отображения находится в первом байте, а атрибут-во втором. Чтобы распечатать красный (цветовой код 0x40) пробел (0x20) символ в первую ячейку на экране, байты должны быть помещены в память следующим образом:

0xb800:0x0000 :  0x20         ; ASCII char for 0x20 is ' '
0xb800:0x0001 :  0x40         ; Red background, black foreground

в ваш код, похоже, вы пытались сделать это с помощью следующего кода:

mov al,0x40 ;colour
mov ah,' ' ;character
.red:
    cmp bx,0x0FA0
    je .end
    mov WORD [es:bx], ax
    inc bx
    jmp .red

к сожалению, потому что x86 архитектура мало-конечная, значения, которые помещаются в память, имеют наименее значимый байт первым и наиболее значимым байтом последним (при работе с 16-битным слово). У вас есть AX содержащих 0x2040 и двигалась все слово С mov WORD [es:bx], ax в видеопамять. Например, он записал бы эти байты в первую ячейку:

0xb800:0x0000 :  0x40         ; ASCII char for 0x40 is `@'
0xb800:0x0001 :  0x20         ; Green background, black foreground

Я считаю, что это зеленый @ но из-за второй ошибки я буду упомяните, что он мог показаться красным. Чтобы исправить это, вам нужно изменить положение символа и атрибута в AX register (поменять значения в ах и АЛ). Код будет выглядеть так:

mov ah,0x40 ;colour is now in AH, not AL 
mov al,' '  ;character is now in AL, not AH
.red:
    cmp bx,0x0FA0
    je .end
    mov WORD [es:bx], ax
    inc bx
    jmp .red

вторая ошибка связана с прохождения видео. Поскольку каждая ячейка занимает 2 байта, вам нужно увеличить BX счетчик на 2 на каждой итерации. Ваш код:

mov WORD [es:bx], ax
inc bx                 ; Only increments 1 byte where it should be 2 
jmp .red

изменить код для добавления 2 к BX:

mov WORD [es:bx], ax
add bx,2               ; Increment 2 since each cell is char/attribute pair 
jmp .red

вы могли бы упростить код с помощью STOSW инструкция, которая принимает значение AX и копирует его в ES: [DI]. Вы можете префикс этой инструкции с REP который повторит это CX раз (он будет обновлять DI соответственно во время каждой итерации). Код может выглядеть это:

error:
    mov ax,0xb800 
    mov es,ax     ;Set video segment to 0xb800
    mov ax,0x4020 ;colour + space character(0x20)
    mov cx,2000   ;Number of cells to update 80*25=2000
    xor di,di     ;Video offset starts at 0 (upper left of screen)
    rep stosw     ;Store AX to CX # of words starting at ES:[DI]

ваш код уже очищает флаг направления с CLD в начале вашего кода, так REP увеличится DI во время каждой итерации. Если бы флаг направления был установлен с STD, DI было бы уменьшено.