Spellcaster presents:


TTTTTTTTTT HH      HH EEEEEEEEEE    MM      MM    AAAA     GGGGGGGGG
    TT     HH      HH EE            MMM    MMM   AA  AA   GG
    TT     HH      HH EE            MM M  M MM  AA    AA  GG  
    TT     HHHHHHHHHH EEEEEE        MM  MM  MM  AAAAAAAA  GG   
    TT     HH      HH EE            MM      MM  AA    AA  GG    GGGG
    TT     HH      HH EE            MM      MM  AA    AA  GG      GG
    TT     HH      HH EEEEEEEEEE    MM      MM  AA    AA   GGGGGGGG

                                                        Issue 9
                                                        20-7-96


-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. The Turbo Assembler Bible
        4. Designing a text adventure - Part II
          4.1. Looking at a room
          4.2. Player input and the command parser
          4.3. Basic movement commands
        5. How to make a 100% assembly program
        6. Scrolling horizontaly in text mode
        7. Sprites Part II - Animation and movement
          7.1. Animating a sprite
          7.2. Moving a sprite
          7.3. Doing it all together
        8. Graphics Part VIII - Text in graphic mode
          8.1. Basics
          8.2. Fixed sized fonts
          8.3. Proportional fonts
          8.4. Colorfonts
        9. Hints and tips
       10. Points of view
       11. The adventures of Spellcaster, part 9


-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

    Welcome to number 9 of 'The Mag', brought to you, as usual, by Spellcaster,
  alias Diogo de Andrade. Big issue... Again... This is becomming a hobbit...
  Ooopss... Sorry, a habbit... :))) I'm reading Tolkien, so this is an
  understandable mistake... :)
    This issue is _VERY_ late... This is another hobbit... :)) It is late
  because I have lots of projects and exams to do and I don't have the time.
    Also, this issue features another article by Scorpio, which like the last
  one, is pretty different from the usual...

    You should be reading this issue in the first of the SpellUtilities,
  SpellView ! It's a program designed to view text with some features, like
  colors and other neat stuff... :)
    You probably can get it from the same place you got this issue, but if you
  can't, look in the BBS's listed somewhere in this issue, or look in my
  HomePage... It includes full source code, so you can change it, and see how
  it was done... :)

    Well, thinking better, don't use SpellView... SpellView SUCKS ! I only
  now noticed that it screws up with files with a certain size... And this
  issue is very big for SpellView to read it... Shit... Well, back to the
  old drawing board... :(((

    Ok, other notice... You can use SpellView, but only version 1.2... That's
  a fixed version that saves up some memory... It can read files almost three
  times larger than version 1.1 could... :)))

    This magazine is dedicated to all the programmers and would-be programmers
  out there, especially to those that can't access the Net easily to get
  valuable information, to those who wish to learn how to program anything,
  from demos to games, passing through utilities and all sort of thing your
  mind can think of, and to those that can't find the right information.

    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).

    As I stated above, this magazine will be made especially for those who don't
  know where to get information, or want it all in the same place, and to those
  who want to learn how to program, so I'll try to build knowledge, building up
  your skills issue by issue. If you sometimes fail to grasp some concept, don't
  despair; try to work it out.
    That's what I did... 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 18 years old, and I just made it in to 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 a year 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 180-190). I can program in C, C++, Pascal, Assembly
  and even BASIC (yech!).

    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. I may be young, but I know a lot about computers (how humble I am;
  I know, modesty isn't one of my qualities).

    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... I don't know if they'll accept it there, because
                     that's a demo only site, and my mag doesn't cover only
                     demos, but anyways, try it out... It has lots of source
                     code of demos.

    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. I'm specially interested in
  articles explaining XMS, EMS, DMA and Soundblaster/GUS.

    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 that is skipping classes to
  code his (first) next demo !! :)

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

    Email: l42686@alfa.ist.utl.pt

    And if you want to contact me on the lighter side, get into the Lost Eden
  talker... To do that telnet to:

                        Alfa.Ist.Utl.Pt  : Port 1414

    If that server is down, try the Cital talker, in

                        Zeus.Ci.Ua.PT    : Port 6969

    I'm almost always there in the afternoon... As you may have guessed already,
  my handle is Spellcaster (I wonder why...)...

    1.6. Hellos and greets

    I'll say hellos and thanks to all my friend, especially for those who put 
  up with my constant whining.
    Special greets go to Denthor from Asphyxia (for the excelent VGA trainers),
  Draeden from VLA (for assembly tutorials), Joaquim Elder Guerreiro, alias
  Dr.Shadow (Delta Team is still up), Joao Neves for sugestions, testing and
  BBS services, and all the demo groups out there.
    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

    Ok, let's open up some mail... The first is from Ben Basset from Australia:

-------------------------------------------------------------------------------
First of all let me say G'DAY from me...Ben Bassett from West Australia.

I started reading through your "The Mag" magazines sometime last week.
I am currently studying Computer Science at Curtin University, so I already
know most of the Pascal stuff.  I'm learning about the mode 13h graphics
mode from your tutorials.  I'm currently working my way through issue 7,
and my goal is to produce a half-way decent demo or game.  

I like your style of writing, informal yet informative, keep up the good
work!

Down to business - Circles issue 4.

Note: What I write below is an observation that I made while looking
      through issue 4.  I think I'm on the right track here, but if not,
      you can throw this heap of utter bollocks in the trash.

*********
	I notice when you pre-generate the sin and cos values, you use
	1800 reals for each.  I think this may be a mistake.  If you are
	using a radian gap of 0.005, you only need 1257 points to draw
	all the way around the circle.  eg.  (2*PI)/0.005 = 1256.637

	What you are doing when you use 1800 points is that every point
	beyond 1257 (ie. points 1258 to 1800) is drawing over a point
	that you have already plotted.  You only need 1257 to reach
	2*PI.  Here's what I've done:

	Declare 2 constants:  

		radian_gap=0.005;
		number_circle_points=round((2*PI)/radian_gap)+1;
 
	and use number_circle_points wherever you used to put 1799
	ie. for i:=0 to number_circle_points do

	you should use this when getting the memory for the sin and cos
	arrays, and also when plotting the points themselves.

	I tried this out on your Tunnel.pas demo, and got around 30%
	speed-up when drawing the circles.

*********

Well, thats about it for the serious stuff (I'm not very serious
by nature anyway).  Hope to see some cool stuff in future mags!

--Ben Bassett.

-------------------------------------------------------------------------------
    Ok, Ben, you got me there... I guess I made the mistake because I used a
  diferent step in my first calculations... Well, thanks for the mistake
  correction... :)
    Just to prove I'm human...

    The second letters comes... Let me see... Oh, Ben Basset again... :)))

-------------------------------------------------------------------------------
I forgot to mention that you should set the circle_point_gap (or whatever
I called it) to reflect the maximum size of circle you want to draw.
There's not much point in plotting thousands of points for circles with
radius 4.  In general I have experiented and come up with the following
values:


circle_point_gap		radius of circle

   0.1				   less than 8
   0.05				   less than 13
   0.01				   less than 49
   0.005   			   less than 97
   0.001 	 		   pretty much any size.


I only mention this because it saves time and memory (and I LIKE time
and memory!).

--Ben Bassett  

-------------------------------------------------------------------------------
    Well, another smart remark... Time and memory are _VERY_ important, and
  they are often opposites to each other (this is, when you want more speed,
  you have less memory, and vice versa)... But in this case, he won both...
  Is this guy learning ? He should be teaching me... :)))
    Well, that's the end of this issue's mailroom...


