How to write to screen with video memory address 0xb8000 from real mode?

后端 未结 1 1128
予麋鹿
予麋鹿 2021-02-14 12:45

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

相关标签:
1条回答
  • 2021-02-14 13:23

    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.

    0 讨论(0)
提交回复
热议问题