I'm trying to create some basic drawing routines in assembly (NASM). Looking at the x86 BIOS interrupt table, I've found the 10h interrupt that offers some video manipulation services.
Using it, I've managed to come up with a routine to draw a square, configuring the interrupt call to Write a Graphics Pixel (AH = 0Ch).
The square is being drawn correctly but takes too long, I can see it being filled. BTW, I'm running my code on QEMU.
I've assumed the screen was being refreshed faster than the instructions were executed. After some research, I didn't find any helpful content. The main possible solutions were to adjust vertical sync and write directly to the video memory.
Considering that I'm using video mode 12h (640x480 - 16 colors), my questions are:
1 - Writing directly to the video memory is faster than calling an interruption?
2 - How the bytes are organized in the video memory space? Each pixel occupies a byte (starting at address 0xa000)?
3 - How can I write to the video memory? Simply write each pixel color sequentially?
4 - In general, when a display is refreshing the screen, it just reads directly from the video memory from time to time?
1) Writing directly to the video memory is much faster.
2) For graphic video modes with only 16 colors(4 bit) we have to use a port access (to 3CEh Pixel-Mask-Register) and additional a dummy read at the target location for to set one pixel to the framebuffer. It is not so simple to explain the way. But i can give you an example of my pixel set routine for my ET4000 graphic card using videomodes with 16 colors:
PIXEL: pusha
add bp, XMIN
add bx, YMIN
mov cx, bp ; Screen-Offset
shr cx, 3
mov ax, PS ; Pixel of a line
mul bx ; * Y
add ax, cx ; + (X/8)
mov di, ax ; = address
cmp BYTE PTR[FLAG_2], 5 ; SVGA ?
jb VGA
mov al, dl ; Bank switch ET4000
shl al, 4 ; for SVGA: 1024x768 and 1280x1024
add al, dl ; overflow 64K border
mov dx, 3CDh ; Port address for bank switching ET4000
out dx, al
VGA: inc cx ; calculate Pixel
shl cx, 3
sub cx, bp
dec cx
mov ah, 1 ; 2 ^ (((X/8) + 1) * 8 - X) - 1
shl ah, cl ; = Pixel pos.
mov dx, 3CEh ; Pixel-Mask-Register
mov al, 8
out dx, ax
mov al, fs:[di] ; Dummy-READ (get the address)
EBENE: mov BYTE PTR fs:[di], 1 ; set Pixel
popa
ret
But video modes with 8,15/16,24/32 bit color are more easy to use without a port access. Example with 8 bit colors it is simple to calculate the address of a pixel. Pixel address= (Y_coordinate * horizontal resolution) + X_coordinate
With 8 bit color each address represent one pixel on the screen. But additional we have a color palette(lookup table) for to determine the red, green and blue parts for each color number. Only for video modes with 15/16 and 24/32 colors the color is fully encoded in the pixel-address with two bytes, or three bytes for the color.
3) Yes, for video modes with 8,15/16,24/32 bit color it is easy to fill the screen with colors.
4) We do not need to tell the graphic card for refreshing the screen content. But we can check the status register of port 3DAh if the cathode ray is at the end on the screen, for to minimize a flickering effect and a tearing of the screen content.
In the first time i also try to use videomode with 4 bit color, but it is not so simple to use like other graphic modes with more colors. Today with my Radeon 7950 card i prefer to use the native resolution of my 28" widescreen monitor in 19202x1200 with 32 bit color using the linear framebuffer located somewhere in the fourth gigabyte. For to switch into this resolution i use the VBE-Bios and the modetable of modenumbers that comes within the VBE-bios. The documentation for that can be found in the vbe3.pdf from vesa.org(costfree but need register/login) in the public section of there page.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With