-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. The Turbo Assembler Bible

    Ok, this is the article writen by Scorpio (Yuppi, less work for me !). I,
  Spellcaster will make my coments between square brackets []...
    Your cue, Scorpio...

    Allright... I'm writing another article today... I was writtin' one
  about 3DStudio, but I think that I don't have enough knowledge about 3DS to
  make an article... Just some Texture-mappings, etc... If you still think
  you could use such an article, email me and I'll be happy to send it to
  Spellcaster so that he puts it in the next issue(or so) of THE MAG .

  [ Write it !!! Less work for me, and a more general mag ! ]

    Let's get to the point, now... This is an article 'bout an Assembly
  book I'm currently reading. It is called "The Waite Group's Turbo Assembler
  Bible", a (+-)720 pages and (+-) 1.5Kg book...:)
    It was printed in 1991, its author is Gary Syck, which was working
  in various data-communications programs but that also was into graphics
  programming. The book follows the usual (academic) guidelines, and here are
  the various chapters:


  o Chapter 1: Programming the MS-DOS systems

             Overview of the MS-DOS Operating System
                Segments & Offsets, registers, PSP, DOS file system, etc...

            [ It would be very nice to see an article on the PSP... They are
              rare... HINT, HINT ! :)))) ]

             Introduction to Using Turbo Assembler
                Let's not forget that this is a book on TASM...
             Introduction to the Turbo Debugger
                Same as above.

  o Chapter 2: Processor Instructions
             Data Movement instructions
                The name says it all...
             Arithmetic, Logic, Bit-Shift Instructions
                .....
             Procedures, Loops and Jumps
                .....
             Processor control and Protected Mode Operation
                Includes some examples on how to change to Pmode and back
                to Real mode, Pmode specific instructions and some general
                info... It seems short...Also, some code to switch through
                various tasks...

  o Chapter 3: TASM Directives and Operators

             Segment declaration
             .......
             Code generation, error handling, .....

  o Chapter 4: Techniques

             Writing ASM modules for HLL (High level languages)
             Using system resources
             Acessing and controlling the Hardware
             Video control: Text and Graphics

  o Chapter 5: Appendixes

             Various TASM/TLINK, ASCII, BIOS/DOS interrupts,etc. tables...


    Allright... As you've probably noticed I've not included comments for
  some of the chapters... It's like this: I'm reading this book page by page,
  and I'm goin' very slowly... between university projects and commercial work,
  I have so little time left to read... I just can't wait to get to the PMODE
  section... Maybe (_MAYBE_) I'll do an article on it... Ok... I've read the

  [ I want that article on Pmode, pronto ! :)) ]

  first and a bit of the second chapter, and it follows the usual conventions:
  Memory segmentation, registers, flags, etc, etc, etc... it is NOT a book to
  ASM experts, yet it IS a book to ASM experts(or almost-experts).
    Why? Well,it goes from the very basics of a simple printf('Hello\n")
  (I wrote a line in C in this article???? I must be sick... :)) to some stuff

  [ You are definetly sick !! No more C ! Please, I hate C... Great language,
    but I don't like anyway... C is forbidden in 'The Mag'... And maybe...
    Hum... Just had an ideia... Maybe I'll do a C version of 'The Mag'...
    What do you people out there think ?? ]

  'bout system resources (LIM EMS usage, mouse programming, COM, etc)...to
  PMODE, to Breshenham's line drawing algorithm, palette handling... There are
  lots of stuff to the newbies and it is also a good book for a later reference
  on some subjects...
    All of these chapters are presented in an informal way (the one we
  like :)) and easy to understand...So, if you can't find a book around there,
  check your university library, 'cos you would be surprised with what you can
  find there... I got this book from University of Minho's library... Don't know
  if anyone is from around here, but if u are... check this one out.

  [ IST library sucks big time !! ]

    Final considerations: THIS IS NOT A MUST-GET book... try PC Magazine's
  lab book, or some other... Anyway, if you can get your hands on it... don't
  loose the opportunity.

    WOW!!! 79 lines??? I can't believe I'm not sleeping by now...
  It's 3:36 a.m. and I have to wake up at 6:30 tomorrow... er... today...
  .. er... in 3 hours... Gotta go, ppl... PLZ let me know if you like (or not)
  this article... PLZ no more flames... I get enough already... Do you know
  what I mean, Spellcaster? Unfortunately, you do...

  [ I sure do, man... :((( ]

    Well, since I am goin' to sleep now, I'd better save this before falling
  with my face in the keyboard..r n,iuooooooooqdxoewqiyh.wizs n.wqpw OOPS!
  sorry 'bout that... CYA next time... (I hope).
    If you wanna tell me anything at all, email to: si17899@ci.uminho.pt
  (preferred) or si17899@thor.di.uminho.pt or try to find me in Moosaico (a
  Moo) at: moo.di.uminho.pt 7777 under the name of (guess:) Scorpio.

  [ I'm there too, sometimes... :) ]

    It's me, Spellcaster again... Just to say my end phrase:

    T-t-t-t-t-h-a-t's all folks ! :))))


-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. Designing a text adventure - Part II

    This article is intended for the beginners who want to do games. I know
  that text adventures are definetly out, but the ideas behind a text adventure
  are very similar to those found in graphic adventures, so what I say here
  can be expanded to a graphic adventure...
    Note that these are my personal views on how a text adventure should be
  coded and designed...
    There can be some errors and irregularities in the code and explanations,
  because I'm writing this at the same time I'm coding the adventure, so I
  will sometimes make mistakes... If you spot one, or have any doubts, mail me !
    This particular issue is about the parser and basic commands.

    4.1. Looking at a room

    Well, one the first thing your text adventure makes is 'looking' at the
  starting room... By 'looking at a room' I mean 'describe the current room'.
  By describing a room, I mean to output the description of the room, the
  objects that there exist, any monsters and other game characteres... Well,
  as we aren't dealing with monsters, objects and characteres, we'll simply
  output the description of the room, that is stored in Rooms[].Desc array...
  So, let's make the look procedure:

            Procedure Look(RoomNumber:Word);
            Var A:Byte;
            Begin
                 Writeln;
                 A:=1;
                 While (A<11) And (Rooms[RoomNumber].Desc[A]<>'*') Do
                 Begin
                      Writeln(Rooms[RoomNumber].Desc[A]);
                      Inc(A);
                 End;
                 Writeln;
            End;

    If you are wondering why don't you use just a For loop for writing the
  ten-line description, remember that the description can have less that 10
  lines, and in that case, the last line has a '*' character only...
    Ooopsss... I almost forgot to describe the exits... To do so, add the
  following lines:

            Writeln('Visible exits:');
            If Rooms[RoomNumber].North<>0 Then Write('North      ');
            If Rooms[RoomNumber].South<>0 Then Write('South      ');
            If Rooms[RoomNumber].East<>0 Then Write('East       ');
            If Rooms[RoomNumber].West<>0 Then Write('West       ');
            Writeln;

    To hilite this info, use a different color, like this... Add before
  describing the room:

            TextColor(White);

    And before the exits description:

            TextColor(Yellow);

    And everything will be a lot nicer... :)
    To use this, you must implement the Play procedure, that will mantain the
  core of the game... For the meanwhile, all the Play procedure does is to
  output the description of the room...

             Procedure Play;
             Var ExitFlag:Boolean;
             Begin
                  ExitFlag:=False;
                  Look(CurrentRoom);
                  Repeat
                  Until ExitFlag;
             End;

    The ExitFlag is a flag that controls the exit of the game... When it is
  set to true, the game will cease it's execution... It can be put to true
  because the players quits or wins the game...
    Then you must add to the main program a call to this procedure, and you
  must define and initialize the CurrentRoom variable... CurrentRoom stores
  the current position of the player in the game, so I make it a variable of
  type Word... And you must initialize it to the value 22, because that is the
  starting room in FangLore...  Do that in the Init procedure...

    4.2. Player input and the command parser

    Now, it is the time to receive input from the user... This is were lots
  of game fail... Why ? Because their command parser suck !!!!
    What's the command parser ?
    Well, the command parser is a procedure (more like a bunch of procedures)
  that get the input of the user, process it (separating commands and
  subjects) and then executes the instructions required... Well, I think this
  is a quite nifty thing to do for a begginner, so I'll try to explain this
  well enough...
    First thing, you must receive the input of the player... You do that using
  the Readln keyword (most adventure games have their own routines to read
  player input, but I think that's a lot more complicated to explain simply
  here).

             ReadLn(Command);

    You put this in the Play command, after the call to the Look procedure.
    You must define the Command variable as a string...
    I gave a little color to this Readln statement in the Fanglore.Pas program:

             TextColor(LightRed);

    And I also put it a prompt, so that the player knows that the game is
  awaiting input:

             TextColor(White);
             Writeln('What is thy bidding, master ?');

    Then, the next step is to split that string in it's components... The ideia
  is to, given a string like this:

               'Get the mask'

    you get it splitted in only the words 'get' and 'mask'... The 'the' would
  vanish, like all the prenoms (I don't know if this word exists... It's similar
  to the portuguese one... :))).
    So, let's do a procedure that given a string, returns an array with the
  words separated:

             Procedure Parse(S:String;Var Parsed:Array[1..10] Of String);

    We use a procedure with a Var parameter instead of a function, because a
  function can't return a whole array... You can't even do it like it is
  above... You must define a type like this:

             Type ParsedType:Array[1..10] Of String;

    And then you can define the procedure like this:

             Procedure Parse(S:String;Var Parsed:ParsedType);

    Now, the first thing to to is to split the phrase in it's components...
  You achieve that by searching for the first space, and getting what's in
  the string from the beggining to the position of that space, and placing it
  in the array, and so forth...
    The complete procedure is like this:

             Procedure Parse(S:String;Var Parsed:ParsedType);
             Var ArrayIndex:Byte;
                 StringIndex:Byte;
                 NextSpace:Byte;
             Begin
                  ArrayIndex:=1;
                  StringIndex:=0;
                  NextSpace:=FindSpace(S,StringIndex);
                  While (StringIndex>=Length(S)) And (ArrayIndex<11) Do
                  Begin
                       NextSpace:=FindSpace(S,StringIndex);
                       Parsed[ArrayIndex]:=GetString(S,StringIndex,NextSpace);
                       StringIndex:=NextSpace;
                       Inc(ArrayIndex);
                  End;
             End;

    This parses the string... It uses two functions, FindSpace and GetString,
  whose code is given next... The first of these finds the next space after
  the given starting location, while the other grabs a part of a string.

             Function FindSpace(S:String;Start:Byte):Byte;
             Begin
                  While (S[Start+1]<>' ') And (Start<Length(S)) Do Inc(Start);
                  FindSpace:=Start;
             End;

             Function GetString(S:String;Start,Finish:Byte):String;
             Var A:Byte;
                 Tmp:String;
             Begin
                  Tmp:='';
                  For A:=Start+1 To Finish Do Tmp:=Tmp+S[A];
                  GetString:=Tmp;
             End;

    Don't to forget to call the Parse procedure after you read the command:

             Parse(Command,Parsed);

    And remember also to define the Parsed array:

             Var Parsed:ParsedType;

    Now, you have an array with the parsed command... You must now convert all
  to uppercase... Why ?
    Well, because you don't want the program to tell you to take a hike if you
  write 'get' or 'Get' instead of 'GET'... Don't forget that uppercase and
  lowercase are very different to the computer...
    So, let's use the Upper procedure to do so:

             Function Upper(S:String):String;
             Var Tmp:String;
                 A:Byte;
             Begin
                  Tmp:='';
                  For A:=1 To Length(S) Do Tmp:=Tmp+UpCase(S[A]);
                  Upper:=Tmp;
             End;

    The UpCase function is a standart Pascal function that converts a character
  that is in lowercase to uppercase, not affecting special simbols and the
  uppercase characteres... Call the Upper procedure for every element of the
  Parsed array, after you parse the command.
    Now, you should eliminate all the prenoms of the parsed array:

             Procedure EliminPrenoms(Var Parsed:ParsedType);
             Var A:Byte;
             Begin
                  For A:=1 To 10 Do
                  Begin
                       If Parsed[A]='THE' Then Elimin(Parsed,A);
                       If Parsed[A]='A' Then Elimin(Parsed,A);
                  End;
             End;

    You should make an If to every word you want eliminated... I don't recall
  any more at this time... The Elimin procedure is a procedure that given a
  variable of type ParsedType and an index number, it eliminates the word
  corresponding to that index... It works by filling that position of the
  array with the next position of the array, that is filled with the one after
  it, and so forth...

             Procedure Elimin(Var Parsed:ParsedType;Var Index:Byte);
             Var A:Byte;
             Begin
                  For A:=Index To 9 Do Parsed[A]:=Parsed[A+1];
                  Parsed[10]:='';
                  Dec(Index);
             End;

    The two variables in this procedure passed by reference (using the Var
  keyword), because we need to decrement the Index after each call to this
  procedure, because the procedure changes the parsed array... I'll do an
  example, imagining that the Parsed array has only four elements...
    Imagine that the phrase given was 'Spellcaster The The Best'. So, the
  parsed array would be like this:

         1:Spellcaster
         2:The
         3:The
         4:Best

    So, the EliminPrenoums would scan the parsed array, starting on index 1.
  Seeing the word 'Spellcaster', it would increase the index. Now index=2.
    Then, it would see the word 'The'... It would call the Elimin procedure,
  and the parsed array would be like this:

         1:Spellcaster
         2:The
         3:Best
         4:

    But, if you don't pass the Index by reference and decreased in the end of
  the Elimin procedure, the index would be incremented and would be equal to
  3 now, so it would skip the 2nd 'The'... So, if you decrease the Index, you
  will be again in Index=2, so the second 'The' is eliminated also... Cool,
  isn't it ?
    Don't forget to call the EliminPrenoms after the other parsing procedures.
    Now probably you have in index 1 of the array the main command, and in
  the other indexes you have the name of the object in which the action will
  be performed, etc...
    Now, you should have a flag that is set when a command is executed
  sucessfully, and when the variable is not set, you should give out an error
  message saying that  you didn't understand the command... Define it as a
  variable of type Boolean, and set it to false just after the Repeat keyword.
    Then, every time you execute a command, set it to true, and in the end,
  just before the Until keyword, check if it is true... If it isn't, type out
  an error message... You could even alter the error messages, so that the
  player don't grow tired of the same message over and over again !
  In the example program, the program selects randomly one of two 'Unknown
  command' irritating phrases... :)

    Now, let's implement two simple commands... The Look command and the Quit
  command... These should be easy to compreend... Just add the following lines
  to the program, after it parses the phrase:

             If Parsed[1]='LOOK' Then
             Begin
                  Valid:=True;
                  Look(CurrentRoom);
             End;
             If Parsed[1]='QUIT' Then
             Begin
                  Valid:=True;
                  TextColor(BrightRed);
                  Writeln;
                  Writeln('Are you sure you want to quit FangLore (Y/N) ?');
                  C:=ReadKey;
                  If UpCase(C)='Y' Then ExitFlag:=True;
             End;

    Define the variable C as a variable of type Char...
    And voila'... Two commands !! Fanglore is really shaping up... :)))

    4.3. Movement commands

    Well, this is fairly obvious to do... You just have to check if the
  corresponding word as been pressed (i.e. 'North', 'N', 'South', etc...), and
  if there is an exit that way, make sure it goes that way... If it goes that
  way, then look at the room... Coding wise:

             If (Parsed[1]='N') Or (Parsed[1]='NORTH') Then
             Begin
                  If Rooms[CurrentRoom].North=0 Then
                  Begin
                    TextColor(LightRed);
                    Writeln;
                    Writeln('Sorry, oh Great Lord, but I can''t go that way !');
                    Writeln;
                  End
                  Else
                  Begin
                    TextColor(LightRed);
                    Writeln;
                    Writeln('You go north...');
                    CurrentRoom:=Rooms[CurrentRoom].North;
                    Writeln;
                    Look(CurrentRoom);
                  End;
                  Valid:=True;
             End;

    And do likewise for the other three directions... Cool, isn't it ?
    Well, so you can look, quit and wander in Fanglore... In the next article
  of the series, I will delve into objects and their interection...
    Every piece of this issue's code is added to the FangLore program, that
  you should get with the ZIP file...


-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. How too make a 100% assembly program

    Ok, this is going to be a short article, since I'm writing this at 22:00,
  and I must wake up at 5 am tomorrow... I'm feeling sleepy just thinking
  about it...
    Well, this article is about coding an 100% assembly program, but I guess
  you knew that already because of the title... Well, I'll add some information
  that isn't on the title... When I say '100% assembly program' I mean a
  program out of Turbo Pascal !!!! Yuppy !!! No Pascal... Ehhehehe... Don't
  take me wrong, Pascal is a great language, but assembly is even better for
  _REAL_ coders... :)))) Ok... Enough for fun... Let's get started...

    First of all, I'll will show you the frame for the vast majority of
  assembler programs, and then I will explain it:

    DOSSEG
    .MODEL SMALL
    .286
    .STACK 200h

    .DATA

    .CODE

    START:
    END START

    You should use your favorite text editor (one that save in ASCII format...
  I usually use Turbo Pascal 6.0 !!! ) to type this up, then save this file
  with any name (example: Test.Asm).
    Then, if your using TASM, you should do something like this in the DOS
  prompt:

    TASM Test.Asm

    The program would compile and a new file, called Test.Obj would be
  created... Then you should LINK it (in other issue I will explain what this
  is)... You should use:

    TLINK Test.Obj

    Then, miracle of miracles ! The Test.Exe file will appear and you can
  execute it !!!!
    Now, I'll explain what each of those commands do...

    DOSSEG - Tells the compiler to organize the segments in your program like
             DOS and other high level languages (like Pascal and C) do, that
             is it creates three segments (code, data and stack).

    .MODEL - Tells the compiler in which model to compile.
             The model can be though as segment limitations...
             If the model is TINY, the code plus data must be smaller than
             64 Kb (this is, by definition, a COM file). If the model is
             SMALL, the code and the data (separetly) can have a maximum of
             64 Kb. Other modes are:

             MEDIUM:      Code > 64 Kb          Data < 64 Kb
             COMPACT:     Code < 64 Kb          Data > 64 Kb
             LARGE:       Code > 64 Kb          Data > 64 Kb
             HUGE:        Code > 64 Kb          Data > 64 Kb    Arrays > 64 Kb

             LARGE and HUGE models are equivelent, but the later is used by
             high level languages. It isn't of no use to us...

    .286 - Enables 286 instructions... It can be .386, .386P (for protected
           mode), etc... I usually use .386... If you don't put anything, the
           compiler will assume 8086 instructions...

    .STACK - Tells the compiler the size of the stack, in bytes... I will
             discuss the stack later. I usually use 200h (that's about
             512 bytes).

    .DATA - Tells the program that the following 'stuff' (you will see what is
            the stuff later) is to be placed in the data segment.

    .CODE - Tells the program that the following 'stuff' is to be placed in
            the code segment.

    START: - Tells the compiler where the main program begins.

    END START - Tells the compiler that this is the place where the code ends.

    I will explain most of this stuff in greater detail later. Right now, if
  you compiled this and run it, you would expect it to just do nothing and
  return to Dos... You're partially right... It doesn't do nothing, but it
  doesn't return to Dos either !!! Why ? Because assembler programs are so
  stupid that they must tell Dos that they are over... To do that, you must
  call the Dos interrupt 21h, function 4c, to end the code... So, to do nothing
  and return to dos, the program would have to be something like this:

    DOSSEG
    .MODEL SMALL
    .286
    .STACK 200h

    .DATA

    .CODE

    START:

         Mov AX,4c00h           ; This is a comment... Anything after a semi-
                                ; -colon will be ignored... Just to tell you
                                ; that 4c00h is an hexadecimal number...
         Int 21h

    END START

    The 'Mov AX,4c00h' command moves to the AX register (I think I already
  told you what a register is) the value 4c00 in hexadecimal (19456 in
  decimal)... I know I told you that the function number is 4c, but this is
  to be put in the high part of the word AX... The low part is the exit-code
  (in this case, it is 0) and is to be used in by parent programs, that is,
  programs that call other programs.
    The 'Int 21h' command, calls interrupt 21h (the Dos interrupt)... More on
  it later...
    Well, it's just 23:00... I think I'm going to bed know... I'll finish this
  tomorrow... Yawn !!! :)))
    Ok, I'm back... I fell a lot better now, after a good (but short) night of
  sleep... Now, I'll talk about the stack...

    The stack is a very important part in your program... It's a wonderfull
  place where you can store stuff you will need later. I've talked about the
  stack in issue 3 of 'The Mag'... Ehehehe... This was short...

    Now, about the 'stuff'... In assembler, you can also define variables,
  altough it is a bit more complicated subject... Variables in assembler
  are composed by an identifier (like Pascal identifiers), and a type:

    DB - Byte
    DW - Word
    DD - DoubleWord

    So, no strings or chars... Just plain numbers... Arrays are also defined
  in a different way... But, let's take a step at a time:

    Spell  DB 10
    VidSeg DW 0a000h
    Large  DD 010101010101001b

    These lines define three variables, named Spell, VidSeg and Large, of
  types Byte, Word and DoubleWord, respectively, and with initial values
  10, 0a000 (hexadecimal) and 010101010101001 (binary). If you don't want to
  assign an initial value, just put a '?' there... Example:

    Spell DB ?

    To access the value of a variable, you put the name of the variable
  in square brackets... Example:

            Mov AX,[VidSeg] -> This would load the value of variable VidSeg
                               in register AX... Remember that there exist
                               some commands that don't work with variables.

    One important thing to notice is that how data is organized in an asm.
    For example, imagine you made the program like this:

    .Data
    Spell  DB 10
    VidSeg DW 0a000h
    Large  DD 010101010101001b

    This means that the variables are in the Data segment. And now for the fun
  stuff... The address of variable Spell:

                       Segment: DS (Data Segment)
                       Offset : 0000

    Cool, isn't it ? But that's not all... Addresses of variables VidSeg and
  Large:

                   Seg(VidSeg)=DS        Seg(Large)=DS
                   Ofs(VidSeg)=0001      Ofs(Large)=0003

    What can you make of this ? That memory addressing is sequencial !!! That
  can be usefull, for accessing arrays... But I'll talk about them later. For
  now, let's talk about tables. Do you knew that you could have something like
  this:

                        Vector1 DB 10,20,30
                                DB 20,10,30
                                DB 30,30,20

    Imagine that as a triangle defined by it's corners, with it's (x,y,z)
  coordinates... You can have all that in just one variable. To access, for
  example, the y coordinate of the second corner (the 2nd value of the 2nd
  line), you would have to do something like this:

                   Mov AX,[Vector+4]

    If the variable was defined as Words (DW) and not bytes (DB), you would
  have to add 8 to the base address of the variable, that is stored in Vector.
  So, really, Vector is a pointer to the first element of data after it's
  definition... [Vector] is the real value of the element (in this case 10).
  Vector+1 would point to the 2nd element, Vector+2 to the 3rd, etc, while
  [Vector+1] would return the 2nd value, [Vector+2] the 3rd, etc...

    Now, let's go to arrays... To define a one-dimensional array, you use use
  the 'dup' keyword... Example, to define an array with 100 elements of type
  Word, you would do:

                  SpellArray DW 100 Dup(0)

    This would make an array initialized with zeros... If you wanted to create
  an unitialized array, you would have to use a '?' instead of a '0'.
    SpellArray will now point to the first element, etc...
    To define a two-dimensional array, you would use... well, I'd rather
  demonstrate... Imagine you want to define a 100x50 array (100 columns, 50
  lines). You would do it like this:

                  SpellArray DW 5000 Dup (?)   ; Because 100*50=5000

    Surprise !! No two-dimensional arrays... But you can use a trick to
  simulate them... Remember the first article I did on mode 13h ? Well, this
  is similar... Imagine youwant to access the element Xth element of line Y.
  You must multiply the Y for the size in columns of the array, and then add
  the X... For example:

           Mov AX,[SpellArray+Y*100+X] ; would load AX with the value of
                                       ; element desired.

    So, I think that it is all said now...
    Check out the example program FLSCR.ASM, that fills the screen in mode 13h,
  with random colors... Notice on how fast it is... :) God, I love assembler...
    Toy with it... In the worst case, you'll crash up your computer and nuke
  your hard drive (you don't knwo how easy is to do this... :))
    Cya in another article...

-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. Scrolling horizontaly in text mode

    Well, this article is made to order... :)
    It's quite easy to scroll horizontally text, altough it is complicated
  (I don't think it's even possible) to do it smoothly in text mode. By
  smoothly I mean to make appear a pixel at a time... I only know how to do
  scrolling in text mode making a character appear at a time (though I'm
  currently studying the possibility of doing this a pixel at a time... Not
  much success, though ! :).
  The ideia is this... You have a string S:

      S:='Spellcaster can''t code anything in Pascal, let alone ASM !';

    And you want to scroll it around a window with only 10 characteres wide.
    What you have to do is this:

             |          |
             |          |   <---- This represents the screen...
             |          |

             |          |
             |          |Spellcaster can''t code...... (the text is off screen)
             |          |

             |          |
             |         S|pellcaster can''t code......
             |          |

             and later you have:

             |          |
         Spel|lcaster ca|n''t code......
             |          |

    Get the picture ? You must choose what piece of text to display...
    So, some code (I'll use the GetString function from the article number 4).

             Program HScroll;

             Uses Crt;

             Const WhereX=10;    { X coordinate of scroller }
                   WhereY=10;    { Y coordinate of scroller }
                   NChars=10;    { Number of characters to display at one time }

             Var S:String;
                 Index:Byte;

             Function GetString(S:String;Start,Finish:Byte):String;
             Var A:Byte;
                 Tmp:String;
             Begin
                  Tmp:='';
                  For A:=Start+1 To Finish Do Tmp:=Tmp+S[A];
                  GetString:=Tmp;
             End;

             Begin
                  S:='Spellcaster can''t code in Pascal, let alone ASM ! ';
                  Index:=1;
                  Repeat
                        { Scroll the area we want to the left }
                        Move(Mem[$B800:((WhereY*80)+WhereX+1)*2],
                             Mem[$B800:((WhereY*80)+WhereX)*2],NChars*2);
                        { Write the character that enters by the right }
                        GotoXY(WhereX+NChars+1,WhereY+1);
                        Write(S[Index]);
                        Inc(Index);
                        If Index>Length(S) Then Index:=1;
                        { Put a small delay, so that you can actually read }
                        { something...                                     }
                        Delay(50);
                  Until KeyPressed;
             End.

    You can add a little color, if you want... It would be nicer... :)
  If you are wondering why you multiply by two when you are converting (X,Y)
  coordinates to an Offset value, you must know one thing:

    Text mode is very similar to graphics mode in the memory adressing with
  some differences:

    - Base address is B800, not A000 like in mode 13h
    - You have two bytes per character, not one byte per pixel.
      The first byte stores the ASCII value of the character, while the second
      byte stores the attributes. The attributes in text mode are: the
      foreground color (the text color), the background color, and if that
      character blinks or not...

      So, you can directly access Text Memory, and that's the reason why I
    multiply by two the calculations... The rest is similar to mode 13h.
      The attribute byte is organized like this:

          Bit No.   7 6 5 4 3 2 1 0

      Bits 0-3 are the foreground color (16 colors possible).
      Bits 4-6 are the background color (8 colors possible).
      Bit 7 is the blinking attribute (if it is set, the text will blink).

    I think this is fairly simple... Any doubts, mail me... :)

    A last minute correction... I've stated in the beggining of this article
  that I didn't think that scrolling smoothly in text mode was possible...
  Well, I was wrong... A friend of mine did it... At least, he says he did it.
  I didn't had the chance to see it, but I'll try to,  and to get the code
  also... If I understand it, I will do an article on that...


