programmers resources
  http://www.intel-assembler.it/  (c)2017 intel-assembler.it   info@intel-assembler.it
 
Search :  
Lingua Italiana    English Language   
Index
 
just an empty assembly space
just an arrow Intel Platform
just an arrow Article & Guides
just an arrow Download Software


23/01/2009 Featured Article: How to remove Buzus Virus (permalink)




:::3141104:::
Bottone Scambio Directory Pubblicitaonline.it
Home Page | Articles & Guides | Download | Intel Platform | Contacts

Google
 


Bookmark and Share
Download 
Tell a friend



SVGA PutPixel

Programming the SVGA card in asm and C

(by abe)

Some good old examples of how to write on a VGA graphic screen. The code is in Asm and C.

"In this mode the screen has 640x480 pixels and each pixel takes one byte in memory since it's 256 colors. As you perhaps know, one segment in real mode on the PC is 64k. 640x480 = 300k (307200 bytes). That means that it would take 300/64, approximately 4.7, 64k segments to fill one 640x480 screen. The PC only uses one 64k segment, A000:0000 - A000:FFFF, for graphic memory. With this you can only reach about 1/5 of the top of the screen. But there has got to be a way to draw an entire 640x480 screen you may say, and it does. The answer is bank switching. With a couple of out instructions to the graphic card you can tell the card which portion of the screen should be mapped to A000:0000 - A000:FFFF. Each "bank" is 64k big. The first bank (bank 0) reaches down to the middle of row 103 (65536/640). To reach the rest of row 103 and the next 102 rows you have to switch to bank 1."
This article is online from 5132 days and has been seen 13045 times




                    Abe's bag of tricks, SVGA Putpixel

Welcome to this, the very first little trick in my bag. Hopefully there will be
lots of tricks here that you can put in your own bag of tricks or perhaps you
prefer to have them in your hat :-). I plan to put small tricks here that
doesn't really fit into any part of the normal demoschool but is good to know
anyway. I also plan to answer often asked questions here.

One question that I've received many mails about is

* How do I put a pixel in svga mode 640*480 256 colors?
  I only seem to be able to put them in the upper part of the screen.

Answer:

In this mode the screen has 640*480 pixels and each pixel takes one byte in
memory since it's 256 colors. As you perhaps know, one segment in real mode on
the PC is 64k. 640*480 = 300k (307200 bytes). That means that it would take
300/64, approximately 4.7, 64k segments to fill one 640*480 screen. The PC only
uses one 64k segment, A000:0000 - A000:FFFF, for graphic memory. With this you
can only reach about 1/5 of the top of the screen. But there has got to be a
way to draw an entire 640*480 screen you may say, and it does. The answer is
bank switching. With a couple of out instructions to the graphic card you can
tell the card which portion of the screen should be mapped to A000:0000 -
A000:FFFF. Each "bank" is 64k big. The first bank (bank 0) reaches down to the
middle of row 103 (65536/640). To reach the rest of row 103 and the next 102
rows you have to switch to  bank 1. The 102 rows after that is bank 2 and so on.
The last row is somewhere in bank 4.

It looks something like this:

Y                            SCREEN
0иии+-----------------------------------------------------+
102и|_______________________ииииBANKи0ииииииииииииииииииии|
ииии|иииииииииииииииииииииии+-----------------------------+
205и|________ииииBANKи1иииииииииииииииииииииииииииииииииии|
ииии|ииииииии+--------------------------------------------+
307и|____________________________________ииииBANKи2иииииии|
ииии|ииииииииииииииииииииииииииииииииииии+----------------+
409и|_________________ииииBANKи3ииииииииииииииииииииииииии|
ииии|иииииииииииииииии+-----------------------------------|
ииии|иииииBANKи4ииииииииииииииииииииииииииииииииииииииииии|
480и+-----------------------------------------------------+
иииии0иииииииииииииииииииииииииииииииииииииииииииииии640ииX

The figure isn't exact but you get the idea I hope.
The address of a pixel is calculated like this:

    address = 640*y + x

Out of this address you get the banknumber:  address/0ffffh
and the offset into the bank:                address%0ffffh

In assembler you can calculate both the banknumber and offset with only one mul
like this:

   mov ax,[y]        ;y to ax
   mul 640           ;(640*y)%0ffffh to ax
                     ;and (640*y)/0ffffh to dx
   add ax,[x]        ;add the x-value
   jnc nocarry       ;if carry is set then
   inc dx            ;increase dx (just passed a bankborder with that add)
nocarry:

;Now dx is the banknumber of the point and ax is the offset into 0a000h
   .
   .                ;switch bank
   .
;just draw the point like we are used to, offset is in ax
   mov bx,0a000h
   mov es,bx        ;a000h to es
   mov di,ax        ;calculated offset to di
   mov ax,[color]   ;the color of the pixel
   mov [es:di],ax   ;draw it

