Spell presents:


////==\\\\ ||      || //========    |\      /|    //\\     //======\
    ||     ||      || ||            ||\    /||   //  \\   //
    ||     ||      || ||            || \  / ||  //    \\  ||
    ||     ||======|| ||====        ||  \/  ||  ||    ||  ||
    ||     ||      || ||            ||      ||  ||====||  ||    ==\\
    ||     ||      || ||            ||      ||  ||    ||  \\      ||
    ||     ||      || \\========    ||      ||  ||    ||   \\====//

                                                        Issue 12
                                                        24-2-97


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  Index:

        1. Introduction
          1.1. About the magazine
          1.2. About the author
          1.3. Distribution
          1.4. Subscribing
          1.5. Contribuitions
          1.6. Hellos and greets
        2. Mailroom
        3. Designing a text adventure - Part V
        4. Isn't it nicer with textures ?!
          4.1. A word on interpolation
          4.2. Afine texture mapping
          4.3. Lambert lighting with textures
          4.4. Mip-mapping
          4.5. Arbitrary-sided polys
        5. Into the Realm of Sound - Part I
          5.1. DIGITAL SOUND?!
          5.2. The Creative Labs Sound Blaster
          5.3. Programming the beast
        6. To float or not to float
          6.1. Introduction
          6.2. Basic Concepts
          6.3. Fixed-Point Math Theory
          6.4. Closing Coments
        7. A simple introduction to C - Part I
          7.1. Functions
          7.2. Including files and comments
          7.3. Datatypes
          7.4. Defining of the variables
          7.5. Calculations
          7.6. Arrays
          7.7. A function called printf
          7.8. Scanf
          7.9. Pointers
          7.10. Allocating and freeing memory
          7.11. Conditions
          7.12. ASCII in C
          7.13. Assembly and Pascal
          7.14. Graphics
        8. Sprites Part V - Colision detection
          8.1. Box collision
          8.2. Pixel perfect colision
        9. Graphics Part XI - Introduction to unchained modes
          9.1. What ?!
          9.2. Planes
          9.3. How ?
          9.4. Putting down a pixel
          9.5. Getting a pixel
          9.6. Changing the viewport
          9.7. The example program
       10. Cool ASM and Pascal tricks
          10.1. Why ?!
          10.2. Full ASM functions
          10.3. Using 32 bit registers
          10.4. Using 32 bit instructions
          10.5. 32 bit parameters
       11. Hints and tips
       12. Points of view
       13. The adventures of Spellcaster, part 12


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  1. Introduction

    1.1. About the magazine

    Ok, here it is, issue 12 of 'The Mag'... This issue is packed with good
  stuff, so I won't bother you with a great introduction... I just hope I can
  finish this issue faster than I did last one... See the index above for
  contents... :)
    This issue may be late by the time I've finished, because I'm working on
  my demo for the Simple'97 party in Portugal !! The first party in Portugal,
  and I hope to get a good placing in the democompo with my 18 bit color
  demo... :))) I dream all the time... There aren't any prizes or anything,
  but it's just for the fun... I'm a very competitive person... :)

    When you read this magazine, I'll assume some things. First, I assume you
  have Borland's Turbo Pascal, version 6 and upwards (and TASM for the assembly
  tutorials). I'll also think you have a 80386 (or 386 for short; a 486 would
  be even better), a load of patience and a sense of humor. This last is almost
  essencial, because I don't receive any money for doing this, so I must have
  fun doing it. I will also take for certain you have the 9th grade (or
  equivelent). Finally, I will assume that you have the last issues of
  'The Mag', and that you have grasped the concepts I tried to transmit. If
  you don't have the issues, you can get them by mail, writing to one of the
  adresses shown below (Snail mail and Email).

    Almost everything I know was learnt from painfull experience. If you
  re-re-re-read the article, and still can't understand it, just drop a line,
  by mail, or just plain forget it. Most of the things I try to teach here
  aren't linked to each other (unless I say so), so if you don't understand
  something, skip it and go back to it some weeks later. It should be clearer
  for you then. Likewise, if you see any terms or words you don't understand,
  follow the same measures as before.

    Ok, as I'm earing the Net gurus and other god-like creatures talking
  already, I'm just going to explain why I use Pascal.
  For starters, Pascal is a very good language, ideal for the beginner, like
  BASIC (yech!), but it's powerfull enough to make top-notch programms.
  Also, I'll will be using assembly language in later issues, and Pascal makes
  it so EASY to use.
  Finally, if you don't like my choice of language, you can stop whining. The
  teory behind each article is very simple, and common with any of the main
  languages (C, C++, Assembly - Yes, that's true... BASIC isn't a decent
  language).

    Just one last thing... The final part of the magazine is a little story
  made up by my distorted mind. It's just a little humor I like to write, and
  it hasn't got nothing to do with programming (well, it has a little), but,
  as I said before, I just like to write it.

    1.2. About the author

    Ok, so I'm a little egocentric, but tell me... If you had the trouble of
  writing hundreds of lines, wouldn't you like someone to know you, even by
  name ?

    My name is Diogo de Andrade, alias Spellcaster, and I'm the creator,
  editor and writer of this magazine.
    I live in a small town called Setubal, just near Lisbon, the capital of
  Portugal... If you don't know where it is, get an encyclopedia, and look for
  Europe. Then, look for Spain. Next to it, there's Portugal, and Setubal is in
  the middle.

    I'm 19 years old, and I'm in the second year in the university (if you do
  want to know, I'm in the Technical Institute of Lisbon, Portugal), so I'm not
  a God-Like creature, with dozens of years of practice (I only program by
  eight years now, and I started in a Spectrum, progressing later to an Amiga.
  I only program in the PC for two years or so), with a mega-computer (I own a
  386SX, 16 Mhz), that wear glasses with lens that look like the bottom of a
  bottle (I use glasses, but only sometimes), that has his head bigger than a
  pumpkin (I have a normal sized head) and with an IQ of over 220 (mine is
  actually something like -220 :) ). I can program in C, C++, Pascal, Assembly,
  Prolog, CaML, Visual C++, lots of idiot languages and even BASIC (yech!)...
  And due to school, this list is increasing... :)))

    So, if I am a normal person, why do I spend time writing this ?
  Well, because I have the insane urge to write thousands of words every now
  and then, and while I'm at it, I may do something productive, like teaching
  someone.

    Just one more thing, if you ever program anything, please send to me... I
  would love to see some work you got, maybe I could learn something with it.
  Also, give me a greet in your program/game/demo... I love seeing my name.

    1.3. Distribution

    I don't really know when can I do another issue, so, there isn't a fixed
  space of time between two issues. General rule, I will try to do one every
  month, maybe more, probably less (Eheheheh). This is getting to an issue
  every two months, so, I'll think I'll change the above text... :)
    'The Mag' is available by the following means:

    - Snail Mail : My address is below, in the Contributions seccion... Just
                   send me a disk and tell me what issues you want, and I
                   will send you them...

    - E-Mail : If you E-mail me and ask me for some issues, I will Email you
               back with the relevant issues attached.

    - Internet : You can access the Spellcaster page and take the issues out
                 of there in:

                 http://alfa.ist.utl.pt/~l42686

                 Follow the docs link...

    - Anonymous ftp : I've put this issue of 'The Mag' on the ftp.cdrom.com
                      site, in the pub/demos/incoming/code... It will probably
                      be moved to pub/demos/code or something like that.

    - BBS's : You can check out the BBS's list that will carry this and all
              the other issues of 'The Mag' in the file SPELL.DOC.

    1.4. Subscribing

    If you want, I'm starting "The Mag"'s subscription list... To get
  'The Mag' by Email every month, you just need to mail me and tell me so...
    Then, you will receive it every time a new issue is made...

    1.5. Contributions

    I as I stated before, I'm not a God... I do make mistakes, and I don't
  have (always) the best way of doing things. So, if you think you've spotted
  an error, or you have thought of a better way of doing things, let me know.
  I'll be happy to receive anything, even if it is just mail saying 'Keep it
  up'. As all human beings, I need incentive.

    Also, if you do like to write, please do... Send in articles, they will be
  welcome, and you will have the chance to see your names up in lights.
    They can be about anything, for a review of a book or program that can
  help a programmer, to a point of view or a moan.

    If anyone out there has a question or wants to see an article about
  something in particular, feel free to write... All letters will be answered,
  provided you give me your address.

    If you have a BBS and you want it to include this magazine, feel free to
  write me... I don't have a modem, so I can only send you 'The Mag' by Email.

    You can also contact me personally, if you study on the IST (if you don't
  know what the IST is, you don't study there). I'm the freshman with the
  black hair and dark-brown eyes... Yes, the one doesn't put a foot in a class
  for ages !

    My adress is:
                 Praceta Carlos Manito Torres, n4/6C
                 2900 Setbal
                 Portugal

    Email: spellcaster@bigfoot.com
           spellcaster@cryogen.com
           dgan@camoes.rnl.ist.utl.pt
           l42686@alfa.ist.utl.pt

    And if you want to contact me in real time, get into the Dragon Mud
  To do that telnet to:

                        Alfa.Ist.Utl.Pt  : Port 5000

    I'm sometimes there... My handle there is Olorin (Ehehe, Tolkien rules !).

    1.6. Hellos and greets

    I'll say hellos and thanks to all my friend, especially for those who put
  up with my not-so-constant whining.
    Special greets go to Scorpio, Denthor from Asphyxia (for the excelent VGA
  trainers), Draeden from VLA (for assembly tutorials) and all those coders
  out there who release source code and tutorials, Garfield (for the 'The Mag'
  logo and general suport) and all the demo groups out there. Oh, and I
  almost forgot all the people at Infinity Network !
    I will also send greets to everybody that responded to my mag... Thank
  you very much !


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  2.  Mailroom

    This issue's mailroom features only one email I got from Viriato, concerning
  LOTS of errors and inaccuracies I made in issue 11...
    As the mail was in portuguese, and all, I will just say what he thought it
  was wrong, maybe I'll delve a bit into some subjects and all...
    Let's start:

    a) There is a part in my mag that I make an ASCII like this:

                            1____2
                            |    |
                            |    |
                            3____4

       Well, vertice 3 and 4 are to be exchanges (as most of you migth have
       understood).


    b) Me and Viriato had a 'long' talk (we exchange about 6 mails about it)
       because of the Z-Buffer and BSP tree sorting methods.
       The conclusions were simple enough:

       (i) It is irrelevant if the camera is looking along the Z axis or so...
           The camera is a relative thing, and as that, any way of implementing
           diferent viewing angles can be reduced to thus. So, the arguments
           against the praticabilty of the other algorithms facing this is
           neglectable.

       (ii) The Z-Buffer method has several advantages that I haven't mencioned.
            In fact, after my talk with Viriato, I came to the conclusion that
            Z-Buffer is the best algorithm there is... I'll do an article about
            it soon... Well, the advantages of Z-Buffer are:

               - It is a perfect algo for the determination of visible polys.
               - It's the faster algo for worlds with LOTS of objects.
               - It doesn't need face sorting, that is, it doesn't have to
                 order the polys in any particular order
               - It enables us to render any geometric form, as long as we can
                 determin it's color and Z coord.
               - We don't have to worry with face intersection
               - It is relatively easy to implement

            In some tests that were made, the conclusion was that the time
            needed to render a scene with Z-Buffer tends to remain constant
            while the number of objects goes up, while in the other sorting
            algos, the time increases.
            Contrary to what I said, the Z-Buffer algo works with any kind of
            camera, fixed or moving... You'll understand why when I make an
            article on camera (probably next issue)
            The D-Buffer method I spoke of, aparently doesn't exist... What
            there is is a method called S-Buffer, which suposedly increases
            the speed of of Z-Buffer, but I don't know what it is about really.

       (iii) BSP trees solve the polygon overlapping/instersection problem, but
             so does the Z-Buffer.
             The BSP tree algo is slow, but only when calculating the tree
             (that is constant if the objects are not moving), so it is the
             fastest way to draw constant scenes (I think)

    c) Now, correcting what I said about backface removal. If I'm not mistaken,
       I said that the method only could apply when the object was directly
       in front of us... This isn't true... The method can be applied in
       any way.
       The theory behind it is simple:
       You check if the dot product between the view vector and the face normal
       is negative... If it is, then you draw the poly.
       The view vector is the vector that connecting the center of the object
       and the camera... In our examples, the camera is at (0,0,0), so the
       view vector for polygon with center in (Xc,Yc,Zc) will be (Xc,Yc,Zc).
       You can see an example in the Draw3dPoly in the TMAP1.PAS program.

    d) In the article about Lambert Shading, I say:

       "This works because of the proprieties of the cosine (it is never
        larger than 1). We must negate the color because the cosines of
        the displayable polygons are always negative."

       Well, this is more of an optimization... If you use an anticlockwise
       vertice numbering, instead of the normal clockwise one, you don't need
       to the negation. Of course, you have to invert everything respectingly
       the backface culling (=backface removal), for example.

    e) Shr doesn't work with signed numbers in Pascal !! The guys at Borland
       screwed up. Pascal should recognize if the number he is working with
       is a signed or unsigned number. If it is unsigned, he uses the normal
       Shr, if it signed, he should use the Sar (Shift Arithmetic Right). But,
       Pascal doesn't do that ! :( It does that selection with the Shl,
       though... It uses Shl for unsigned and Sal for signed... Just take care.
       This is particulary true when dealing with fixed point maths...

    f) I said that the signed numbers are stored "as eight bits, the leftmost
       one storing the sign (0=positive, 1=negative), and the remaining seven
       bits store the numbers"... Well, this is wrong... The signed numbers are
       stored using the "two's complement"... You obtain the two's complement
       by getting the logical Not of the number and adding one. For example:

                      -1 = Not(1) + 1 = 11111111

       The property of numbers stored this way is that it is easier to do
       subtractions... Aditions work in the same way as usual (you do a binary
       sum), and subtractions work by adding the simetrical number. This only
       works in this way, because if you used the sign bit storing format, you
       would get a wrong result.

    g) I say in the Mode 13h article that the palette data goes to video
       memory... Well, this isn't true... The palette data goes to an array
       of register, the RAMDAC

    h) I said that it was 4 times faster putting a word in video memory than
       putting a byte... I was wrong (surprise, surprise)... It is only 2 times
       faster.

    i) My assembler HLine routine is wrong. Why ? Because if the starting and
       ending points of the line are equal, it will not draw nothing, while
       it should draw one dot. To avoid this, add 1 to Cx after it calculates
       the diference between the starting and ending points... There's a great
       optimization to do in it also, but check out hints and tips...

    The rest of Viriato's comments make the greater part of this issue's hints
  and tips section, since they are not errors, just optimizations I forgot
  to explain...
    Well, as you can see, I make LOTS of mistakes... You shouldn't take every-
  thing you read here as some sort of Bible...It is just my (limited) knowledge.
  I think I'm a fairly good teacher, and that is why I write 'The Mag'. It isn't
  to show off, because I don't have that much to show off...
    Most of the errors I make is due to lack of testing, since I learn most
  of what I teach a week or so before writing the article... But, if I spent
  more time learning things the better, I would take longer to write 'The Mag'.
  And we don't want that, do we ?
    So, you should read the stuff here, and if something interests or puzzles
  you, don't think "If he says, he must know it!", but think "This guy's an
  ass !!! He doesn't know shit about this... Still, he gave me some ideas"... :)
    Well, I finished for now... Thanks, Viriato, for spotting the errors and
  beeing such a sour-ass... :)))


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  3. Designing a text adventure - Part V

    This issue's tutorial will lead us to the manipullation of the gamescape.
  What is the gamescape ? Well, it is all that it isn't monsters or objects.
  For example, the painting in on the dining room, or the stove in the
  kitchen of Fanglore...
    This is fairly easy to achieve, but I got to cover it anyway... :)
    So, what can we manipulate in the Fanglore enviroment ? If I remember
  correctly (it has been sometime since I've worked on this...), we can
  open and close the oven in the kitchen, and we can push a bookshelf in
  the library. Each of these things have diferent reactions.
    Let's first do the examination manipulation:

          { The specified parameter refers a gamescape object }
          Flag:=False;
          If Param='PICTURE' Then
          Begin
               Flag:=True;
               WriteLn;
               TextColor(Yellow);
               WriteLn('It is a painting of naked men and women doing...');
               WriteLn('OH MY GOD ! What are they doing with that sheep !!!');
               WriteLn;
          End;
          If Param='BOOKSHELF' Then
          Begin
               Flag:=True;
               WriteLn;
               TextColor(Yellow);
               WriteLn('This bookshelf has an assorted array of books...');
               WriteLn('Authors include Tolkien, Stephen King and Stephen');
               WriteLn('Donaldson... Nice choice...');
               WriteLn;
          End;
          If Param='OVEN' Then
          Begin
               Flag:=True;
               WriteLn;
               TextColor(Yellow);
               WriteLn('It is a gas-heated oven...');
               If Oven Then WriteLn('It is open...')
                       Else WriteLn('It is closed...');
               WriteLn;
          End;

    See where to put this in line 440 of the FangLore.Pas program... The
  Oven variable is a variable that tells if the oven is opened or close...
  It is initialize to FALSE (closed).
    Now, let's manipulate the enviroment... First let's open and close the
  oven ! :)

           If Parsed[1]='OPEN' Then
           Begin
                Valid:=True;
                TextColor(LightRed);
                If Parsed[2]='OVEN' Then
                Begin
                     If CurrentRoom=7 Then
                     Begin
                          If Oven Then WriteLn('It''s already open !')
                          Else
                          Begin
                               Oven:=True;
                               WriteLn('You open the oven...');
                               If Objects[2].Pos=255 Then
                               Begin
                                    WriteLn('You see a gas mask inside it...');
                                    Objects[2].Pos:=7;
                               End
                               Else WriteLn('It is empty...');
                          End;
                     End
                     Else WriteLn('I can''t see no oven...');
                End
                Else WriteLn('Sorry, I can''t open a ',Parsed[2],'...');
           End;
           If Parsed[1]='CLOSE' Then
           Begin
                Valid:=True;
                TextColor(LightRed);
                If Parsed[2]='OVEN' Then
                Begin
                     If CurrentRoom=7 Then
                     Begin
                          If Not Oven Then WriteLn('It''s already closed !')
                          Else
                          Begin
                               Oven:=False;
                               WriteLn('You close the oven...');
                               If Objects[2].Pos=7 Then
                                 Objects[2].Pos:=255;
                          End;
                     End
                     Else WriteLn('I can''t see no oven...');
                End
                Else WriteLn('Sorry, I can''t close a ',Parsed[2],'...');
           End;

    This code should be put in the Play procedure... It is VERY simple. It's
  just a bunch of 'if then else''s strum together... Talk about sloppy code.
  Now, let's push the bookshelf... :)

           If Parsed[1]='PUSH' Then
           Begin
                Valid:=True;
                TextColor(Yellow);
                If Parsed[2]='BOOKSHELF' Then
                Begin
                     If CurrentRoom=8 Then
                     Begin
                          If Book Then WriteLn('I can''t push it further...')
                          Else
                          Begin
                               Book:=True;
                               Write('You push the bookshelf, revealing ');
                               WriteLn('a secret passage !');
                               Rooms[8].North:=11;
                               Rooms[8].Desc[4]:='There is a secret passage to the ';
                               Rooms[8].Desc[4]:=Rooms[8].Desc[4]+'north !';
                               Rooms[8].Desc[5]:='*';
                          End;
                     End
                     Else WriteLn('I can''t see a bookshelf...');
                End
                Else WriteLn('Sorry, I can''t push a ',Parsed[2],'...');
           End;

    Again this code is to be put in the Play procedure... The Book variable
  is a boolean similar to the Oven variable... Notice that we've not only
  created a new passage in the game, but we also changed a bit the description
  of the room !!!!

    So, we're done for this article... Very simple one, but I'm fed up with
  this issue, so this is all you'll get ! :)
    Next issue we'll discuss reduncy and loading and saving the position
  in the game...


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  4. Isn't it nicer with textures ?!

    Let's start with the beggining... What is texture mapping ? Well, nowadays,
  this is quite a stupid question, since every 3d game nowadays uses texture
  mapping... But, anyway, texture mapping is the act of getting a 2d bitmap
  and stick it to a 3d object, giving it a sense of texture (if well done).
    Initially, this article was going into various texture mapping
  considerations, including lighting, perspective correct texture mapping and
  mip-mapping, but due to problems with time and other things, I've not included
  the perspective correct texture mapping. I'll include it in the next issue.
    Before we start the texture mapping part, I'll just do one thing. I'll
  change the FPoly procedure. If you don't remember, the FPoly proceedure draws
  a filled polygon. We'll change it because it eases up our work in programming
  the texture mapping procedure from it.
    What we are going to do is to divide the polygon drawing into two phases:

                      1) Calculate the maximmum/minimum x's. Let's call this
                         the calcultation phase.
                      2) Draw the poly. Let's call this the draw phase.

    Before, we calc'ed the maximmum and minimum x coords for each y and draw
  it. Now, we find all the coords and then in the end draw it...

   Procedure FPoly(X1,Y1,X2,Y2,X3,Y3,X4,Y4:Integer;Color:Byte;Where:Word);
   Var MnY,MxY:Integer;
       A:Integer;

       Procedure Side(X1,Y1,X2,Y2:Integer);
       { This routine updates the Poly variable, based on the side it is }
       Var Temp:Integer;
           X,XInc:Integer;
           A:Integer;
       Begin
            If Y1=Y2 Then Exit;
            { Exchange (X1,Y1) with (X2,Y2), in case Y2 is bigger than Y1.
              It is better than doing a routine for each case, and in the
              case of normal poly, drawing from (X1,Y1) to (X2,Y2) is the
              same as drawing from (X2,Y2) to (X1,Y1) }
            If Y2<Y1 Then
            Begin
                 Temp:=Y2;
                 Y2:=Y1;
                 Y1:=Temp;
                 Temp:=X2;
                 X2:=X1;
                 X1:=Temp;
            End;
            { Calc the slope. The Shl 7 is used to implement a rough sort of
              9.7 fixed point math }
            XInc:=((X2-X1) Shl 7) Div (Y2-Y1);
            { Initial position in fixed point }
            X:=X1 Shl 7;
            For A:=Y1 To Y2 Do
            Begin
                 { Calc the minimum and maximum X coordinates in this
                   scanline }
                 If (X Shr 7<Poly[A].MinX) Then Poly[A].MinX:=X Shr 7;
                 If (X Shr 7>Poly[A].MaxX) Then Poly[A].MaxX:=X Shr 7;
                 X:=X+XInc;
            End;
       End;

   Begin
        { Reset the Poly variable... Do this in assembler if you want a
          good speed up }
        For A:=0 To 199 Do
        Begin
             Poly[A].MinX:=MaxInt;
             Poly[A].MaxX:=-MaxInt;
        End;
        { Set the maximmum and minimum Y coordinate }
        MnY:=Y1;
        MxY:=Y1;
        If MnY>Y2 Then MnY:=Y2;
        If MnY>Y3 Then MnY:=Y3;
        If MnY>Y4 Then MnY:=Y4;
        If MxY<Y2 Then MxY:=Y2;
        If MxY<Y3 Then MxY:=Y3;
        If MxY<Y4 Then MxY:=Y4;
        If MnY<0 Then MnY:=0;
        If MxY>199 Then MxY:=199;
        { Calc the sides }
        Side(X1,Y1,X2,Y2);
        Side(X2,Y2,X3,Y3);
        Side(X3,Y3,X4,Y4);
        Side(X4,Y4,X1,Y1);
        { Draw the poly }
        For A:=MnY to MxY Do
          HLine(A,Poly[A].MinX,Poly[A].MaxX,Color,Where);
   End;

    The Poly variable is the variable that stores the minimum and maximum Y
  values for each scanline on the screen. It is defined like this:

        Var Poly:Array[0..199] Of Record
                                        MinX,MaxX:Integer;
                                  End;

    This should be easy to understand... It's the same procedure as before,
  but more modulated and divided in two parts... The hardest part is probably
  figuring out the fixed point part, but that's easy... Read the article about
  it in this issue...

    4.1. A word on interpolation

    In the course of this article (and other, like Gourad Shading,etc), you'll
  read lot's of time 'interpolate this' and 'interpolate that', and some of
  you might wonder: 'What the hell is interpolation ???'...
    Well, interpolation is the process of finding out some value of a function,
  given only some points of that function... In most functions, this will give
  an aproximate function, but by using the correct interpolation method, you can
  get a very acurate aproximation function...
    There are dozens of interpolation methods, including linear, quadric and
  cubic splines, nth splines, linear interpolation, bi-quadric interpolation,
  etc... The method we will talk when we refer to interpolation is linear
  interpolation... This is quite easy... Given two points A=(Ax,Ay) and
  B=(Bx,By), we will find the points that lie between them both, using a
  straight line to find it. As you might know, in this case, the line is
  defined by:
                        (Y-Ay)=M*(X-Ax)
  where:
                        M=(By-Ay)/(Bx-By)     <- This is the gradient of the
                                                 line

  So, the interpolated line would be something like this:

                         (By-Ay)
                      Y= ------- * (X-Ax) + Ay
                         (Bx-Ax)

    So, if you wanted to know the position the point C=(Cx,Cy), just had to
  substitute the coordinates in the equation, like this:

                         (By-Ay)
                     Cy= ------- * (Cx-Ax) + Ay
                         (Bx-Ax)

    As I said, this is quite simple. But know, let's go to the thing that
  interests us most... This is what I call 'discrete interpolation', because
  it gives us the coordinates of discrete points, not any point.
    Example: In the above equation, you could get the coordinate of the point
  which haves an X coordinate equal to 2.5, then the point with the X equal to
  2.6, or even 2.435362351... With 'discrete interpolation', you have a step
  value (let's say, for example, 1), and you can only find out the coordinates
  of the points with the X equal to (for example) 0,1,2, etc... In the case
  of most of the computer aplications, this is sufficient (with a smaller or
  bigger step value). But why is this more usefull ?
    Because this way, if the distance between the points we want to find out
  is constant in any axis (X or Y), there will be a fixed ammount to displace
  the coordinate correspondent to the other axis (Y or X), and that will lead
  to that you only need one addition for point, instead of using a divide and
  a multiply !!!
    So, given points A and B as above, you can interpolate it like this:

                            X0=Ax
                            Y0=Ay
                            Repeat
                                  X0=X0+XInc
                                  Y0=Y0+YInc
                            Until All Points We Want

    In the middle of the loop, (X0,Y0) have the interpolated point according
  to step. XInc and YInc are the step variables. We'll see later how do we
  calculate them.
    This is particular usefull if you want to get from a point A (in texture
  space, for example) to another point B in a number of steps N. You just have
  to set (X0,Y0) to the coordinates A, and then XInc and YInc to:

                          XInc=(Bx-Ax)/N
                          YInc=(By-Ay)/N

    We will use this quite extensivelly in the texture mapping procedure and
  in gourad shading...
    I don't know if I explained myself well, or not... This is a bit hard for
  me to explain, though it is simple to understand if the teaching was right.
    So, experiment with this, because this is an essential part of texture
  mapping...

    4.2. Afine texture mapping

    This is the first of the texture mapping processes I'm going to teach. This
  is not the best nor the fastest, bla bla, but I figure it out alone, so I'm
  not sure if this is the way everybody does... But it works !! And that's that
  count.
    The theory behind texture mapping is pretty easy. For example, you have
  a texture like this:

  ABCA      Each letter corresponds to a color. This is a 4x4 texture.
  BBAC      If we'll try to map it to the polygon shown below:
  ACEF
  BBAA                          P1 ____ P2
                                   |  |
                                   |  |
                                P4 ---- P3

    We'll get something like this:

    ABCA    See ? We establish a relation between the points and the edge
    BBAC    of the texture. Now, let's rotate the poly around the Y axis,
    ACEF    like this:
    BBAA                        P2 ____ P1
                                   |  |
                                   |  |
                                P3 ---- P4

    If we texture map it now, we'll get:

    ACBA    Still the relationship holds !!! Now, let's imagine we rotate it
    CABB    around the Z axis, like this:
    FECA
    AABB                             . P1
                                    / \
                                   /   \
                               P4 .     . P2
                                   \   /
                                    \ /
                                     . P3

    We would get:
                    A
                   C C      <- The holes are just an ASCII imperfection.
                  F A B        I'm just drawing this examples in 2d, but
                 A E B A       3d texture mapping obeys the same principle.
                  A C B
                   B A
                    B

    When I was drawin the ASCII above (the last 'texture mapped' poly), I
  first drawed the edges, like this:

      A
     C C       Then, I interpolated, that is, I filled in the blanks,
    F   B      in a linear (=afine) manner... That is why this kind of
   A     A     texture mapping is called afine texture mapping.
    A   B
     B A
      B

    The ideia is this. We find the textel (this is the texture's pixels) of
  the vertices of the polygon (Fig 1), then we interpolate to calculate the
  edges of the polygon (Fig 2), and then we interpolate between the edges to
  get the polygon's area (Fig 3).

                    A              A               A
                                  C C             C C
                                 F   B           F A B
                 A     A        A     A         A E B A
                                 A   B           A C B
                                  B A             B A
                    B              B               B

                  Fig.1          Fig.2           Fig.3

    This is basically the same we do when we are drawing a normal filled poly.
  When we draw a normal polygon, we first have the vertices, then we interpolate
  the edges, and then the area within. Putting this in steps:

                 1) The vertices are given
                 2) Interpolate the edges
                 3) Interpolate the interior

    With a normal poly, step 1 is given, step 2 is when we find the max and min
  X coord, and step 3 is when we draw the horizontal line joining them. So,
  we just add to the code of the normal poly the calculation of the texture
  coordinates. This is where the 'problems' start... But even this is simple:
    We know from the start to which textel the vertice of the polygons
  correspond. From there, in the calc phase, we calculate (using linear
  interpolation) the textel corresponding to the edge pixel and get for
  (MinX,Y) and (MaxX,Y) two textels, that we call (U1,V1) and (U2,V2). I use
  the letters U and V to represent vectors (points) in the texture because
  that's some kind of convention. Then, in the draw phase, we traverse the
  texture from (U1,V1) To (U2,V2) in (MaxX-MinX) steps, getting the colors
  for the texture !!!
    This is easy, yet it can be pretty confusing... I'll present now the code
  for the texture mapping procedure. Just two thing before that.

          1) The Poly array changed to include the texture information for
             each scanline.

                  Var Poly:Array[0..199] Of Record
                                                  MinX,MaxX:Integer;
                                                  U1,V1:Integer;
                                                  U2,V2:Integer;
                                            End;

          2) The texture mapping procedure accepts now another parameter,
             that is a pointer to the texture map, stored in the same way
             as the sprites in my tutorials are (first word is X size, second
             word is Y size, and then the bitmap data).

    So, let's see the texture mapping in phases... First, we set clear the
  Poly array:

         For A:=0 To 199 Do
         Begin
              Poly[A].MinX:=MaxInt;
              Poly[A].MaxX:=-MaxInt;
              Poly[A].U1:=0; Poly[A].V1:=0; Poly[A].U2:=0; Poly[A].V2:=0;
         End;

    We can get a good speedup by coding this part in assembler... It is easy,
  but I'll leave that for you... :)
    Then, we get the size of the texture map... The texture is stored in the
  same way as the images for the sprites tutorial, that is, first word is
  X size, second word is Y size and then comes the bitmap data...

         Segm:=Seg(Texture^);
         Offs:=Ofs(Texture^);
         Move(Mem[Segm:Offs],XSize,2);
         Move(Mem[Segm:Offs+2],YSize,2);
         Offs:=Offs+4;

    We add 4 to the offset so that Segm:Offs point to the start of the bitmap,
  since we won't need the Xsize and Ysize data anymore...
    Then, we set the Y maximmum and minimmum, like we do in the normal poly
  procedure:

         MnY:=Y1;
         MxY:=Y1;
         If MnY>Y2 Then MnY:=Y2;
         If MnY>Y3 Then MnY:=Y3;
         If MnY>Y4 Then MnY:=Y4;
         If MxY<Y2 Then MxY:=Y2;
         If MxY<Y3 Then MxY:=Y3;
         If MxY<Y4 Then MxY:=Y4;
         If MnY<0 Then MnY:=0;
         If MxY>199 Then MxY:=199;

    Then we go the first stage interpolation... The edge interpolation. This
  is done via a procedure which gets the X and Y coordinates of the two points
  of the poly that define the side we want to interpolate... The side is also
  passed as a parameter, so that we know which side are we talking about. Side
  1 is the top side, and the order goes clockwise.
    We must check if Y1 and Y2 are equal, or else we risk a divide by zero
  operation... :(
    The interpolation is always done according the Y axis, that is, the number
  of steps for the interpolation depends on the 'height', that is equal to
  Y2-Y1. Then, we set up the interpolation, according to the side, setting
  up the (U,V) coordinates on texture space, and the texture steps:

             If Side=1 Then
             Begin
                  Us:=0; Vs:=0; Ue:=XSize; Ve:=0;
                  TextIncX:=(XSize Shl 7) Div Height;
                  TextIncY:=0;
             End
             Else
             If Side=2 Then
             Begin
                  Us:=XSize; Vs:=0; Ue:=XSize; Ve:=YSize;
                  TextIncX:=0;
                  TextIncY:=(YSize Shl 7) Div Height;
             End
             Else
             If Side=3 Then
             Begin
                  Us:=XSize; Vs:=YSize; Ue:=0; Ve:=YSize;
                  TextIncX:=(-XSize Shl 7) Div Height;
                  TextIncY:=0;
             End
             Else
             If Side=4 Then
             Begin
                  Us:=0; Vs:=YSize; Ue:=0; Ve:=0;
                  TextIncX:=0;
                  TextIncY:=(-YSize Shl 7) Div Height;
             End;

    Then, we must do two rotines, in case the Y2 is above Y1 or not. They
  are basically equal, with just some diferences concerning some signs and
  starting texture coordinates... You should be able to understand the diference
  if you do a scheme... I'll just explain one of the routines, when Y2 is
  smaller than Y1 (that is Y2 is above Y1)...
    First we calculate the X step (for the edges of the polygon) and then we
  set the starting (U,V) coordinates. Then, we traverse the polygon, updating
  the Poly array:

                  For A:=Y2 To Y1 Do
                  Begin
                       { Calc the minimum and maximum X coordinates in this
                         scanline }
                       If (X Shr 7<Poly[A].MinX) Then
                       Begin
                            Poly[A].MinX:=X Shr 7;
                            Poly[A].U1:=U Shr 7;
                            Poly[A].V1:=V Shr 7;
                       End;
                       If (X Shr 7>Poly[A].MaxX) Then
                       Begin
                            Poly[A].MaxX:=X Shr 7;
                            Poly[A].U2:=U Shr 7;
                            Poly[A].V2:=V Shr 7;
                       End;
                       X:=X+XInc;
                       U:=U+TextIncX;
                       V:=V+TextIncY;
                  End;

    Then, after finding the edges in texture and screen space for the polygon,
  we interpolate the inside of the polygon, interpolating the coordinates in
  texture space... This is another rotine you should convert to assembler,
  since it is easy and should be very fast:

         For A:=MnY to MxY Do
         Begin
              DeltaX:=Poly[A].MaxX-Poly[A].MinX;
              If DeltaX<>0 Then
              Begin
                   TextIncX:=((Poly[A].U2-Poly[A].U1) Shl 7) Div DeltaX;
                   TextIncY:=((Poly[A].V2-Poly[A].V1) Shl 7) Div DeltaX;
                   U:=Poly[A].U1 Shl 7;
                   V:=Poly[A].V1 Shl 7;
                   For B:=Poly[A].MinX To Poly[A].MaxX Do
                   Begin
                        { Get the textel }
                        Temp:=((V Shr 7)*XSize)+(U Shr 7);
                        C:=Mem[Segm:Offs+Temp];
                        PutPixel(B,A,C,Where);
                        U:=U+TextIncX;
                        V:=V+TextIncY;
                   End;
              End;
         End;
    End;

    So, this is it !! As the matter of fact, there are lots of improvements
  you could do, but the best of them all is converting the whole texture
  mapping procedure to assembler, since it is fairly easy to do... Even I can
  do it ! :)
    Well, it is example time everyone !!! :)
    The example is the standart 'rotating cube with textures on the sides'...
  I've used backface removal in this example (a modified way... See the
  mailroom department to understand it) to avoid using face sorting... I use
  two diferent sized textures to show you the potencialities, and five diferent
  textures... The fringes of the object are a bit mangled, don't know why is
  this yet, but I'm sure it is due to errors in the interpolation... I think
  that if you use a more accurate fixed point, you will not get this error.
    The file is TMAP1.PAS (It's 400 lines long!)... Check it out ! :)
  Afine texture mapping is pretty good when it comes to polygons that don't
  vary too much in the Z axis. If they do, and if the poly is too close to the
  camera, the texture will become distorted. This is due to the fact that this
  kind of texture mapping doesn't take in account the geometry of the object,
  it just shrinks and expands an image to fit a polygon on screen. This is
  sorted out by using smaller polys, or polys that are away from the camera,
  or using perspective correct texture mapping. I'll delve into it in next
  issue. For now, I'll just move on to

    4.3. Lambert lighting with textures

    There are several ways to do this. A really good one is the shademaps
  method, that requires some precalculation, but it is very fast and gives
  out pretty good results.
    What are shademaps ? Well, imagine that you had a pixel with colour 32,
  and you wanted to know what colour what it would be if you dimmed that
  pixel to 25% of his intensity. Using shademaps, you just needed to access
  position 32 of an array called something like Shade25, that gives out the
  shaded colours correspondent to some other colour. In our example, it could
  be something like 54. So, if you wanted to dim all pixels with colour 32
  25%, you just needed to replace them with colour 54.
    Now, the 'though' part is calculating the shademap, but even that is
  pretty simple.
    Let's create a new structure, called shademap:

                 Type ShadeMap=Array[0..255] Of Byte;

    Now, let's do a nice procedure:

          Procedure GenTint(Pal:RgbList;Var Map:ShadeMap;R,G,B:Byte);

    So, for example, to create the Shade25 array in the above example, you
  would type in
                 Var Shade25:ShadeMap;
                     Pal:RgbList;
                 ................................
                 GetPalette(Pal);
                 GenTint(Pal,Shade25,25,25,25);

    This would create a shademap where all the colours would be put with it's
  red, green and blue components at 25% of their intensity. In the same way,
  you could call GenTint(Shade2,200,200,200), that would double the intensity
  of the colours.
    But let's do now the real procedure. First we must convert the percentages
  given in the procedure call into factors that really matter:

                         FactR:=R/100;
                         FactG:=G/100;
                         FactB:=B/100;

    Then, for every colour in the palette, we must find out the colour that
  we would get in a true colour display corresponding to the shading indicated:

          RealR:=Round(Pal[A].R*FactR); If RealR>63 Then RealR:=63;
          RealG:=Round(Pal[A].G*FactG); If RealG>63 Then RealG:=63;
          RealB:=Round(Pal[A].B*FactB); If RealB>63 Then RealB:=63;

    We need to make sure that the colour factor does not exceed 63, for obvious
  reasons. We could do that faster with an AND, but speed isn't important,
  because all of this is precalculated, before the game/demo/whatever really
  starts to run.
    Finally, we must find out what colour that exists in the palette is closer
  to that true color:

          MinC:=0; MinD:=MaxInt;
          For C:=0 To 255 Do
          Begin
               { Find distance of colour C to the real colour }
               Dist:=Abs(RealR-Pal[C].R)+
                     Abs(RealG-Pal[C].G)+
                     Abs(RealB-Pal[C].B);
               { If the distance is the smallest so far... }
               If Dist<MinD Then
               Begin
                    { ...then this is the colour we're after so far }
                    MinD:=Dist;
                    MinC:=C;
               End;
          End;
          { Add the colour to the shademap }
          Map[A]:=MinC;
     End;

    This is it... But this can be better done knowing one thing. Colour in
  a computer can be thought of as a cube, with the 3 components (red, green
  and blue) as axis. Keeping this in mind, and knowing a bit of geometry, we
  come to the conclusion that the calculation of the distance done above:

               Dist:=Abs(RealR-Pal[B].R)+
                     Abs(RealG-Pal[B].G)+
                     Abs(RealB-Pal[B].B);

    isn't really the best. That's saying that x+y+z give the distance of an
  point (x,y,z) from the origin. That's not true. The true distance is
  sqrt(x^2+y^2+z^2). So, using the same arguments, we convert it to:

               Dist:=Sqrt(Sqr(RealR-Pal[B].R)+
                          Sqr(RealG-Pal[B].G)+
                          Sqr(RealB-Pal[B].B));

    This gives better results, allthough some colours still look strange, but
  that doesn't matter. There are methods to better this, but this is suitable
  for most applications... The whole procedure looks like this:

      Procedure GenTint(Pal:RgbList;Var Map:ShadeMap;R,G,B:Byte);
      Var FactR,FactG,FactB:Real;
          RealR,RealG,RealB:Byte;
          A,C:Byte;
          MinC:Byte;
          MinD,Dist:Real;
      Begin
           { Determine the real factors }
           FactR:=R/100;
           FactG:=G/100;
           FactB:=B/100;
           { Determine the map }
           For A:=0 To 255 Do
           Begin
                { Find the real colour }
                RealR:=Round(Pal[A].R*FactR); If RealR>63 Then RealR:=63;
                RealG:=Round(Pal[A].G*FactG); If RealG>63 Then RealG:=63;
                RealB:=Round(Pal[A].B*FactB); If RealB>63 Then RealB:=63;
                { Find now the most approximate colour in the palette }
                MinC:=0; MinD:=MaxInt;
                For C:=0 To 255 Do
                Begin
                     { Find distance of colour C to the real colour }
                     Dist:=Sqrt(Sqr(RealR-Pal[C].R)+
                                Sqr(RealG-Pal[C].G)+
                                Sqr(RealB-Pal[C].B));
                     { If the distance is the smallest so far... }
                     If Dist<MinD Then
                     Begin
                          { ...then this is the colour we're after so far }
                          MinD:=Dist;
                          MinC:=C;
                     End;
                End;
                { Add the colour to the shademap }
                Map[A]:=MinC;
           End;
      End;

    This can look a bit complicated at first, but look at the example
  SHADEMAP.PAS. Remember that you can use this procedure to tint a picture.
  For example, to tint a picture red, do something like:

                       GenTint(Pal,RedPal,100,25,25);

    The red component of the pallete wouldn't be altered, but the other two
  components would be dimmed to one forth of their intensity, giving the
  picture a reddish tone...
    Now that this is fixed, it's fairly easy to see how we can combine texture
  mapping with Lambert shading. We just create various shademaps for diferent
  shades (in my example, I use 16 diferent shades, from 10% to 100%), we
  calculate the correct shademap to use for the poly (using the normal and
  light vector) and then use an altered version of the texture mapping
  procedure (just one line is diferent, one of the last). You can see a full
  working example in TMAP2.PAS... The light source moves around the cube, as
  it can be seen in the lower left corner of the screen.
    So, it is easy to implement lighting with texture mapping. Let's now move
  to something that can sometimes improve dramastically the quality of the
  3d graphics:

    4.4. Mip-mapping

    Mipmapping is a method to improve the quality of the texture mapped
  polygon's appearence onscreen... It isn't very used, though, as far as I
  know...
    The ideia is pretty simple to understand and implement. You don't even
  have to change the texture mapping procedure, just the polygon drawing one.
    The ideia of it is that the images (textures) loose quality when they
  are streched and shrunken, so if you can process a picture before it looses
  the quality you gain quality.
    Confused ? Well, see the image TEXTURE.PCX... You you'll see three 'Spell'
  logos, of two diferent sizes. The largest one is the original texture, the
  one below is the same logo when halved in size, and the lowest one is the
  second logo after beeing a bit retouched. It is clear to see that to use
  the third logo instead of the second is an improvement.
    The implementation of mipmapping is simple. In our example, every poly
  has two textures, a near texture, and a far away texture. Then, the polygon
  drawing procedure checks if the poly 'near' or 'far' and tells the computer
  to draw the poly with the near or far texture. In our example, the first
  logo is the near texture (when the poly is closer to the camera it appears
  larger) and the bottom one in the far texture (when the poly is farther from
  the camera it appears smaller). We gain quality by minimizing the distorcion
  of the poly. Check out the example program TMAP3.PAS. In InitCube procedure,
  where the cube is generated, change the 14th and the 15th parameters of the
  GenPoly clauses to see the effects of mipmapping. Change the (1,2) pairs
  by (2,2), for example, and you'll see the dramatic effects mipmapping can
  make.
    Some games (I think Doom, for example) used some sort of mipmapping
  interpolation, but that's a very advanced technique. Basically it creates
  the texture the poly will use, using interpolation of the near and far
  textures, but that's a too complicated matter to look into it now...
    So, let's move on too something ULTRA-SUPER-DUPER-MEGA-USEFULL...

    4.5. Arbitrary-sided polys

    As the title implies, we'll now discuss arbitrary-sided polygons. What are
  they ? Well, they are polys that don't have a fixed number of sides, that is,
  instead of using what we've used so far (4 sided polys), you can have 3,4,5,
  and more sided polys, so stuff like triangles and pentagons become possible.
  There are lots of people that prefer to model their 3d universes in triangles,
  and sometimes four sided polys can't do certain things (like pyramids, for
  instance). There are even more usefull in polygon clipping, a thing you'll
  bump in sooner or later (I'm going into it in the next issue of 'The Mag').
    I'm jumping right into texture mapped, arbitrary sided polys because I'm
  not in the mood of making example code for it. But they are both simple,
  altough it took me several hours to write the example code... Why ? Because
  I screwed up... I made a mistake in the rotation procedure then spent lots
  of hours trying to find out where... The real texture mapping code worked
  the first time I tried it ! :)
    But let's get serious... In four sided polys, every vertice had a
  correspondence to the texture, and then we interpolated. With n sided polys,
  we'll do the same thing, but in four sided polys, we assumed that the
  vertices's (U,V) were set up by the computer has being the corners of the
  texture. With n sided polys we'll have to tell the computer where are the
  (U,V) pairs corresponding to each vertice.
    You'll soon see that it was wise to make the FPoly procedure more modular,
  since you only have to change a bit the code of the Side procedure, and the
  calls to it... Let's get started. We'll need two new data structures:

          Type ArbtrPoly3d=Record
                                 NSides:Byte;
                                 Verts:Array[1..MaxVerts] Of Record
                                                                   X,Y,Z:Real;
                                                                   U,V:Integer;
                                                             End;
                                 Xc,Yc,Zc:Real;
                                 Xn,Yn,Zn:Real;
                                 Texture:Byte;
                           End;

          Type ArbtrPoly2d=Record
                                 NSides:Byte;
                                 Verts:Array[1..MaxVerts] Of Record
                                                                   X,Y:Integer;
                                                                   U,V:Integer;
                                                             End;
                                 Texture:Byte;
                           End;

    The first, ArbtrPoly3d is the structure that stores an arbitrary sided 3d
  polygon, with the normals and the center point, alongside the number and
  coordinates (in real space and texture space) of the vertices and the
  texture number. The second is just used for the usage of the texture mapping
  procedure, and it will be used to store the 2d projected version of a 3d
  poly.
    I used an array to store the vertices, and so I have a constant that
  specifies the maximmum number of vertices (MaxVerts). A good coder (or a less
  lazy one) would implement this as a linked list, to disable limits, and so
  should you, when you decide to optimize the code.
    Now, we must build the poly... The GenPoly procedure we used to use is
  no longer usefull, and it is replaced by two procedures. Building a poly is
  now a two step procedure. First you must set up all the vertices of the
  poly with the Gen3dVert procedure:

          Procedure Gen3dVert(N1,N2:Word;X,Y,Z:Real;
                              U,V:Integer);
          Begin
               Polygons[N1].Verts[N2].X:=X;
               Polygons[N1].Verts[N2].Y:=Y;
               Polygons[N1].Verts[N2].Z:=Z;
               Polygons[N1].Verts[N2].U:=U;
               Polygons[N1].Verts[N2].V:=V;
          End;

    N1 is the number of the poly as in the GenPoly procedure, and N2 is the
  number of the vertice to alter. As you see, besides the 3d coordinates of
  the vertice, you must also supply the coordiantes in texture space.
    After the vertices have been set, you must initialize the poly with the
  Gen3dPoly procedure:

          Procedure Gen3dPoly(N1,N2,T:Word);
          Var X1,X2,X3:Real;
              Y1,Y2,Y3:Real;
              Z1,Z2,Z3:Real;
              Module:Real;
          Begin
               { Set the number of sides }
               Polygons[N1].NSides:=N2;
               { Find the center of the poly }
               Polygons[N1].Xc:=0;
               Polygons[N1].Yc:=0;
               Polygons[N1].Zc:=0;
               For A:=1 To N2 Do
               Begin
                    Polygons[N1].Xc:=Polygons[N1].Xc+Polygons[N1].Verts[A].X;
                    Polygons[N1].Yc:=Polygons[N1].Yc+Polygons[N1].Verts[A].Y;
                    Polygons[N1].Zc:=Polygons[N1].Zc+Polygons[N1].Verts[A].Z;
               End;
               Polygons[N1].Xc:=Polygons[N1].Xc/N2;
               Polygons[N1].Yc:=Polygons[N1].Yc/N2;
               Polygons[N1].Zc:=Polygons[N1].Zc/N2;
               { Calc the normals }
               X1:=Polygons[N1].Verts[1].X;
               X2:=Polygons[N1].Verts[2].X;
               X3:=Polygons[N1].Verts[3].X;
               Y1:=Polygons[N1].Verts[1].Y;
               Y2:=Polygons[N1].Verts[2].Y;
               Y3:=Polygons[N1].Verts[3].Y;
               Z1:=Polygons[N1].Verts[1].Z;
               Z2:=Polygons[N1].Verts[2].Z;
               Z3:=Polygons[N1].Verts[3].Z;
               Polygons[N1].Xn:=(Z2-Z1)*(Y3-Y1)-(Y2-Y1)*(Z3-Z1);
               Polygons[N1].Yn:=(Z2-Z1)*(X3-X1)-(X2-X1)*(Z3-Z1);
               Polygons[N1].Zn:=(Y2-Y1)*(X3-X1)-(X2-X1)*(Y3-Y1);
               { Normalize the normals }
               Module:=Sqrt(Sqr(Polygons[N1].Xn)+Sqr(Polygons[N1].Yn)+
                            Sqr(Polygons[N1].Zn));
               Polygons[N1].Xn:=Polygons[N1].Xn/Module;
               Polygons[N1].Yn:=-Polygons[N1].Yn/Module;
               Polygons[N1].Zn:=Polygons[N1].Zn/Module;
               Polygons[N1].Texture:=T;
          End;

    In this procedure, you must supply the number of vertices, the number of
  the texture to use. The procedure calcs the middle point of the poly, and
  then it calcs the normals. The calculation of the normals is equal to the
  usual, since a normal is calculated taking only in account 3 points, so I
  choose the first 3 points of the poly to be those points. In case you're
  wondering why is a minus signal in the normalization of the Y component of
  the normal, it's pretty simple to understand why... Normally, the Y axis
  runs from down to up, but in computer world, it is the other way around, so
  we must invert it.
    Now, we must change the translation and rotation procedures, since they
  were locked on for 4 sided polys. Check out the example program...
    So, let's now delve into texture mapping procedure. It is almost equal.
  The call is a bit diferent. Now, the TMap procedure only receives the whole
  poly structure. The last version received the texture and all the 4 vertices,
  because it knew the correspondences between a vertice and it's textel.
  The greatest diference is in the calling of the Side procedure, and the
  conversion from a 3d poly to a 2d one:

               { Convert the 3d poly to a 2d one }
               P2.NSides:=P.NSides;
               P2.Tetxure:=P.Texture;
               For A:=1 To P.NSides Do
               Begin
                    Conv3d(P.Verts[A].X,P.Verts[A].Y,P.Verts[A].Z,
                           P2.Verts[A].X,P2.Verts[A].Y);
                    P2.Verts[A].U:=P.Verts[A].U;
                    P2.Verts[A].V:=P.Verts[A].V;
               End;

    Finding out the minimmum and maximmum Y value also changed a bit:

               { Set the maximmum and minimum Y coordinate }
               MnY:=P2.Verts[1].Y;
               MxY:=P2.Verts[1].Y;
               For A:=1 To P2.NSides Do
               Begin
                    If P2.Verts[A].Y>MxY Then MxY:=P2.Verts[A].Y;
                    If P2.Verts[A].Y<MnY Then MnY:=P2.Verts[A].Y;
               End;
               If MnY<0 Then MnY:=0;
               If MxY>199 Then MxY:=199;

    Now, the call to the side procedure is done n times (where n is the number
  of sides). It now includes thr (U,V) coordinates of the start and end points,
  instead of the number of the side we used before to find out the (U,V)'s...
    First it is called the Side procedure for the side that runs from the 1st
  to the 2nd vertice, and then from the 2nd to the 3rd and so on until from
  the (n-1)th to the nth vertice, and finally from the nth to the 1st again.

               { Calc the sides }
               For B:=1 To P2.NSides-1 Do
                 Side(P2.Verts[B].X,P2.Verts[B].Y,
                      P2.Verts[B+1].X,P2.Verts[B+1].Y,
                      P2.Verts[B].U,P2.Verts[B].V,
                      P2.Verts[B+1].U2,P.Verts[B+1].V);
               Side(P2.Verts[B+1].X,P2.Verts[B+1].Y,
                    P2.Verts[1].X,P2.Verts[1].Y,
                    P2.Verts[B+1].U,P2.Verts[B+1].V,
                    P2.Verts[1].U,P2.Verts[1].V);

    Until know the diferences are structural, since the data structure changed,
  The Side procedure also changed little:

              Procedure Side(X1,Y1,X2,Y2,Us,Vs,Ue,Ve:Integer);
              { This routine updates the Poly variable, based on the side it
                is. The sides are numbered clockwise. Side 1 is the topmost
                (when the poly has suffered no rotation }
              Var Temp:Integer;
                  X,XInc:Integer;
                  A:Integer;
              Begin
                   { Set the start and end coordinates of the texture map }
                   If Y1=Y2 Then Exit;
                   Height:=Y2-Y1;
                   TextIncX:=Round(((Ue-Us)/Height)*128);
                   TextIncY:=Round(((Ve-Vs)/Height)*128);
                   ............................................
                   ............................................

    The rest is equal. Basically we took of the if's in the beginning, since
  the (U,V) coordiantes are now specified in the calling of the procedure.
    I use real arithmetics and then I round it to fixed point in the two last
  lines to spare from an if to know if we ought to negate or no the result, as
  we used to do.
    So this is it... See the example program TMAP4.PAS for a good example of
  this code... Don't forget the usefullness of this procedures... It is usefull
  even if you use 4 sided polys, since you can specify the (U,V) pairs. "Why
  the hell do I want that for ?", you are asking. Well, for example, imagine
  you have a face texture, an image of a face. And you want to map it to a
  3d face. Don't forget that a good 3d face is made up of various polys, to
  form the nose and eyebrows, lips, etc, so it is good, for example, to map
  one of the eyes to the eye poly... Use your imagination...
    In the next issue's 3d articles, I'll delve into perspective correct texture
  mapping, Z-buffering, and 3d clipping... See you then...


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  5. Into the Realm of Sound - Part I


    I'm a Spell's friend (I think :|) and I'm in the same course and the same

    [ Of course you are !!! :))) ]

  school Spell is. You may not believe, but we have never seen each other.
    We met on the News, if I recall.
    My 'war name' is Viriato, the name of a lusitan chief warrior, who is
  closely linked to Portugal's history.
    Well, I've heard that Spell would like to have same article(s) about
  programing the Sound Blaster, for his special 'The Mag' issue 12, and I
  decided to write one, an introductory one, but about only the digital
  sound capabilitys of the Sound Blaster (mono).
    I though that it would be nice to include a bit (very basic and informal)
  of theory about digital sound. After all, that's what this is all about.
    Troughout the article I will build a small Sound Blaster Digital Sound
  Unit (SBDSU).
    I will do all my programming in Pascal. I hear you laugh. Don't.
  Pascal is a language understandable by almost everyone. My intention is
  not to make the fastest and smaller driver ever, but only to give some
  basics. If I wanted speed, I would have done it in ASM, but then no one
  would understand a damn thing.
    Well, I'm not an expert so, if you note some 'barbarity' in the text,
  just let me know (if you don't, then I must be a god-like creature,
  :):)). And if you didn't understand something, even after the
  re-re-reading that Spell him self advices, then drop me a note at
  l41324@alfa.ist.utl.pt or nmasj@camoes.rnl.ist.utl.pt. The first email
  address is preferable.
    But lets get started...

    5.1. DIGITAL SOUND?!

    Sound can be considered as a complicated wave, or (maybe more precisely)
  as a mixture of many different simple waves (sinusoids). Then, if sound
  is a wave, we can represent it graphically. We can also use math to
  transform or 'join'(mix) waves. Lets start by drawing (oh my God! the man
  is about to use Ascii art!) a simple wave:
    The T axis represents time. The I axis       I ^
  represents intensity. Time is always time,       |
  but intensity can be air pressure (sound         |    __
  wave, the thing we are interested about),        |  _-  -_
  luminosity (a light wave) or even voltage,       | /      \
  when we use electricity to 'represent' a         |/--------\--------/-->
  wave, like in your hi-fi system. A pure          |          \      /   T
  wave (sound, light or electric) is continuous    |           -_  _-
  each means exactly the same as when you talk     |             --
  about a continuous function in math. The wave    |
  in the figure is a continuous (well, you must
  try to imagine the draw as a continuous one) sine wave. A long time ago,
  some one finded out that we can 'transform' sound in electricity and
  vice-versa, with a microphone and a speaker. The intensity of electricity
  trough a wire can represent a sound, as she varies in time (in the draw,
  it would be measured in Volts, for example). And how can we record a
  sound? For example in a tape (cassette). What a tape does is to record
  the intensity (using a microphone) of the sound in her strip as time goes
  by. Then if we revert the process (read the intensity of sound from the
  strip as time goes by), we can reproduce the recorded sound (using a
  speaker). The problem that arises when we think of using a computer to
  record sound is that sound is a continuous quantity and a computer can
  only keep and treat discrete quantitys. So what can we do? We must
  quantitize sound. Instead of record the In (I will use In instead of I
  for intensity from now on) value at *every* T value (impossible, because
  T is continuous, real valued), we record it only at every Ti seconds. And
  the In value we record will be an approximated In value. For example, we
  would record a In value (this act is what we call 'take a sample'; and the
  act of repeatedly take a sample we call 'sampling') every second, and the
  value would take values in {..., -1, -0.5, 0, 0.5, 1, 1.5, ...}. Here is an
  example of a pure sine wave and its sampled version:

      ^ In                                   ^ In
    3 |                                    3 |
      |       __-__                          |        ===
    2 |     _-     -_                      2 |     ===   ===
      |    /         \                       |
    1 |  /             \                   1 |  ===         ===
      | /               \         T          |                           T
    0 |/-----------------\--------->       0 |==---------------===-------->
      0  1  2  3  4  5  6 \8 10 12           0  1  2  3  4  5  6  8 10 12
   -1 |                    \              -1 |                    ===
      |                      \               |
   -2 |                       -_          -2 |                       ===
      |                         --_          |                          =
   -3 |                                   -3 |

              Pure Sine Wave                       Sampled Sine Wave

    As you can see, some information is lost on the sampled wave. Note that
  the sampled value at T = n is considered the value for time
  T = [n; n + Ti], where Ti is the interval between two reads of In (one
  second, in our case).
    The number of times we take a sample in a second is called the sampling
  rate, or sampling frequency. In the example, if we had a higher sampling
  rate, we would have less information loss. The sampling rate we used was
  1 Hz, or 'one sample/second'. On the other hand, we would have less
  losses if we had used a In 'step' smaller than 0.5, the step we used.
    We can think of each possible In value (sampled value) as an integer
  number, wich we can call sample value. Then we have the table

        In real value  |   In sampled value          (In = intensity)
                       |
           - 3         |          0
          - 2.5        |          1
           - 2         |          2
          - 1.5        |          3
           - 1         |          4
          - 0.5        |          5
            0          |          6
           0.5         |          7
            1          |          8
           1.5         |          9
            2          |         10
           2.5         |         11
            3          |         12

    Since In of our example wave is in [-3; 3], we don't need to go further
  on the sampled value. We used 13 values (0-12), so it can be stored using
  (minimum) 4 bit words. If you have ever heard that CD sound is recorded
  at 44.1 Khz and 16 bits, now you know what that means. Each sample is a
  16 bit word (has 65535 possible values, or intensity possible values) and
  samples are taken 44100 times in a second. "Big" parameters, to minimize
  sampling errors (the loss of information). At 16 bit and 44.1Khz sampled
  sound, sampling errors are too small to be perceptible by us. When
  someone choose these values, he/they just didn't play with a coin. There
  is a theorem, the Nyquist theorem (also so called the sampling theorem)
  that gives us a way to calculate the minimum sampling frequency, and the
  choice of 16bits as to do with a thing called something like signal-noise
  relationship. But this is out of our scope...
    Notice that I used unsigned numbers for the sampled value; I could have
  used signed numbers. There is no special reason for use one or another
  'type' of numbers, in general. Well, in some calculations, one type is
  better than the other, as for volume control: it is easier to do it with
  signed samples, because it is enough to multiply each sample by a
  constant; if the samples are unsigned, it's not that simple. Typicality,
  in the world of PC unsigned numbers are used for digitised sound (in
  fact, it seems that from SB16 up the two types are supported, but
  normally unsigned samples are used). In the world of Amiga, signed
  numbers are used.

    5.2. The Creative Labs Sound Blaster

    A way of using a computer to record sound it would be to have a card that
  would transform electric intensity in a (integer) number. Then we could
  just plug in a microphone and keep reading the value of electric
  intensity from the "card's Input port", storing it in an array. If the
  card could also transform numbers in electric intensitys, we could pick
  up the array values and send them to the "card's Output port",
  reproducing the recorded sound. This is basically what a Sound Blaster
  can do, concerning digital sound, or 'quantitised' sound.
    The entity who converts electric In (intensity) in numbers is called ADC,
  short for Analogic To Digital Converter. Analogic is the name given to
  real valued quantities. The entity who does the reverse thing (numbers ->
  electric In) is called DAC, short for Digital to Analog Converter. These
  two entitys are electronic devices, electronic chips. Actually, we can go
  out to an electronics store and bye such devices.
    An SB has a heart, much like a PC has one (microprocessor), and it is
  called DSP, Digital Signal Processor. A DSP is a processor especiallized
  in treating digital signals. The SB's DSP controls all the operations
  that are made by the card, related to digital sound (and MIDI hardware).
  Lets make a small blocks diagram representing an SB (unless stated
  contrarily, I speak only about the mono SBs, versions 1.x and 2.x;
  however, everything is appliable to all SBs, because they are downwards
  compatible)


                To PC (ISA bus)
          ______________________________
         /                              \        +-----+     / Mic
                                                 | ADC | <- |
     +----------------------+         +-----+ <- +-----+     \ Line In
     | FM synthesizer (OPL) |         | DSP |
     +----------------------+         +-----+ -> +-----+
             |                                   | DAC |
             |                                   +-----+
             |        +-----------------+           |
             +------> | Sound Amplifier | <---------+
                      +-----------------+
                             |  |
                             |  +-----> Speakers
                             +--------> Phones


    As you can see, SBs have two practically distinct parts: the synthesized
  sound part (FM synthesizer) and the digital sound part (DSP/DAC/ADC).
    Here I will talk only about the digital sound part.
  The DSP chip manages all the actions. Anything you want to do, you will
  do giving commands to the DSP (I'm only speaking about digital sound,
  remember that).
    SB is able to sample sound and to reproduce it. We can program it (the
  DSP, more precisely) to give/reproduce one sample whenever we want
  (direct mode), or to give/reproduce a 'sound block', this is, an array of
  samples (DMA mode). That array can be stored/retrieved directly into/from
  the PC's memory, trough DMA. This last mode is much powerfull, because
  the processor can do something else while the SB treats the sound, in
  background. If you don't know much (or nothing) about DMA, don't cry: we
  will talk about it when needed.

  [ And there's an article on DMA in this issue... ]

    The DSP is responsible for the compressed sound (ADPCM) also. ADPCM is
  a compression technique used (optimized) for sound.
    Here is a port address map for the SB (digital part)

      2x6h   DSP - Reset
      2xAh   DSP - Data In (in to PC, DSP -> PC)
      2xCh   DSP - Data Out, commands and WriteReady bit (bit 7)
      2xEh   DSP - Data Available (ReadReady bit (bit 7))

    The 'x' in the addresses depends on the base port address of the SB. The
  base address can be from 210h to 260h. If your SB's base address is 220h
  (very common) then the DSP - Reset port address is 226h.
    Well, now that we know a bit more about SB and SB's internals, we can
  start with real the funny thing: program it.

    5.3. Programming the beast

    The first command you should (must) do is to reset the DSP, so he gets
  ready to receive commands, and to 'clean up' any mess another program
  migth have done.
    The algorithm is as follows:

      1. Write a 1 to port DSP - Reset (2x6h)
      2. Wait at least 3.3 microseconds        (for DSP reaction)
      3. Write a 0 to port DSP - Reset (2x6h)
      4. Wait at least 100 microseconds for DSP data available (2xEh)
      5. Read from DSP Data In (2xAh)

    The value read in step 5. must be AAh. If it's not, then there is no DSP
  and therefore no SB (at the tested base port), or the DSP reset was
  unsuccessful (damage DSP?). We can now make a function that performs a
  DSP reset assuming that the SB is at a given base port, and returns TRUE
  or FALSE accordingly. If there is an SB, but the DSP reset was
  unsuccessful we will assume no SB on the system.

     Function DSPReset (Base_Port:Word):Boolean;
     { Performs a DSP reset, returning TRUE only if successful }
     Begin
          Port[Base_Port+$6]:=1;
          Delay(1);              { 1/1000 second is more than enough }
          Port[Base_Port+$6]:=0;
          Delay(1);              { It's enough time for ReadReady bit in port }
                                 { DSP - DataAvailable (2xEh) go to 1         }
          DSPReset:=(Port[Base_Port+$A]=$AA)
     End;  { DSPReset }

    The intention of step 4 is to wait for the DSP to have data available
  for us to read. We could do it by cycling until bit 7 of port DSP - Data
  Available be 1, indicating available data to be read from the DSP. But if
  there weren't no DSP at the specified port, the wait loop would be for
  ever. So, we just give it more than enough time to the DSP to respond, if
  there are any (minimum: the 100 microseconds stated on step 4.).
    From this function, we can make another one to detect if there is an SB
  present in the system. At the same time, we will detect the SB base port
  and keep it. To keep this information, and future SB information, like
  the base port and DSP version, we will use a global variable record.

     Sb:Record
              Base_Port:Word;           { SB's base port address }
              Irq_Number:Word;          { SB's irq number }
              Dma_Channel:Word;         { SB's DMA channel }
              Dsp_Major_Version:Byte;   { DSP version:             }
              Dsp_Minor_Version:Byte    { 1.x - SB1.x; 2.x - SB2.x }
        End;

    This variable 'is seen' by all the routines in the unit.
    The function to detect if an SB is present in the system and its base
  port is rather simple. All we need to do is to test (try to perform a DSP
  reset) for a DSP on every possible SB's (base) ports (about 6: 210h,220h,
  230h, 240h, 250h and 260h). Lets call this function SBPresent.

     Function SBPresent:Boolean;
     { Returns TRUE only if an SB has been detected and keeps its base port }
     Begin
          Sb.Base_Port:=$210;
          { Look for a DSP in the possible ports }
          While (Sb.Base_Port<$270) And
                Not(DSPReset(Sb.Base_Port)) Do
                Inc(Sb.Base_Port,$10);           { Try next possible port }
          { Return TRUE if Sb.Base_Port in [210h..260h] or FALSE otherwise }
          SBPresent:=(Sb.Base_Port>=$210) And (Sb.Base_Port<=$260);
     End;  { SBPresent }

    Well, we are done with the initialization thing (for now... it's still
  missing the dma and irq, but we will do it later).
    Now, lets talk about giving commands to the DSP.
    We send commands to the DSP trough port DSP - Data Out (2xCh). Commands
  are a one byte 'op code' followed by zero or more arguments, depending on
  the operation. First of all, we must ask "hey DSP, are you ready to obey
  my commands or receive data?". This is necessary because SBs work *much*
  slowly than PC's microprocessors. Algorithm:

      1. Loop until bit 7 of port DSP - Data Out (2xCh) is 0
      2. Send command or data byte to DSP trough port DSP - Data Out (2xCh)

    When we want to read data from the DSP, we will also have to wait, until
  data is available:

      1. Loop until bit 7 of port DSP - Data Available (2xEh) is 1
      2. Read data byte from DSP trough port DSP - Data In (2xAh)

    Lets do two routines to help us with DSP data in and out:

     Procedure DSPWrite(Value:Byte);
     { Writes a value (command or data) into the DSP }
     Begin
          While (Port[Sb.Base_Port+$C] And $80)=0 Do;   { Wait for bit 7 }
            Port[Sb.Base_Port+$C]:=Value
     End;  { DSPWrite }

     Function DSPRead:Byte;
     { Reads a data value from the DSP }
     Begin
          While (Port[Sb.Base_Port+$E] And $80)=0 Do;
          DSPRead := Port[Sb.Base_Port+$A]
     END;  { DSPRead }

  Here is a list of some simple DSP commands with its form of use, for a
  smoooooth start:

       10h   Direct 8 bit DAC:
                 1. Write command 10h        (into DSP)
                 2. Write the 8 bit sample   (into DSP)
       20h   Direct 8 bit ADC:
                 1. Write command 20h        (...)
                 2. Read the 8 bit sample    (from DSP)
       D1h   Enable Speaker:
                 1. Write command D1h
       D3h   Disable Speaker:
                 1. Write command D3h
       D8h   Speaker Status:
                 1. Write command D8h
                 2. Read speaker status (0 - OFF; FFh - ON)
       E1h   DSP Version:
                 1. Write command E1h
                 2. Read major version number
                 3. Read minor version number

    Commands 10h and 20h, Direct 8-bit DAC and Direct 8 bit ADC respectively,
  allows us to do exactly what I said in the beginning of the last part of
  this article. With command 10h we can 'send' an 8 bit sample to the card's
  output (speakers), and with command 20h we can take an 8 bit sample from
  the card's input (mic or line in), thus reproduce or record a sound, if we
  do it repeatedly. The card deals with unsigned samples. In 8 bit samples,
  the central point, intensity 0, is represented by value 07Fh (127). Keep
  this in mind, when you play around with the card. Commands D1h, D3h and D8h
  respectively turn the speaker on, off, and return the speaker status. When
  programming your SB, you will always find the speaker disabled, because a
  reset disables the speaker. So, before you 'send' any sound to the card you
  must turn the speaker ON. Don't do like I did. I almost punched the computer's
  face before I realized the only problem with my routines was that I forgot
  to turn the speaker on. Frustrating... The speaker status command tells
  you if the speaker is on or off. It's a good programing practice to, on the
  way out, leave everything as you have found, so, I advise you to turn the
  speaker off before your program terminates.
    As an example, here is a routine that gets the DSP version and puts it in
  the variable Sb:

     Function GetDSPVersion:Byte;
     { Gets the DSP version of an SB's DSP and keeps it in the variable Sb. }
     { It also returns the major version number (useful for easy SB id).    }
     Begin
          DSPWrite($E1);
          Sb.Dsp_Major_Version:=DSPRead;
          Sb.Dsp_Minor_Version:=DSPRead;
          GetDSPVersion:=Sb.Dsp_Major_Version
     End;

    Sometimes it's important to know each SB model (SB 1.0, SB PRO, ...) we
  have available on the system, because some models do more than others.
  The DSP version of an SB can identify the SB model, as follows:

      DSP Version             SB Model
         1.xx             Sound Blaster 1.x
         2.xx             Sound Blaster 2.0
         3.xx             Sound Blaster Pro
         4.0x             Sound Blaster 16
         4.12+            Sound Blaster AWE32

    Now that you are a bit more familiarized with command sending to the DSP,
  lets do something more interesting: a graphics mode input scope, or
  graphics mode oscilloscope. A scope shows us the waveform of the sound
  being inputted to the SB, trough a microphone or a sound source connected
  to "line In". It is very easy to implement. All we have to do is to
  continuously read a sample from the DSP and plot a graph on the screen.
    The graph will have time on the horizontal axis and intensity on the
  vertical axis. Remember that a sample represents an intensity.
    We choose a number of pixels for the horizontal axis. Then, we take a
  sample and at each pixel's column we calculate the y value according to
  the sample, and put a pixel on the screen at that location. Next we move
  no next pixel's column and re-take a sample, put a pixel at right
  location, and so on. When we reach the last pixel of the horizontal axis
  we go back to the first pixel. It's more easy to see than is to explain:

      For X_Cord:=1 To 100 Do            { I choose a 100 pixels large scope }
      Begin
           { Take a sample }
           DSPWrite($20);                { Direct 8 bit ADC command }
           Sample:=DSPRead;              { Read a sample }
           { Calculate the y coordinate of pixel and plot it }
           Y_Cord:=YRes-Sample;          { YRes is the vertical resolution }
             Putpixel(X_Cord, Y_Cord, Color, VGA)
      End;

    As simple as this. Well, to work fine, you now only have to repeatedly
  execute this FOR and erase the older pixels before you plot the new ones.
    YRes is the vertical resolution. This subtract (YRes - sample) is needed
  because the Y axis grow down on the screen (if you read all other mags,
  you probably know this already). Look at the entire program that comes
  with the mag, SCOPE.PAS.

    Lets move on to another example of what we can do with simple SB
  commands. Lets make a parrot. - Uouuu! Wait a minute! I though this was a
  SB programming paper! - don't get nervous, a parrot is a program that
  repeats everything you say, man! We will use the Direct ADC command to
  record the sound inputting to SB for a while, and then we will use the
  Direct DAC command to playback the recorded sound. If we do it repeatedly
  we have... a parrot.
    First, to record the sound. We will have to take a sample every t
  seconds, (t is much smaller than one second!) so that the sound goes
  uniformly sampled. The samples are kept in a buffer (array). We will use
  the timer to control the timing. After the buffer is filled, we start
  playingback, until the entire buffer is playedback. Then we restart the
  recording. We will have to install a IRQ handler for the timer, which
  will be responsible for taking a sample (when recording), and playingback
  a sample (when playingback). Therefore, the handler will have two
  distinct parts, or, lets say two operating modes, record and playback.
    The variable Playing controls which part of the handler is active in which
  moment.

     Procedure TimerHandler; Interrupt;
     Begin
        { If playing, play samples until will have played the entirebuffer }
        { Else, take a sample, until we have filled the entire buffer.    }
          If Playing Then
          Begin
               DSPWrite($10);                 { Send Direct DAC command  }
               DSPWrite(Buffer[Sam_Num]);     { Send sample to be played }
          End
          Else                     { If it's not playing, it's recording }
          Begin
               DSPWrite($20);                  { Send Direct ADC command }
               Buffer[Sam_Num]:=DSPRead;       { Read (take) a sample }
          End;

        { 'Go to' next sample }
        Inc(Sam_Num);
        { If the end of the buffer is reached, swap the 'mode' and }
        { reset sam_num.                                           }
        If Sam_Num>Buffersize Then
        Begin
             Playing:=Not Playing;
             Sam_Num:=1
        End;

        { If it is the time, update the BIOS clock }
        Dec(BIOSClockCounter);
        If BIOSClockCounter = 0 Then
        Begin
             BIOSClockCounter:=iBIOSClockCounter;
             Inc(BIOSClock)                        { Update the BIOS clock }
        End;

        { Warn the Master PIC about the ending interrupt }
        Port[$20]:=$20
     End;

    At the beginning of the program, we reprogram the timer to 'tick' faster,
  this is, to invoke Int 8 lots of times a second, at a determined
  frequency. The rest of the code is in the example program, PARROT.PAS.
    I just wanto talk a bit about the BIOS clock. The normal int 8 handler is
  called ~18.2 times a second. Among other things, he increments a longint
  variable in the BIOS data area, the clock. The clock is used for many
  things, such as for the files time stamp, to control the time which the
  floppy motor keeps running and, of course, for timing operations. Thus,
  it is important to maintain the clock running at the normal speed,
  whenever we take control of the handler. Imagine we reprogram the timer
  to trigger int 8 at 72 Hz (int 8 is called 72 times a second). From the
  new handler, our handler, we need to update the clock every
  72 / 18.2 = ~4 interrupts. To do it, get a variable counter, and start it
  with 4. Decrement it every int 8. When it reaches 0, update the clock and
  reload the variable with 4. In the TimerHandler procedure above,
  iBIOSClockCounter keeps the 4 and BIOSClockCounter is the variable
  counter. In PARROT.PAS, I choose a default sampling frequency of 10KHz
  ('practical frequencys go from about 5KHz to 44.1KHz; below 5KHz, the
  sound is just too bad and above 44.1KHz the sound won't sound 'even
  better'). The sound quality won't be great, but with Direct DAC/ADC
  commands we can't go much further. The time needed by DSPWrite and
  DSPRead operations limit the frequency. Feel free to make experiments
  with this frequency.
    Well, I could give some more examples of what we can do with this two
  simple SB commands, DirectDAC and DirectADC, but I've write a lot
  already, and I yet have a lot to write, and Spellcaster must be nervous
  by now, thinking of the space area my article will occupy... this mag
  will end up with more lines written by me, hehe...
  Anyway, I think now you have a lot to experiment with... you could try to
  do a program that triggers something whenever a loud sound is made... or
  a program to display a VU (a power measurer)... or you could do a simple
  *.SAM/*.SMP player (SAMs and SMPs are Amiga files, so, the samples are
  signed; therefor, before you play them, you must transform them into
  unsigned samples:

    Sample_Unsigned = Sample_Signed+2^(Number_Bits_Sample-1)

    example for 8bit samples:  Unsigned = Signed+2^(8-1)= Signed+128

    or... whatever you can get out of your creative mind (you are creative
  dudes, aren't you?).
    Remember this paper is an introductory one, so as the examples.
    Everything in the examples can be improved, and believe me, there is a
  lot to improve. And to finish this chapter, I would like to tell you how
  to mix two or more sounds (sampled sounds, of course).
    When you listen someone talking, and a big fuzzy and noisy car passes
  near you, you listen the two sounds, together, mixed. What happens to the
  waves? They are summed, and the resulting wave has the two sounds. Well,
  then it's easy to mix two sampled sounds, you just add one to the other,
  sample by sample! Yes, it's almost right. There's only one detail to take
  in consideration: if you have 8 bit samples, adding two of them may
  result in a sample that doesn't fit in 8 bit, like (255 + 255). The
  solution is then to make an arithmetic medium, like
  (sample1[n] + sample2[n]) div 2 (the samples are in two arrays, sample1
  and sample2). If you have 3 sampled sounds to mix, then

     Mixed_Sample[N] = (Sample1[N]+Sample2[N] + Sample3[N]) Div 3

  and so on. An improvement you can do to this formulae, for S sampled
  sounds is

     Mixed_Sample[N] = (Sample1[N]+...+SampleS[N] + S Div 2) Div S

    Why the term '+ S div 2'? This term will make the result be symmetrically
  round. It is equivalent to summing 0.5. This will result in better sound
  quality, because the calculations are made in a more precise way.
    Note that, as you increase the number of mixed sounds, you decrease the
  volume of each one. There are other mixing technics, some of them better
  than this one.


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  6. To float or not to float

    [ Note: The writer of this article is a C lover, and a Pascal hater...
            Because of that, the article had some C code. I was going to take
            it off, but I've decided to keep it... ]

    6.1. Introduction

    Up until the Pentium processor series,floating point math was a true pain;
  just watch:

	* FPU units (a.k.a. co-pros) were not standard equipment, so coders
	 couldn't count on them.
	* Floating Point Math was waaaaay too slow for a lot of realtime
         applications (the kewl ones at least =).
	* High-Level languages had FPU emulators, but these were even slower
         than using the FPU.
	* The highest percentage of asm coders to commit suicide was trying to
	 code floating point routines with FPU emulation.

    Given the above, it's darn easy to understand the virtues of integer math:

	* Integer Math is available in every CPU. :>
	* It's waaaaaaay much faster than floating point math.
	* Asm coders can now commit suicide for a refreshingly whole universe
	 of reasons. :]

    6.2. Basic Concepts

    So, what the hell is this integer math stuff?
    Well, it's mainly the simulation of floating point operations using only
  integers. The main idea is to "reserve" i bits for the integer part of the
  number and f bits for the fractional part; the integer variable then looks
  like this:

		 b	     b 	 b         b
		 n           i 	 i-1       0
		 |   <-i->   | . |  <-f->  |

    As you could see if I could do ascii :P, the decimal point is fixed so
  the range suported by a certain fixpoint representation is:

     i    f                                                16    16
    2 -1.2 -1, or for the case of a 16.16 representation, 2  -1.2  -1, or in
  plain English, 65535.65535 (assuming unsigned data).

    * Precision:

    One fact you should keep in mind is that fixed-point math is nowhere near
  as precise as floating point; as a matter of fact, 16.16 fixed-point numbers
  have a maximum of 5 digits of precision, or in other words, an error of
  +-5e-5 (that's about +-0.00005). Of course, if your needs of fractional
  precision are more demanding and you don't need to represent big integer
  numbers, you can always allocate more bits for the fractional part; let's
  say you are working with a normalized coordinate system...the range of
  numbers will be [-1..1], which means you will only need 2 bits for the
  integer part: 1 for sign and 1 for the integer part [0,1], which leaves you
  with 30 fractional bits. Now, 2.30 fixed-point numbers have a maximum error
  of +-5e-10 (about +-0.0000000005), which is more than enough;so the key idea
  here is: DON'T STICK TO A FORMAT! Fixed-Point math can pay if you use it
  wisely, so, before getting some source and sticking it into your code, think
  twice. :P
    Try to understand the principles and then adapt it to your specific needs,
  you'll be pleased with the results...
    Now, away with the bullshit and let's start working:

    * Getting Started

    First thing you want to do is to convert between data representations since
  fixed-point data is not widely available anywhere as it's not a native format
  of our beloved CPUs...
    Note that I'm assuming you code for 32-bits...if you're still stuck in the
  16-bit nightmare, keep reading, what i'll say here applies to both, only the
  examples will be 32-bit code...

      * Base data type

        If you're a high-level language coder, you need a base data type to
        hold your precious fixed-point numbers:

        	     In C:

			typedef int FixFloat;
			typedef unsigned FixFloat;

		     In (argh!) Pascal:

        	        Type FixFloat=Word;
			(i'm not sure about this...SpellCaster?-)

                        [ You got it wrong, but I corrected it, as I
                          will do througout this article ]

      * Integer -> Fixed-Point

        The conversion from integer to Fixed-Point format is as easy as
        shifting...you just SHIFT the integer into its proper place, past
        the fractional area:

     		     In C:

			A=[Number]<<[f];

			   In 2.30:      A=[Number]<<30;

	             In (puke!) Pascal:

		        A=[Number] Shl [f];

			   In 2.30:      A=[Number] Shl 30;

      * Floating Point -> Fixed-Point

        The conversion is as easy, only you can't use shifts; instead, you
        figure out how much is 2 to the power of [f], say N, and multiply:

	             In C or (=O) Pascal:

			A=[Number]*N;

			   In 16.16:     A=[Number]*65536.0;

      * Fixed-Point -> Integer/Floating Point

        Basically you just do the inverse operations, either by shifting right
        F bits to get the integer part, or by dividing by N to get a floating
        point value back... :)

    6.3. Fixed-Point Math Theory

    So now you have your cute fixed-point numbers waiting to be used, let's
  do it then:

    * Basic Operations

      * Adding and Subtracting

        These are stupidly simple operations:

			In C/Pascal:

			   C=A+B;

      * Multiplication

          Now comes the fun part... *g*
	Remember your first years of school?
	  They taught us to multiply/divide BY HAND! What are computers for
        anyway? :-)
	  Well, at least that's gonna help you a lot in understanding this crap:

					d.f
				      x d.f
				      -----
				      dd.ff

	  Imagine you're multiplying two fractional numbers; to do so, you
        just ignore the decimal point and multiply them just like integers
        counting the decimal digits afterwards. In fixed-point multiplication
        you do exactly the same way, only that you know exactly how many
        fractional digits you'll have since the decimal point is fixed. :)
          So, to start, you just mul the numbers together; the kind CPU will
        return a 64-bit number(for 32-bit mul) as a result, with 2*f fractional
        digits; so, given that you want a result in d.f format, u just shift
        away the least significant f fractional digits (bits) so as to convert
        dd.ff into d.f; easy no? :-)
	  After you recover from my last explanation, take a look at the code
        below, assuming 2.30 fixed point numbers and of course, 32-bit code:

	             [ 1st number is in EAX, 2nd is in EDX ]

			imul edx 	   ; EDX:EAX=EAX*EDX (64-bit)
		   [R]  add eax, 20000000h ; EDX:EAX+=0.5  (rounding)
		   [R]	adc edx, 0	   ; Add overflow to EDX
			shrd eax, edx, 30  ; get rid of extra frac.
					   ; numbers and get the de-
					   ; cimal part in EAX.

    [ Jesus, you are going to scare off everybody... Well, I'll clear it up
      later, if you don't... :) ]

          The rounding instructions (marked with [R]) aren't necessary, but
      can help with the precision; once again, the choice is yours...
      0x20000000h is 0.5 in 2.30 fixed-point format or 1<<(f-1), 1<<29. The
      adc line just sums the overflow from the add to the high dword of the
      result. The last line just gets rid of the lowest significant fractional
      digits to make room for the integer component. =)

      * Division

	  Given the above, it's easy to understand the division operation, it's
        just the inverse situation:

				dd.ff  | d.f
				       -----
					 d.f

	  Given a 64-bit number and a 32-bit one, it's just a matter of
        actually dividing, yep it's that easy! :)

                     [ 1st numb in EDX, 2nd in EBX ]

                        sub eax, eax     ; EAX=0
                        shrd eax, edx, 2 ; 2 least significant bits (lsb)
                                         ; from EDX go to EAX
                        sar edx, 2       ; loose the 2 lsb keeping
                                         ; the sign information
                        idiv ebx         ; result now in EAX, in 2.30
                                         ; format, courtesy of your
                                         ; friendly CPU. :>

	  Easy or what?
          Now, this might confuse you a bit, so let's try to make it clear since
        it really got me confused when i started learning it... :)
          In the beginning you have a 32-bit number in EDX which will be
        divided by the 32-bitter in EBX; but before you divide you've got to
        get your 1st number in 64-bits, or else you can kiss your precision
        goodbye. To do so, you just have to recall the format given above for
          multiplication results: dd.ff! (Just remember that dd represents *4*
        bits and ff *60*...)
          Now, you have d.f wich means 2 integer digits plus 30 fractional
        digits, and you want to get it into 4 decimals plus 60 fractional, so
        how can we do that?
          Loose 2 digits from EDX into EAX and you have your 4 decimal digits!
        So that's what SHRD does, it copies the 2 least significant fractional
        digits from EDX into the 2 most significant digits of EAX. Now we can
          safely adjust EDX, loosing the least 2 digits wich are now safely kept
        in EAX; SAR does the job, preserving the sign information as a bonus...
          After the above operations, EDX will contain 4 decimal digits + 28
        fractional digits and EAX will have 2 fractional digits + 30 fractional
        zeros...
          Once you have your kewl 64-bitter, just divide, and the number will
        automatically be in 32 bits, 2.30 format and all...Of course the same
        applies to other formats...
	  Note that no rounding is performed, and it usually isn't needed; if
        you're a decadent sort of person, you could implement it, but that's
        unnecessary overhead in a slow operation by nature. It's a good idea
        in multiplication though and the overhead is minimum...

      * Transcendental (wow!) functions and the pizza monster

        By now i hear you cry: "What about functions? I need my sine doses!"
      and others: "Why the f*k did I start reading this sh*t?".
        Calm down, the first group can keep reading; as for the second one,
      there are a lot of free mailbombers on the net, get the best! *g*
        The best way to implement functions is by the use of lookup tables...
      for functions like sine/co-sine/etc. It's as easy as making a choice
      of resolution; how many diferent values will you have? Well, it's up
      to you actually; 256 is a good number, it's not much more than 1 degree
      resolution and it may be well worthy...on the other hand, if you're
      kinky and want to have a full 360 values table, well...go ahead and see
      if I care :)
        Basically, you just have to code a separate proggie to calculate the
      M values for your tables, convert each one to the fixed point format of
      your choice, and output them to a file if you don't need to generate
      it realtime... :)
	For those functions which can't be hardcoded on a lookup table, you
      don't have many choices:

	      * 1. Kick your teacher's head until he gives you a good and
                   inexpensive approximation function.
	      * 2. Derive your own approximation, Taylor Series, whatever...
	      * 3. Ask someone who has done it. Menace to kill his/her pet
                   if it won't let you have it.

        Once that we are at it, there's a really nice article by Kiwidog on
      Imphobia #12 about Fixed-Point Square Roots using lookup tables...
      Read it!

    6.4. Closing Coments

    This is my first damn doc/tutorial/whatever, so.... :}
    Before leaving the few of you that got this far, i have a few last words,
  well, one word actually: OPTIMISE! What i mean by optimisation is not the
  obvious code-it-in-asm talk, just remember that even if you code those basic
  fixed-point operations as inline asm funcs (as you should:), there's always
  a small amount of overhead like for example, in the division by getting the
  number to 64-bits, so if you have a certain formula that gets called often
  enough to justify it, code it inline instead of calling the basic ones
  separately!
    As an example, suppose you have some formula like:

		a * b
		----- (does it look familiar? :>>)
		  c

    if you calc it as Fdiv(Fmul(a,b),c)), you'll get something like:

    /* Assuming a,b,c already in eax,edx and ebx */
    imul edx
    add eax, 20000000h
    adc edx, 0
    shrd eax, edx, 30  ; Redundant, we're converting to 32-bits
    sub eax, eax       ; and will now convert again to 64-bits after loosing
    shrd eax, edx, 2   ; 30 bits of fractional precision, while we could have
    sar edx, 2         ; used the 64-bit result directly from the mul and get
    idiv ebx           ; a more precise result and a speed gain... :O

    while if you coded it as a separate func:

    /* a in eax, b in edx, c in ebx */
    imul edx
    add eax, 20000000h
    adc edx, 0
    idiv ebx

    As you can guess, it gets worse quickly as you increase the number of
  operations... Weird? No Way! Life's that kind of a *itch!
    Now, suppose you're using this example func loads of times inside an inner
  loop...guess what? :>
    The main point is: try to UNDERSTAND the principles, if not from what I
  wrote, from the wealth of other documents on this subject, then use that
  power for your own requirements...it all depends on the needs of your
  application.
    Also, if you're coding a nifty proggie and spent nights optimising it for
  Pentium, DON'T USE FIXED-POINT! It doesn't pay to use it on P5's...use the
  FPU instead. Also, i'm not sure about the speed advantages of fixpoint on the
  486 either, but then i'm too lazy to code some benchmarks and Pentium is
  becoming a standard anyway and... ;)
    I'm sorry if you're disappointed, but there's not much more to say about
  the subject, now it's a matter of getting your hands dirty and code the
  basics, then some tables, then some test proggies, then some often used
  expressions, then... :)

    As we say in my country:

                "If you don't like it, don't eat it!" :P

    After reading this awfull doc, you'll be surely pleased to read some others
  (there's a nice one by Rage Technologies or something...ftp1_rti.zip if i
  recall) on:

	        ftp://ftp.cdrom.com
                         or           (not sure...they're both kewl:)
	        ftp://x2ftp.oulu.fi

    If you still have problems, don't hesitate to mail me:

                      i930339@dei.isep.ipp.pt

    or find me on irc on Undernet #coders as Gh8 or Gate... :)

    Hope i could be of some help to someone; happy coding!

    Spellcaster's back !!! That's, Gh8, I'll take it from here...
    Now it's my turn !! Ahaha !! :)
    Well, you Pascal freaks out there are probably wondering how can you put
  the stuff that was just taught in practice... God knows I was ! :))
    Now, to implement 8.8 fixed point maths, it is easy... You just have to
  use Pascal code... No fancy ASM needed... As the matter of fact, for any
  kind of maths that just need 16 bits can be made to work nicely enough with
  Pascal code without the need to Asm... To prove it, I made a unit called
  Fixed16 that enables to specify the number of the bits for the integer and
  fraccional part (you just specify the fracional part, the unit calcs the
  rest)... The code is in Fixed16.Pas.

    Now, let's try to do a 32 bit fixed point unit... This is trickier, as you
  will see...
    So, first of all, we need to use assembler to implement this stuff... I
  know some of you hate assembler, but I don't know any other way of doing
  this. If you have read the previous issues of 'The Mag', you probably know
  how to use assembler with Pascal (if not, read those issues about it... I
  don't recall which ones are... Find out yourself...). The problem with
  Pascal's inline assembler is that it can't do 32/64 bit intructions !!!
    And that's a serious problem, since we need to handle 64 bit numbers (if
  there was a data structure in Pascal that could handle 64 bit numbers, we
  would use it and wouldn't need assembler... But there isn't...).
    So, using a series of tricks which I describe in article 10 (or something
  like that... The one on ASM tricks...), I could code a similar set of
  operations that I did in the Fixed16.Pas unit... The code is in Fixed32.Pas.
  But remember, don't just copy and paste to your program... The code can be
  slightly improved, and I think it is really a great source of info on
  getting your hands really dirty in Turbo Pascal ! :)
    And this is the end of this article... Spellcaster out ! :)


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  7. A simple introduction to C - Part I

    This article was written by a Finish friend of mine, Matti Junkkarinen,
  and it is dedicated to all you 'traitors' out there ! :) I changed a bit
  the original text, since it was an adaptation of my own tutorials, but it
  is almost the same... So let's start:

    Well, I, Matti Junkkarinen from Finland, translated Spellcaster's MAG to C.
 I'm not good at English (though I have 9 at English and grading here Finland
 goes from 4 to 10), so try to understand my ridiculous sentences...if you
 have anything to ask you can send e-mail to me at address:

                    matti.junkkarinen@mbnet.fi.

   I started this dirty work by ask of Spellcaster...or Diogo...or whatever he
 like to himself be called. I program at Borland C++ 3.0, but I program C-code.
 I hate C++. I can also code in C++, but I just hate it. Well, try to understand
 my translations...my manner to program is little different than others, but it
 is so with any C-programmer. This is not very good introduction...there are
 many good tutorials to C in the Net...seek them if you want to learn...or buy a
 good book.

    7.1. Functions

    Well, now it's time for introduction to C...Let's start with functions...
  If you know Pascal you know there are procedures and functions, but in
  C you have only functions...Or if they don't return any value they will
  still be called functions. In every program you have to have a function
  called main... It's the main function and OS, which run your program will
  run it. Your programs will always start at the start of main and usually
  they end at the end of main. A sample main function is like this...

    void main(void)
    {
    }

    It does nothing...Good start I think :) Commands are places inside { and
  }. Void before main means that that function returns no values...It's
  like a procedure in Pascal. Void between ( and ) means that it won't get
  any arguments. NOTE: C is a case sensitive... So it's not same to write
  void or Void or VOId etc... All keywords in C are in lower case. Well,
  let's write a program what prints legendary Hello world to the screen...

    #include <stdio.h>

    void main(void)
    {
      printf("Hello world! \n");
    }

    So, printf is a function, which is declarated at file called stdio.h.
  You can call any function by this way...

    function_name(argument_1, arg_2, arg_3, ...);

    You have to put ; to end of all C sentences...(exclude function starts
  like main there... And all conditions and loops). "Hello world! \n" is a
  string, which is sent to function printf as it's argument. Anything
  between " and " is a string...that \n means line changing.

    7.2. Including files and comments

    You can include files...Like stdio.h or test.c like this way...

    #include <name>    - if file is at include-directory
    #include "name"    - if file is somewhere else

    An example...

    #include <stdlib.h>
    #include "test.c"   - This is at the same directory
    #include "e:\c\code\misc\test\mystic.c"

    Hey, comment-characters are in C /* and */...everything between them is
  a comment...If you use C++ compiler you can use // comment too, which
  declares only one line to a comment... Well, here's an example about
  functions, their declarations and calls...

    #include <stdio.h>       /* Including stdio.h...it contains declaration */
                             /* for printf... */

    void Test(void);         /* Declarations of Test and Test2 functions. */
    void Test2(void);        /* You have to put ; to end of the.. */

    void main(void)
    {
      printf("This is main\n");  /* Printing a message */
      Test();                    /* Calls function Test...no arguments  */
      Test2();                   /* Calls function Test2...no arguments */
      printf("Main again...program will end soon...\n");
    }

    void Test(void)              /* Start of Test...don't put ; here! */
    {
      printf("This is Test\n");  /* Prints a message... */
    }                            /* Test ends here...running goes back to
                                    the program which called it...in this
                                    case it's main...so it'll run the next
                                    command of main below call of the Test */
    void Test2(void)
    {
      printf("This is Test2\n");
    }

    7.3. Datatypes

    Well, here are datatypes of C...

    Type                 Description
    ---------------------------------------------------------------------------
    char                 A character...it can be signed or unsigned (that
                         belongs settings of compiler). It's like a one
                         character... It's size is one byte.
    signed char          This is a signed character...it can have a number
                         between -128 to 127. It's size is one byte.
    unsigned char        Unsigned charaster...it can have a number from 0 to
                         255. Size if one byte.
    int/short/           Signed integer. This variable type can hold numbers
    signed int           between -32768 and 32767. It's size is two bytes.
    unsigned int/        Unsigned integer. Range is from 0 to 65536. It's size
    unsigned             is two bytes.
    long/long int        Signed long number. It can contain numbers between
                         -2147483648 and 2147483647. It's size is four bytes.
    unsigned long/       Unsigned long integer. It can hold number between
    unsigned long int    0 and 4294967296. It's size is four bytes.

    There are still floats and doubles, but I'll descripe them later.

    7.4. Defining of the variables

    You can define variables like this...

    int an_integer;
    char very_long_name_for_one_character;

    Just put datatype first (you can make also your own datatypes, but about
  them later...), then name of the variable and then ;. You can declare
  variables also like this...

    int var, var2, var3, var4, var5, var6;

    That declares 6 variables, which are all type of signed int. You can put
  values to variables when you declare them...like this...

    unsigned long test_variable = 2436437, test_variable2 = 23712868;
    char a = 25;

    And in program you can put values to variables like this...

    int a;

    a = -2345;

    Variables can be defined anywhere in the program, contrary to highly
  tipified languages like Pascal, that have a data definition area... You can
  define a variable when you are about to use it, like this

            int a=function_that_returns_an_integer();

    7.5. Calculations

    You can make calculations between variables...

    int a, b, c;

    a = 10;
    b = - 200;
    c = 40;

    a = b + c;
    b = a;
    c = a * (a - b);
    b = c / (b * a + c);
    a = c + 24;
    b = 20 * a - b / 2;

    You can use +, -, * and /  to add, substract, multiply and division your
  variables and numbers.

    7.6. Arrays

    You can define an array of any datatype like this..

    data_type array_name[size_off_array];

    ie.
    int array[200];
    unsigned char array2[10];
    long array3[4];

    You can put values to your array at same time you declare it...

    char a[5] = {26, 24, 12, 34, -2};

    ...put numbers in { and } and separate them with ,. Here's how you can use
  your arrays...

    unsigned int mystique[4];
    int a;

    mystique[0] = 2324;
    a = 2 * mystique[0];
    mystique[1] = a + 32;
    mystique[3] = mystique[1] - mystique[0];

    Just remember that, the first number of array is 0...not 1...so the last
  number is the size of array - 1. You can define text strings like this...

    char str[10] = {'H', 'e', 'l', 'l', 'o', '!'};

    NOTE: By character between  ' and ' you can get it's ASCII value.

    char t = 'A';        /* Now t is 65 */

    You can also define....hmmm...I don't know that name...

    int array[10][10];

    That will define ten arrays size of ten. You can access them as the same
  way like normal arrays...

    array[2][4] = 2376;

    You can also use variables as indexes to arrays...

    int a;
    char array[10];

    a = 4;
    array[a] = 22;   /* And now the fourth member of that array will be 22 */

    Well, I'll write a little about printf and scanf...So lets move on....
  You can print your variables and text with printf....

    7.7. A function called printf

    int a;
    unsigned b;
    char str[10] = {'H', 'e', 'l', 'l', 'o', '!'};

    a = 30;
    b = 60000;
    printf("%d\n", a);
    printf("%u\n", b);
    printf("Value of variable a is %d and b is %u\n", a, b);
    printf("String is: %s\n", str);    /* Put here just str..not str[10]....

    You have to place %? between " and " and then place your variables at same
  order which they was inside of " and ". %u and %d are printf format
  specifiers. There are some of them....

   %d and %i = signed integer
   %u = unsigned integer
   %c = character
   %l = long
   %s = string

    7.8. Scanf

    Well, that's about printf...now I'll show you scanf. You can get values to
  your variables by using scanf...

    int t;

    printf("Type a number: ");
    scanf("%d", &t);

    As you can see there's & before the variable name...That means you send to
  function a pointer to variable... Not copy of variable as it will be done
  if you use only t... Then scanf can't change value of t... It just changes
  value of it's copy and your original variable won't change. So you have to
  put that & there. Format specifiers are same as they are in printf. You
  can get more than one value at the same time by doing like this...

    int t, t2, t3;

    printf("Please type three numbers separated by space: ");
    scanf("%d %d %d", &t, &t2, &t3);

    If you want to get a string...

    char str[20];

    printf("Write something: ");
    scanf("%s", str);

    Notice that now you don't place that &. That's because only str (not str[])
  is a pointer to the first member of that array. Or you can do it like this
  way, but it's exactly same...

    scanf("%s", &str[20]);

    If you want to define how long string will be what will be get...

    scanf("%20s", str);

  ...just put number between % and s. Well, there are pointers and memory
  allocation left, if I explain same things in C that Spellcaster explained
  in Pascal. And I think I have to do so, because otherwise at the next MAG
  there could be something that uses them and then I have to explain them
  then...I'll explain them now, so I don't have to care about them later...

    7.9. Pointers

    I just can explain them in English...I'm not good enough. Well in Finnish
  I could, but... PLEASE WRITE THIS START AGAIN, SPELLCASTER... I'll just
  show how to define and use them....
    You can define a pointer like this...

    char *a;

    That * means that a is a pointer. Like this you can point something by
  using a pointer...


    char *p, t;

    p = &t;

    Now variable p points to t. Remember & means variables address...so p
  contains address of variable t. You can change pointed data...

    char *p, t;

    p = &t;
    *p = 20;

    Now t = 20 and p still points to t. You have to put * before p...That means
  it changes pointed data value. I won't now explain pointers of pointers or
  pointers of pointers of pointers...You can guess why :)

    7.10. Allocating and freeing memory

    You can't define too big arrays, like Spellcaster wrote above at end of
  his introduction to Pascal. There are several functions to allocate memory
  in standard libraries but now I'll explain only malloc. Include file
  stdlib.h, because malloc and free are defined there. Here's a small program
  that should explain how to alloc, use and free memory....

    #include <stdlib.h>        /* Including stdlib.h and stdio.h */
    #include <stdio.h>

    void main(void)
    {
     char *pointer;             /* defining a pointer */

     pointer = malloc(64000);   /* allocating 64000 bytes memory and pointer
                                   points at start of it */

     if (pointer == NULL)       /* if pointer is NULL (0) then allocating */
        exit(1);                /* failured and program ends...I haven't told */
                                /* anything about conditions yet so don't */
                                /* amaze...I'll explain them at Part 2 like */
                                /* Spellcaster */

     printf("Memory allocation succeed!");

     pointer[0] = 23;           /* Setting first byte of allocated memory to */
     pointer[1] = 44;           /* 23 and second to 44 */

     free(pointer);             /* Freeing memory...don't forget to do this! */
  }

    That was it...I commented it so you can understand it much better...well,
  now I have writed this part of introduction to C...

    7.11. Conditions

    Conditions in C are similar to Pascal. They don't require the THEN clause
  and are a bit more flexible. For example, to find if a number is larger than
  another, you do this:

      int a = 10;
      int b = 15;

      if (a > b) printf("A is biggest");

    The sintax for conditions in C is as follows:

       if (condition)
       {
         command
       }

    NOTE: If you have only one command to run if condition is true then you
          can leave { and } off...

    The condition is anything that returns a logic result (TRUE or FALSE).
  While in Pascal there is a type BOOLEAN that is TRUE or FALSE, in C there is
  no such thing. Any numeric type can be used in a condition. If that number is
  0, that's the same as being false. If it is other number, then it is true.
    Example:

          int a;

          a = 10;
          if (a) printf("This gets printed\n");
          a = 0;
          if (a) printf("This never gets printed\n");

    The available operators are as follows:

    Ŀ
     Operator   Desciption    Example                                   
    Ĵ
        ==       Equal to     A==B : Returns TRUE if A equals B         
                                                                        
        !=     Not Equal to   A!=B : Returns TRUE if A is diferent to B 
                                                                        
        <       Less than     A<B : Returns TRUE if A is less than B    
                                                                        
        >      Greater than   A>B : Returns TRUE if A is greater than B 
                                                                        
        <=     Less than or   A<=B : Returns TRUE if A is less or equal 
                 Equal to            to B                               
                                                                        
        >=     Greater than   A>=B : returns TRUE is A is greater or    
                or Equal to          equal to B                         
    

    Let's see a complete example:

          #include <stdio.h>

          void main(void)
          {
           long a, b;

           printf("Write number 1:");
           scanf("%d", &a);
           printf("Write number 2:");
           scanf("%d", &b);
           if (a == b)
             printf("The numbers are equal");
           if (a < b)
             printf("a is smaller than b");
           if (a > b)
             printf("a is bigger than b");
           getch();
          }

    getch() is a function that returns a character that is pressed. It is very
  common to be used to make a pause in the code, since it waits for a key to
  be pressed if there aren't any in the buffer.

    In C, contrary to Pascal, you can't compare strings like variables...you
  can compare them by functions...I remember that one is strcmp, because I
  use my own string functions...I'm not sure about strcmp's syntax, but I
  think it goes like this...

    int strcmp(char *str1, char *str2);

    So that strcmp will return a value...if strings are same it'll return 0.
  If first is bigger then second one it will return > 0. And if second
  string is biggert than first string it will return < 0. So, you can use
  it like this...

    char string1[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    char string2[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '5'};
    int cond;

    cond = strcmp(string1, string2);
    if (cond == 0)
      printf("Strings are equal.");
    if (cond > 0)
      printf("First string is bigger.");
    if (cond < 0)
      printf("Second string is bigger.");

    Hey, the prototype of strcmp is in file string.h...so, you have to include
  it at the start of program....like this...

    #include <string.h>

    In C { is like Begin and } is like End in Pascal, so you aren't limited to
  just one command per if.

    7.12. ASCII in C

    While in Pascal you had to make something like A=Ord('A') to obtain the
  ASCII code of A, in C it is easier. You just do:

         A='A' -> Gives you the ASCII code of the character A.

    In C you don't need to have a function like that ORD, beause in C char is
  exactly same as byte...I didn't know before they are separated in Pascal.
  Well, if you want to print variables ASCII code or variable itself you
  just choose which you want to print... Maybe this example helps...

       #include <stdio.h>

       void main(void)
       {
        char a;

        a = 'a';
        printf("The ASCII code of %c is %d", a, a);  /* You choose character or
                                                        ASCII code to print by
                                                        selecting %d or %c. */
        a = 66;
        printf("The char with ASCII code of %d is %c", a, a);
        /* You can see that it's much easier in C... */
       }

    7.13. Assembly and Pascal

    To use Assembly in C (in Borland C++....other compilers have usually
  different ways...look them for help or books...) ...

    printf("Anything...");
    .....
    ....
    asm {
         mov ax, 10h
         mov bx, 15h
         shr ax
        }
    .....
    .....

    The assembly part is equal to Pascal, or any other compiler (with the
  exception of the compilers that use the AT&T syntax, which I ain't gonna
  discuss here).
    Now, to end this tutorial, I'll delve into graphics. C suffers
  (or suffered) from the same problem as Pascal when it came to graphics. BGI's
  SUCK ! So, we use a bit of assembler and other stuff to get more speed and
  flexibility. The theory behind this is explained in issue 2. Here you are
  only going to find the direct translations of the procedures in that issue's
  graphic tutorial:

    7.14. Graphics

    void Initgraph(void)
    {
     asm {
          mov ah, 0
          mov al, 13h
          int 10h
         }
    }

    void Closegraph(void)
    {
     asm {
          mov ah, 0
          mov al, 03h
          int 10h
         }
    }

    void BIOSPutpixel(unsigned x, unsigned y, unsigned char col)
    // This is very slow
    {
     asm {
          mov ah, 0ch
          mov al, ss:col /* You can use too mov al, [col], but variables are in
                        stack...thata ss:? is better I think, but you can use
                        which one you want... */
          mov cx, ss:x
          mov dx, ss:y
          mov bx, 1
          int 10h
         }
    }

    While in Pascal we could access any memory position by using the MEM array,
  in C it isn't that easy. We must use the poke and pokeb functions:

                poke(segment, offset, value);
                pokeb(segment, offset, value);

    poke puts a word VALUE in the memory position SEGMENT:OFFSET, while pokeb
  puts only a byte.
    So, to put down a pixel, ultra fast:

        void Putpixel(unsigned x, unsigned y, unsigned char col)
        {
         pokeb(0xa000, 320 * y + x, col);
        }

    Well, if I haven't tell it to you...In C you can use hexadecimal numbers by
  putting 0x before it... Like this...

             Assembly             Pascal            C
             -------------------------------------------------
             10h                  $10               0x10
             324837h              $324837           0x324837
             200h                 $200              0x200

    It's very simple... In inline assembler you can use ?h numbers too....

    Now, let's see a full example. This was lots to translate, but this is
  just nothing when you look next MAGs... I won't comment this...OK, I will
  comment hardest parts like Spellcaster wrote to me...

  #include <stdio.h>
  #include <stdlib.h>     /* This is included because this program uses random
                             command and it's prototype is in stdlib.h */

  #define VGA = 0xa000    /* Well, you can define constants like this in C...
                             I think I haven't tell it to you yet... just but
                             that #define first and then constant's name and
                             then = and then value of the constant...don't put
                             that ; after #defines.... */
  void Initgraph(void);   /* These are prototypes of those functions...if you */
  void Closegraph(void);  /* don't put then before main you have to protype */
  void BIOSputpixel(unsigned x, unsigned y, unsigned char col); /* like this.*/
  void MEMputpixel(unsigned x, unsigned y, unsigned char col);
  void Cls(unsigned char col);

  void main(void)
  {
   unsigned a, b;

   printf("This program tests two putpixels.\n");
   printf("First it test the BIOS put pixel, then it tests MEM put pixel.\n");
   printf("It was written by Spellcaster and translated to C by Mazar.\n");
   printf("Press return to start");
   getch();

   Initgraph();
   for (a = 0; a <= 199; a ++)
      for (b = 0; <= 319; b ++)
         BIOSputpixel(b, a, random(256);
   getch();
   Cls(0);
   for (a = 0; a <= 199; a ++)
      for (b = 0; <= 319; b ++)
         MEMputpixel(b, a, random(256);
   getch();
   Closegraph();
   printf("That's all folks...");
  }

  void Initgraph(void)
  {
   asm {
      mov ah, 0
      mov al, 13h
      int 10h
    }
  }

  void Closegraph(void)
  {
   asm {
      mov ah, 0
      mov al, 03h
      int 10h
    }
  }

  void BIOSPutpixel(unsigned x, unsigned y, unsigned char col)
  {
   asm {
      mov ah, 0ch
      mov al, ss:col /* You can use too mov al, [col], but variables are in
                        stack...thata ss:? is better I think, but you can use
                        which one you want... */
      mov cx, ss:x
      mov dx, ss:y
      mov bx, 1
      int 10h
    }
  }

  void MEMPutpixel(unsigned x, unsigned y, unsigned char col)
  {
   pokeb(0xa000, 320 * y + x, col);
  }

  void Cls(unsigned char col)
  {
   /* Well, I don't know is there any function to set mem in C...I can't re-
      member only one...I use my own functions...I'll write a function in
      Assembly for you...maybe in future MAGs you will know how it works... */

   asm {
      mov al, ss:col
      mov ah, al
      mov cx, 32000
      xor di, di
      mov es, 0xa000
      rep stosw
    }
   /* That function is fast....I think it's so fast it can be....or it can
      be speeded up by using 32-bit registers, but not at this time... */
  }

    So, this is the end of this introduction to C... Hope you enjoyed it. I
  will try to get Matti to write some more for future issues... Now, let's get
  back to a real language :) TURBO PASCAL ! :)


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  8. Sprites Part V - Colision detection

    I think I don't need to explain what collision detection, but to avoid
  confusions, I better do... Collision detection occurs to check if two
  sprites are colliding with each other... Simple, isn't it ?
    There are two important types of collision detection: box and pixel
  perfect. A good program should use a combination of the two to achieve a
  good and fast collision detector...

    8.1. Box collision

    Box collision detection is a type of collision that for some objects it
  gives excelent results, while with others terrible results ! :)
    For the sake of simplicity and explanation, imagine that the sprites we
  are trying to detect if they are colliding are of rectangle-like shape,
  like a tank or something like that...
    I arrived to the conclusions I'm going to explain by grabbing a pen and
  a sheet of paper and thinking for two minutes... Ah, notice that I've
  changed the Sprite structure a bit, to include the X and Y size, since they
  come in handy in this, and it is more pratical to have it in a variable
  than searching the memory for the correct image and getting the size from
  there...
    Now, what are we going to check out ? Well, we're are going to check out
  if the rectangles that are each of the sprites overlap ! That's simple to
  do it... I divided the checking in two parts: the X overlapping, and the Y
  overlapping. To keep this short:

                Ŀ        Ŀ      Ŀ           Ŀ
                                Ŀ       
                   Ŀ      Ŀ                
                         
                  A         B          C           D

    In situation A there is no overlapping, in B there is X overlapping and
  no Y overlapping, in C there is Y overlapping, and X overlapping, and in
  D there is X and Y overlapping. Only in situation D the objects are colliding,
  so we come to the conclusion that there must be X and Y overlapping to have
  a collision.
    So, how do we check for X and Y overlapping ? They are equal (you just
  need to change the X's for Y's).
    The way I do it is by checking the displacement of one sprite, relatively
  to the other. So
                            Delta:=2.X-1.X

    Where 2.X and 1.X represent the X coordinates of sprite number 2 and number
  1, respectively.
    Now, if Delta is larger than 0, it means that sprite 2 is to the left
  of sprite 1, and if that Delta is smaller than the size of sprite 1, there
  is X overlapping. On the other hand, if Delta is smaller than 0, it means
  that sprite 2 is to the right of sprite 1, and if the module (the absolute
  value) of Delta is smaller than the size of sprite 2, there is overlapping
  in X... If this sounds confusing, draw simple schemes and you'll see that
  it is easy ! :)
    The code of the collision detection will look something like this:

             Function Col(Spr1,Spr2:Sprite):Boolean;
             Var XCol,YCol:Boolean;
                 Delta,CompSize:Integer;
             Begin
                  Delta:=Spr2.X-Spr1.X;
                  If Delta<0 Then
                    If Abs(Delta)<Spr2.XSize Then XCol:=True Else XCol:=False
                  Else
                    If Delta<Spr1.XSize Then XCol:=True Else XCol:=False;
                  Delta:=Spr2.Y-Spr1.Y;
                  If Delta<0 Then
                    If Abs(Delta)<Spr2.YSize Then YCol:=True Else YCol:=False
                  Else
                    If Delta<Spr1.YSize Then YCol:=True Else YCol:=False;
                  Col:=XCol And YCol;
             End;

    And we have box collision detection...

    8.2. Pixel perfect colision

    Now, if the objects aren't rectangles, this won't look very nice... For
  example, check example Col1.Pas... Notice that the ship starts to explode
  before the asteroid. Why ? Because the box collision detection checks if
  the squares of the sprites are overlapping, not if there are actual pixels
  there ! So, what does this mean ? It means that we must also check that
  in the place that the colision occured there are 'collidable pixels', that
  is, if there is anything there to colide with... This piece of code is
  more complicated and slower to execute, so to save some time, you should
  first perform box collision, and only after you see that there is a collision
  you start to analyse the place where it took place...
    Now, the first step is to check out what is the intersection area. By
  intersection area I don't refer to the screen area, but the sprite area.
  What I mean by this is like this: if you have a screen and the following
  collision:

    Sprite1: ABCD
             BCDA           Screen:
             CDAB           (X is the intersection):
             DABC
                                            ABCD
    Sprite2: FGHI                           BCDA
             IFGH                           CDXXHI
             HIFG                           DAXXGH
             GHIF                             HIFG
                                              GHIF

    The intersection area on the sprites is the X's on the pictures:

                  Sprite 1: ABCD    Sprite2: XXHI
                            BCDA             XXGH
                            CDXX             HIFG
                            DAXX             GHIF

    Get it ? First we have to determine the relative coordinates of the
  sprite collision. We do that by aplying the following formulas... These
  formulas were obtained by lots of paperwork... :) Just pure observation
  of various collision situations. I'll write only the X part, the Y part is
  obtained by exchanging every X for a Y...


               Col1=(X2-X1)    <- This gives the relative X coordinate for
                                  sprite 1
               Col2=(X1-X2)    <- This gives the relative X coordinate for
                                  sprite 2

    Is it pretty obvious to see, one of these coordinates will be 0. So, to
  optimize this a bit, we convert that coordinate to a 0 ! Why ? The negative
  coordinate informs us that that part of the intersection area is out of
  the scope of that sprite, so we don't need to check it out !
    For the Y it's the same thing...
    Now, we have the coordinates (in relative means) of the intersection area,
  so we must find out the size of the intersection, to have the area. The size
  is equal for the two sprites and the way to calc it varies with the position
  of the sprites (relative to each other). We can make use of the sign of
  Col1 and Col2 that we've got from above... For example if Col1 is negative,
  that sprite 1 is to the right of sprite 2. In that case, the size in X
  coordinates will be:
                          DeltaX=(X2+XSize2)-X1
    Otherwise it will be:
                          DeltaX=(X1+XSize1)-X2

    Aftr we've got this, we must add a check to see if the deltas are bigger
  than the sprite... This accounts for cases of collision like this:

              Ŀ
       Ŀ   
          
              

  ...when one of the sprites is 'inside' the other... This is why we calc in
  the beggining the size of the smallest sprite.
    After we have the intersection area, we just need to check it out to see
  if there are any 'collidable' pixels in that region. By collidable I mean
  visible (that is, not equal to 0)... This could be any other thing, and you
  could create a collision mask, that is an image associated to the image of
  the sprite that tells the computer what regions are collidable and what
  regions aren't... So, the complete code should be:

            Function PPCol(Spr1,Spr2:Sprite):Boolean;
            Var DeltaX,DeltaY:Integer;
                Sx1,Sx2,Sy1,Sy2:Integer;
                Seg1,Seg2,Ofs1,Ofs2:Word;
                X,Y:Integer;
                C1,C2:Byte;
                MinY,MinX:Integer;
            Begin
                 If (Spr1.XSize<Spr2.XSize) Then MinX:=Spr1.XSize
                                            Else MinX:=Spr2.XSize;
                 If (Spr1.YSize<Spr2.YSize) Then MinY:=Spr1.YSize
                                            Else MinY:=Spr2.YSize;
                 { Get intersect area }
                 Sx1:=Spr2.X-Spr1.X;
                 Sx2:=Spr1.X-Spr2.X;
                 If Sx1<0 Then
                 Begin
                      DeltaX:=Spr2.X+Spr2.XSize-Spr1.X;
                      If DeltaX>MinX Then DeltaX:=MinX;
                      Sx1:=0;
                 End
                 Else
                 Begin
                      DeltaX:=Spr1.X+Spr1.XSize-Spr2.X;
                      If DeltaX>MinX Then DeltaX:=MinX;
                      Sx2:=0;
                 End;
                 Sy1:=Spr2.Y-Spr1.Y;
                 Sy2:=Spr1.Y-Spr2.Y;
                 If Sy1<0 Then
                 Begin
                      DeltaY:=Spr2.Y+Spr2.YSize-Spr1.Y;
                      If DeltaY>MinY Then DeltaY:=MinY;
                      Sy1:=0;
                 End
                 Else
                 Begin
                      DeltaY:=Spr1.Y+Spr1.YSize-Spr2.Y;
                      If DeltaY>MinY Then DeltaY:=MinY;
                      Sy2:=0;
                 End;
                 { Check the intersection }
                 Seg1:=Seg(Spr1.Img^); Ofs1:=Ofs(Spr1.Img^)+4;
                 Seg2:=Seg(Spr2.Img^); Ofs2:=Ofs(Spr2.Img^)+4;
                 For X:=0 To DeltaX-1 Do
                 Begin
                      For Y:=0 To DeltaY-1 Do
                      Begin
                           C1:=Mem[Seg1:(Ofs1+Spr1.XSize*(Y+Sy1)+(Sx1+X))];
                           C2:=Mem[Seg2:(Ofs2+Spr2.XSize*(Y+Sy2)+(Sx2+X))];
                           If (C1<>0) And (C2<>0) Then Break;
                      End;
                      If (C1<>0) And (C2<>0) Then Break;
                 End;
                 If (C1<>0) And (C2<>0) Then PPCol:=True Else PPCol:=False;
            End;

    A good optimization of this code can be made if we think that every
  checking of a line is linear, so instead of doing a mult by pixel, you do
  a mult by line ! Check the example code Col2.Pas to see this implemented...
    Well, this is what I remembered to teach you about collision detection.
  Not too complicated, but this is something that should be optimized to the
  max, since it will be routines that should be used a lot in an action game,
  for example...
    On next issue, if there aren't any requests on sprite related subjects,
  I'll start to make a games using the stuff we learned here...
    Cya...


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  9. Graphics Part XI - Introduction to unchained modes

    9.1. What ?!

    What is ModeX ? Well, to explain this, I'll start with why did it appear...
  Have you ever wondered that even VERY old videocards had 256Kb of memory,
  and that a normal mode 13h screen only uses 64Kb ? So, where are the other
  192Kb ? Well, trapped inside the limitations of segmentation... Remember
  when I told you that the limit for a segment was 64 Kb ? Well, this is why
  you can't normally use the 256Kb memory... But, don't despair... There are
  ways around this... One of them is to use VESA, which I'm not an expert, but
  someday I'll delve into... The other is to use unchained modes.
    What are unchained modes ? Well, they are one way to use the whole 256Kb
  memory of a video card... But it has it's drawbacks... You loose linearity !
  You no longer can use those nice linear putpixels... :( But that's a small
  price you pay for the versatility of unchained modes.
    With unchained modes, you can have a page with 640x400x256, or four pages
  with 320x200x256, and you can switch from one to another in a fraction of
  the time it would take you to copy a virtual screen to the physical screen.
    There are all sorts of weird modes you can enter, like 320x600x256, and
  with unchained modes, you can do all sorts of cool effects (for instance,
  my demo works in unchained mode, to achieve the transparency effect).
    In this series of tutorials, we will study several modes that are more
  used. In this article, we will go into 320x200x256 unchained, with four
  pages. This mode is also known as the Chain-4 mode.
    This tutorial will first tell you what we want to achieve and how we do
  it, then I'll explain...

    9.2. Planes

    No, no, I ain't gonna talk about those marvels of modern engineering that
  fly around... Well, from the above explanation, you might be wondering how
  we are going to access 256Kb of memory, since a segment can only have 64Kb.
    The answer is planes...
    In unchained modes, we will still do "Mem[$A000:Pos]:=C" to set a pixel.
  But now Pos will have a diferent meaning... It will address 4 pixels, instead
  of one !! Confused ? Don't be... It's simple... For example Mem[$A000:0000] is
  the position for points (0,0),(1,0),(2,0) and (3,0). But this doesn't mean
  that we can only draw 4 pixels at a time (although we can... More on this
  later)... To set individual pixels, we have to set a special register to
  tell which point are we talking about, or more accuratly, what plane are we
  talking about. In the above example, point (0,0) is on plane 0, point (1,0)
  is on plane 1 and so on... Another example is point (4,0). It is access by
  activing the writing of plane 0, and placing a byte at position $A000:0001.
    We will soon see how to find out the plane and how to find out the position
  in memory of any point...

    9.3. How ?

    To achieve an unchained mode, we must program several registers of the
  video card. So, I'll start. To enter the mode 320x200x256, you just have
  to enter Mode 13h and then reprogram some registers. I admit that I don't
  know why do we need to set some of them (if someone knows, I would appreciate
  an explanation), but we need to...
    In most of the register setting operation, we need to first tell the
  video card what index of the register we are going to change. We do that
  with a "Port[register]:=index" instruction. This is because we have some
  registers that perform more than one function. In most cases, we will not set
  new values in the registers, but alter the existing ones.
  So, the InitChain4 procedure is something like this:

          Procedure InitChain4; Assembler;
          Asm
  1          Mov Ax,0013h
  2          Int 10h
  3
  4          Mov Dx,03c4h
  5          Mov Al,4
  6          Out Dx,Al
  7          Inc Dx
  8          In Al,Dx
  9          And Al,11110111b
  10         Or Al,00000100b
  11         Out Dx,Al

  12         Mov Dx,3ceh
  13         Mov Al,5
  14         Out Dx,Al
  15         Inc Dx
  16         In Al,Dx
  17         And Al,Not 10h
  18         Out Dx,Al

  19         Dec Dx
  20         Mov Al,6
  21         Out Dx,Al
  22         Inc Dx
  23         In Al,Dx
  24         And Al,Not 02h
  25         Out Dx,Al

  26         Mov Dx,3d4h
  27         Mov Al,14h
  28         Out Dx,Al
  29         Inc Dx
  30         In Al,Dx
  31         And Al,Not 40h
  32         Out Dx,Al

  33         Dec Dx
  34         Mov Al,17h
  35         Out Dx,Al
  36         Inc Dx
  37         In Al,Dx
  38         Or Al,40h
  39         Out Dx,Al

  40         Mov Dx,3d4h
  41         Mov Al,13h
  42         Out Dx,Al
  43         Inc Dx
  44         Mov Al,[Size]
  45         Out Dx,Al
          End;

    "ARGH !! ASSEMBLER !!!", I hear you cry... Why ? Well, because I'll lazy,
  and I didn't wanted to convert the code from ASM to Pascal... But it is easy
  to understand the code after I explain it (that's why the code has numbers
  in the lines)...
    First, in lines 1/2 we enter in ordinary mode 13h... Then, we proceed into
  setting the various registers.
    Between lines 4 and 11 we change the contents of register 03c4h, index 4.
    I look into some magic book (in my case it is Norton Guides) and I find
  out the utility of register 03c4h (the sequencer register), index 4:

    --------------------------------------------------------------------------
     Register: 03c4h (Sequencer)
     Index:    4     (Memory Mode Register)

     Bit 0: Set if in alphanumeric mode, clear if in graphics mode
         1: Set if more video card has more than 64Kb
         2: If set, then the Odd/Even addressing is on. This means that all
            odd bytes will be put in planes 1 and 3, while the even bytes will
            be put on planes 0 and 2.
         3: If bit 1 is set, then it selects video memory planes (256 colours)
            rather than the Map Mask and Read Map Select registers...
    --------------------------------------------------------------------------

    So, what we are going to do is to reset bit 3 (don't know why... That's
  line 9 in the code) and we're going to set bit 2 (line 10)... It is clear why
  do we want Odd/Even addressing, regarding what we know about planes...
    So, next we are going to tamper with register 03ceh, index 5:

    --------------------------------------------------------------------------
     Register: 03ceh (Graphics)
     Index:    5     (Mode Register)

     Bit 0/1: Write Mode: Controls how data from the CPU is transformed before
                          being written to display memory:
                            0: Mode 0 works as a Read-Modify-Write operation.
                               First a read access loads the data latches of
                               the EGA/VGA with the value in video memory at
                               the addressed location.
                               Then a write access will provide the destination
                               address and the CPU data byte. The data
                               written is modified by the function code in
                               the Data Rotate register (3CEh index 3) a
                               function of the CPU data and the latches, then
                               data is rotated as specified by the same
                               register.
                            1: Mode 1 is used for video to video transfers.
                               A read access will load the data latches with
                               the content of the addressed byte of video
                               memory. A write access will write the contents
                               of the latches to the addressed byte. Thus a
                               single MOVSB instruction can copy all pixels in
                               the source address byte to the destination
                               address.
                            2: Mode 2 writes a color to all pixels in the
                               addressed byte of video memory. Bit 0 of the
                               CPU data is written to plane etc. Individual bits
                               can be enabled or disabled through the Bit Mask
                               register (3CEh index 8).
                            3: Doesn't exist
           2: (EGA only): Forces all outputs to a high impedance state if set.
           3: Read Mode:
                         0: Data is read from one of 4 bit planes depending
                            on the Read Map Select Register (3CEh index 4).
                         1: Data returned is a comparison between the 8 pixels
                            occupying the read byte and the color in the Color
                            Compare Register (3CEh index 2).
                            A bit is set if the color of the corresponding pixel
                            matches the register.
           4: Enables Odd/Even mode if set (See 3C4h index 4 bit 2).
           5: Enables CGA style 4 color pixels using even/odd bit pairs if set.
           6: (VGA only) Enables 256 color mode if set.

    --------------------------------------------------------------------------

    In line 17, we clear bit 4 (we disable Odd/Even mode, don't now why again.
  Now that I think of it, I don't know why we do most of the stuff... :) But
  I know I have to do it !).
    Now, let's continue to tweak register 03ceh, but this time, index 6:

    --------------------------------------------------------------------------
     Register: 03ceh (Graphics)
     Index:    6     (Miscellaneous Register)

     Bit   0: Indicates graphics mode if set, alphanumeric mode otherwise.
           1: Enables Odd/Even mode if set
         2/3: Memory mapping:
                             0: use A000h-BFFFh
                             1: use A000h-AFFFh
                             2: use B000h-B7FFh
                             3: use B800h-BFFFh

    --------------------------------------------------------------------------

    Notice that for you to shut down Odd/Even mode, you have to change various
  registers (I wonder why...). So, in line 24, we clear bit 1...
    We now move on to register 03d4h, index 14h:

    --------------------------------------------------------------------------
     Register: 03d4h (CRTC)
     Index:    14h   (Underline Location Register)

     Bit 0-4: Position of underline within Character cell.
           5: (VGA only) If set memory address is only changed every fourth
              character clock.
           6  (VGA only) Double Word mode addressing if set.

    --------------------------------------------------------------------------

    In line 31 we clear 6, disabling Double Word addressing... Why ? Don't
  know... I know, I'm a poor teacher... But what can I do ! :) I don't have
  that much info...
    Following next comes index 17 of the same register:

    --------------------------------------------------------------------------
     Register: 03d4h (CRTC)
     Index:    17h   (Mode Control Register)

     Bit 0: If clear use CGA compatible memory addressing system by swapping
            address bit 0 and 13. Creates 2 banks for even and odd scan lines.
         1: If clear use Hercules compatible memory addressing system by
            swapping address bit 1 and 14.
         2: If set increase scan line counter only every second line.
         3: If set increase memory address counter only every other character
            clock.
         4: (EGA only) If set disable the EGA output drivers. This bit is used
            for other purposes in some Super VGA chips.
         5: When in Word Mode bit 15 is rotated to bit 0 if this bit is set
            else bit 13 is rotated into bit 0.
         6: If clear system is in word mode. Addresses are rotated 1 position
            up bringing either bit 13 or 15 into bit 0.
         7: Clearing this bit will reset the display system until the bit is
            set again.

    --------------------------------------------------------------------------

    Line 38 sets bit 6, so the system is not in word mode (I take it is in
  byte mode, since we have disabled double word mode). Again, I don't know
  why... "So many question, so few answears... :)"... What a shitty Chain4
  tutorial... Anyway, let's move on to one I do know how to move around:

    --------------------------------------------------------------------------
     Register: 03d4h (CRTC)
     Index:    13h   (Offset Register)

     Bit 0-7: Number of bytes per scanline / K, where K is 2 in double word
              mode, 4 in word mode and 8 in byte mode.

    --------------------------------------------------------------------------

    In line 44 we are going to ajust the width of the virtual screen. The
  physical has always (in this unchained mode) 320 pixels. So, if we set the
  width to be 640, we'll have 2 screen of width per 2 screen of height. If
  we set the width to be 320, we get 1 screen width and 4 screens height. If
  we set 1280, we get 4 screens width and 1 screen height. We can also set
  stuff like 1000, which would get you 3.125 screens width per 1.28 screens
  of height... So, you must choose the the size... You'll need the size for
  almost every dealing with unchained modes, so it is pratical to put it in
  a constant. In the examples, I want a 640x400 virtual screen, so Size is
  equal to 640/8=80.
    So now the mode is set... To shut it down, you just have to return to text
  mode (the procedure is equal to the CloseGraph procedure in the Mode13h unit).
    Let's move on to...

    9.4. Putting down a pixel

    This operation is slightly slower and more dificult than putting down a
  pixel in mode 13h, because we lost the linearity of the memory. In mode 13h
  we had (the number are adresses in memory):

     0                                            319
     --------------------- .............. -----------
  0  |0000|0001|0002|0003| .............. |013E|013F|
     --------------------- .............. -----------
  1  |0140|0141|0142|0143| .............. |027E|027F|
     --------------------- .............. -----------
     ................................................
     --------------------- .............. -----------
 199 |F8C0|F8C1|F8C2|F8C3| .............. |F9FE|F9FF|
     --------------------- .............. -----------

     In unchained mode Chain4, we will have something like:

      0    1    2    3    4                     638  639
     -------------------------- .............. -----------
  0  |0000|0000|0000|0000|0001| .............. |009F|009F|
     -------------------------- .............. -----------
  1  |00A0|00A0|00A0|00A0|00A1| .............. |013F|013F|
     -------------------------- .............. -----------
     .....................................................
     -------------------------- .............. -----------
 199 |7C60|7C60|7C60|7C60|7C61| .............. |7CFF|7CFF|
     -------------------------- .............. -----------
     .....................................................
     -------------------------- .............. -----------
 399 |F960|F960|F960|F960|F961| .............. |F9AF|F9AF|
     -------------------------- .............. -----------

     See ?! It's a bit more complicated... :) But do not despair. You only
   need to find out two things: the address in memory on which to write the
   the plane to which you want to write.
     Let's start with the plane. As you can easily see, there are groups of
   four pixels that share the same address. Thinking a bit, you come to the
   conclusion that
                             Plane:=X Mod 4;

    The Mod function returns the remainder of the integer division. Now, we
  must tell the computer to write to that plane. We do that by using register
  03c4h, index 2:

    --------------------------------------------------------------------------
     Register: 03c4h (Sequencer)
     Index:    2h    (Map Mask Register)

     Bit 0: Enables writes to plane 0 if set
         1: Enables writes to plane 1 if set
         2: Enables writes to plane 2 if set
         3: Enables writes to plane 3 if set

    --------------------------------------------------------------------------

    We in the example programs use the PortW array, instead of the Port array.
  The PortW array is equal to the Port array, with the diference that it puts
  a word in the register, instead of a byte. So:

                          Port[Register]:=A;
                          Port[Register+1]:=B;

    will be equal to:
                          PortW[Register]:=(B Shl 8)+A;

    So, the code to select the plane can be:

                 Plane:=X Mod 4;
                 If Plane=0 Then PortW[$3c4]:=1+2;
                 If Plane=1 Then PortW[$3c4]:=2+2;
                 If Plane=2 Then PortW[$3c4]:=4+2;
                 If Plane=3 Then PortW[$3c4]:=8+2;

    But this code would be very slow (comparisons are always slow), so it
  comes the time for clever thinking. Look that the plane mask (1,2,4 and 8)
  corresponds to the numbers 0,1,2 and 3. So the plane mask is equal to:

                             BitMask:=2^Plane;

    Or, in computer terms:
                             BitMask:=1 Shl Plane;

    Nice isn't it ? Shl's are very fast, so it's a good improvement.
    Now, let's move on to memory calculation... In mode 13h, we used the formula

                       Address:=320*Y+X;

    We need to find a similar formula for unchained modes. I remember that this
  stuff is identical in all unchained modes (with one or two structural
  diferences). Now, we know that we have N groups of four bytes that share the
  same address per scanline (N=XSize Div 4=Size*8 Div 4=Size*2), so after some
  minutes of thinking (I'll save you the trouble) we come across the formula:

                            Address:=Y*(Size*2)+(X Div 4);

    And voila, here we have it... I've read a bunch of unchained modes tutorials
  when I was preparing this article, and lots of them didn't know where the *2
  came from !!! It comes from the fact that Size is the size of the screen
  dividing by 8, but we have groups of 4 bytes with the same address, so we
  have to multiply Size by 2 to have the exact number of groups of addresses.
    The complete putpixel procedure (after some slight optimization):

                 Procedure PutPixel(X,Y:Integer; Color:Byte);
                 Var Pos:Word;
                     BitMask:Byte;
                 Begin
                      Pos:=Y*(Size Shl 1)+(X Div 4);
                      BitMask:=1 Shl (X Mod 4);
                      PortW[$3c4]:=(BitMask Shl 8)+2;
                      Mem[$A000:Pos]:=Color;
                 End;

    Not too hard, is it ?! :)
    Now let's move to something that is sometimes required...

    9.5. Getting a pixel

    I hear you say "Well, instead of writing to memory, I read from there !"
  But it is not like that... Why ? Although the address is the same, the plane
  selection register is diferent:

    --------------------------------------------------------------------------
     Register: 03ceh (Graphics)
     Index:    4h    (Read Map Select Register)

     Bit 0-1: Selects plane from where to read

    --------------------------------------------------------------------------

    So, the GetPixel function will be:

                 Function GetPixel(X,Y:Integer):Byte;
                 Var Pos:Word;
                     BitMask:Byte;
                 Begin
                      Pos:=Y*(Size Shl 1)+(X Div 4);
                      BitMask:=X Mod 4;
                      PortW[$3ce]:=(BitMask Shl 8)+4;
                      GetPixel:=Mem[$A000:Pos};
                 End;


    Ok... Moving on...

    9.6. Changing the viewport

    The viewport is the part of the unchained screen that you see... Changing
  the viewport only requires you to write to two registers, and the operation
  takes only 1/60th of a second !!! It is the fastest page flip there can be !

    --------------------------------------------------------------------------
     Register: 03d4h (CRTC)
     Index:    Ch    (Start Address High Register)

     Bit 0-7: Upper 8 bits of the start address of the display buffer

    --------------------------------------------------------------------------
     Register: 03d4h (CRTC)
     Index:    Dh    (Start Address Low Register)

     Bit 0-7: Lower 8 bits of the start address of the display buffer

    --------------------------------------------------------------------------

    In simple words, this two registers must have the address of the upper-left
  corner pixel of the screen... So, if you want the image to be displayed from
  any pixel of the screen, you must get it's address and put it into this
  register:
                        Procedure ChangeViewPort(X,Y:Word);
                        Var A:Word;
                        Begin
                             A:=Y*(Size Shl 1)+X;
                             PortW[$3d4]:=(Hi(A) Shl 8)+$C;
                             PortW[$3d4]:=(Lo(A) Shl 8)+$D;
                        End;

    So, this is it for the basics... You should check out the Chain4-2.Pas
  program... It has routines to read a 640x400 PCX and scroll around. I'm
  going to explain the Chain4-1.Pas program...

    9.7. The example program

    First of all, as you may have noticed already, if you set the Map Mask
  Register to 15, you are enabling the write for ALL planes... The result ?
  Well, if you haven't tried it already (and you should have :) ) is that in
  fact you write to the four planes, placing four pixels at a time !!!! This
  can be great, specially when rendering solid color polys, where when you are
  making an effect that spends lots of the processor. Doing an effect in a
  80x50 buffer and then mapping it to the Chain4 screen is faster than making
  the a 320x200 effect... It can look worse, but it is many times faster...
    Now, we come to the a good point... How do we map a 80x50 buffer on the
  320x200 screen... Well, after enabling the write to all planes, you just copy
  the first line of the buffer (80 bytes) to the first, second, third and
  forth lines of the screen. Notice now that a 320x200 screen has 80x200 bytes
  of memory allocated to it, so you have to copy four times in the Y axis, to
  get the correct aspect ratio... We do this in the example program...
    Now, let's go to plasmas... What is a plasma ? A plasma is... well... er...
  Check out the example program... That's a plasma... A realtime one (there are
  other ones)... Cool, isn't it ? This will be shortest plasma tutorial of all
  time... Maybe one day I'll make a better one... :) But for now, this is
  enough. To make a Chain4 plasma, you just have to follow some basic steps:

           1) Setup a nice palette, that makes good transitions from one colour
              to other... This is not a rule, but if you don't do this, you'll
              loose the mellow look, and get a more hardcore effect (check out
              Colour Blind... There is a plasma there (a not-realtime plasma,
              in fact) that makes you want to puke because of the setting of
              colours... :) )
           2) Make the 80x50 buffer (in the example program I used a 80x55
              buffer, can't remember why... But it doesn't matter ! :) )
           3) Make and move the plasma
           4) Copy the buffer to the screen
           5) Goto step 3 'till you puke ! :)

    Now, doing the plasma is quite simple... A plasma is (by definition) a
  fractal... Fractals are mathematical entities... When used in the computer
  sense, a fractal is an image created from an equation...
    A plasma usually (this isn't a rule) is the sum of sine and cosine waves.
  In the example program, there are four, with diferent wavelengths, etc, which
  make the distorted effect you see...
    Check out the source code and see how it works... It's rather simple, after
  a couple of hours of code-snooping... I'm fed up with this issue, and it is
  already about 6 months late, and I've got to finish 2 articles still ! :(


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  10. Cool ASM and Pascal tricks

    I decided to make this article so that you can understand what the strange
  and criptic code in the Fixed32.Pas unit is all about.
    This article is mainly on how to use 32 bit code in our old 16 bit Pascal.
  But first things first.

    10.1. Why ?!

    Well, we need to use tricks to use 32 bit code because the guys at Borland
  decided that they should dump the greatest language on Earth: Borland Pascal.
    Now, people who love Pascal above everything else (like me) can do three
  things:

         a) Be a traitor and change to a nice 32 bit flat mode C compiler and
            get into the flow...
         b) Get a copy of a 32 bit flat mode Pascal compiler. There is one, in
            shareware, called TMT Pascal. Version 1.0 was recently released
            and it is available for download in www.tmt.com. This compiler is
            very promising... Lots of friends of mine are using it. If there
            are any requests, I'm sure I can make some articles on it, with
            the help of the number 1 die hard fan of TMT Pascal (don't pass
            out, Brain Power... :))
         c) Learn a bunch of Borland Pascal tricks that really don't cut into
            it as a dedicated 32 bit Pascal would, but they do most things
            with a bit more work...

    Option (b) is the best (for me at least)... But this article is really
  about (c)... So, onward.

    10.2. Full ASM functions

    You probably have something like this in ye old good Pascal:

        Function DoStupidStuff(A:Byte):Byte;
        Begin
             DoStupidStuff:=A*2+5;
        End;

    and you want to convert it to super-duper-fast ASM code... This is very
  simple. Just know this:

          (a) When the result of the function is a byte (or shorting), the
              value that AL stores in the end of the function code will be
              the result of the function.
          (b) When the result of the function is a word (or integer), the
              value that AX stores in the end of the function code will be
              the result of the function.
          (c) When the result of the function is a longint, the value that
              DX:AX stores in the end of the function code will be the result
              of the function. DX:AX means that the high part of the result
              must be stored in DX and the low part in AX... In a good 32 bit
              compiler, this is EAX, instead of DX:AX (I think most 32 bit
              compilers do it this way).

    So, the above function would be something like:

        Function DoStupidStuff(A:Byte):Byte; Assembler;
        Asm
           Mov Al,[A]   { AL = A }
           Shl Al,1     { AL = AL*2 }
           Add Al,5     { AL = AL+5... The result will be in AL, as we wanted }
        End;

    This is 32 bit trick number one... Now for number two:

    10.3. Using 32 bit registers

    This one if also fairly easy. This trick enables you to use the 32 bit
  registers present in the 386 (I think they aren't available on the 286, but
  I'm not sure), EAX, EBX, ECX and EDX (and it probably enables you to use
  EDI and ESI, but I never tried it... Never needed them... :).
    So, how do you do this. Imagine you wanted to do:

                            Mov EAX,5

    The code that you need is:

                            DB 66h; Mov AX,5

    What is this ?
    Well, the DB statement makes Pascal's inline assembler compiler (henceforth
  PIAC) put down the value that follow, in this case 66h.
    For what ?
    Well, the designers of the 386 designed the 66h opcode as being the opcode
  that changed the type of argument acordingly to the memory model the computer
  was operating in. So, when the computer is operating in 16 bit mode (like
  Pascal always does), every command (every opcode) that is preceded with the
  66h opcode is a 32 bit command, while if the computer operates in 32 bit,
  the 66h opcode makes the command a 16 bit one.
    The DB trick can be used for other stuff beside using the extended
  registers. One of the possible uses is to make a segment override that
  uses the 386's new segment registers. Example:

             Db 65h; Mov Ax,[Di]   is equal to   Mov Ax,Gs:[Di]
             Db 64h; Mov Ax,[Di]   is equal to   Mov Ax,Fs:[Di]

    You can use combinations of the opcodes:

          Db 64h; Db 66h; Mov Ax,[Di]   is equal to   Mov Eax,Fs:[Di]

    But the Db 66h only works for 32 bit commands that have a 16 bit counter-
  -part... So, we come to another trick, probably the hardest one to describe:

    10.4. Using 32 bit instructions

    One instruction that doesn't have a 16 bit counterpart is

                        Shrd dest,src,count

    which shifts the register dest, [count] bits to the right. The blank
  positions in dest are filled with the [count] most significant bits of
  src.
    So, imagine you wanted to do the instruction:

                            Shrd EAX,EDX,5

    You might think of doing this like:

                         Db 66h; Shr AX,DX,5

    But the PIAC will give you an error, because Shr's syntax doesn't allow
  you to specify where you should get the bits for the blank positions. That
  really sucks, and it took me LOTS of time to get this working.
    The solution is to change Shrd EAX,EDX,5 into:

                 Db 66h; Db 0Fh; Db 0ACh; Db 0D0h; Db 05h;

    "WHAT IS THIS ?", you will probably scream out loud. Simple: it is the
  translation that any decent 32 bit assembler will do of the Shrd EAX,EDX,5
  command. What we do is to find out exactly what sequence of bytes implement
  the code we want and can't get into Pascal. Then we put it there using the
  DB statement, that make the PIAC put the code there. This is a very nice
  trick, yet can be a pain the ass. I recomend Turbo Debug to find out the
  opcodes you need...
    So, this is the end of trick 3... Now to the last one, trick 4:

    10.5. 32 bit parameters

    Now, imagine you wanted to do something like this:

         Function Stuff(A:LongInt):LongInt;
         Begin
              Stuff:=A*2;
         End;

    You would do something like this in Assembler:

         Function Stuff(A:LongInt):LongInt; Assembler;
         Asm
            Db 66h; Mov Ax,[A]          { Mov EAX,[A] }
            Db 66h; Shl Ax,1            { Shl EAX,1   }
            { The following code translates something in EAX to something
              in DX:AX, to export 32 bit data }
            Db 66h; Mov Dx,Ax           { Mov EDX,EAX }
            Db 66h; Shr Dx,16           { Shrd EDX,8  }
         End;

    If you tried to run this, you would get an error... Why ? Because the
  stupid PIAC doesn't know that the DB 66h statement is converting the AX in
  the Mov Ax,[A] instruction into EAX. So he thinks that we are trying to
  put a Longint (32 bit value) into AX (16 bit value), which obviously enough
  is a no-no... :(
    So, we have to use a very confusing trick to get around this. We must get
  the parameters in a diferent way. We'll use the stack. Pascal (as most of
  languages) push the parameters into the stack. But where in the stack will
  we get them ?
    When Pascal (or any other language, including full ASM) call a procedure
  (or function), IP (and CS if the procedure is defined as far) are pushed
  into the stack. Then, Pascal pushes into the stack the contents of DI and
  of DS (or ES... I can never remember which... Try it yourselfs). Finally
  Pascal pushes the parameters, and this is where we can get them.
    Notice that Pascal in the end will pop the values back, including the
  parameters (that's why a variable can't be altered without the VAR clause),
  so we can't just POP them out ourselfs... We'll have to be cunning, and use
  the SP and SS registers, which point to the stack. The look of the stack
  will be something like this (I'll do the scheme for the following procedure):

                Function Mult(A,B:LongInt):LongInt;


                                [A]          10/11/12/13
                                [B]           6/ 7/ 8/ 9
                                DS            4/ 5
                                DI            2/ 3
                         SP ->  IP            0/ 1

    The numbers on the right are the offsets. So, if we wanted DS, we would
  do Mov Ax,Word Ptr [SS:SP+4];
    Just one word of caution. We can't use SP+4 as the offset in the example
  above. We should use Mov BX,SP in the beggining and then use BX+4, since
  BX is the only register that enables this kind of addressing. So, the
  first procedure would look like:

         Function Stuff(A:LongInt):LongInt; Assembler;
         Asm
            Mov Bx,Sp
            Db 66h; Mov Ax,Word Ptr [SS:Bx+6]   { Mov EAX,DWord Ptr [SS:Bx+6] }
            Db 66h; Shl Ax,1                    { Shl EAX,1   }
            { The following code translates something in EAX to something
              in DX:AX, to export 32 bit data }
            Db 66h; Mov Dx,Ax                   { Mov EDX,EAX }
            Db 66h; Shr Dx,16                   { Shrd EDX,8  }
         End;

    This is a very nifty trick, and can lead to endless crashes if not handled
  properly... But it is very usefull...

    Well, this is the end of this article. These concepts are a bit confusing
  in the beggining, but with a little use they become very simple (with the
  exception of the last, which requires tons of paper so that you know where
  the hell in the stack are those parameters you need... :) ).
    If you are serious in 32 bit programming, you should get TMT Pascal...
  I'll try to really review it in next issue or so.


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  11. Hints and Tips

                *   - Begginners tip
                **  - Medium tip
                *** - Advanced tip

    -  Incremental calculation method (*)

       In the previous issue, I told you to exchange the piece of code:

             For A:=1 To 50 Do
             Begin
                  For B:=1 To 100 Do
                  Begin
                       C:=A*10+B;
                       WriteLn(C);
                  End;
             End;

       for the piece:

             For A:=1 To 50 Do
             Begin
                  D:=A*10;
                  For B:=1 To 100 Do
                  Begin
                       C:=D+B;
                       WriteLn(C);
                  End;
             End;

       Well, in this case, Viriato reminded me that I could use one of the best
       optimizations: the incremental calculation method:                0

             D:=1;
             For A:=1 To 50 Do
             Begin
                  Inc(D,10);
                  For B:=D to D+99 Do
                    Writeln(B)
             End

       The ideia of this method is using the last result to calculate the next.
       For example, imagine you have a sucession of numbers defined like this:

                                 U(n)=10*n

       This defines a sucession like this:

                   0 10 20 30 40 50 60 70 80 ....

       Now, you could define this sucession using the anterior result.
       Look:
                         U(3)= 30 = 20+10 = U(2)+10
                         U(2)= 20 = 10+10 = U(1)+10
                         U(1)= 10 =  0+10 = U(0)+10
                         U(0)=  0

       So, you could define the sucession as:

                    | U(0)= 0
                    | U(n)= U(n-1)+10

       And you don't need to use a costly mult... This is used a lot in graphic
       programming... I use it in the texture mapping article, combining with
       the interpolation stuff... I called it (at the time) 'discrete
       interpolation'...

    -  Fast PutPixel (**)

       Viriato told me a way to optimize the PutPixel rotine even further.
       I told you that you could do it like this:

        Procedure PutPixel(X,Y:Word;C:Byte;Too:Word); Assembler;
        Asm
           Mov Ax,[Too]
           Mov Es,Ax
           Mov Bx,[X]
           Mov Dx,[Y]
           Mov Di,Bx
           Mov Bx,Dx
           Shl Dx,8
           Shl Bx,6
           Add Dx,Bx
           Add Di,Dx
           Mov Al,[C]
           Stosb
        End;

       The base for Viriato's optimization is the fact that StosB is VERY slow
       for putting just one pixel, because it takes some time to get itself
       ready, and after putting down the pixel it increases Di, which for one
       pixel only, we don't need.
       Viriato's routine has other minor optimizations, regarding mostly the
       movement of memory and saving registers:

        Procedure PutPixel(X,Y:Integer;Color:Byte;Too:Word);Assembler;
        Asm
           Mov Es,Too         { I thought that I couldn't load Es directly }
           Mov Bx,Y
           Mov Di,Bx
           Shl Bx,6
           Shl Di,8
           Add Di,X
           Mov Al,Color
           Mov Es:[Di+Bx],Al    { This is faster than STOSB }
        End;

       If anyone has a faster putpixel, tell me all about it ! :))

    -  Fast Horizontal Line (**)

       Well, I told you in the previous issue that you could optimize this
       routine if you could put two pixels at a time, but that would only work
       with a line that had an even number of pixels.
       Well, I forgot to mention that you could do this anyway, if you drew the
       pixels you could two at a time, and then putting down an extra pixel
       if the number of pixels was odd... But, I didn't say because I thought
       that the test would take longer than just placing the pixels one at a
       time (since jumps are so slow). Well, Viriato (who else ?!) told me of
       a VERY clever way of doing this without needing to jump:

       Procedure HLine(Y,X1,X2:Word;Color:Byte;Where:Word); Assembler;
       Asm
           { Set the starting coordinate and the color both in the high and
             low part of Ax, since we may draw two pixels at a time }
           Mov Es,Where
           Mov Bx,Y
           Mov Di,Bx
           Shl Bx,6
           Shl Di,8
           Add Di,Bx
           Add Di,X1
           Mov Al,Color
           Mov Ah,Al

           Mov Cx,X2
           Sub Cx,x1
           Inc Cx       { Cx now holds the number of pixels to draw }

           Mov Bx,Cx    { Save the Cx register }
           And Cx,1     { If Cx was odd, Cx will be 1, else it will be 0 }
           Rep StosB    { If Cx=0 (that is, Cx was even),then this doesn't
                          do anything }
           Mov Cx,Bx    { Restore Cx }
           Shr Cx,1     { Divide Cx by 2 }
           Rep StosW    { If Cx=0 (that is, Cx was odd), then this doesn't do
                          anything also. But if it has something, this places
                          two pixels in a row ! }
       End;

       This is a very good routine for solid polys... :) Thanks again, Viriato.


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-


  12. Points of View

    Well, this is the end of another issue of 'The Mag'... Hope you enjoyed
  this issue, even though it came VERY LATE. But, I had good reasons for the
  delay... Number one, school... Number two, vacaciones... Number three, my
  demo Lost Love... And number four, lazyness ! :)
    Be sure to check out my demo Lost Love... It features mellow effects and
  music, with the aid of a 21 bit mode to smooth things up... Similar (some
  stuff is a authentic rippoff) to Luminati, by Tran. It doesn't run as good
  as I wanted to make it, but some stuff is nice altogether...
    Moving on, since I'm fed up 'till the tip of my hairs of this issue. Next
  issue will feature more text adventure tutorial, an article on object oriented
  programming, an article on Z-buffer and 3d clipping (including the code and
  explanation of perpective correct texture mapping), an article on how you go
  about writing your 3d engine (some ideias, related to the object oriented
  stuff). On the sprite side, we'll have the beggining of the making of a full
  action game, on the unchained mode tutorials I'm gonna teach you ModeX, ModeY
  and other neat stuff. Also the followup of the sound article (whatever it's
  me or Viriato that write it) and an article on mouse control !!
    I think it will be a nice issue, isn't it ?
    So, cya in some months ! :)))