-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. Sprites Part II - Animation and movement

    So, let's continue our sprite tutorial... In this issue, we'll discuss
  basic animation and movement control... We'll use the procedures in the
  Sprite unit I gave last issue... Let's start with...

    7.1. Animating a sprite

    Animation is what you see in a cartoon, right ? WRONG ! In computer terms,
  an animation is... Well...er... it is, for example, a static objects that
  changes itself...
    But, what is animation really ? How can we create it ?
    Well, we use the same principle as the movies... Our eyes have a limit
  number of 'updates' that it can receive... By updates, I mean changes in the
  enviroment... I think the limit is 22 changes per second... The movies, for
  example, change the image 50 or 60 times a second, that is, the image is
  slightly altered 50 or 60 times a second... So, the eyes are deceiveds in
  thinking the image is moving... You change the image rapidly... But, you
  don't need to change the image 22 times a second to obtain an animation...
  You can change it just 1 time a second, or one time every 10 seconds... It
  doens't really matter, altough the movement is smoother if you update the
  image more frequently... But enough talk... Let's get to business...
    I've created two sprites of a spaceship, and they are slightly different
  from each other... So, I will display one after another on the screen, in
  the same coordinate, so you'll get the illusion of animation... The
  procedures are the same used in the last issue of The Mag...

             Program Animation;

             Uses Mode13H, Sprites, Crt;

             Var Images:Array[1..2] Of Pointer;
                 F:File;

             Begin
                  { Initialize graphics and palette }
                  InitGraph;
                  SetColor(1,63,0,0);
                  SetColor(2,63,40,0);
                  SetColor(3,63,63,0);
                  { Load images from a file }
                  Assign(F,'Ship.Img');
                  Reset(F,1);
                  LoadImage(F,Images[1]);
                  LoadImage(F,Images[2]);
                  Close(F);
                  { Animate them, until a keypressed occurs !! }
                  Repeat
                        PutImage(160,100,Images[1],VGA);
                        Delay(150);
                        PutImage(160,100,Images[2],VGA);
                        Delay(150);
                  Until Keypressed;
                  { Wraps things up }
                  KillImage(Images[1]);
                  KillImage(Images[2]);
                  CloseGraph;
             End.

    Not too impressive, I know... But it's a start... The next part is
  better...

    7.2. Moving a sprite

    Moving a sprite follows the same ideia as animating it... The ideia is
  to move the sprite, draw it, clear it, move it again, and so forth...
  Adapting to the last piece of code... Let's define two more variables,
  X and Ix, of type Word and ShortInt. Initialize variable X with the value 0,
  and variable Ix with value 1. Then, replace the Repeat Until cicle with...
  Or, stuff it, I'll dump here the full code:

            Program Movement;

            Uses Mode13H, Sprites, Crt;

            Var Images:Array[1..2] Of Pointer;
                F:File;
                X:Word;
                Ix:ShortInt;

            Begin
                 { Initialize graphics and palette }
                 InitGraph;
                 SetColor(1,63,0,0);
                 SetColor(2,63,40,0);
                 SetColor(3,63,63,0);
                 { Load image from a file }
                 Assign(F,'Ship.Img');
                 Reset(F,1);
                 LoadImage(F,Images[1]);
                 Close(F);
                 { Move it, until a keypressed occurs !! }
                 X:=0;
                 Ix:=3; { The bigger the Ix, the faster the sprite will move }
                 Repeat
                       { Draw it }
                       PutImage(X,100,Images[1],VGA);
                       Delay(50);
                       { Clear it }
                       Cls(0,VGA);
                       { Update it }
                       X:=X+Ix;
                       If X>=300 Then Ix:=-3;
                       If X=0 Then Ix:=3;
                 Until Keypressed;
                 { Wraps things up }
                 KillImage(Images[1]);
                 KillImage(Images[2]);
                 CloseGraph;
            End.

    Well, this flickers a lot... The way to mend this is by using virtual
  screens, or clear just the part of the screen... You should be able to do
  that yourself... I'm so lazy today...

    7.3. Doing it all together

    So, you know how to move it and you know how to animate it... But do you
  know how to do it at the same time ?
    This is easy... For each frame it draws, it shows a picture that has been
  both moved and animated... Let's see some code, because I'm too lazy to
  explain it... and because this is soooo easy:

          Program Animation_And_Movement;

          Uses Mode13H, Sprites, Crt;

          Var Images:Array[1..2] Of Pointer;
              F:File;
              X:Word;
              Ix:ShortInt;
              Pic:Byte;

          Begin
               { Initialize graphics and palette }
               InitGraph;
               SetColor(1,63,0,0);
               SetColor(2,63,40,0);
               SetColor(3,63,63,0);
               { Load images from a file }
               Assign(F,'Ship.Img');
               Reset(F,1);
               LoadImage(F,Images[1]);
               LoadImage(F,Images[2]);
               Close(F);
               { Animate them, until a keypressed occurs !! }
               X:=0;
               Ix:=1; { The bigger the Ix, the faster the sprite will move }
               Pic:=1;
               Repeat
                     { Draw it }
                     PutImage(X,100,Images[Pic],VGA);
                     Delay(50);
                     { Clear it }
                     Cls(0,VGA);
                     { Update it }
                     X:=X+Ix;
                     If X>=300 Then Ix:=-3;
                     If X=0 Then Ix:=3;
                     If Pic=1 Then Pic:=2 Else Pic:=1;
               Until Keypressed;
               { Wraps things up }
               KillImage(Images[1]);
               KillImage(Images[2]);
               CloseGraph;
          End.

    So, here you have it... This is very easy...
    Let's wrap it up... In next tutorial about sprites, I'll teach you how to
  make this move over a background, transparency, and maybe a couple of
  tricks...