You may have noticed that I skipped the switch bank part. That was on purpose
because there are two ways to do this. Either you can write directly to the
ports of the graphic card or you can use the VESA bankswitch interrupt. To write
to the ports seems to be slightly different depending on which card you have.
That is NO GOOD. You'd have to write many bankswitch routines which works for
their special card and detect which card is in the machine at the initialization
of the program. This is too much work for our bag of tricks so we use the slow
VESA bankswitch interrupt which at least works on all modern cards.

It's straightforward, just put 4f05 in ax, 0 in bx and banknumber*16 in dx.
Then call int 10h.
Here is the bankswitching code:

  asm mov ax,4f05h     /* VESA switch bank */
  asm xor bx,bx        /* bx should be 0 */
  asm mov dx,[bank]    /* dx = bank*16 */
  asm shl dx,4
  asm int 10h          /* and do it */

Remember to save ax if you put it in the pixel code above. I have written a very
small C-program that puts pixels on a 640*480 screen in different psychedelic
patterns. Then the palette is rotated, just like in the demoschool part I. You
can change pattern by pressing + or -. Stop the paletterotation with space and
end the program with Escape. If you keep pressing + a while there will appear
some very interesting patterns. I didn't count with this, it was just a
coincident but that's cool.

The C-code, Mr Ant on Magic Mushrooms:

<-------------------------------Cut---Here-----------------------------------
>

/**********************************************************************/
/*              Abe's bag of tricks, SVGA Putpixel                    */
/*                                                                    */
/*                                                                    */
/* In part 4 I said that part 5 would be about filled vector graphics */
/* But this is something completely different, this is Svga.          */
/* So I can't call this part 5. I could call it part 4.5 or I could   */
/* Create a new section in the demoschool, and that's what I did.     */
/*                                                                    */
/*                  1996-04-30 Abe Racadabra                          */
/*                  mail: dat94avi@bilbo.mdh.se                       */
/*                                                                    */
/**********************************************************************/

#include<conio.h>
#include<stdio.h>

#define PALSIZE 256

int bank;            /* bank and pal is global just because I'm so lazy */
char pal[PALSIZE*3];

void printinfo(void)
{
clrscr();
printf("This silly little program's sole purpouse is to demonstrate\n");
printf("how to use a putpixel routine in svga 640*480 256 colors.\n\n");
printf("Keys:   +     increase w\n");
printf("        -     decrease w\n");
printf("        space stop cycling the palette\n");
printf("        Esc   quit\n\n");
printf("Press a key to begin\n");
getch();
}
void wtsync(void)
{
asm mov     dx,3DAh
WaitVR1:
asm in      al,dx
asm test    al,8
asm jne     WaitVR1
WaitVR2:
asm in      al,dx
asm test    al,8
asm je      WaitVR2
}

void getpal(void)
{
int i;
outp(0x3c7,0);
for(i=0;i<256*3;i++)
pal[i]=inp(0x3c9);
}

void setpal(void)
{
int i;
outp(0x3c8,0);
for(i=0;i<256*3;i++)
outp(0x3c9,pal[i]);
}


void cyclepal(void)
{
char r,g,b;
int i;
r=pal[0];
g=pal[1];
b=pal[2];
for(i=0;i<(PALSIZE-1)*3;i++) pal[i]=pal[i+3];
pal[(PALSIZE-1)*3+0]=r;
pal[(PALSIZE-1)*3+1]=g;
pal[(PALSIZE-1)*3+2]=b;
wtsync();
setpal();
}

/* change these color-runs for different colors (duh . . .) */
void coolpal(void)
{
int i,r=0,g=0,b=0,col;

col=0;

outp(0x3c8,0);

for(i=0;i<64;i++)  /* color 0-63 */
{
        pal[col++]=r;
        pal[col++]=g;
        pal[col++]=b;
        if(r<63) r++;
}
for(i=0;i<64;i++)  /* color 64-127 */
{
        pal[col++]=r;
        pal[col++]=g;
        pal[col++]=b;
        if(g<63) g++;
}
for(i=0;i<64;i++)  /* color 128-191 */
{
        pal[col++]=r;
        pal[col++]=g;
        pal[col++]=b;
        if(b<63) b++;
}
for(i=0;i<64;i++)  /* color 192-255 */
{
        pal[col++]=r;
        pal[col++]=g;
        pal[col++]=b;
        if(r>0) r--;
        if(g>0) g--;
        if(b>0) b--;
}
setpal();
}

void setmode(int mode)
{
asm     mov     ax,mode
asm     int     10h
}

