![]()
::[ PART 01 - THE BASICS
]::[ Source
]::[ Printable Version
]::
::[
DISCLAIMER
]::
I will hold no responsibility to whatever happens to you, your computer, your sanity, your pet, or whatever that may happen to your existence for your reading these texts and for the outcome of the various source code(s) given in each tutorial. So in short, read at your own risk!
If you dunno anything about programming graphics, then don't be discouraged! That's what tutorials are for!...:). It has been decided that mode 13H will be introduced first, if you know this mode already, then skip this file! After which, when Part 3 is out, we will go on to learn how to enter higher resolution modes. Mode 13H was popular in the early 1990s, but because newer, higher, and better screen resolutions came out, well, usage of this mode just seemed to have "died out". But nonetheless, if you grasp how to program in this mode, then you'd have little trouble programming in, say, 640x480-8bit modes. Oh yeah, throughout these tutorials, I'll be assuming you know some C...:) SideNote: In "Mode 13H", the "13H" is a hexadecimal number, and is pronounced "one three hex", but I just call it "thirteen hex"...:).
An ordinary decimal number 0 is 0x0 in hex. If you haven't notice, you just add a "0x" before the number itself to tell the compiler that you're using hexadecimal notation for the said number. So now that's over, let's start. The first useful thing you'd have to know is how to get your screen to be in 320x200x8bit mode...this is simple, just copy the following function:
//----8<----[ CODE BEGIN ]--------//
void setGFXMODE() {
_AX = 0x0013;
geninterrupt(0x10);
}
//----8<----[ CODE END ]--------//
I'm having a gut feeling some of you guys are saying: "Argh Willie! Whatever is THAT???"... Now, don't let this code scare you off, 'cause I'm going to explain each line... Well, if you know basic C, the "void setGFXMODE()" line simply states that you're creating a void function named setGFXMODE()... Now what about "_AX = 0x0013"??? Well, _AX is actually a register. Just think of it (for now) as a variable that is always there inside your computer. Now these variables (aka "registers") may be used to store infos just before you call an interrupt. Now an interrupt is what it says it is...it interrupts and grabs control of the whole program execution, then when it's done doing whatever it needs to do, it returns control to the main program from which it originally interrupted. The interrupt was done in the line "geninterrupt(0x10)", 0x10 is the interrupt number for changing video modes. Different interrupt nos. do different things, so don't just go about changing the 0x10 to, say, 0x11, 'cause your program might hang! (Actually, there're two types of interrupts, the software interrupts and the hardware interrupts, but I won't go into that now...probably next time...:) ). Now what relationship does the _AX have with our calling of interrupt 0x10??? Well, what interrupt 0x10 does is look at what value _AX has, if it sees _AX to have the value 0x0013, then it changes your screen to 320x200x8bit mode. And there! The last four paragraphs explains all that you need to know (for now) about going into mode 13H (of course, you could just copy the code and use it, which I did myself before actually understanding the idea behind the registers, interrupts, base, segments, offsets, etc....:) ). Now how to get back to text mode? Well, if 0x0013 gets you to 320x200-8bit mode, then 0x0003 gets you to text mode!...here's a sample code:
//----8<----[ CODE BEGIN ]--------//
void setTXTMODE() {
_AX = 0x0003;
geninterrupt(0x10);
}
//----8<----[ CODE END ]--------//
WHOOPEEE!!!...:). And so now you have the codes that would let you get in and out of graphics mode...pat yourself on the back 'cause you've just learned one of the most important thing when programming mode 13H...:). But wait, what use is getting into the mode if you can't show ANYTHING there? Now don't worry, plotting a pixel is a breeze...:). What's a pixel you might ask? A pixel is a "dot" on the screen. Given that you're working in 320x200 screen resolution, that gives you 320 "dots" across and 200 "dots" high...:). There're two ways of plotting a pixel in this mode, one's via BIOS call (which is slow, and is discouraged!), and the other is writing directly to the screen via the mode's memory address (which is faster). When using the BIOS to plot the pixel, you use an interrupt to plot...now interrupts are SLOWWWWW...so avoid them as much as possible...here's a sample code:
//----8<----[ CODE BEGIN ]--------//
void pixelBIOS(int x,int y,char color) {
_AH = 0x0C;
_AL = color;
_CX = x;
_DX = y;
_BX = 0x01;
geninterrupt(0x10);
}
//----8<----[ CODE END ]--------//
Again, _AH, _AL, _CX, _DX, and _BX are just registers, think of them (for now) as local variables created by the compiler everytime you create a program. Let's look at the parameters, it takes in integers x and y along with a character color. Obviously, the x and y is the coordinate where you will plot the pixel. The coordinate system in computer graphics is different from the ones taught in school...here's the difference:
TAUGHT IN SCHOOL: IN DOS GRAPHICS:
^ +Y ^ -Y
| |
| |
-X <-----+-----> +X -X <-----+-----> +X
| |
| |
v -Y v +Y
So you see, in the Cartesian Coordinate System, as you go up the y-axis, you get higher y-values, but in computer graphics, as you go up the y-axis, you get smaller y-values... Now if you'd plot a pixel in coordinate (0,0) using the above function, it'd show up:
THE SCREEN: +--------------------+ |* | * = the pixel plotted at (0,0) | | | | | | +--------------------+ Obviously, coordinate (0,0) is at the upperleftmost part of the screen, and coordinate (319,199) is the lower rightmost part of the screen... (You might be wondering why you can't plot a pixel at coordinate (320,200) when the graphics mode is 320x200 in resolution. Well, since the coordinate system starts with (0,0), it ends with (319,199), don't believe me? Try counting...:) ) Now as said before, mode 13H is equal to 320x200-8bit, the "320x200" part you know already, it's the screen resolution, so what about the "8bit" part? It's the number of colors you can plot! So how big is 8bit? Just solve for 2(raised to 8)...it should equal to 256 (now you know what they mean by 16/32bit graphics...:) ). Since you're in an 8bit mode (i.e. 256 color mode), that means you can assign a color number from 0-255 ONLY. So if you were to type in:
pixelBIOS(23, 74, 31);
inside, say, the main() function, you'd be plotting a pixel at coordinates (23,74) with color number 31. The other way of plotting a pixel is through direct memory access ("Argh Memory! Willie! Are we going to use pointers and linked lists and stuffs???" - Hardly, we'll learn about the base address and the 64K segments, and about offsets also, which I'll go into now...) First, here's a sample code:
//----8<----[ CODE BEGIN ]--------//
void pixelMEMORY(int x, int y, char color) {
memset(MK_FP(0xA000, x+(y*320)), color, 1);
}
//----8<----[ CODE END ]--------//
Now, the base address of mode 13H in memory is 0xA000, it's like the starting address in your computer's memory that points to the monitor that you're facing...:). Now each base address has a range of 64K (that's 65536!). That 64K range is how far your segment can access in memory relative to your base. Just think of it this way: I hope that was clear...:). Continuing the analogy, since the base address of mode 13H is 0xA000, you'll be standing on address 0xA000, and the next 64K of memory after 0xA000 can be accessed via your specially elongated arm!...So if you want to plot a pixel at, say, coordinate (20,13), how far away should you reach to plot the point? It's solved through this equation:
far_away = x +(y *320)
-> far_away = 20+(13*320)
The 320 you multiply to with 13 is the width of your screen resolution (we're in 320x200, remember?...:) ). So you just add where you're standing (that's 0xA000) with the value of far_away, and use your wonderfully long hand to paint the color you want at that point, and voila! Your first pixel! So looking back at the code, what memset does is paint a character color at base address 0xA000 and offset x+(y*320)...the MK_FP() function stands for MAKE FAR POINTER...which is actually what the first parameter of memset is asking...MK_FP()'s first parameter asks for the base, and the second for the offset...which looks like this:
MK_FP(base_address, offset)
Look at the code for a while and you'll understand...:).
Note that even though I said above that plotting pixels directly to memory is fast...well, guess what? There're ways to make this faster!...but that'll have to wait when we reach the topic about bitwise shifts and stuffs. Part 2 will be on how you can set the color of, say, color number 82. It'll teach you how to set and get the palette. It'll be out about next week...:). Briefly, mode 13H has a palette, this palette stores colors that mode 13H uses, so if you plot with a color 93, you're looking up on an array that says color 93 has this intensity of Red, Green, and Blue...:).
|
|