-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. Graphics Part VIII - Text in graphic mode

    8.1. Basics

    Text in graphic mode is very different from text in text mode (as the name
  implies)... You can't use the standart function Writeln (that is, you can,
  but it will look UGLY !), nor can you use the Readln (you must program your
  own function to read input in graphic mode).
    It's fairly easy to do text in graphics mode. You just have to take in
  account WHAT do you want to print, WHERE do you want to print it, and with
  what type of character... Yes, you can use various kinds of characters.
  That's one the advantages of using text in graphics.
    To the diferent types of characters (that is, every charset) you call
  a font.
    There are several kinds of fonts:

      - Fixed sized fonts -> As the name implies, the characters have all the
                             same width and height, not to mention color.

      - Proportional fonts -> Characters can have different widths and heights,
                              altough usually they only change in width. The
                              color is the same in all character.

      - Colorfonts -> Colorfonts can be fixed sized or proportional. The only
                      difference between this and the last ones I mentioned,
                      is that the characters can have different colors... Not
                      different from each others, it's different inside them.
                      You'll see what I mean latter...

    Graphical text can be very important in any kind of program you make;
  almost every demo has a scroller (I'll do an article on them someday), every
  game shows the score or the briefing or something... Trust me... They are
  important... So, let's get down and dirty...

    8.2. Fixed sized fonts

    The principle behind this is easy: you use the theory behind the sprite,
  changing the procedures a little (you don't need to read the size of the
  sprite, it's already set; you don't put different colors; you don't use
  pointers, etc...). Let's check out the procedures... Oh, I almost forgot...
  You must create a structure that lets you store the various characters:

            Const XSize=16;
                  YSize=16;

            Type FSFont=Array[1..XSize,1..YSize] Of Byte;
                 Chars=array[' '..''] Of FSFont;

            Var Font:Chars;

    In case you're wondering, you can define an array like that... You access
  the elements of the array, like this, for example:

                      Font['A',10,10]:=10;

                      Or

                      C:Char;
                      ..........
                      ..........
                      C:='A';
                      Font[C,10,10]:=10;

    So, let's read the font:

              Procedure LoadFont(Filename:String);
              Var F:File;
                  Garbage:Word;
              Begin
                   Assign(F,Filename);
                   Reset(F,1);
                   Blockread(F,Garbage,2);
                   Blockread(F,Garbage,2);
                   Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4);
                   Close(F);
              End;

    The structure of the file I'm using has it's first four bytes indicating
  the X and Y size of the font (you might need it... In this case it isn't
  needed, so we read them to discardable variables: Garbage). Then it has
  a sequence of bytes indicating if a certain point is on or off... Caution:
  in this case, that isn't the color, but if the pixel is draw or not... We'll
  get to colorfonts soon...
    I'll just explain the instruction that really reads the data:

         Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4);

    Well, I think I've already talked about the Ptr function... Well, the Ptr
  function is usefull to transforms any structure into a pointer. Basically
  what it does is transform two values (segment and offset) in a pointer. If
  you supply the two values as beeing the segment and offset of an existing
  structure (as you do above), and then read to that place in memory, the data
  will be read into that structure... That's quite useful, as you can see... :)
    So, let's move on... Putting down a letter... Nothing could be easier:

        Procedure PutLetter(X,Y:Integer;Col:Byte;N:Char;Where:Word);
        Var Cfx,Cfy:Byte;
            Ccc:Byte;
        Begin
             For Cfy:=1 To YSize Do
             Begin
                  For Cfx:=1 To XSize Do
                  Begin
                       Ccc:=Font[N,Cfy,Cfx];
                       If Ccc<>0 Then PutPixel(X+Cfx-1,Y+Cfy-1,Col,Where);
                  End;
             End;
        End;

    Well, what the hell is this ??? Simple... You just scan the array for
  the character you want, then the computer scans the X and Y indexes of that
  particular character... When it finds an element different of 0, it places
  a pixel of the specified color in the VGA screen or in any of the virtual
  screens... Quite easy, I think...
    To write a string, you just have to go through the string, writing the
  characters it founds in sequence. Notice that the increment of the X
  coordinate is fixed, because all the letters have the same size:

        Procedure PutString(X,Y:Integer;Col:Byte;N:String;Where:Word);
        Var Index:Byte;
        Begin
             For Index:=0 To Length(N)-1 Do
               PutLetter(X+Index*16,Y,Col,N[Index+1],Where);
        End;

    I've included a sample font, to use with the example program, but that font
  only contains uppercase letters, so use if wisely, my sons... I intend to
  release a font editor/grab, together with a sprite editor/graber... Maybe
  I'll ask help from anyone, since I don't have enough time to do all I want...
    But, without further due, here's a complete working example:

            Program FixedSizedFont;

            Uses Crt,Mode13h;

            Const XSize=16;
                  YSize=16;

            Type FSFont=Array[1..XSize,1..YSize] Of Byte;
                 Chars=array[' '..''] Of FSFont;

            Var Font:Chars;

            Procedure LoadFont(Filename:String);
            Var F:File;
                Garbage:Word;
            Begin
                 Assign(F,Filename);
                 Reset(F,1);
                 Blockread(F,Garbage,2);
                 blockread(F,Garbage,2);
                 Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4);
                 Close(F);
            End;

            Procedure PutLetter(X,Y:Integer;Col:Byte;N:Char;Where:Word);
            Var Cfx,Cfy:Byte;
                Ccc:Byte;
            Begin
                 For Cfy:=1 To YSize Do
                 Begin
                      For Cfx:=1 To XSize Do
                      Begin
                           Ccc:=Font[N,Cfy,Cfx];
                           If Ccc<>0 Then PutPixel(X+Cfx-1,Y+Cfy-1,Col,Where);
                      End;
                 End;
            End;

            Procedure PutString(X,Y:Integer;Col:Byte;N:String;Where:Word);
            Var Index:Byte;
            Begin
                 For Index:=0 To Length(N)-1 Do
                   PutLetter(X+Index*16,Y,Col,N[Index+1],Where);
            End;

            Begin
                 LoadFont('Fixed.Fnt');
                 InitGraph;
                 SetColor(1,63,63,0);
                 SetColor(2,63,0,0);
                 SetColor(3,0,63,0);
                 SetColor(4,0,63,63);
                 PutString(0,0,1,'THIS IS A TEST !',VGA);
                 PutString(0,30,2,'SPELLCASTER RULES !',VGA);
                 PutString(0,60,3,'I KNOW MODESTY IS',VGA);
                 PutString(0,90,4,'NOT ONE OF MY',VGA);
                 PutString(0,120,5,'QUALITIES...',VGA);
                 Repeat Until Keypressed;
                 Closegraph;
            End.

    So, if this is understood, let's move forward... If it isn't, read it
  again, and experiment !!! Experimentation is the key to good programming
  practice... :)
    I think I'll stop here for now... I'm sleepy (I just write this at night,
  after college, so it's very tiring)... I'll write the next stuff tomorrow...

    8.3. Proportional fonts

    I'm back !!! Well, where was I... er... Oh... Proportional fonts... So,
  what's a proportional font ?
    To answear simply... It's a font that isn't fixed in size... So, the x and
  y sizes of the font varie from character to character... Evidently, we can
  not use the same structure as above. But, if the bitmaps for the chars are
  of different sizes, why not use the sprite system implemented in last
  issue ? So, for all chars:

                  Type Chars=array[' '..''] Of Pointer;

    So, you read the font to the array of pointers, using the LoadImage
  function that I gave last issue. So, after you read the chars that matter
  (in the example program I'll give, I only read some chars, because I didn't
  had time to draw more chars), you must write them out... To draw _ONE_ char,
  you use the PutImage_C procedure (we use the clipping procedure because we
  don't know if the text is wider than the screen).
    So, the real difficulty is in writing a string. So, when the font was fixed
  sized, you just used the formula:

                       X:=Xi+CharNumber*XSize

    to find out the X coordinate of the character indexed by CharNumber. Xi is
  the original X coordinate (the coordinate of the first character), and XSize
  is the horizontal size of the font. So, if the horizontal size varies, this
  formula must varie. Let's use a variable, that increases with the size of
  the font. For example, we initialize that variable to Xi in the start of the
  WriteString procedure:

                        X:=Xi;

    Then, imagine the first character was 20 pixels wide. We draw it, and then
  add that number to var X:

                        X:=X+XSize(FirstCharacter)+Space

    And so forth... The Space is any number that specifies the number of pixels
  computer should leave between chars... If you don't put it there, the letters
  will be all crunched together...
    See ? Easy... Let's see some _CODE_ !!! :)))
    Oooopppppssss... I almost forgot... We must change the PutImage_C procedure
  for two reasons:

      1) What is saved in the file is whetever a certain pixel is on or off,
         not it's color.
      2) This procedure erases all background image, and usually text is output
         on the top of an image or something.

    When can fix up both problems in just one sitting:

           Procedure PutChar(X,Y:Integer;Var Img:Pointer;Where:Word);
           Var Dx,Dy:Word;
               A,B:Word;
               Segm,Offs:Word;
           Begin
                Segm:=Seg(Img^);
                Offs:=Ofs(Img^);
                Move(Mem[Segm:Offs],Dx,2);
                Move(Mem[Segm:Offs+2],Dy,2);
                Offs:=Offs+4;
                A:=Y;
                While (A<=Y+DY-1) And (A<MaxY) Do
                Begin
                     B:=X;
                     While (B<=X+DX-1) And (B<MaxX) Do
                     Begin
                          If (X>=MinX) And (Y>=MinY) Then
                          { Check if the pixel is to be set or not }
                          If Mem[Segm:Offs]<>0 Then
                              PutPixel(B,A,Mem[Segm:Offs],Where);
                          Inc(Offs);
                          Inc(B);
                     End;
                     Inc(A);
                End;
           End;

    Another thing we'll need is a function that returns the X size of a
  certain character. This is easy:

          Function GetX(Img:Pointer):Word;
          Var Dx:Word;
              Segm,Offs:Word;
          Begin
               Segm:=Seg(Img^);
               Offs:=Ofs(Img^);
               Move(Mem[Segm:Offs],Dx,2);
               GetX:=Dx;
          End;

    Well, this is excessive programming, but... :))) It's easier to
  understand...
    So, the example program:

                Program Proportional;

                Uses Crt,Sprites,Mode13h;

                Type Chars=array[' '..''] Of Pointer;

                Var Font:Chars;

                Procedure ReadFont;
                Var F:File;
                    A:Char;
                Begin
                     Assign(F,'Proport.Fnt');
                     Reset(F,1);
                     LoadImage(F,Font[' ']);
                     LoadImage(F,Font['!']);
                     LoadImage(F,Font[',']);
                     LoadImage(F,Font['-']);
                     LoadImage(F,Font['.']);
                     For A:='0' To '9' Do LoadImage(F,Font[A]);
                     For A:='A' To 'Z' Do LoadImage(F,Font[A]);
                     Close(F);
                     { I just loaded some characters because the file only }
                     { had those characters, saved in that order.          }
                End;

                Procedure PutChar(X,Y:Integer;Col:Byte;N:Char;Where:Word);
                Var Dx,Dy:Word;
                    A,B:Word;
                    Segm,Offs:Word;
                    Img:Pointer;
                Begin
                     Img:=Font[N];
                     Segm:=Seg(Img^);
                     Offs:=Ofs(Img^);
                     Move(Mem[Segm:Offs],Dx,2);
                     Move(Mem[Segm:Offs+2],Dy,2);
                     Offs:=Offs+4;
                     A:=Y;
                     While (A<=Y+DY-1) And (A<MaxY) Do
                     Begin
                          B:=X;
                          While (B<=X+DX-1) And (B<MaxX) Do
                          Begin
                               If (X>=MinX) And (Y>=MinY) Then
                               { Check if the pixel is to be set or not }
                               If Mem[Segm:Offs]<>0 Then
                                 PutPixel(B,A,Col,Where);
                               Inc(Offs);
                               Inc(B);
                          End;
                          Inc(A);
                     End;
                End;

                Function GetX(Img:Pointer):Word;
                Var Dx:Word;
                    Segm,Offs:Word;
                Begin
                     Segm:=Seg(Img^);
                     Offs:=Ofs(Img^);
                     Move(Mem[Segm:Offs],Dx,2);
                     GetX:=Dx;
                End;

                Procedure PutString(X,Y:Integer;Col:Byte;N:String;Where:Word);
                Var Index:Byte;
                    Dx:Word;
                Begin
                     Dx:=X;
                     For Index:=0 To Length(N)-1 Do
                     Begin
                          PutChar(Dx,Y,Col,N[Index+1],Where);
                          Dx:=Dx+GetX(Font[N[Index+1]])+3;
                     End;
                End;

                Begin
                     ReadFont;
                     InitGraph;
                     SetColor(1,63,63,0);
                     SetColor(2,63,0,0);
                     SetColor(3,0,63,0);
                     SetColor(4,0,63,63);
                     PutString(0,0,1,'GRABING',VGA);
                     PutString(0,50,2,'PROPORTIONAL',VGA);
                     PutString(0,100,3,'FONTS',VGA);
                     PutString(0,150,4,'SUCKS !',VGA);
                     Repeat Until Keypressed;
                     Closegraph;
                End.

    Again, don't use lowercase letters, because they aren't drawn...
    The problem of proportional fonts is that they must well designed, or else
  it will happen to some chars the same as happened to 'u' in the example
  program...
    Proportional fonts are just a variation of fixed sized fonts... Easy and
  effective... Personally, I hate proportional fonts... But if you have enough
  pacience to work with them, go ahead... They always look good...
    Well, I'm going to bed again... I'll finish this tomorrow... It's almost
  2 am... And I must rise at 5 am... LIFE SUCKS !!!! :))) Cya tomorrow... ;)

    8.4. Colorfonts

    Ok, back again... I'm really getting fed up with this issue of 'The Mag',
  so I'm gonna wrap this up really quickly... :))))

    Colorfonts are fonts with colors in the same char... So, when you store a
  colorfont, you don't store only if the pixel is on or off, you store it's
  color... So, as I want to get this over, I'll just dump some code onto
  your screen... This is a complete example program, that shows how fixed
  sized colorfonts work:

            Program ColorFonts;

            Uses Crt,Mode13h;

            Const XSize=16;
                  YSize=16;

            Type FSFont=Array[1..XSize,1..YSize] Of Byte;
                 Chars=array[' '..''] Of FSFont;

            Var Font:Chars;
                CFontPal:RGBList;

            Procedure LoadFont(Filename:String);
            Var F:File;
                Garbage:Word;
            Begin
                 Assign(F,Filename);
                 Reset(F,1);
                 Blockread(F,Garbage,2);
                 blockread(F,Garbage,2);
                 Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4);
                 Close(F);
            End;

            Procedure PutLetter(X,Y:Word;N:Char;Where:Word);
            Var Cfx,Cfy:Byte;
                Ccc:Byte;
            Begin
                 For Cfy:=1 To YSize Do
                 Begin
                      For Cfx:=1 To XSize Do
                      Begin
                           Ccc:=Font[N,Cfy,Cfx];
                           If Ccc<>0 Then PutPixel(X+Cfx-1,Y+Cfy-1,Ccc,Where);
                      End;
                 End;
            End;

            Procedure PutString(X,Y:Integer;N:String;Where:Word);
            Var Index:Byte;
            Begin
                 For Index:=0 To Length(N)-1 Do
                   PutLetter(X+Index*16,Y,N[Index+1],Where);
            End;

            Begin
                 LoadFont('CFont.Fnt');
                 { Load a palette to use with the font }
                 LoadPal('CFont.Pal',CFontPal);
                 InitGraph;
                 SetPalette(CFontPal);
                 PutString(0,0,'THIS IS A TEST !',VGA);
                 PutString(0,30,'SPELLCASTER RULES !',VGA);
                 PutString(0,60,'I KNOW MODESTY IS',VGA);
                 PutString(0,90,'NOT ONE OF MY',VGA);
                 PutString(0,120,'QUALITIES !!!',VGA);
                 Repeat Until Keypressed;
                 Closegraph;
            End.

    I know I'm lazy... :))
    This program reads a palette for the color font... You get the ideia when
  you run it...
    Try the program out and experiment stuff, like scrolling text and other
  stuff like that... Scrollies are a part in almost every demo made by men
  and aliens... And colorfonts are usually a part of them... So, I say
  goodbye for now...