/* this is the slooow way to switch bank, with the VESA bios interrupt */
/* it is faster to write to the ports of the graphic card directly */
/* but that seems to bee different for every card, this works for all */
void setbank(void)   /* the bank that shall be set is a global variable */
{
asm mov ax,4f05h     /* VESA switch bank */
asm xor bx,bx        /* bx should be 0 */
asm mov dx,bank      /* dx = bank*16 */
asm shl dx,4
asm int 10h          /* and do it */
}

/* The adress is calculated with is 640*y + x */
/* a mul works great because it puts the high 16 bits of a mul in dx */
/* and the low 16 bits in ax */
/* after the mul, dx will contain the bank number */
/* and ax will be the offset into that bank */
void putpixel(int x,int y,unsigned char col)
{
int ofs;
asm mov ax,640
asm mov bx,y
asm mul bx         /* ax = 640*y, dx = bank */
asm add ax,x       /* ax = (640*y + x)%(64k) */
asm jnc noc        /* if the add went into a new bank (carry set) */
asm inc dx         /* then bank = bank + 1 */
noc:
asm mov ofs,ax     /* mov ax (offset into bank) to ofs */
asm cmp dx,bank    /* compare this pixel's bank to the current bank */
asm jz same        /* if it's the same, don't set the bank */
asm mov bank,dx    /* else update bank */
setbank();         /* and set new bank (faster to put the code here directly) */
same:
asm push di
asm mov ax,0a000h  /* and put the pixel into a000:offset */
asm mov es,ax
asm mov di,ofs
asm mov al,col
asm mov [es:di],al
asm pop di
}

void main(void)
{
int i,j,w;
char ch;
printinfo();
setmode(0x005f); /* change into 640*480 256 colors (on my cirrus logic) */
                 /* I'm not sure if mode 5f is 640*480 for all cards */

bank=0;
w=48;       /* how the screen will look depends on w */
getpal();
coolpal();  /* set a smooth palette, comment this line out for a strange
               effect */
do
{
  for(j=0;j<480;j++) /* draw a screen */
  {
     for(i=0;i<640;i++) /* I just experimented until I got this formula */
     putpixel(i,j,i*j+i*i+j*j+i*j*w); /* change this formula for different
                                         effects, for instance try sin & cos
                                         combinations . . . */
  }

  do
  {
     cyclepal();      /* cycle the palette until a key is pressed */
  }while(!kbhit());
  ch=getch();        /* check what key was pressed */

  switch(ch)         /* and react to the keypress */
  {
    case ' ':  getch(); /* if space, stop cycling */
    break;
    case '+':  w++;    /* case +, increase w */
    break;
    case '-':  w--;    /* case -, decrease w */
    break;
  }
}while(ch!='\x1b');    /* Escape ends */


setmode(3);             /* get back to dos mode */
printf("\nw = %d\n",w); /* write the last w just incase it was really cool */
                        /* and you want know what value w had */
}

<-------------------------------Cut---Here-----------------------------------
>

Well that's all folks. *Click*



Top
Download 
Tell a friend
Bookmark and Share



Similar Articles

Accessing 256 kB of memory on a standard VGA-card
How to access unchained memory using bitplanes
(by Vulture)

Bezier
An asm routine for drawing a Bezier curve
(by Ron Thomas)

Bresenham's Line and Circle Algorithms
design line and circle algorithm
(by Mark Feldman)

Crossfade Asm Effect
Asm source code for a crossfading effect
(by Esak)

ELYSiUM's VGA Graphics & Sound Blaster Coding
Beginner VGA Graphics & Sound Blaster Programming
(by ELYSiUM)

Linear Frame Buffer (LFB) Emulator
An LFB emulator for VESA with Asm source code
(by Alexei A. Frounze)

S-BUFFER FAQ
Method to implement advanced video buffering
(by Paul Nettle)

Scrolling
Basics of scrolling with many infos and pseudocode
(by Alec Thomas)

Simple Horizontal Scroller
Asm code for scrolling text in vga
(by Carlos Hasan)

The PC GAMES PROGRAMMERS ENCYCLOPEDIA 1.0
A collection of 85 asm programming documents
(by various)

VGA Programmers Master Reference Manual
Complete VGA programming manual
(by Andrew Scott)

VGA Trainer Program
A course for graphic programming in Pascal/ASM
(by Grant Smith Denthor of ASPHYXIA)

 Tags: vga, 2d


webmaster jes
writers rguru, tech-g, aiguru, drAx

site optimized for IE/Firefox/Chrome with 1024x768 resolution

Valid HTML 4.01 Transitional


ALL TRADEMARKS ® ARE PROPERTY OF LEGITTIMATE OWNERS.
© ALL RIGHTS RESERVED.

hosting&web - www.accademia3.it

grossocactus
find rguru on
http://www.twitter.com/sicurezza3/
... send an email ...
Your name

Destination email

Message

captcha! Code