In order to get anywhere with this, you will need to include these header files:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
[size="5"]What You NEED To Know First
Okay, so you've got this txt file, and you want to start coding your own fire routine. What do you need to know first? Well, you will need to know how to set up a video buffer (ie, virtual screen) and flip it to the active video screen, and that's about it aside from setting the video mode, but for those of you that need it, I'll even explain how to do that as well.
[size="5"]Setting The Video Mode
The mode you will most likely be working in is mode 13h, which is a resolution of 320x200 pixels. To set the video card into this mode, you need to put the value 0x13 (hence 13h) into register AX on the cpu, to do this, you:
void graph_mode(void)
{
asm
{
mov AX, 0x13 // move 13h into AX
int 10h // call interrupt 10h, the video interrupt.
}
}
void text_mode(void)
{
asm
{
mov AX, 0x03
int 10h
}
}
[size="5"]Video Buffer (Virtual Screen)
Ok, now you can set your video mode, now you need to set up your video buffer. Why do you need a buffer? Because directly accessing your video memory is just simply _TOO_ damn slow for what your going to be doing.
first set up your pointer (I would make this global)
unsigned char *vbuffer = NULL;
void mk_buff(void)
{
vbuffer = (unsigned char *) malloc(64000);
if (vbuffer == NULL)
{
text_mode();
printf("Not enough available memory, exiting...");
exit(1);
}
}
[size="5"]Pointing to Video Memory
If you don't know anything else, you should know this, you will NEED a pointer to your video memory, or _NOTHING_ is going to work. To set this up add a line like this to the beginning of your source file (make it global)
unsigned char *video = (unsigned char *)MK_FP(0xA000, 0);
[size="5"]Choosing a Palette
If you haven't noticed before, by some act of the gods, the default color palette isn't set up with smooth running colors, it just jumps all over the damned place.... Soooooo, you will need to set this up as well. Now don't panic, it's not that hard. If I can do it, you can do it (trust me on this one).
The first thing you need to do, set a few defines, and create your RGB triple color structure, you will do that like this:
#define palette_mask 0x3c6 // tells the vga card we'll be messing w/ the palette
#define palette_write 0x3c8 // tells it we'll be writing values
#define palette_data 0x3c9 // this is the port we'll write to
typedef struct RGB_Type
{
unsigned char red;
unsigned char green;
unsigned char blue;
} RGB_color, *RGB_color_ptr;
first make a procedure set_pal():
void set_pal(int index, RGB_color_ptr color)
{
outp(palette_mask, 0xff); // tell it we'll be messing w/ it
outp(palette_write, index); // which color we'll be updating
outp(palette_data, color->red);
outp(palette_data, color->green);
outp(palette_data, color->blue);
}
void init_colors(void) // set up smooth running color palette
{
int i, count;
RGB_color_ptr col;
for (i = 1; i < 63; i++)
{
col->red = i; // there are 63 possible intensities for each color
col->green = 0; // so we'll loop through and set from 1 to 62 for
col->blue = 0; // shades of red.
set_pal(i, col);
}
col->red = 0;
col->green = 0;
col->blue = 0;
for (i = 63; i < 255; i++) // this will set _ALL_ other colors to black
set_pal(i, col); // I'll explain why we do this later
}
[size="5"]Cleaning Your Memory
Since we'll be reading values from our newly created video buffer, it'd be nice to know that when we start out, they are all zero, or we're going to have _PROBLEMS_, because un-initialized variables tend to take on ugly forms and will throw out random 'stuff' at you and bad things will happen. Sooooo, let's write a little tid-bit to do just that:
void Cls(unsigned char Col, unsigned char *Where)
{
// initialize screen or buffer to color Col
_fmemset(Where, Col, 64000);
}
Cls(0, vbuffer);
Side note, this actually fills the screen with color 'Col' Sooo, if you use say, Cls(20, vbuffer); it will "clear" the screen to whatever color 20 happens to be set to.
[size="5"]A Little Optimization Tip
In this section I will describe a method of accessing the video memory or buffer faster than you normally would think you could. Most of you are probably used to using the video[Y * 320 + X] method, well, what i'm going to show you is in sense, the same thing, accept it's impregnated, that is, precalculated Y values. To do this, create a global variable
int ytab[319];
void mk_ytab(void) // create Y * 320 lookup table
{
int x,y;
for (y = 0; y < 319; y++)
ytab[y] = y * 320;
}
[size="5"]Setting Up Your 'Hot Spots'
What are Hot Spots anyways? Well, they are the pixels you will set randomly on the bottom row (119) that are going to be the base of your entire routine. Without these babies, you 'aint gettin' no where... But before we get to the code for the hotspots, there is a function that you will want to add that will clear row 119 in between each run. We'll call this function cll.
void cll(unsigned char *where) //clear line 119 to black (resetting hotspots)
{
_fmemset(where + 63680, 0, 320);
}
Ok, now back to the originally scheduled program... Hotspots. The idea here is to go through a for loop, running from 119,0 to 119,319. The reason we don't go to 320, is that it only goes to 319, but you have to remember that it starts at 0, so in essence, it's still 320 columns wide. Ok, I think you're ready to see some example code to to this, well here you go:
void set_hot(void)
{
int x;
cll(vbuffer); // clear line for next run
for (x = 0; x < 319; x++)
{
if (random(100) < 55) vbuffer[ytab[199] + x] = 63;
}
}
[size="5"]Doing The *UGH* Math
Ok, now it's time to get our hands dirty. There will be a few functions covered / created in this section, because they are tied together. Just to keep your mind thinking in the right order, i'll show them to you in the order they are executed, however, there will be a call in this first function that you have not seen yet. But it will be defined directly after. This first function will examine _every_ single pixel on our working screen, do the calculations, and make the changes that will set you on your way.
void mk_lines(void)
{
int x, y;
for (x = 0; x < 319; x++)
for (y = 199; y > 110; y--)
vbuffer[ytab[y-1] + x] = average(x,y);
}
What about that average(x,y) call you say? Well, that's the call I was refering to before. You will need a function that will take the pixels 2 to the left, 2 to the right, 1 above, 1 below, and the current one, and average them all together, Then plot your new value above the current pixel you are calculating for. Here's a diagram of how this works if you're confused:
0[nbsp][nbsp][nbsp][nbsp][nbsp]0[nbsp][nbsp][nbsp][nbsp]*0*[nbsp][nbsp][nbsp][nbsp]0[nbsp][nbsp][nbsp][nbsp][nbsp]0
0[nbsp][nbsp][nbsp][nbsp]24[nbsp][nbsp][nbsp][nbsp]63[nbsp][nbsp][nbsp][nbsp]24[nbsp][nbsp][nbsp][nbsp]0
0[nbsp][nbsp][nbsp][nbsp][nbsp]0[nbsp][nbsp][nbsp][nbsp]*0*[nbsp][nbsp][nbsp][nbsp]0[nbsp][nbsp][nbsp][nbsp][nbsp]0
For simplicity, we'll use a 5x3 screen here. If we are on 3,2 (the one with 63 in it) we would do the following:
foo = (int)(0 + 24 + 63 + 24 + 0 + 0 + 0) / 7;
So, without further ado, here is an example average() function:
unsigned char average(int x, int y)
{
unsigned char ave_color;
unsigned char ave1, ave2, ave3, ave4, ave5, ave6, ave7;
ave1 = vbuffer[ytab[y + 1] + x];
ave2 = vbuffer[ytab[y - 1] + x];
ave3 = vbuffer[ytab[y] + x + 1];
ave4 = vbuffer[ytab[y] + x - 1];
ave5 = vbuffer[ytab[y] + x + 2];
ave6 = vbuffer[ytab[y] + x - 2];
ave7 = vbuffer[ytab[y] + x];
ave_color = (ave1 + ave2 + ave3 + ave4 + ave5 + ave6 + ave7) / 7;
return(ave_color);
}
[size="5"]In the Home Stretch
Ok, we've got our hotspots set up, and we've got our averaging going, but nothing is happening. Why? Because you are drawing on the virtual screen, or video buffer. In order to see your new creation, you must transfer the virtual screen to the actual video screen. This is a simple one line function that looks like this:
void flip(void)
{
// move contents of vbuffer to screen
_fmemcpy(video, vbuffer, 64000);
}
[size="5"]One Final Task
Unless you like the idea of losing your memory to dead tasks, before your routine exits, it would be in your best interest to release your allocated video buffer back to the system. This, like flip(); is another quick and painless routine, even less typing that flip!! woopie.
void shutdown(void)
{
free(vbuffer);
}
[size="5"]Putting It All Into Action
So now you've got all the code (hopefully) you need to implement your very first fire routine. All you need is a main() to go with your functions. A quick and dirty main would look something like this:
void main(void)
{
graph_mode();
mk_ytab();
mk_buff();
Cls(0, vbuffer);
Cls(0, video);
init_colors();
randomize(); // this is crucial!
do
{
set_hot(); // generate random hotspots
mk_lines(); // do all the math and screen updating
flip(); // move your new frame to the video screen
} while(!kbhit());
shutdown();
text_mode();
}
[size="5"]Got Milk? Err, I Mean Flickering?
If you happen to be one of those people with an insanely fast processor, or plan to release your code, you will probably want to add in *ONE* more function. Even though we're writing _ALL_ of the video buffer to the screen at once, it is still not 100% instantaneous. And if your video card is pumping out frames faster than your monitor's refresh rate, you will experience what we call 'Flickering', or 'Snow' for you EGA guys out there. This is easily dealt with though. It is caused when the screen is being updated in the middle of a screen write, causing an inconsistent screen. Luckily, Card manufacturers were kind enough to give us access to determine if the electron bombarder is currently drawing, or retracing back to the top-left corner of the screen, which is when you want to update your screen.
The function to do this looks like this:
void vsync(void)
{
// wait for verticle retrace to avoid flicker
while (inportb(0x3da) & 0x08);
/* vga is in retrace */
while (!(inportb(0x3da) & 0x08));
/* wait for start of retrace */
}
do
{
set_hot(); // generate random hotspots
mk_lines(); // do all the math and screen updating
vsync();
flip(); // move your new frame to the video screen
} while(!kbhit());
[size="5"]Graduation
And there you go! That's as far as this document is going to take you. There are obviously some optimizations that can be done to this code. For instance, the process of averaging EVERY SINGLE PIXEL and dividing by 7 isn't very nice on your CPU. One way to overcome this would be to add an 8th value in the averaging and instead of dividing by 7, divide by 8, _BUT_ not by ' foo / 8' because that would be just as slow as now, slower even. By shifting the bits 3 places to the right, you accomplish the same thing as by dividing by 8. Soooo,
foo = (ave1 + ave2 ... ave8) >> 3;
foo = (ave1 + ave2 ... ave7) / 7;
This document may be (and is ENCOURAGED TO BE!) freely distributed as long as it stays in its original form. I wrote this because quite frankly all of the fire tutorials our there, ALL OF THEM, aren't worth a shit. And it's about time someone released one that actually bothers to explain _HOW_ to do the things instead of only giving a brief overview of _WHAT_ needs to be done to accomplish it.