-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. Hints and Tips

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

    This issue's Hints and Tips will go deeper into the subject of using
  logic operators with "normal" numbers.

    - Odd or Even ? (**)

      Imagine you want to find out if a certain number is odd or even.
      Think of the internal representation of the numbers 50 and 51:

               50 = 00110010
               51 = 00110011

      Have you seen the difference ? An even number has it's rightmost bit
      unset and an odd number has it set... So, how can you check for it ?
      Simple, use the AND operand:

               A:=50 AND 1

      Now, A is equal to 0, that means that the number is even.
      So, what's the ideia ?

                     50 = 00110010
                      1 = 00000001
               50 AND 1 = 00000000

      If we used 51:

                     51 = 00110011
                      1 = 00000001
               51 AND 1 = 00000001

      51 AND 1 would return 1 !!! Cool, isn't it ?

    - Positive or negative ? (**)

      In the internal conception, integer numbers are represented with 15 bits
      for the value and 1 bit to act as a flag for positive/negative.
      So, knowing that the signal bit is the leftmost, we can use a method
      similar to the one we used to determine if the number was odd or even.
      For example:

                        -50 = 1000000000110010
                         -0 = 1000000000000000

      I know that -0 doesn't really exist, but in computer terms it exists.
      Notice this:

                         -0 = 1000000000000000
                              ||-------------|
                     Signal Bit   Value Bits

      So, continuing with the explanation:

                          -50 AND -0 = -0

      Getting a -0 means the number is negative. Otherwise the "function"
      returns a +0.
      Well, but in Pascal you can't use -0 as an operand, so how do you
      resolve the problem ?
      Well, as Pascal is a very flexible language , it can mix integers with
      words (unsigned integers), so integer (-0) is equal word 32768. So:

                      -50 = 1000000000110010
                    32768 = 1000000000000000
            -50 AND 32768 = 1000000000000000

      So, if the result is equal to 32768, then the number is negative...

      These are two of the many effects you can do with this kind of algebra.
      You can even compare numbers... But I'll leave that for next issue...
      I'm tired and I want to go to bed... Yawn...
      Just before I fall asleep in front of the keyboard,  I'll just tell you
      that this kind of think is called masking... You are masking bits
      (covering them up)... Just for the record, it was Scorpio who reminded
      me of these effects... Cya in another article...


