问题
I created simple code to load second sector from hard drive, and then write to whole screen, with spaces with red background. The problem is that always instead of spaces I got @ signs. This is the code:
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
according to this code screen should be filled with spaces but it is not.
回答1:
When writing to video memory (starting @ 0xb8000) there are 2 bytes for every cell on the screen. The character to display is in the first byte, and the attribute in the second. To print out a red (color code 0x40) space (0x20) character to the first cell on the screen the bytes need to be placed in memory like this:
0xb800:0x0000 : 0x20 ; ASCII char for 0x20 is ' '
0xb800:0x0001 : 0x40 ; Red background, black foreground
In your code it seems you were trying to do this with code like:
mov al,0x40 ;colour
mov ah,' ' ;character
.red:
cmp bx,0x0FA0
je .end
mov WORD [es:bx], ax
inc bx
jmp .red
Unfortunately because x86 architecture is little-endian, the values that get placed into memory have the least significant byte first and most significant byte last (when dealing with a 16-bit WORD). You have AX containing 0x2040 and moved the entire WORD with mov WORD [es:bx], ax
to video memory. For example it would have written these bytes to the first cell:
0xb800:0x0000 : 0x40 ; ASCII char for 0x40 is `@'
0xb800:0x0001 : 0x20 ; Green background, black foreground
I believe this is a green @
but because of the second bug I will mention it may have appeared red. To fix this you need to reverse the position of the character and attribute in the AX register (swap the values in AH and AL). The code would look like this:
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
The second bug is related to traversing the video area. Because each cell takes 2 bytes you need to increment the BX counter by 2 on each iteration. Your code does:
mov WORD [es:bx], ax
inc bx ; Only increments 1 byte where it should be 2
jmp .red
Modify the code to add 2 to BX:
mov WORD [es:bx], ax
add bx,2 ; Increment 2 since each cell is char/attribute pair
jmp .red
You could have simplified the code by using the STOSW instruction that takes the value in AX and copies it to ES:[DI]. You can prefix this instruction with REP which will repeat it CX times (it will update DI accordingly during each iteration). The code could have looked like this:
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]
Your code already clears the direction flag with CLD at the beginning of your code, so REP will increase DI during each iteration. Had the direction flag been set with STD, DI would have been decremented.
来源:https://stackoverflow.com/questions/33681795/how-to-write-to-screen-with-video-memory-address-0xb8000-from-real-mode