1
votes

How to draw a pixel in VESA graphic mode?

I am trying interrupt 10h function 0ch, but it's not working. What is wrong?

(Note: I wrote this code in NASM syntax and tested it with qemu)

; Code:
Mov ax, 4F02h
Mov bx, 0105h ; 1024x768 pixels
Int 10h

Mov ah, 0ch ; Draw pixel function
Mov cx, 2           ; column
Mov dx, 3           ; row
Mov bh, 0    
Mov bl, 00000101b   ; colour
Int 10h

Jmp $            ; jump forever
Times 510-($-$$) db 0
Dw 0xaa55
1
Please answer this question in NASM syntax or NASM like (TASM, MASM, etc.), not GAS Syntax. Because, I am could not use GNU Assembler syntax.Juan
@Juan: the terms you're looking for are Intel vs. AT&T syntax. Anyway, you draw pixels by storing to video memory with mov byte [bx], 123 (or word or whatever, depending on the pixel format you set.) Set ds or es to the video RAM base address.Peter Cordes
2 downvotes? The user provided 1. What they were trying to accomplish, 2. What they tried, 3. A description of what the code SHOULD do, 4. That the code can't draw a pixel. Looking at the accepted answer, it seems what was attempted wasn't FAR off, either..? It doesn't make sense to say: "The problem is I forgot to put the color in the low byte of ax." No need to upvote, but the question and answer were an interesting problem and solution in my opinion.user176692
@user176692: removed my downvote. When I first read this, I missed that there was an attempt to draw a pixel as well as setting a video mode. (I didn't know there was an int 10h function for that. Looks like it takes more code to call it than to store to video memory, though.) Still not a minimal reproducible example, and solved just by reading the docs. (Except then you might not learn that storing to video RAM directly is much faster than int 10h.)Peter Cordes

1 Answers

9
votes

The function Int 10h/AH=0Ch should work even when using a VESA VBE mode.

Make sure to use it correctly, the pixel's colour goes into al.

;Set video mode
mov ax, 4f02h
mov bx, 105h
int 10h

;Draw pixel
mov ax, 0c09h    ;09h = Blue
mov cx, 2 
mov dx, 3     
xor bx, bx   
int 10h

Technically, you should use Int 10h/AX=4F01h to retrieve the video mode information, including bit 2 (2 BIOS output supported) of the mode attributes field, to check if the BIOS functions will work.

Writing a high-resolution image using the BIOS function could be too slow, it may be worth investing in writing directly to a linear or windowed frame buffer.
Writing to a linear framebuffer will probably require using the unreal mode, it's possible to avoid that by using a windowed framebuffer.
This, besides being slower, is also more cumbersome.

Here is a very simple program that fills 30 rows with a horrible shade of grey.
Note that I stripped all the checks for the sake of compactness and clarity, I made a lot of assumptions to avoid making some math.
This is a bad practice, I used it only for prototyping.
I strongly encourage you to read the full information returned by Int 10h/AX=4F01h and use that information to select the correct window, do the right padding and calculations.

BITS 16

ORG 100h

mov ax, ds
mov es, ax 

;Set video mode
mov ax, 4f02h
mov bx, 105h
int 10h

;Get video mode info
mov ax, 4f01h
mov cx, 105h
mov di, modeInfo 
int 10h

;Assume first window is valid 
mov ax, WORD [es:modeInfo + 08h]
mov es, ax

;Example of how to change the window 
mov ax, 4f05h
xor bx, bx
mov dx, 5       ;This is granularity units
int 10h

xor di, di 
mov al, 0f1h
mov cx, 3*1024*20

rep stosb

;Wait for key
xor ax, ax
int 16h

;Restore DOS text mode
mov ax, 0003h
int 10h

;Exit
mov ax, 4c00h
int 21h

modeInfo    TIMES 256 db 0

This is in NASM syntax, I usually use TASM (out of affection) for DOS programs but this time I was in a hurry.
The result is

A grey band in the, more or less, middle of a black screen

Remember that each scanline can, in general, be padded (the size of a scanline is returned in the video mode information).
For a 1024 pixel wide scanline, with 3x8bpp we have 3072 bytes/scanline, since this is divisible by 4, no padding is likely to happen.
The window start address is given in the granularity unit (also found in the video mode information), the total framebuffer is 1024x768x3 bytes = 2.25 MiB, assuming no padding.
The window size is also found in the video mode information.

All this is enough to write to the framebuffer.
A linear framebuffer is easier to handle (padding is still an aspect to consider) once the unreal mode has been set up.