Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 518
vakus Avatar asked Nov 12 '15 21:11

vakus


1 Answers

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.

like image 66
Michael Petch Avatar answered Oct 14 '22 15:10

Michael Petch