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
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.
mov byte [bx], 123
(or word or whatever, depending on the pixel format you set.) Setds
ores
to the video RAM base address. – Peter Cordesint 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 thanint 10h
.) – Peter Cordes