-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. Points of View

    God, I'm tired of this issue... It was a struggle to make this, because
  I'm in the middle of exams and all... But, finally it's over...
    This was the biggest of all issues of 'The Mag' so far... The Mag is
  growing... Next issue, the continuation of the Text Adventure tut, plus an
  article on how to make the flame effect (made by Scorpio... I'll just add
  somethings I was meaning to do for ages...). Also, I'll continue to do
  the articles on Sprites (transparency and moving over a background)... On
  the mode 13h tutorials, I'll teach how to convert the procedures to
  assembler... Yep, that's right... Full assembly procedures in next issue...
  I'll probably start basic 3d stuff, just to introduce a couple of effects
  that use standart 3d transformations... I'll also teach how to do 2d
  rotation of a sprite... Er... I think that's it... Maybe I'll remember
  something else to put there... Maybe I'll convince Scorpio to do some
  article (eheheh... Did you hear me, Scorpio ???? :)))) ).
    Well, That's all the time I've got for this issue... I'm already receiving
  death threat's by people complaining about the long delay on this issue...
  Oh, just a bit of publicity... Get the 'Infinity Mag, Issue 1'... That's a
  portuguese mag made by people in the portuguese demoscene, about the
  demoscene... Issue 2 is almost out, and both issues include my articles of
  'The Mag'... So, stay tuned and get it... It's pretty cool... Altough it
  requires a computer with VESA... But the look is great... Download it and
  see... You can get it at the ftp.cdrom.com site (it was in the
  pub/demos/incoming/mags directory when I last checked, but maybe Snowman
  moved it from there... If you can't find it in that dir, try the
  pub/demos/mags/1996... The filename is infy1.zip). You can also try the
  Realm Of Darkness BBS... The numbers are somewhere in this issue or in the
  info file... So, bye bye folks... See you in super-special-issue 10 !! :)