-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x


  13. The adventures of Spellcaster, the rebel programmer of year 2018.

  Episode 12 - Beyond enemy lines

  - Well, I guess this is it, Karl... - I said, while I looked towards the
  monitor...
    We were getting ready to crack The Gate... Karl already tried it, but he
  was caught... But he didn't had a massive HexaMind computer, loaded with
  the most sofisticated program on Earth: a human mind...
  - Ok, crank it up, Spell... - the cold and electronic voice of my old friend
  and partner said.
    I connected the computer to the city's comm system and seconds later, the
  welcome screen of CompTel appeared in the screen. I've activated the log
  option of my computer to record everything that happened (well, I've also
  recorded the voice part of this event, so some stuff weren't actually
  inserted in the computer... That stuff is in square brackets):

  Comptel Computer: Hello... I'm Billy 1237... What can I do to help you, sir ?
  DeathRipper: Access NeuroFrame's main control system
  CC: Please type in password...
  [ Spellcaster: Karl, do you know the password ?!?!
    DR: Yep... I got it from some guy Comptel fired, without knowing that he
        knew the password... Eheheheh... ]
  DR: **************
  CC: Access granted... Rerouting data to Neuroframe...
  NeuroFrame: What is your option ?
  DR: Format NeuroFrame: /U /V:FUCK_YOU /F:0000.1
  NF: Ilegal command... What is your option ?
  DR: FDisk NeuroFrame:
  NF: Ilegal command... What is your option ?
  DR: Del *.* /s
  NF: Ilegal command... You have run out of chances... Central command will
      be notified of this ilegal access... Tracing call...
  [ DR: Spell, shut us down, NOW !!!!
    SC: Sure... ]

    I pulled the plug of the small terminal and I waited a few seconds before
  plugging it in again...
  - Shit, that was close... - DeathRipper said, wipping it's virtual brow.
  - Yep... Could he have traced use in such short notice ?
  - No... It was too fast for him... I don't know what happened... They must
  have upgraded or changed the system from the last time I've been there.
  Fuck !!! We must found out how to break it...
  - But how ?!
  - We must go in there...
  - WHAT ?!!!! How do you want to do that ?!
  - Well, you and Excalibur could desguise yourselfs and get a job at Comptel.
  After some months, tops, you should be able to know what the hell can we
  do against that fucking beast of a computer !
  - No way !!!! That would be suicide...
  - It's the only way, Spell !
  - I won't go there !
  - Come on... We've come so far !
  - No, no, no and no...
  - Ok, ok... Talking about Kristie, where's she ?
  - Er... Well...
  - Spit it out, Diogo... - the electronic voice said... I swear I could have
  sensed a bit of curiosity in the voice...
  - Well, she sort of run away yesterday...
  - Sort of ?!
  - I don't know where the hell she is... We've talked and she ran off...
  - Talked ?!
  - Well, I kissed her...
  - Go on... - Karl said, with an amusing sound in his voice.
  - And she punched me hard... When I got up, she was no longer there...
  - Well... I probably know were she is... - Karl said, with a sly voice.
  - Well ?! What are you waiting for telling me ?
  - I won't tell you.
  - WHAT ?! WHY ?!
  - I should I bother telling you if you don't bother doing me a favour ?!
  - WHAT ?! YOU BLACKMALLING SON OF A BITCH, YOU BIG FAT BUCKET OF SCREWS !!!
  - Now, now, insults won't get you anywhere...
  - Fine... Don't tell me... I don't need to know... She doesn't care about me,
  so I don't care about her !
  - Ok... If you say so...
    Silence fell between us two... I just looked everywhere. I don't know if
  I was searching for Kristie or if I was looking for something to distract
  me of it... Finally, I gave up when I remembered that Karl had an infinite
  ammount of pacience, since he was half machine now.
  - Ok... You win. Tell me where she is and I'll go to Comptel... - I said with
  a disgraced voice.
  - She's in an old windmill outside of town, in the Arabda Natural Park. She
  always went there when something went wrong...
  - Ok... - I said, absently, as I turned and climbed into our jetcar.
    I accelerated in the airway, heading North, towards Arabda Park...


                                               See you in the next issue
                                              Diogo "SpellCaster" Andrade