Home » Assembler » Graphics with Direct Memory Access

Graphics with Direct Memory Access

Drawing pictures on the screen using the bios interrupts is all very easy, but when push comes to shove, its also very, very slow as the bios routines are built to cope with every graphics mode. A faster way of plotting pixels is to directly place the bits in video memory, using, for example, a move instruction. This is very, very fast. but it does limit you to the resolution for which the routine was written.

The simplest video modes for demostrating this principle, is that of mode 19, (13h) which has 320 x 200 pixels and uses 256 colours. 256 is 2^8, meaning that the colour value takes up exactly one byte per pixel. This makes the actual setting of pixels very easy, move the colour value into the appropriate memory byte. The video memory for this mode begins at memory address A000h, and the pixels are then linearly in memory row by row. The memory location to write to for pixel (x,y) is A000h + (y * 320) + x.

Unfortunately, this involves a previously unencountered complication. With the programs we have written so far, the data and program have all been placed in the one segment. However, the graphics memory is not within that segment, so we need a segment offset. This offset is the start of video memory. To access an address we now use two parts, the segment (stored in the es register) and the offset (stored in the di register). The whole address is referenced es:[di].

Drawing horizontal and vertical lines in this mode is easy: to draw a horizontal line, simply fill all memory addresses from the starting point to the end point; to draw a vertical line, add 320 to the current pixel position and this gives the next point. Below is a sample program demostrating this.

jmp start
;==============================
;  Draws a horiz and vert line
;==============================
  startaddr	dw	0a000h	;start of video memory
  colour	db	1
;==============================
 start:
   mov ah,00
   mov al,19
   int 10h			;switch to 320x200 mode
 ;=============================
 horiz:
   mov es, startaddr		;put segment address in es
   mov di, 32000		;row 101 (320 * 100)
   add di, 75			;column 76
   mov al,colour		;cannot do mem-mem copy so use reg
   mov cx, 160			;loop counter
  hplot:
    mov es:[di],al		;set pixel to colour
    inc di			;move to next pixel
  loop hplot
 vert:
   mov di, 16000		;row 51 (320 * 50)
   add di, 160			;column 161
   mov cx, 100			;loop counter
  vplot:
    mov es:[di],al
    add di, 320			;mov down a pixel
  loop vplot
 ;=============================
 keypress:
   mov ah,00
   int 16h			;await keypress
 end:
   mov ah,00
   mov al,03
   int 10h
   mov ah,4ch
   mov al,00			;terminate program
   int 21h

That, basically is all there is to it. Note how for switching to and from graphics mode we still use the int calls. This is because the change only occurs generally once per program and so, unlike pixel plotting is not a bottle-neck.

The primary use of assembler for graphics is frequently to embed the code in a higher level language. Given below, then is an implementation of a few basic graphics primatives created in assembler but embedded withing pascal functions (these will work with Borland/Inprise’s Turbo Pascal Compilers or can be easily converted to equivalent C/C++ functions).

const
  vga : word = $A000;

var
  oldmode : byte;

Procedure setMCGA; assembler;
{Sets the graphics mode, including saving
the previous graphics mode}
asm
  mov ax,0F00h
  int 10h
  mov oldmode,al
  mov ax,0013h
  int 10h
end;

Procedure settext; assembler;
{Returns the program to the graphics
mode it was previous in}
asm
  mov ah,00h
  mov al,oldmode
  int 10h
end;

procedure putpixel( x,y : word; colour : byte);
{sets the pixel at (x,y) to the
colour given by colour.
Calculations are done using shifts
and additions not multiplications}
begin
  if (x>319) or (y>199) then exit;
  asm
    push ds	{save these two registers...}
    push di	{...by putting values on stack}

    mov ax,y
    shl ax,1	{ax=y*2}
    mov bx,ax	{bx=y*2}
    shl ax,2	{ax=y*8}
    add ax,bx	{ax=(y*8)+(y*2)=y*10}
    shl ax,5	{ax=(y*10)*2^5=y*320}

    mov bx,x	{ax has y offset, bx has x}
    add ax,bx	{add the offsets}

    mov di,ax	{di now has currect offset}
    mov ah,colour
    mov ds,vga	{es now has segment}
    mov ds:[di],ah	{plot the pixel}

    pop di	{restore reg values}
    pop ds
  end;
end;

Leave a Reply

Your email address will not be published. Required fields are marked *

Name *
Email *
Website

April 2024
M T W T F S S
1234567
891011121314
15161718192021
22232425262728
2930  

Archives