-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. The adventures of Spellcaster, the rebel programmer of year 2018.

  Episode 9 - Living on a razor's edge (sometimes cut)

    As predicted by Deathripper, Kristie was back before supper, but she
  refuse to say even a word to me...
  - Let's get over the plan again... - I said, looking at the Gundsen Brothers.
  - Again ?!! - said finally Kristie, in a loud voice. - How many times will
    we have to go over that blasted plan ?!! I'm sick and tired of it !! You
    make it sound like it is complicated !
  - Hey, I just don't want any complications... - I responded in a calm voice.
  - Knock it off, you two ! - Karl said. - Can't you be a little time together
    without killing each other ?
  - It's not my fault that this no-good programmer keeps treating use like
    morons !!! - Excalibur said.
  - No good-programmer ? - I responded, in an angry tone. - Twice you called
    me a lammer, Kristie... If you keep up like this, I will walk away and you
    can both blow Comptel alone... If you can without me...
  - Well, mister Shitcaster... If that's the way you think, go right ahead...
    Good ridance... - Kristie said, looking me in the eyes... For a moment, I
  thought about swallowing my pride, and confess to her my love, but I
  restrained myself and I turn my back to both of them and walked away.
  - Good work, Kristie... - Gundsen said.
  - Well, Karl, what are we supposed to do now ?
  - He'll be back... - Karl said, with a smile on his face
    Kristie just looked at Karl, with a sad look on her face.
  - I don't think he hates you... - said Karl, laughing and turning his back
  on Kristie, who looked very embaressed...

    Again, Karl's previsions turned out right... In the morning after, the
  morning of the attack to the COHISS, I was waiting for them near the jetcar.
  - You came back ! - said Excalibur, excited, calming down in the next
  second. - I mean... It's about time ! - she continued in a stern voice.
  - I want to destroy Comptel... And I don't care who does it with me... - I
  answered.
  - So, let's go ! - Karl said, going into the car...
    Kristie led the car, until we arrived at 100 meters from the COHISS: a
  small shop, full of hitech equipment... A small fortress... One meter of
  solid cement, and lot's of guards with lazer machine-guns on the roof.
  - So, this is it... Are you sure you want to go throuht with this ? - I
  questioned.
  - Yep... - Karl said, without hesitating.
  - As damn sure as I'm gonna be... - Kristie answered, pressing the accelerator
  pedal to the ground, directly aimming the car at the COHISS's door...
    The guards on the roof opened fire, but the armour of our vehicle holded
  on, and we crashed against the door !
    The door collapsed with the strength of the Durallium block. We went off
  the car, with lazer riffles in our hands and we started to defend ourselfs
  from the guards, while Kristie loaded the car with the stuff we needed.
    In less than ten minutes, the vehicle was loaded, and we started to hear
  in the distance the sound of a airship comming in our direction. Almost
  twenty guards laid in front of us, dead, dripping blood from every wound.
  - Let's go !! Reinforcements are coming ! - Karl shouted, running for the
  car.
    Suddently, one of the dead guards wasn't so dead after all, and he lifted
  himself up, and he shot Karl right in the chest... He screamed and fell on
  the ground. I opened fire on the guard and hitted him in his head, making
  blood spill all over the place.
  - NOOOOOOOO !!!! - Me and Kristie shouted simultaneosly.
  - Shit ! - I said, running for Karl's body. He was still breathing... At
  some cost, I managed to pull him to the car, and then Kristie accelerated.
  We had killed almost every guard in the place, and running from the others
  was easy... Difficult was to outrace the above airship, but even that was
  simple, because we went into narrow streets, where it couldn't follow.
  - Hang on Karl... - I said, holding Karl's hand. He looked at me, with pain
  and agony visible in his eyes.
  - Take care of Kristie... - he said, almost in a whisper.
  - Hang on just a little bit more. - I said.
    Kristie continued to speed up the jetcar in the streets, trying to reach
  out headquarters, before it was too late...

                                             See you in the next issue
                                            Diogo "SpellCaster" Andrade