home *** CD-ROM | disk | FTP | other *** search
/ SuperHack / SuperHack CD.bin / CODING / MISC / MAG09.ZIP / MAG09.TXT < prev    next >
Encoding:
Text File  |  2010-12-05  |  84.1 KB  |  1,997 lines

  1. Spellcaster presents:
  2.  
  3.  
  4. TTTTTTTTTT HH      HH EEEEEEEEEE    MM      MM    AAAA     GGGGGGGGG
  5.     TT     HH      HH EE            MMM    MMM   AA  AA   GG
  6.     TT     HH      HH EE            MM M  M MM  AA    AA  GG  
  7.     TT     HHHHHHHHHH EEEEEE        MM  MM  MM  AAAAAAAA  GG   
  8.     TT     HH      HH EE            MM      MM  AA    AA  GG    GGGG
  9.     TT     HH      HH EE            MM      MM  AA    AA  GG      GG
  10.     TT     HH      HH EEEEEEEEEE    MM      MM  AA    AA   GGGGGGGG
  11.  
  12.                                                         Issue 9
  13.                                                         20-7-96
  14.  
  15.  
  16. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  17.  
  18.  
  19.   Index:
  20.  
  21.         1. Introduction
  22.           1.1. About the magazine
  23.           1.2. About the author
  24.           1.3. Distribution
  25.           1.4. Subscribing
  26.           1.5. Contribuitions
  27.           1.6. Hellos and greets
  28.         2. Mailroom
  29.         3. The Turbo Assembler Bible
  30.         4. Designing a text adventure - Part II
  31.           4.1. Looking at a room
  32.           4.2. Player input and the command parser
  33.           4.3. Basic movement commands
  34.         5. How to make a 100% assembly program
  35.         6. Scrolling horizontaly in text mode
  36.         7. Sprites Part II - Animation and movement
  37.           7.1. Animating a sprite
  38.           7.2. Moving a sprite
  39.           7.3. Doing it all together
  40.         8. Graphics Part VIII - Text in graphic mode
  41.           8.1. Basics
  42.           8.2. Fixed sized fonts
  43.           8.3. Proportional fonts
  44.           8.4. Colorfonts
  45.         9. Hints and tips
  46.        10. Points of view
  47.        11. The adventures of Spellcaster, part 9
  48.  
  49.  
  50. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  51.  
  52.  
  53.   1. Introduction
  54.  
  55.     1.1. About the magazine
  56.  
  57.     Welcome to number 9 of 'The Mag', brought to you, as usual, by Spellcaster,
  58.   alias Diogo de Andrade. Big issue... Again... This is becomming a hobbit...
  59.   Ooopss... Sorry, a habbit... :))) I'm reading Tolkien, so this is an
  60.   understandable mistake... :)
  61.     This issue is _VERY_ late... This is another hobbit... :)) It is late
  62.   because I have lots of projects and exams to do and I don't have the time.
  63.     Also, this issue features another article by Scorpio, which like the last
  64.   one, is pretty different from the usual...
  65.  
  66.     You should be reading this issue in the first of the SpellUtilities,
  67.   SpellView ! It's a program designed to view text with some features, like
  68.   colors and other neat stuff... :)
  69.     You probably can get it from the same place you got this issue, but if you
  70.   can't, look in the BBS's listed somewhere in this issue, or look in my
  71.   HomePage... It includes full source code, so you can change it, and see how
  72.   it was done... :)
  73.  
  74.     Well, thinking better, don't use SpellView... SpellView SUCKS ! I only
  75.   now noticed that it screws up with files with a certain size... And this
  76.   issue is very big for SpellView to read it... Shit... Well, back to the
  77.   old drawing board... :(((
  78.  
  79.     Ok, other notice... You can use SpellView, but only version 1.2... That's
  80.   a fixed version that saves up some memory... It can read files almost three
  81.   times larger than version 1.1 could... :)))
  82.  
  83.     This magazine is dedicated to all the programmers and would-be programmers
  84.   out there, especially to those that can't access the Net easily to get
  85.   valuable information, to those who wish to learn how to program anything,
  86.   from demos to games, passing through utilities and all sort of thing your
  87.   mind can think of, and to those that can't find the right information.
  88.  
  89.     When you read this magazine, I'll assume some things. First, I assume you
  90.   have Borland's Turbo Pascal, version 6 and upwards (and TASM for the assembly
  91.   tutorials). I'll also think you have a 80386 (or 386 for short; a 486 would
  92.   be even better), a load of patience and a sense of humor. This last is almost
  93.   essencial, because I don't receive any money for doing this, so I must have
  94.   fun doing it. I will also take for certain you have the 9th grade (or
  95.   equivelent). Finally, I will assume that you have the last issues of
  96.   'The Mag', and that you have grasped the concepts I tried to transmit. If
  97.   you don't have the issues, you can get them by mail, writing to one of the
  98.   adresses shown below (Snail mail and Email).
  99.  
  100.     As I stated above, this magazine will be made especially for those who don't
  101.   know where to get information, or want it all in the same place, and to those
  102.   who want to learn how to program, so I'll try to build knowledge, building up
  103.   your skills issue by issue. If you sometimes fail to grasp some concept, don't
  104.   despair; try to work it out.
  105.     That's what I did... Almost everything I know was learnt from painfull
  106.   experience. If you re-re-re-read the article, and still can't understand it,
  107.   just drop a line, by mail, or just plain forget it. Most of the things I 
  108.   try to teach here aren't linked to each other (unless I say so), so if you
  109.   don't understand something, skip it and go back to it some weeks later. It
  110.   should be clearer for you then. Likewise, if you see any terms or words you 
  111.   don't understand, follow the same measures as before.
  112.  
  113.     Ok, as I'm earing the Net gurus and other god-like creatures talking
  114.   already, I'm just going to explain why I use Pascal.
  115.   For starters, Pascal is a very good language, ideal for the beginner, like 
  116.   BASIC (yech!), but it's powerfull enough to make top-notch programms.
  117.   Also, I'll will be using assembly language in later issues, and Pascal makes
  118.   it so EASY to use. 
  119.   Finally, if you don't like my choice of language, you can stop whining. The
  120.   teory behind each article is very simple, and common with any of the main
  121.   languages (C, C++, Assembly - Yes, that's true... BASIC isn't a decent
  122.   language).
  123.  
  124.     Just one last thing... The final part of the magazine is a little story
  125.   made up by my distorted mind. It's just a little humor I like to write, and
  126.   it hasn't got nothing to do with programming (well, it has a little), but, 
  127.   as I said before, I just like to write it.
  128.  
  129.     1.2. About the author
  130.  
  131.     Ok, so I'm a little egocentric, but tell me... If you had the trouble of 
  132.   writing hundreds of lines, wouldn't you like someone to know you, even by 
  133.   name ?
  134.  
  135.     My name is Diogo de Andrade, alias Spellcaster, and I'm the creator, 
  136.   editor and writer of this magazine. 
  137.     I live in a small town called Setubal, just near Lisbon, the capital of
  138.   Portugal... If you don't know where it is, get an encyclopedia, and look for
  139.   Europe. Then, look for Spain. Next to it, there's Portugal, and Setubal is in
  140.   the middle.
  141.  
  142.     I'm 18 years old, and I just made it in to the university (if you do want
  143.   to know, I'm in the Technical Institute of Lisbon, Portugal), so I'm not 
  144.   a God-Like creature, with dozens of years of practice (I only program by 
  145.   eight years now, and I started in a Spectrum, progressing later to an Amiga.
  146.   I only program in the PC for a year or so), with a mega-computer (I own a 
  147.   386SX, 16 Mhz), that wear glasses with lens that look like the bottom of a 
  148.   bottle (I use glasses, but only sometimes), that has his head bigger than a 
  149.   pumpkin (I have a normal sized head) and with an IQ of over 220 (mine is 
  150.   actually something like 180-190). I can program in C, C++, Pascal, Assembly
  151.   and even BASIC (yech!).
  152.  
  153.     So, if I am a normal person, why do I spend time writing this ?
  154.   Well, because I have the insane urge to write thousands of words every now
  155.   and then, and while I'm at it, I may do something productive, like teaching
  156.   someone. I may be young, but I know a lot about computers (how humble I am;
  157.   I know, modesty isn't one of my qualities).
  158.  
  159.     Just one more thing, if you ever program anything, please send to me... I
  160.   would love to see some work you got, maybe I could learn something with it.
  161.   Also, give me a greet in your program/game/demo... I love seeing my name.
  162.  
  163.     1.3. Distribution
  164.  
  165.     I don't really know when can I do another issue, so, there isn't a fixed
  166.   space of time between two issues. General rule, I will try to do one every
  167.   month, maybe more, probably less (Eheheheh). This is getting to an issue
  168.   every two months, so, I'll think I'll change the above text... :)
  169.     'The Mag' is available by the following means:
  170.  
  171.     - Snail Mail : My address is below, in the Contributions seccion... Just
  172.                    send me a disk and tell me what issues you want, and I
  173.                    will send you them...
  174.  
  175.     - E-Mail : If you E-mail me and ask me for some issues, I will Email you
  176.                back with the relevant issues attached.
  177.  
  178.     - Internet : You can access the Spellcaster page and take the issues out
  179.                  of there in:
  180.  
  181.                  http://alfa.ist.utl.pt/~l42686
  182.  
  183.                  Follow the docs link...
  184.  
  185.     - Anonymous ftp: I've put this issue of 'The Mag' on the ftp.cdrom.com
  186.                      site... I don't know if they'll accept it there, because
  187.                      that's a demo only site, and my mag doesn't cover only
  188.                      demos, but anyways, try it out... It has lots of source
  189.                      code of demos.
  190.  
  191.     1.4. Subscribing
  192.  
  193.     If you want, I'm starting "The Mag"'s subscription list... To get 'The Mag'
  194.   by Email every month, you just need to mail me and tell me so...
  195.     Then, you will receive it every time a new issue is made...
  196.  
  197.     1.5. Contributions
  198.  
  199.     I as I stated before, I'm not a God... I do make mistakes, and I don't 
  200.   have (always) the best way of doing things. So, if you think you've spotted
  201.   an error, or you have thought of a better way of doing things, let me know.
  202.   I'll be happy to receive anything, even if it is just mail saying 'Keep it 
  203.   up'. As all human beings, I need incentive.
  204.  
  205.     Also, if you do like to write, please do... Send in articles, they will be
  206.   welcome, and you will have the chance to see your names up in lights.
  207.     They can be about anything, for a review of a book or program that can
  208.   help a programmer, to a point of view or a moan. I'm specially interested in
  209.   articles explaining XMS, EMS, DMA and Soundblaster/GUS.
  210.  
  211.     If anyone out there has a question or wants to see an article about 
  212.   something in particular, feel free to write... All letters will be answered,
  213.   provided you give me your address.
  214.  
  215.   If you have a BBS and you want it to include this magazine, feel free to
  216.   write me... I don't have a modem, so I can only send you 'The Mag' by Email.
  217.  
  218.     You can also contact me personally, if you study on the IST (if you don't
  219.   know what the IST is, you don't study there). I'm the freshman with the
  220.   black hair and dark-brown eyes... Yes, the one that is skipping classes to
  221.   code his (first) next demo !! :)
  222.  
  223.     My adress is:
  224.                  Praceta Carlos Manito Torres, nº4/6ºC
  225.                  2900 Setúbal
  226.                  Portugal
  227.  
  228.     Email: l42686@alfa.ist.utl.pt
  229.  
  230.     And if you want to contact me on the lighter side, get into the Lost Eden
  231.   talker... To do that telnet to:
  232.  
  233.                         Alfa.Ist.Utl.Pt  : Port 1414
  234.  
  235.     If that server is down, try the Cital talker, in
  236.  
  237.                         Zeus.Ci.Ua.PT    : Port 6969
  238.  
  239.     I'm almost always there in the afternoon... As you may have guessed already,
  240.   my handle is Spellcaster (I wonder why...)...
  241.  
  242.     1.6. Hellos and greets
  243.  
  244.     I'll say hellos and thanks to all my friend, especially for those who put 
  245.   up with my constant whining.
  246.     Special greets go to Denthor from Asphyxia (for the excelent VGA trainers),
  247.   Draeden from VLA (for assembly tutorials), Joaquim Elder Guerreiro, alias
  248.   Dr.Shadow (Delta Team is still up), Joao Neves for sugestions, testing and
  249.   BBS services, and all the demo groups out there.
  250.     I will also send greets to everybody that responded to my mag... Thank
  251.   you very much !
  252.  
  253. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  254.  
  255.  
  256.   2. Mailroom
  257.  
  258.     Ok, let's open up some mail... The first is from Ben Basset from Australia:
  259.  
  260. -------------------------------------------------------------------------------
  261. First of all let me say G'DAY from me...Ben Bassett from West Australia.
  262.  
  263. I started reading through your "The Mag" magazines sometime last week.
  264. I am currently studying Computer Science at Curtin University, so I already
  265. know most of the Pascal stuff.  I'm learning about the mode 13h graphics
  266. mode from your tutorials.  I'm currently working my way through issue 7,
  267. and my goal is to produce a half-way decent demo or game.  
  268.  
  269. I like your style of writing, informal yet informative, keep up the good
  270. work!
  271.  
  272. Down to business - Circles issue 4.
  273.  
  274. Note: What I write below is an observation that I made while looking
  275.       through issue 4.  I think I'm on the right track here, but if not,
  276.       you can throw this heap of utter bollocks in the trash.
  277.  
  278. *********
  279.     I notice when you pre-generate the sin and cos values, you use
  280.     1800 reals for each.  I think this may be a mistake.  If you are
  281.     using a radian gap of 0.005, you only need 1257 points to draw
  282.     all the way around the circle.  eg.  (2*PI)/0.005 = 1256.637
  283.  
  284.     What you are doing when you use 1800 points is that every point
  285.     beyond 1257 (ie. points 1258 to 1800) is drawing over a point
  286.     that you have already plotted.  You only need 1257 to reach
  287.     2*PI.  Here's what I've done:
  288.  
  289.     Declare 2 constants:  
  290.  
  291.         radian_gap=0.005;
  292.         number_circle_points=round((2*PI)/radian_gap)+1;
  293.  
  294.     and use number_circle_points wherever you used to put 1799
  295.     ie. for i:=0 to number_circle_points do
  296.  
  297.     you should use this when getting the memory for the sin and cos
  298.     arrays, and also when plotting the points themselves.
  299.  
  300.     I tried this out on your Tunnel.pas demo, and got around 30%
  301.     speed-up when drawing the circles.
  302.  
  303. *********
  304.  
  305. Well, thats about it for the serious stuff (I'm not very serious
  306. by nature anyway).  Hope to see some cool stuff in future mags!
  307.  
  308. --Ben Bassett.
  309.  
  310. -------------------------------------------------------------------------------
  311.     Ok, Ben, you got me there... I guess I made the mistake because I used a
  312.   diferent step in my first calculations... Well, thanks for the mistake
  313.   correction... :)
  314.     Just to prove I'm human...
  315.  
  316.     The second letters comes... Let me see... Oh, Ben Basset again... :)))
  317.  
  318. -------------------------------------------------------------------------------
  319. I forgot to mention that you should set the circle_point_gap (or whatever
  320. I called it) to reflect the maximum size of circle you want to draw.
  321. There's not much point in plotting thousands of points for circles with
  322. radius 4.  In general I have experiented and come up with the following
  323. values:
  324.  
  325.  
  326. circle_point_gap        radius of circle
  327.  
  328.    0.1                   less than 8
  329.    0.05                   less than 13
  330.    0.01                   less than 49
  331.    0.005                  less than 97
  332.    0.001                 pretty much any size.
  333.  
  334.  
  335. I only mention this because it saves time and memory (and I LIKE time
  336. and memory!).
  337.  
  338. --Ben Bassett  
  339.  
  340. -------------------------------------------------------------------------------
  341.     Well, another smart remark... Time and memory are _VERY_ important, and
  342.   they are often opposites to each other (this is, when you want more speed,
  343.   you have less memory, and vice versa)... But in this case, he won both...
  344.   Is this guy learning ? He should be teaching me... :)))
  345.     Well, that's the end of this issue's mailroom...
  346.  
  347.  
  348. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  349.  
  350.  
  351.   3. The Turbo Assembler Bible
  352.  
  353.     Ok, this is the article writen by Scorpio (Yuppi, less work for me !). I,
  354.   Spellcaster will make my coments between square brackets []...
  355.     Your cue, Scorpio...
  356.  
  357.     Allright... I'm writing another article today... I was writtin' one
  358.   about 3DStudio, but I think that I don't have enough knowledge about 3DS to
  359.   make an article... Just some Texture-mappings, etc... If you still think
  360.   you could use such an article, email me and I'll be happy to send it to
  361.   Spellcaster so that he puts it in the next issue(or so) of THE MAG .
  362.  
  363.   [ Write it !!! Less work for me, and a more general mag ! ]
  364.  
  365.     Let's get to the point, now... This is an article 'bout an Assembly
  366.   book I'm currently reading. It is called "The Waite Group's Turbo Assembler
  367.   Bible", a (+-)720 pages and (+-) 1.5Kg book...:)
  368.     It was printed in 1991, its author is Gary Syck, which was working
  369.   in various data-communications programs but that also was into graphics
  370.   programming. The book follows the usual (academic) guidelines, and here are
  371.   the various chapters:
  372.  
  373.  
  374.   o Chapter 1: Programming the MS-DOS systems
  375.  
  376.              Overview of the MS-DOS Operating System
  377.                 Segments & Offsets, registers, PSP, DOS file system, etc...
  378.  
  379.             [ It would be very nice to see an article on the PSP... They are
  380.               rare... HINT, HINT ! :)))) ]
  381.  
  382.              Introduction to Using Turbo Assembler
  383.                 Let's not forget that this is a book on TASM...
  384.              Introduction to the Turbo Debugger
  385.                 Same as above.
  386.  
  387.   o Chapter 2: Processor Instructions
  388.              Data Movement instructions
  389.                 The name says it all...
  390.              Arithmetic, Logic, Bit-Shift Instructions
  391.                 .....
  392.              Procedures, Loops and Jumps
  393.                 .....
  394.              Processor control and Protected Mode Operation
  395.                 Includes some examples on how to change to Pmode and back
  396.                 to Real mode, Pmode specific instructions and some general
  397.                 info... It seems short...Also, some code to switch through
  398.                 various tasks...
  399.  
  400.   o Chapter 3: TASM Directives and Operators
  401.  
  402.              Segment declaration
  403.              .......
  404.              Code generation, error handling, .....
  405.  
  406.   o Chapter 4: Techniques
  407.  
  408.              Writing ASM modules for HLL (High level languages)
  409.              Using system resources
  410.              Acessing and controlling the Hardware
  411.              Video control: Text and Graphics
  412.  
  413.   o Chapter 5: Appendixes
  414.  
  415.              Various TASM/TLINK, ASCII, BIOS/DOS interrupts,etc. tables...
  416.  
  417.  
  418.     Allright... As you've probably noticed I've not included comments for
  419.   some of the chapters... It's like this: I'm reading this book page by page,
  420.   and I'm goin' very slowly... between university projects and commercial work,
  421.   I have so little time left to read... I just can't wait to get to the PMODE
  422.   section... Maybe (_MAYBE_) I'll do an article on it... Ok... I've read the
  423.  
  424.   [ I want that article on Pmode, pronto ! :)) ]
  425.  
  426.   first and a bit of the second chapter, and it follows the usual conventions:
  427.   Memory segmentation, registers, flags, etc, etc, etc... it is NOT a book to
  428.   ASM experts, yet it IS a book to ASM experts(or almost-experts).
  429.     Why? Well,it goes from the very basics of a simple printf('Hello\n")
  430.   (I wrote a line in C in this article???? I must be sick... :)) to some stuff
  431.  
  432.   [ You are definetly sick !! No more C ! Please, I hate C... Great language,
  433.     but I don't like anyway... C is forbidden in 'The Mag'... And maybe...
  434.     Hum... Just had an ideia... Maybe I'll do a C version of 'The Mag'...
  435.     What do you people out there think ?? ]
  436.  
  437.   'bout system resources (LIM EMS usage, mouse programming, COM, etc)...to
  438.   PMODE, to Breshenham's line drawing algorithm, palette handling... There are
  439.   lots of stuff to the newbies and it is also a good book for a later reference
  440.   on some subjects...
  441.     All of these chapters are presented in an informal way (the one we
  442.   like :)) and easy to understand...So, if you can't find a book around there,
  443.   check your university library, 'cos you would be surprised with what you can
  444.   find there... I got this book from University of Minho's library... Don't know
  445.   if anyone is from around here, but if u are... check this one out.
  446.  
  447.   [ IST library sucks big time !! ]
  448.  
  449.     Final considerations: THIS IS NOT A MUST-GET book... try PC Magazine's
  450.   lab book, or some other... Anyway, if you can get your hands on it... don't
  451.   loose the opportunity.
  452.  
  453.     WOW!!! 79 lines??? I can't believe I'm not sleeping by now...
  454.   It's 3:36 a.m. and I have to wake up at 6:30 tomorrow... er... today...
  455.   .. er... in 3 hours... Gotta go, ppl... PLZ let me know if you like (or not)
  456.   this article... PLZ no more flames... I get enough already... Do you know
  457.   what I mean, Spellcaster? Unfortunately, you do...
  458.  
  459.   [ I sure do, man... :((( ]
  460.  
  461.     Well, since I am goin' to sleep now, I'd better save this before falling
  462.   with my face in the keyboard..r n,iuooooooooqdxoewqiyh.wizs n.wqpw OOPS!
  463.   sorry 'bout that... CYA next time... (I hope).
  464.     If you wanna tell me anything at all, email to: si17899@ci.uminho.pt
  465.   (preferred) or si17899@thor.di.uminho.pt or try to find me in Moosaico (a
  466.   Moo) at: moo.di.uminho.pt 7777 under the name of (guess:) Scorpio.
  467.  
  468.   [ I'm there too, sometimes... :) ]
  469.  
  470.     It's me, Spellcaster again... Just to say my end phrase:
  471.  
  472.     T-t-t-t-t-h-a-t's all folks ! :))))
  473.  
  474.  
  475. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  476.  
  477.  
  478.   4. Designing a text adventure - Part II
  479.  
  480.     This article is intended for the beginners who want to do games. I know
  481.   that text adventures are definetly out, but the ideas behind a text adventure
  482.   are very similar to those found in graphic adventures, so what I say here
  483.   can be expanded to a graphic adventure...
  484.     Note that these are my personal views on how a text adventure should be
  485.   coded and designed...
  486.     There can be some errors and irregularities in the code and explanations,
  487.   because I'm writing this at the same time I'm coding the adventure, so I
  488.   will sometimes make mistakes... If you spot one, or have any doubts, mail me !
  489.     This particular issue is about the parser and basic commands.
  490.  
  491.     4.1. Looking at a room
  492.  
  493.     Well, one the first thing your text adventure makes is 'looking' at the
  494.   starting room... By 'looking at a room' I mean 'describe the current room'.
  495.   By describing a room, I mean to output the description of the room, the
  496.   objects that there exist, any monsters and other game characteres... Well,
  497.   as we aren't dealing with monsters, objects and characteres, we'll simply
  498.   output the description of the room, that is stored in Rooms[].Desc array...
  499.   So, let's make the look procedure:
  500.  
  501.             Procedure Look(RoomNumber:Word);
  502.             Var A:Byte;
  503.             Begin
  504.                  Writeln;
  505.                  A:=1;
  506.                  While (A<11) And (Rooms[RoomNumber].Desc[A]<>'*') Do
  507.                  Begin
  508.                       Writeln(Rooms[RoomNumber].Desc[A]);
  509.                       Inc(A);
  510.                  End;
  511.                  Writeln;
  512.             End;
  513.  
  514.     If you are wondering why don't you use just a For loop for writing the
  515.   ten-line description, remember that the description can have less that 10
  516.   lines, and in that case, the last line has a '*' character only...
  517.     Ooopsss... I almost forgot to describe the exits... To do so, add the
  518.   following lines:
  519.  
  520.             Writeln('Visible exits:');
  521.             If Rooms[RoomNumber].North<>0 Then Write('North      ');
  522.             If Rooms[RoomNumber].South<>0 Then Write('South      ');
  523.             If Rooms[RoomNumber].East<>0 Then Write('East       ');
  524.             If Rooms[RoomNumber].West<>0 Then Write('West       ');
  525.             Writeln;
  526.  
  527.     To hilite this info, use a different color, like this... Add before
  528.   describing the room:
  529.  
  530.             TextColor(White);
  531.  
  532.     And before the exits description:
  533.  
  534.             TextColor(Yellow);
  535.  
  536.     And everything will be a lot nicer... :)
  537.     To use this, you must implement the Play procedure, that will mantain the
  538.   core of the game... For the meanwhile, all the Play procedure does is to
  539.   output the description of the room...
  540.  
  541.              Procedure Play;
  542.              Var ExitFlag:Boolean;
  543.              Begin
  544.                   ExitFlag:=False;
  545.                   Look(CurrentRoom);
  546.                   Repeat
  547.                   Until ExitFlag;
  548.              End;
  549.  
  550.     The ExitFlag is a flag that controls the exit of the game... When it is
  551.   set to true, the game will cease it's execution... It can be put to true
  552.   because the players quits or wins the game...
  553.     Then you must add to the main program a call to this procedure, and you
  554.   must define and initialize the CurrentRoom variable... CurrentRoom stores
  555.   the current position of the player in the game, so I make it a variable of
  556.   type Word... And you must initialize it to the value 22, because that is the
  557.   starting room in FangLore...  Do that in the Init procedure...
  558.  
  559.     4.2. Player input and the command parser
  560.  
  561.     Now, it is the time to receive input from the user... This is were lots
  562.   of game fail... Why ? Because their command parser suck !!!!
  563.     What's the command parser ?
  564.     Well, the command parser is a procedure (more like a bunch of procedures)
  565.   that get the input of the user, process it (separating commands and
  566.   subjects) and then executes the instructions required... Well, I think this
  567.   is a quite nifty thing to do for a begginner, so I'll try to explain this
  568.   well enough...
  569.     First thing, you must receive the input of the player... You do that using
  570.   the Readln keyword (most adventure games have their own routines to read
  571.   player input, but I think that's a lot more complicated to explain simply
  572.   here).
  573.  
  574.              ReadLn(Command);
  575.  
  576.     You put this in the Play command, after the call to the Look procedure.
  577.     You must define the Command variable as a string...
  578.     I gave a little color to this Readln statement in the Fanglore.Pas program:
  579.  
  580.              TextColor(LightRed);
  581.  
  582.     And I also put it a prompt, so that the player knows that the game is
  583.   awaiting input:
  584.  
  585.              TextColor(White);
  586.              Writeln('What is thy bidding, master ?');
  587.  
  588.     Then, the next step is to split that string in it's components... The ideia
  589.   is to, given a string like this:
  590.  
  591.                'Get the mask'
  592.  
  593.     you get it splitted in only the words 'get' and 'mask'... The 'the' would
  594.   vanish, like all the prenoms (I don't know if this word exists... It's similar
  595.   to the portuguese one... :))).
  596.     So, let's do a procedure that given a string, returns an array with the
  597.   words separated:
  598.  
  599.              Procedure Parse(S:String;Var Parsed:Array[1..10] Of String);
  600.  
  601.     We use a procedure with a Var parameter instead of a function, because a
  602.   function can't return a whole array... You can't even do it like it is
  603.   above... You must define a type like this:
  604.  
  605.              Type ParsedType:Array[1..10] Of String;
  606.  
  607.     And then you can define the procedure like this:
  608.  
  609.              Procedure Parse(S:String;Var Parsed:ParsedType);
  610.  
  611.     Now, the first thing to to is to split the phrase in it's components...
  612.   You achieve that by searching for the first space, and getting what's in
  613.   the string from the beggining to the position of that space, and placing it
  614.   in the array, and so forth...
  615.     The complete procedure is like this:
  616.  
  617.              Procedure Parse(S:String;Var Parsed:ParsedType);
  618.              Var ArrayIndex:Byte;
  619.                  StringIndex:Byte;
  620.                  NextSpace:Byte;
  621.              Begin
  622.                   ArrayIndex:=1;
  623.                   StringIndex:=0;
  624.                   NextSpace:=FindSpace(S,StringIndex);
  625.                   While (StringIndex>=Length(S)) And (ArrayIndex<11) Do
  626.                   Begin
  627.                        NextSpace:=FindSpace(S,StringIndex);
  628.                        Parsed[ArrayIndex]:=GetString(S,StringIndex,NextSpace);
  629.                        StringIndex:=NextSpace;
  630.                        Inc(ArrayIndex);
  631.                   End;
  632.              End;
  633.  
  634.     This parses the string... It uses two functions, FindSpace and GetString,
  635.   whose code is given next... The first of these finds the next space after
  636.   the given starting location, while the other grabs a part of a string.
  637.  
  638.              Function FindSpace(S:String;Start:Byte):Byte;
  639.              Begin
  640.                   While (S[Start+1]<>' ') And (Start<Length(S)) Do Inc(Start);
  641.                   FindSpace:=Start;
  642.              End;
  643.  
  644.              Function GetString(S:String;Start,Finish:Byte):String;
  645.              Var A:Byte;
  646.                  Tmp:String;
  647.              Begin
  648.                   Tmp:='';
  649.                   For A:=Start+1 To Finish Do Tmp:=Tmp+S[A];
  650.                   GetString:=Tmp;
  651.              End;
  652.  
  653.     Don't to forget to call the Parse procedure after you read the command:
  654.  
  655.              Parse(Command,Parsed);
  656.  
  657.     And remember also to define the Parsed array:
  658.  
  659.              Var Parsed:ParsedType;
  660.  
  661.     Now, you have an array with the parsed command... You must now convert all
  662.   to uppercase... Why ?
  663.     Well, because you don't want the program to tell you to take a hike if you
  664.   write 'get' or 'Get' instead of 'GET'... Don't forget that uppercase and
  665.   lowercase are very different to the computer...
  666.     So, let's use the Upper procedure to do so:
  667.  
  668.              Function Upper(S:String):String;
  669.              Var Tmp:String;
  670.                  A:Byte;
  671.              Begin
  672.                   Tmp:='';
  673.                   For A:=1 To Length(S) Do Tmp:=Tmp+UpCase(S[A]);
  674.                   Upper:=Tmp;
  675.              End;
  676.  
  677.     The UpCase function is a standart Pascal function that converts a character
  678.   that is in lowercase to uppercase, not affecting special simbols and the
  679.   uppercase characteres... Call the Upper procedure for every element of the
  680.   Parsed array, after you parse the command.
  681.     Now, you should eliminate all the prenoms of the parsed array:
  682.  
  683.              Procedure EliminPrenoms(Var Parsed:ParsedType);
  684.              Var A:Byte;
  685.              Begin
  686.                   For A:=1 To 10 Do
  687.                   Begin
  688.                        If Parsed[A]='THE' Then Elimin(Parsed,A);
  689.                        If Parsed[A]='A' Then Elimin(Parsed,A);
  690.                   End;
  691.              End;
  692.  
  693.     You should make an If to every word you want eliminated... I don't recall
  694.   any more at this time... The Elimin procedure is a procedure that given a
  695.   variable of type ParsedType and an index number, it eliminates the word
  696.   corresponding to that index... It works by filling that position of the
  697.   array with the next position of the array, that is filled with the one after
  698.   it, and so forth...
  699.  
  700.              Procedure Elimin(Var Parsed:ParsedType;Var Index:Byte);
  701.              Var A:Byte;
  702.              Begin
  703.                   For A:=Index To 9 Do Parsed[A]:=Parsed[A+1];
  704.                   Parsed[10]:='';
  705.                   Dec(Index);
  706.              End;
  707.  
  708.     The two variables in this procedure passed by reference (using the Var
  709.   keyword), because we need to decrement the Index after each call to this
  710.   procedure, because the procedure changes the parsed array... I'll do an
  711.   example, imagining that the Parsed array has only four elements...
  712.     Imagine that the phrase given was 'Spellcaster The The Best'. So, the
  713.   parsed array would be like this:
  714.  
  715.          1:Spellcaster
  716.          2:The
  717.          3:The
  718.          4:Best
  719.  
  720.     So, the EliminPrenoums would scan the parsed array, starting on index 1.
  721.   Seeing the word 'Spellcaster', it would increase the index. Now index=2.
  722.     Then, it would see the word 'The'... It would call the Elimin procedure,
  723.   and the parsed array would be like this:
  724.  
  725.          1:Spellcaster
  726.          2:The
  727.          3:Best
  728.          4:
  729.  
  730.     But, if you don't pass the Index by reference and decreased in the end of
  731.   the Elimin procedure, the index would be incremented and would be equal to
  732.   3 now, so it would skip the 2nd 'The'... So, if you decrease the Index, you
  733.   will be again in Index=2, so the second 'The' is eliminated also... Cool,
  734.   isn't it ?
  735.     Don't forget to call the EliminPrenoms after the other parsing procedures.
  736.     Now probably you have in index 1 of the array the main command, and in
  737.   the other indexes you have the name of the object in which the action will
  738.   be performed, etc...
  739.     Now, you should have a flag that is set when a command is executed
  740.   sucessfully, and when the variable is not set, you should give out an error
  741.   message saying that  you didn't understand the command... Define it as a
  742.   variable of type Boolean, and set it to false just after the Repeat keyword.
  743.     Then, every time you execute a command, set it to true, and in the end,
  744.   just before the Until keyword, check if it is true... If it isn't, type out
  745.   an error message... You could even alter the error messages, so that the
  746.   player don't grow tired of the same message over and over again !
  747.   In the example program, the program selects randomly one of two 'Unknown
  748.   command' irritating phrases... :)
  749.  
  750.     Now, let's implement two simple commands... The Look command and the Quit
  751.   command... These should be easy to compreend... Just add the following lines
  752.   to the program, after it parses the phrase:
  753.  
  754.              If Parsed[1]='LOOK' Then
  755.              Begin
  756.                   Valid:=True;
  757.                   Look(CurrentRoom);
  758.              End;
  759.              If Parsed[1]='QUIT' Then
  760.              Begin
  761.                   Valid:=True;
  762.                   TextColor(BrightRed);
  763.                   Writeln;
  764.                   Writeln('Are you sure you want to quit FangLore (Y/N) ?');
  765.                   C:=ReadKey;
  766.                   If UpCase(C)='Y' Then ExitFlag:=True;
  767.              End;
  768.  
  769.     Define the variable C as a variable of type Char...
  770.     And voila'... Two commands !! Fanglore is really shaping up... :)))
  771.  
  772.     4.3. Movement commands
  773.  
  774.     Well, this is fairly obvious to do... You just have to check if the
  775.   corresponding word as been pressed (i.e. 'North', 'N', 'South', etc...), and
  776.   if there is an exit that way, make sure it goes that way... If it goes that
  777.   way, then look at the room... Coding wise:
  778.  
  779.              If (Parsed[1]='N') Or (Parsed[1]='NORTH') Then
  780.              Begin
  781.                   If Rooms[CurrentRoom].North=0 Then
  782.                   Begin
  783.                     TextColor(LightRed);
  784.                     Writeln;
  785.                     Writeln('Sorry, oh Great Lord, but I can''t go that way !');
  786.                     Writeln;
  787.                   End
  788.                   Else
  789.                   Begin
  790.                     TextColor(LightRed);
  791.                     Writeln;
  792.                     Writeln('You go north...');
  793.                     CurrentRoom:=Rooms[CurrentRoom].North;
  794.                     Writeln;
  795.                     Look(CurrentRoom);
  796.                   End;
  797.                   Valid:=True;
  798.              End;
  799.  
  800.     And do likewise for the other three directions... Cool, isn't it ?
  801.     Well, so you can look, quit and wander in Fanglore... In the next article
  802.   of the series, I will delve into objects and their interection...
  803.     Every piece of this issue's code is added to the FangLore program, that
  804.   you should get with the ZIP file...
  805.  
  806.  
  807. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  808.  
  809.   5. How too make a 100% assembly program
  810.  
  811.     Ok, this is going to be a short article, since I'm writing this at 22:00,
  812.   and I must wake up at 5 am tomorrow... I'm feeling sleepy just thinking
  813.   about it...
  814.     Well, this article is about coding an 100% assembly program, but I guess
  815.   you knew that already because of the title... Well, I'll add some information
  816.   that isn't on the title... When I say '100% assembly program' I mean a
  817.   program out of Turbo Pascal !!!! Yuppy !!! No Pascal... Ehhehehe... Don't
  818.   take me wrong, Pascal is a great language, but assembly is even better for
  819.   _REAL_ coders... :)))) Ok... Enough for fun... Let's get started...
  820.  
  821.     First of all, I'll will show you the frame for the vast majority of
  822.   assembler programs, and then I will explain it:
  823.  
  824.     DOSSEG
  825.     .MODEL SMALL
  826.     .286
  827.     .STACK 200h
  828.  
  829.     .DATA
  830.  
  831.     .CODE
  832.  
  833.     START:
  834.     END START
  835.  
  836.     You should use your favorite text editor (one that save in ASCII format...
  837.   I usually use Turbo Pascal 6.0 !!! ) to type this up, then save this file
  838.   with any name (example: Test.Asm).
  839.     Then, if your using TASM, you should do something like this in the DOS
  840.   prompt:
  841.  
  842.     TASM Test.Asm
  843.  
  844.     The program would compile and a new file, called Test.Obj would be
  845.   created... Then you should LINK it (in other issue I will explain what this
  846.   is)... You should use:
  847.  
  848.     TLINK Test.Obj
  849.  
  850.     Then, miracle of miracles ! The Test.Exe file will appear and you can
  851.   execute it !!!!
  852.     Now, I'll explain what each of those commands do...
  853.  
  854.     DOSSEG - Tells the compiler to organize the segments in your program like
  855.              DOS and other high level languages (like Pascal and C) do, that
  856.              is it creates three segments (code, data and stack).
  857.  
  858.     .MODEL - Tells the compiler in which model to compile.
  859.              The model can be though as segment limitations...
  860.              If the model is TINY, the code plus data must be smaller than
  861.              64 Kb (this is, by definition, a COM file). If the model is
  862.              SMALL, the code and the data (separetly) can have a maximum of
  863.              64 Kb. Other modes are:
  864.  
  865.              MEDIUM:      Code > 64 Kb          Data < 64 Kb
  866.              COMPACT:     Code < 64 Kb          Data > 64 Kb
  867.              LARGE:       Code > 64 Kb          Data > 64 Kb
  868.              HUGE:        Code > 64 Kb          Data > 64 Kb    Arrays > 64 Kb
  869.  
  870.              LARGE and HUGE models are equivelent, but the later is used by
  871.              high level languages. It isn't of no use to us...
  872.  
  873.     .286 - Enables 286 instructions... It can be .386, .386P (for protected
  874.            mode), etc... I usually use .386... If you don't put anything, the
  875.            compiler will assume 8086 instructions...
  876.  
  877.     .STACK - Tells the compiler the size of the stack, in bytes... I will
  878.              discuss the stack later. I usually use 200h (that's about
  879.              512 bytes).
  880.  
  881.     .DATA - Tells the program that the following 'stuff' (you will see what is
  882.             the stuff later) is to be placed in the data segment.
  883.  
  884.     .CODE - Tells the program that the following 'stuff' is to be placed in
  885.             the code segment.
  886.  
  887.     START: - Tells the compiler where the main program begins.
  888.  
  889.     END START - Tells the compiler that this is the place where the code ends.
  890.  
  891.     I will explain most of this stuff in greater detail later. Right now, if
  892.   you compiled this and run it, you would expect it to just do nothing and
  893.   return to Dos... You're partially right... It doesn't do nothing, but it
  894.   doesn't return to Dos either !!! Why ? Because assembler programs are so
  895.   stupid that they must tell Dos that they are over... To do that, you must
  896.   call the Dos interrupt 21h, function 4c, to end the code... So, to do nothing
  897.   and return to dos, the program would have to be something like this:
  898.  
  899.     DOSSEG
  900.     .MODEL SMALL
  901.     .286
  902.     .STACK 200h
  903.  
  904.     .DATA
  905.  
  906.     .CODE
  907.  
  908.     START:
  909.  
  910.          Mov AX,4c00h           ; This is a comment... Anything after a semi-
  911.                                 ; -colon will be ignored... Just to tell you
  912.                                 ; that 4c00h is an hexadecimal number...
  913.          Int 21h
  914.  
  915.     END START
  916.  
  917.     The 'Mov AX,4c00h' command moves to the AX register (I think I already
  918.   told you what a register is) the value 4c00 in hexadecimal (19456 in
  919.   decimal)... I know I told you that the function number is 4c, but this is
  920.   to be put in the high part of the word AX... The low part is the exit-code
  921.   (in this case, it is 0) and is to be used in by parent programs, that is,
  922.   programs that call other programs.
  923.     The 'Int 21h' command, calls interrupt 21h (the Dos interrupt)... More on
  924.   it later...
  925.     Well, it's just 23:00... I think I'm going to bed know... I'll finish this
  926.   tomorrow... Yawn !!! :)))
  927.     Ok, I'm back... I fell a lot better now, after a good (but short) night of
  928.   sleep... Now, I'll talk about the stack...
  929.  
  930.     The stack is a very important part in your program... It's a wonderfull
  931.   place where you can store stuff you will need later. I've talked about the
  932.   stack in issue 3 of 'The Mag'... Ehehehe... This was short...
  933.  
  934.     Now, about the 'stuff'... In assembler, you can also define variables,
  935.   altough it is a bit more complicated subject... Variables in assembler
  936.   are composed by an identifier (like Pascal identifiers), and a type:
  937.  
  938.     DB - Byte
  939.     DW - Word
  940.     DD - DoubleWord
  941.  
  942.     So, no strings or chars... Just plain numbers... Arrays are also defined
  943.   in a different way... But, let's take a step at a time:
  944.  
  945.     Spell  DB 10
  946.     VidSeg DW 0a000h
  947.     Large  DD 010101010101001b
  948.  
  949.     These lines define three variables, named Spell, VidSeg and Large, of
  950.   types Byte, Word and DoubleWord, respectively, and with initial values
  951.   10, 0a000 (hexadecimal) and 010101010101001 (binary). If you don't want to
  952.   assign an initial value, just put a '?' there... Example:
  953.  
  954.     Spell DB ?
  955.  
  956.     To access the value of a variable, you put the name of the variable
  957.   in square brackets... Example:
  958.  
  959.             Mov AX,[VidSeg] -> This would load the value of variable VidSeg
  960.                                in register AX... Remember that there exist
  961.                                some commands that don't work with variables.
  962.  
  963.     One important thing to notice is that how data is organized in an asm.
  964.     For example, imagine you made the program like this:
  965.  
  966.     .Data
  967.     Spell  DB 10
  968.     VidSeg DW 0a000h
  969.     Large  DD 010101010101001b
  970.  
  971.     This means that the variables are in the Data segment. And now for the fun
  972.   stuff... The address of variable Spell:
  973.  
  974.                        Segment: DS (Data Segment)
  975.                        Offset : 0000
  976.  
  977.     Cool, isn't it ? But that's not all... Addresses of variables VidSeg and
  978.   Large:
  979.  
  980.                    Seg(VidSeg)=DS        Seg(Large)=DS
  981.                    Ofs(VidSeg)=0001      Ofs(Large)=0003
  982.  
  983.     What can you make of this ? That memory addressing is sequencial !!! That
  984.   can be usefull, for accessing arrays... But I'll talk about them later. For
  985.   now, let's talk about tables. Do you knew that you could have something like
  986.   this:
  987.  
  988.                         Vector1 DB 10,20,30
  989.                                 DB 20,10,30
  990.                                 DB 30,30,20
  991.  
  992.     Imagine that as a triangle defined by it's corners, with it's (x,y,z)
  993.   coordinates... You can have all that in just one variable. To access, for
  994.   example, the y coordinate of the second corner (the 2nd value of the 2nd
  995.   line), you would have to do something like this:
  996.  
  997.                    Mov AX,[Vector+4]
  998.  
  999.     If the variable was defined as Words (DW) and not bytes (DB), you would
  1000.   have to add 8 to the base address of the variable, that is stored in Vector.
  1001.   So, really, Vector is a pointer to the first element of data after it's
  1002.   definition... [Vector] is the real value of the element (in this case 10).
  1003.   Vector+1 would point to the 2nd element, Vector+2 to the 3rd, etc, while
  1004.   [Vector+1] would return the 2nd value, [Vector+2] the 3rd, etc...
  1005.  
  1006.     Now, let's go to arrays... To define a one-dimensional array, you use use
  1007.   the 'dup' keyword... Example, to define an array with 100 elements of type
  1008.   Word, you would do:
  1009.  
  1010.                   SpellArray DW 100 Dup(0)
  1011.  
  1012.     This would make an array initialized with zeros... If you wanted to create
  1013.   an unitialized array, you would have to use a '?' instead of a '0'.
  1014.     SpellArray will now point to the first element, etc...
  1015.     To define a two-dimensional array, you would use... well, I'd rather
  1016.   demonstrate... Imagine you want to define a 100x50 array (100 columns, 50
  1017.   lines). You would do it like this:
  1018.  
  1019.                   SpellArray DW 5000 Dup (?)   ; Because 100*50=5000
  1020.  
  1021.     Surprise !! No two-dimensional arrays... But you can use a trick to
  1022.   simulate them... Remember the first article I did on mode 13h ? Well, this
  1023.   is similar... Imagine youwant to access the element Xth element of line Y.
  1024.   You must multiply the Y for the size in columns of the array, and then add
  1025.   the X... For example:
  1026.  
  1027.            Mov AX,[SpellArray+Y*100+X] ; would load AX with the value of
  1028.                                        ; element desired.
  1029.  
  1030.     So, I think that it is all said now...
  1031.     Check out the example program FLSCR.ASM, that fills the screen in mode 13h,
  1032.   with random colors... Notice on how fast it is... :) God, I love assembler...
  1033.     Toy with it... In the worst case, you'll crash up your computer and nuke
  1034.   your hard drive (you don't knwo how easy is to do this... :))
  1035.     Cya in another article...
  1036.  
  1037. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  1038.  
  1039.  
  1040.   6. Scrolling horizontaly in text mode
  1041.  
  1042.     Well, this article is made to order... :)
  1043.     It's quite easy to scroll horizontally text, altough it is complicated
  1044.   (I don't think it's even possible) to do it smoothly in text mode. By
  1045.   smoothly I mean to make appear a pixel at a time... I only know how to do
  1046.   scrolling in text mode making a character appear at a time (though I'm
  1047.   currently studying the possibility of doing this a pixel at a time... Not
  1048.   much success, though ! :).
  1049.   The ideia is this... You have a string S:
  1050.  
  1051.       S:='Spellcaster can''t code anything in Pascal, let alone ASM !';
  1052.  
  1053.     And you want to scroll it around a window with only 10 characteres wide.
  1054.     What you have to do is this:
  1055.  
  1056.              |          |
  1057.              |          |   <---- This represents the screen...
  1058.              |          |
  1059.  
  1060.              |          |
  1061.              |          |Spellcaster can''t code...... (the text is off screen)
  1062.              |          |
  1063.  
  1064.              |          |
  1065.              |         S|pellcaster can''t code......
  1066.              |          |
  1067.  
  1068.              and later you have:
  1069.  
  1070.              |          |
  1071.          Spel|lcaster ca|n''t code......
  1072.              |          |
  1073.  
  1074.     Get the picture ? You must choose what piece of text to display...
  1075.     So, some code (I'll use the GetString function from the article number 4).
  1076.  
  1077.              Program HScroll;
  1078.  
  1079.              Uses Crt;
  1080.  
  1081.              Const WhereX=10;    { X coordinate of scroller }
  1082.                    WhereY=10;    { Y coordinate of scroller }
  1083.                    NChars=10;    { Number of characters to display at one time }
  1084.  
  1085.              Var S:String;
  1086.                  Index:Byte;
  1087.  
  1088.              Function GetString(S:String;Start,Finish:Byte):String;
  1089.              Var A:Byte;
  1090.                  Tmp:String;
  1091.              Begin
  1092.                   Tmp:='';
  1093.                   For A:=Start+1 To Finish Do Tmp:=Tmp+S[A];
  1094.                   GetString:=Tmp;
  1095.              End;
  1096.  
  1097.              Begin
  1098.                   S:='Spellcaster can''t code in Pascal, let alone ASM ! ';
  1099.                   Index:=1;
  1100.                   Repeat
  1101.                         { Scroll the area we want to the left }
  1102.                         Move(Mem[$B800:((WhereY*80)+WhereX+1)*2],
  1103.                              Mem[$B800:((WhereY*80)+WhereX)*2],NChars*2);
  1104.                         { Write the character that enters by the right }
  1105.                         GotoXY(WhereX+NChars+1,WhereY+1);
  1106.                         Write(S[Index]);
  1107.                         Inc(Index);
  1108.                         If Index>Length(S) Then Index:=1;
  1109.                         { Put a small delay, so that you can actually read }
  1110.                         { something...                                     }
  1111.                         Delay(50);
  1112.                   Until KeyPressed;
  1113.              End.
  1114.  
  1115.     You can add a little color, if you want... It would be nicer... :)
  1116.   If you are wondering why you multiply by two when you are converting (X,Y)
  1117.   coordinates to an Offset value, you must know one thing:
  1118.  
  1119.     Text mode is very similar to graphics mode in the memory adressing with
  1120.   some differences:
  1121.  
  1122.     - Base address is B800, not A000 like in mode 13h
  1123.     - You have two bytes per character, not one byte per pixel.
  1124.       The first byte stores the ASCII value of the character, while the second
  1125.       byte stores the attributes. The attributes in text mode are: the
  1126.       foreground color (the text color), the background color, and if that
  1127.       character blinks or not...
  1128.  
  1129.       So, you can directly access Text Memory, and that's the reason why I
  1130.     multiply by two the calculations... The rest is similar to mode 13h.
  1131.       The attribute byte is organized like this:
  1132.  
  1133.           Bit No.   7 6 5 4 3 2 1 0
  1134.  
  1135.       Bits 0-3 are the foreground color (16 colors possible).
  1136.       Bits 4-6 are the background color (8 colors possible).
  1137.       Bit 7 is the blinking attribute (if it is set, the text will blink).
  1138.  
  1139.     I think this is fairly simple... Any doubts, mail me... :)
  1140.  
  1141.     A last minute correction... I've stated in the beggining of this article
  1142.   that I didn't think that scrolling smoothly in text mode was possible...
  1143.   Well, I was wrong... A friend of mine did it... At least, he says he did it.
  1144.   I didn't had the chance to see it, but I'll try to,  and to get the code
  1145.   also... If I understand it, I will do an article on that...
  1146.  
  1147.  
  1148. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  1149.  
  1150.  
  1151.   7. Sprites Part II - Animation and movement
  1152.  
  1153.     So, let's continue our sprite tutorial... In this issue, we'll discuss
  1154.   basic animation and movement control... We'll use the procedures in the
  1155.   Sprite unit I gave last issue... Let's start with...
  1156.  
  1157.     7.1. Animating a sprite
  1158.  
  1159.     Animation is what you see in a cartoon, right ? WRONG ! In computer terms,
  1160.   an animation is... Well...er... it is, for example, a static objects that
  1161.   changes itself...
  1162.     But, what is animation really ? How can we create it ?
  1163.     Well, we use the same principle as the movies... Our eyes have a limit
  1164.   number of 'updates' that it can receive... By updates, I mean changes in the
  1165.   enviroment... I think the limit is 22 changes per second... The movies, for
  1166.   example, change the image 50 or 60 times a second, that is, the image is
  1167.   slightly altered 50 or 60 times a second... So, the eyes are deceiveds in
  1168.   thinking the image is moving... You change the image rapidly... But, you
  1169.   don't need to change the image 22 times a second to obtain an animation...
  1170.   You can change it just 1 time a second, or one time every 10 seconds... It
  1171.   doens't really matter, altough the movement is smoother if you update the
  1172.   image more frequently... But enough talk... Let's get to business...
  1173.     I've created two sprites of a spaceship, and they are slightly different
  1174.   from each other... So, I will display one after another on the screen, in
  1175.   the same coordinate, so you'll get the illusion of animation... The
  1176.   procedures are the same used in the last issue of The Mag...
  1177.  
  1178.              Program Animation;
  1179.  
  1180.              Uses Mode13H, Sprites, Crt;
  1181.  
  1182.              Var Images:Array[1..2] Of Pointer;
  1183.                  F:File;
  1184.  
  1185.              Begin
  1186.                   { Initialize graphics and palette }
  1187.                   InitGraph;
  1188.                   SetColor(1,63,0,0);
  1189.                   SetColor(2,63,40,0);
  1190.                   SetColor(3,63,63,0);
  1191.                   { Load images from a file }
  1192.                   Assign(F,'Ship.Img');
  1193.                   Reset(F,1);
  1194.                   LoadImage(F,Images[1]);
  1195.                   LoadImage(F,Images[2]);
  1196.                   Close(F);
  1197.                   { Animate them, until a keypressed occurs !! }
  1198.                   Repeat
  1199.                         PutImage(160,100,Images[1],VGA);
  1200.                         Delay(150);
  1201.                         PutImage(160,100,Images[2],VGA);
  1202.                         Delay(150);
  1203.                   Until Keypressed;
  1204.                   { Wraps things up }
  1205.                   KillImage(Images[1]);
  1206.                   KillImage(Images[2]);
  1207.                   CloseGraph;
  1208.              End.
  1209.  
  1210.     Not too impressive, I know... But it's a start... The next part is
  1211.   better...
  1212.  
  1213.     7.2. Moving a sprite
  1214.  
  1215.     Moving a sprite follows the same ideia as animating it... The ideia is
  1216.   to move the sprite, draw it, clear it, move it again, and so forth...
  1217.   Adapting to the last piece of code... Let's define two more variables,
  1218.   X and Ix, of type Word and ShortInt. Initialize variable X with the value 0,
  1219.   and variable Ix with value 1. Then, replace the Repeat Until cicle with...
  1220.   Or, stuff it, I'll dump here the full code:
  1221.  
  1222.             Program Movement;
  1223.  
  1224.             Uses Mode13H, Sprites, Crt;
  1225.  
  1226.             Var Images:Array[1..2] Of Pointer;
  1227.                 F:File;
  1228.                 X:Word;
  1229.                 Ix:ShortInt;
  1230.  
  1231.             Begin
  1232.                  { Initialize graphics and palette }
  1233.                  InitGraph;
  1234.                  SetColor(1,63,0,0);
  1235.                  SetColor(2,63,40,0);
  1236.                  SetColor(3,63,63,0);
  1237.                  { Load image from a file }
  1238.                  Assign(F,'Ship.Img');
  1239.                  Reset(F,1);
  1240.                  LoadImage(F,Images[1]);
  1241.                  Close(F);
  1242.                  { Move it, until a keypressed occurs !! }
  1243.                  X:=0;
  1244.                  Ix:=3; { The bigger the Ix, the faster the sprite will move }
  1245.                  Repeat
  1246.                        { Draw it }
  1247.                        PutImage(X,100,Images[1],VGA);
  1248.                        Delay(50);
  1249.                        { Clear it }
  1250.                        Cls(0,VGA);
  1251.                        { Update it }
  1252.                        X:=X+Ix;
  1253.                        If X>=300 Then Ix:=-3;
  1254.                        If X=0 Then Ix:=3;
  1255.                  Until Keypressed;
  1256.                  { Wraps things up }
  1257.                  KillImage(Images[1]);
  1258.                  KillImage(Images[2]);
  1259.                  CloseGraph;
  1260.             End.
  1261.  
  1262.     Well, this flickers a lot... The way to mend this is by using virtual
  1263.   screens, or clear just the part of the screen... You should be able to do
  1264.   that yourself... I'm so lazy today...
  1265.  
  1266.     7.3. Doing it all together
  1267.  
  1268.     So, you know how to move it and you know how to animate it... But do you
  1269.   know how to do it at the same time ?
  1270.     This is easy... For each frame it draws, it shows a picture that has been
  1271.   both moved and animated... Let's see some code, because I'm too lazy to
  1272.   explain it... and because this is soooo easy:
  1273.  
  1274.           Program Animation_And_Movement;
  1275.  
  1276.           Uses Mode13H, Sprites, Crt;
  1277.  
  1278.           Var Images:Array[1..2] Of Pointer;
  1279.               F:File;
  1280.               X:Word;
  1281.               Ix:ShortInt;
  1282.               Pic:Byte;
  1283.  
  1284.           Begin
  1285.                { Initialize graphics and palette }
  1286.                InitGraph;
  1287.                SetColor(1,63,0,0);
  1288.                SetColor(2,63,40,0);
  1289.                SetColor(3,63,63,0);
  1290.                { Load images from a file }
  1291.                Assign(F,'Ship.Img');
  1292.                Reset(F,1);
  1293.                LoadImage(F,Images[1]);
  1294.                LoadImage(F,Images[2]);
  1295.                Close(F);
  1296.                { Animate them, until a keypressed occurs !! }
  1297.                X:=0;
  1298.                Ix:=1; { The bigger the Ix, the faster the sprite will move }
  1299.                Pic:=1;
  1300.                Repeat
  1301.                      { Draw it }
  1302.                      PutImage(X,100,Images[Pic],VGA);
  1303.                      Delay(50);
  1304.                      { Clear it }
  1305.                      Cls(0,VGA);
  1306.                      { Update it }
  1307.                      X:=X+Ix;
  1308.                      If X>=300 Then Ix:=-3;
  1309.                      If X=0 Then Ix:=3;
  1310.                      If Pic=1 Then Pic:=2 Else Pic:=1;
  1311.                Until Keypressed;
  1312.                { Wraps things up }
  1313.                KillImage(Images[1]);
  1314.                KillImage(Images[2]);
  1315.                CloseGraph;
  1316.           End.
  1317.  
  1318.     So, here you have it... This is very easy...
  1319.     Let's wrap it up... In next tutorial about sprites, I'll teach you how to
  1320.   make this move over a background, transparency, and maybe a couple of
  1321.   tricks...
  1322.  
  1323.  
  1324. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  1325.  
  1326.  
  1327.   8. Graphics Part VIII - Text in graphic mode
  1328.  
  1329.     8.1. Basics
  1330.  
  1331.     Text in graphic mode is very different from text in text mode (as the name
  1332.   implies)... You can't use the standart function Writeln (that is, you can,
  1333.   but it will look UGLY !), nor can you use the Readln (you must program your
  1334.   own function to read input in graphic mode).
  1335.     It's fairly easy to do text in graphics mode. You just have to take in
  1336.   account WHAT do you want to print, WHERE do you want to print it, and with
  1337.   what type of character... Yes, you can use various kinds of characters.
  1338.   That's one the advantages of using text in graphics.
  1339.     To the diferent types of characters (that is, every charset) you call
  1340.   a font.
  1341.     There are several kinds of fonts:
  1342.  
  1343.       - Fixed sized fonts -> As the name implies, the characters have all the
  1344.                              same width and height, not to mention color.
  1345.  
  1346.       - Proportional fonts -> Characters can have different widths and heights,
  1347.                               altough usually they only change in width. The
  1348.                               color is the same in all character.
  1349.  
  1350.       - Colorfonts -> Colorfonts can be fixed sized or proportional. The only
  1351.                       difference between this and the last ones I mentioned,
  1352.                       is that the characters can have different colors... Not
  1353.                       different from each others, it's different inside them.
  1354.                       You'll see what I mean latter...
  1355.  
  1356.     Graphical text can be very important in any kind of program you make;
  1357.   almost every demo has a scroller (I'll do an article on them someday), every
  1358.   game shows the score or the briefing or something... Trust me... They are
  1359.   important... So, let's get down and dirty...
  1360.  
  1361.     8.2. Fixed sized fonts
  1362.  
  1363.     The principle behind this is easy: you use the theory behind the sprite,
  1364.   changing the procedures a little (you don't need to read the size of the
  1365.   sprite, it's already set; you don't put different colors; you don't use
  1366.   pointers, etc...). Let's check out the procedures... Oh, I almost forgot...
  1367.   You must create a structure that lets you store the various characters:
  1368.  
  1369.             Const XSize=16;
  1370.                   YSize=16;
  1371.  
  1372.             Type FSFont=Array[1..XSize,1..YSize] Of Byte;
  1373.                  Chars=array[' '..'º'] Of FSFont;
  1374.  
  1375.             Var Font:Chars;
  1376.  
  1377.     In case you're wondering, you can define an array like that... You access
  1378.   the elements of the array, like this, for example:
  1379.  
  1380.                       Font['A',10,10]:=10;
  1381.  
  1382.                       Or
  1383.  
  1384.                       C:Char;
  1385.                       ..........
  1386.                       ..........
  1387.                       C:='A';
  1388.                       Font[C,10,10]:=10;
  1389.  
  1390.     So, let's read the font:
  1391.  
  1392.               Procedure LoadFont(Filename:String);
  1393.               Var F:File;
  1394.                   Garbage:Word;
  1395.               Begin
  1396.                    Assign(F,Filename);
  1397.                    Reset(F,1);
  1398.                    Blockread(F,Garbage,2);
  1399.                    Blockread(F,Garbage,2);
  1400.                    Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4);
  1401.                    Close(F);
  1402.               End;
  1403.  
  1404.     The structure of the file I'm using has it's first four bytes indicating
  1405.   the X and Y size of the font (you might need it... In this case it isn't
  1406.   needed, so we read them to discardable variables: Garbage). Then it has
  1407.   a sequence of bytes indicating if a certain point is on or off... Caution:
  1408.   in this case, that isn't the color, but if the pixel is draw or not... We'll
  1409.   get to colorfonts soon...
  1410.     I'll just explain the instruction that really reads the data:
  1411.  
  1412.          Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4);
  1413.  
  1414.     Well, I think I've already talked about the Ptr function... Well, the Ptr
  1415.   function is usefull to transforms any structure into a pointer. Basically
  1416.   what it does is transform two values (segment and offset) in a pointer. If
  1417.   you supply the two values as beeing the segment and offset of an existing
  1418.   structure (as you do above), and then read to that place in memory, the data
  1419.   will be read into that structure... That's quite useful, as you can see... :)
  1420.     So, let's move on... Putting down a letter... Nothing could be easier:
  1421.  
  1422.         Procedure PutLetter(X,Y:Integer;Col:Byte;N:Char;Where:Word);
  1423.         Var Cfx,Cfy:Byte;
  1424.             Ccc:Byte;
  1425.         Begin
  1426.              For Cfy:=1 To YSize Do
  1427.              Begin
  1428.                   For Cfx:=1 To XSize Do
  1429.                   Begin
  1430.                        Ccc:=Font[N,Cfy,Cfx];
  1431.                        If Ccc<>0 Then PutPixel(X+Cfx-1,Y+Cfy-1,Col,Where);
  1432.                   End;
  1433.              End;
  1434.         End;
  1435.  
  1436.     Well, what the hell is this ??? Simple... You just scan the array for
  1437.   the character you want, then the computer scans the X and Y indexes of that
  1438.   particular character... When it finds an element different of 0, it places
  1439.   a pixel of the specified color in the VGA screen or in any of the virtual
  1440.   screens... Quite easy, I think...
  1441.     To write a string, you just have to go through the string, writing the
  1442.   characters it founds in sequence. Notice that the increment of the X
  1443.   coordinate is fixed, because all the letters have the same size:
  1444.  
  1445.         Procedure PutString(X,Y:Integer;Col:Byte;N:String;Where:Word);
  1446.         Var Index:Byte;
  1447.         Begin
  1448.              For Index:=0 To Length(N)-1 Do
  1449.                PutLetter(X+Index*16,Y,Col,N[Index+1],Where);
  1450.         End;
  1451.  
  1452.     I've included a sample font, to use with the example program, but that font
  1453.   only contains uppercase letters, so use if wisely, my sons... I intend to
  1454.   release a font editor/grab, together with a sprite editor/graber... Maybe
  1455.   I'll ask help from anyone, since I don't have enough time to do all I want...
  1456.     But, without further due, here's a complete working example:
  1457.  
  1458.             Program FixedSizedFont;
  1459.  
  1460.             Uses Crt,Mode13h;
  1461.  
  1462.             Const XSize=16;
  1463.                   YSize=16;
  1464.  
  1465.             Type FSFont=Array[1..XSize,1..YSize] Of Byte;
  1466.                  Chars=array[' '..'º'] Of FSFont;
  1467.  
  1468.             Var Font:Chars;
  1469.  
  1470.             Procedure LoadFont(Filename:String);
  1471.             Var F:File;
  1472.                 Garbage:Word;
  1473.             Begin
  1474.                  Assign(F,Filename);
  1475.                  Reset(F,1);
  1476.                  Blockread(F,Garbage,2);
  1477.                  blockread(F,Garbage,2);
  1478.                  Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4);
  1479.                  Close(F);
  1480.             End;
  1481.  
  1482.             Procedure PutLetter(X,Y:Integer;Col:Byte;N:Char;Where:Word);
  1483.             Var Cfx,Cfy:Byte;
  1484.                 Ccc:Byte;
  1485.             Begin
  1486.                  For Cfy:=1 To YSize Do
  1487.                  Begin
  1488.                       For Cfx:=1 To XSize Do
  1489.                       Begin
  1490.                            Ccc:=Font[N,Cfy,Cfx];
  1491.                            If Ccc<>0 Then PutPixel(X+Cfx-1,Y+Cfy-1,Col,Where);
  1492.                       End;
  1493.                  End;
  1494.             End;
  1495.  
  1496.             Procedure PutString(X,Y:Integer;Col:Byte;N:String;Where:Word);
  1497.             Var Index:Byte;
  1498.             Begin
  1499.                  For Index:=0 To Length(N)-1 Do
  1500.                    PutLetter(X+Index*16,Y,Col,N[Index+1],Where);
  1501.             End;
  1502.  
  1503.             Begin
  1504.                  LoadFont('Fixed.Fnt');
  1505.                  InitGraph;
  1506.                  SetColor(1,63,63,0);
  1507.                  SetColor(2,63,0,0);
  1508.                  SetColor(3,0,63,0);
  1509.                  SetColor(4,0,63,63);
  1510.                  PutString(0,0,1,'THIS IS A TEST !',VGA);
  1511.                  PutString(0,30,2,'SPELLCASTER RULES !',VGA);
  1512.                  PutString(0,60,3,'I KNOW MODESTY IS',VGA);
  1513.                  PutString(0,90,4,'NOT ONE OF MY',VGA);
  1514.                  PutString(0,120,5,'QUALITIES...',VGA);
  1515.                  Repeat Until Keypressed;
  1516.                  Closegraph;
  1517.             End.
  1518.  
  1519.     So, if this is understood, let's move forward... If it isn't, read it
  1520.   again, and experiment !!! Experimentation is the key to good programming
  1521.   practice... :)
  1522.     I think I'll stop here for now... I'm sleepy (I just write this at night,
  1523.   after college, so it's very tiring)... I'll write the next stuff tomorrow...
  1524.  
  1525.     8.3. Proportional fonts
  1526.  
  1527.     I'm back !!! Well, where was I... er... Oh... Proportional fonts... So,
  1528.   what's a proportional font ?
  1529.     To answear simply... It's a font that isn't fixed in size... So, the x and
  1530.   y sizes of the font varie from character to character... Evidently, we can
  1531.   not use the same structure as above. But, if the bitmaps for the chars are
  1532.   of different sizes, why not use the sprite system implemented in last
  1533.   issue ? So, for all chars:
  1534.  
  1535.                   Type Chars=array[' '..'º'] Of Pointer;
  1536.  
  1537.     So, you read the font to the array of pointers, using the LoadImage
  1538.   function that I gave last issue. So, after you read the chars that matter
  1539.   (in the example program I'll give, I only read some chars, because I didn't
  1540.   had time to draw more chars), you must write them out... To draw _ONE_ char,
  1541.   you use the PutImage_C procedure (we use the clipping procedure because we
  1542.   don't know if the text is wider than the screen).
  1543.     So, the real difficulty is in writing a string. So, when the font was fixed
  1544.   sized, you just used the formula:
  1545.  
  1546.                        X:=Xi+CharNumber*XSize
  1547.  
  1548.     to find out the X coordinate of the character indexed by CharNumber. Xi is
  1549.   the original X coordinate (the coordinate of the first character), and XSize
  1550.   is the horizontal size of the font. So, if the horizontal size varies, this
  1551.   formula must varie. Let's use a variable, that increases with the size of
  1552.   the font. For example, we initialize that variable to Xi in the start of the
  1553.   WriteString procedure:
  1554.  
  1555.                         X:=Xi;
  1556.  
  1557.     Then, imagine the first character was 20 pixels wide. We draw it, and then
  1558.   add that number to var X:
  1559.  
  1560.                         X:=X+XSize(FirstCharacter)+Space
  1561.  
  1562.     And so forth... The Space is any number that specifies the number of pixels
  1563.   computer should leave between chars... If you don't put it there, the letters
  1564.   will be all crunched together...
  1565.     See ? Easy... Let's see some _CODE_ !!! :)))
  1566.     Oooopppppssss... I almost forgot... We must change the PutImage_C procedure
  1567.   for two reasons:
  1568.  
  1569.       1) What is saved in the file is whetever a certain pixel is on or off,
  1570.          not it's color.
  1571.       2) This procedure erases all background image, and usually text is output
  1572.          on the top of an image or something.
  1573.  
  1574.     When can fix up both problems in just one sitting:
  1575.  
  1576.            Procedure PutChar(X,Y:Integer;Var Img:Pointer;Where:Word);
  1577.            Var Dx,Dy:Word;
  1578.                A,B:Word;
  1579.                Segm,Offs:Word;
  1580.            Begin
  1581.                 Segm:=Seg(Img^);
  1582.                 Offs:=Ofs(Img^);
  1583.                 Move(Mem[Segm:Offs],Dx,2);
  1584.                 Move(Mem[Segm:Offs+2],Dy,2);
  1585.                 Offs:=Offs+4;
  1586.                 A:=Y;
  1587.                 While (A<=Y+DY-1) And (A<MaxY) Do
  1588.                 Begin
  1589.                      B:=X;
  1590.                      While (B<=X+DX-1) And (B<MaxX) Do
  1591.                      Begin
  1592.                           If (X>=MinX) And (Y>=MinY) Then
  1593.                           { Check if the pixel is to be set or not }
  1594.                           If Mem[Segm:Offs]<>0 Then
  1595.                               PutPixel(B,A,Mem[Segm:Offs],Where);
  1596.                           Inc(Offs);
  1597.                           Inc(B);
  1598.                      End;
  1599.                      Inc(A);
  1600.                 End;
  1601.            End;
  1602.  
  1603.     Another thing we'll need is a function that returns the X size of a
  1604.   certain character. This is easy:
  1605.  
  1606.           Function GetX(Img:Pointer):Word;
  1607.           Var Dx:Word;
  1608.               Segm,Offs:Word;
  1609.           Begin
  1610.                Segm:=Seg(Img^);
  1611.                Offs:=Ofs(Img^);
  1612.                Move(Mem[Segm:Offs],Dx,2);
  1613.                GetX:=Dx;
  1614.           End;
  1615.  
  1616.     Well, this is excessive programming, but... :))) It's easier to
  1617.   understand...
  1618.     So, the example program:
  1619.  
  1620.                 Program Proportional;
  1621.  
  1622.                 Uses Crt,Sprites,Mode13h;
  1623.  
  1624.                 Type Chars=array[' '..'º'] Of Pointer;
  1625.  
  1626.                 Var Font:Chars;
  1627.  
  1628.                 Procedure ReadFont;
  1629.                 Var F:File;
  1630.                     A:Char;
  1631.                 Begin
  1632.                      Assign(F,'Proport.Fnt');
  1633.                      Reset(F,1);
  1634.                      LoadImage(F,Font[' ']);
  1635.                      LoadImage(F,Font['!']);
  1636.                      LoadImage(F,Font[',']);
  1637.                      LoadImage(F,Font['-']);
  1638.                      LoadImage(F,Font['.']);
  1639.                      For A:='0' To '9' Do LoadImage(F,Font[A]);
  1640.                      For A:='A' To 'Z' Do LoadImage(F,Font[A]);
  1641.                      Close(F);
  1642.                      { I just loaded some characters because the file only }
  1643.                      { had those characters, saved in that order.          }
  1644.                 End;
  1645.  
  1646.                 Procedure PutChar(X,Y:Integer;Col:Byte;N:Char;Where:Word);
  1647.                 Var Dx,Dy:Word;
  1648.                     A,B:Word;
  1649.                     Segm,Offs:Word;
  1650.                     Img:Pointer;
  1651.                 Begin
  1652.                      Img:=Font[N];
  1653.                      Segm:=Seg(Img^);
  1654.                      Offs:=Ofs(Img^);
  1655.                      Move(Mem[Segm:Offs],Dx,2);
  1656.                      Move(Mem[Segm:Offs+2],Dy,2);
  1657.                      Offs:=Offs+4;
  1658.                      A:=Y;
  1659.                      While (A<=Y+DY-1) And (A<MaxY) Do
  1660.                      Begin
  1661.                           B:=X;
  1662.                           While (B<=X+DX-1) And (B<MaxX) Do
  1663.                           Begin
  1664.                                If (X>=MinX) And (Y>=MinY) Then
  1665.                                { Check if the pixel is to be set or not }
  1666.                                If Mem[Segm:Offs]<>0 Then
  1667.                                  PutPixel(B,A,Col,Where);
  1668.                                Inc(Offs);
  1669.                                Inc(B);
  1670.                           End;
  1671.                           Inc(A);
  1672.                      End;
  1673.                 End;
  1674.  
  1675.                 Function GetX(Img:Pointer):Word;
  1676.                 Var Dx:Word;
  1677.                     Segm,Offs:Word;
  1678.                 Begin
  1679.                      Segm:=Seg(Img^);
  1680.                      Offs:=Ofs(Img^);
  1681.                      Move(Mem[Segm:Offs],Dx,2);
  1682.                      GetX:=Dx;
  1683.                 End;
  1684.  
  1685.                 Procedure PutString(X,Y:Integer;Col:Byte;N:String;Where:Word);
  1686.                 Var Index:Byte;
  1687.                     Dx:Word;
  1688.                 Begin
  1689.                      Dx:=X;
  1690.                      For Index:=0 To Length(N)-1 Do
  1691.                      Begin
  1692.                           PutChar(Dx,Y,Col,N[Index+1],Where);
  1693.                           Dx:=Dx+GetX(Font[N[Index+1]])+3;
  1694.                      End;
  1695.                 End;
  1696.  
  1697.                 Begin
  1698.                      ReadFont;
  1699.                      InitGraph;
  1700.                      SetColor(1,63,63,0);
  1701.                      SetColor(2,63,0,0);
  1702.                      SetColor(3,0,63,0);
  1703.                      SetColor(4,0,63,63);
  1704.                      PutString(0,0,1,'GRABING',VGA);
  1705.                      PutString(0,50,2,'PROPORTIONAL',VGA);
  1706.                      PutString(0,100,3,'FONTS',VGA);
  1707.                      PutString(0,150,4,'SUCKS !',VGA);
  1708.                      Repeat Until Keypressed;
  1709.                      Closegraph;
  1710.                 End.
  1711.  
  1712.     Again, don't use lowercase letters, because they aren't drawn...
  1713.     The problem of proportional fonts is that they must well designed, or else
  1714.   it will happen to some chars the same as happened to 'u' in the example
  1715.   program...
  1716.     Proportional fonts are just a variation of fixed sized fonts... Easy and
  1717.   effective... Personally, I hate proportional fonts... But if you have enough
  1718.   pacience to work with them, go ahead... They always look good...
  1719.     Well, I'm going to bed again... I'll finish this tomorrow... It's almost
  1720.   2 am... And I must rise at 5 am... LIFE SUCKS !!!! :))) Cya tomorrow... ;)
  1721.  
  1722.     8.4. Colorfonts
  1723.  
  1724.     Ok, back again... I'm really getting fed up with this issue of 'The Mag',
  1725.   so I'm gonna wrap this up really quickly... :))))
  1726.  
  1727.     Colorfonts are fonts with colors in the same char... So, when you store a
  1728.   colorfont, you don't store only if the pixel is on or off, you store it's
  1729.   color... So, as I want to get this over, I'll just dump some code onto
  1730.   your screen... This is a complete example program, that shows how fixed
  1731.   sized colorfonts work:
  1732.  
  1733.             Program ColorFonts;
  1734.  
  1735.             Uses Crt,Mode13h;
  1736.  
  1737.             Const XSize=16;
  1738.                   YSize=16;
  1739.  
  1740.             Type FSFont=Array[1..XSize,1..YSize] Of Byte;
  1741.                  Chars=array[' '..'º'] Of FSFont;
  1742.  
  1743.             Var Font:Chars;
  1744.                 CFontPal:RGBList;
  1745.  
  1746.             Procedure LoadFont(Filename:String);
  1747.             Var F:File;
  1748.                 Garbage:Word;
  1749.             Begin
  1750.                  Assign(F,Filename);
  1751.                  Reset(F,1);
  1752.                  Blockread(F,Garbage,2);
  1753.                  blockread(F,Garbage,2);
  1754.                  Blockread(F,Ptr(Seg(Font),Ofs(Font))^,Filesize(F)-4);
  1755.                  Close(F);
  1756.             End;
  1757.  
  1758.             Procedure PutLetter(X,Y:Word;N:Char;Where:Word);
  1759.             Var Cfx,Cfy:Byte;
  1760.                 Ccc:Byte;
  1761.             Begin
  1762.                  For Cfy:=1 To YSize Do
  1763.                  Begin
  1764.                       For Cfx:=1 To XSize Do
  1765.                       Begin
  1766.                            Ccc:=Font[N,Cfy,Cfx];
  1767.                            If Ccc<>0 Then PutPixel(X+Cfx-1,Y+Cfy-1,Ccc,Where);
  1768.                       End;
  1769.                  End;
  1770.             End;
  1771.  
  1772.             Procedure PutString(X,Y:Integer;N:String;Where:Word);
  1773.             Var Index:Byte;
  1774.             Begin
  1775.                  For Index:=0 To Length(N)-1 Do
  1776.                    PutLetter(X+Index*16,Y,N[Index+1],Where);
  1777.             End;
  1778.  
  1779.             Begin
  1780.                  LoadFont('CFont.Fnt');
  1781.                  { Load a palette to use with the font }
  1782.                  LoadPal('CFont.Pal',CFontPal);
  1783.                  InitGraph;
  1784.                  SetPalette(CFontPal);
  1785.                  PutString(0,0,'THIS IS A TEST !',VGA);
  1786.                  PutString(0,30,'SPELLCASTER RULES !',VGA);
  1787.                  PutString(0,60,'I KNOW MODESTY IS',VGA);
  1788.                  PutString(0,90,'NOT ONE OF MY',VGA);
  1789.                  PutString(0,120,'QUALITIES !!!',VGA);
  1790.                  Repeat Until Keypressed;
  1791.                  Closegraph;
  1792.             End.
  1793.  
  1794.     I know I'm lazy... :))
  1795.     This program reads a palette for the color font... You get the ideia when
  1796.   you run it...
  1797.     Try the program out and experiment stuff, like scrolling text and other
  1798.   stuff like that... Scrollies are a part in almost every demo made by men
  1799.   and aliens... And colorfonts are usually a part of them... So, I say
  1800.   goodbye for now...
  1801.  
  1802.  
  1803. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  1804.  
  1805.  
  1806.   9. Hints and Tips
  1807.  
  1808.                 *   - Begginners tip
  1809.                 **  - Medium tip
  1810.                 *** - Advanced tip
  1811.  
  1812.     This issue's Hints and Tips will go deeper into the subject of using
  1813.   logic operators with "normal" numbers.
  1814.  
  1815.     - Odd or Even ? (**)
  1816.  
  1817.       Imagine you want to find out if a certain number is odd or even.
  1818.       Think of the internal representation of the numbers 50 and 51:
  1819.  
  1820.                50 = 00110010
  1821.                51 = 00110011
  1822.  
  1823.       Have you seen the difference ? An even number has it's rightmost bit
  1824.       unset and an odd number has it set... So, how can you check for it ?
  1825.       Simple, use the AND operand:
  1826.  
  1827.                A:=50 AND 1
  1828.  
  1829.       Now, A is equal to 0, that means that the number is even.
  1830.       So, what's the ideia ?
  1831.  
  1832.                      50 = 00110010
  1833.                       1 = 00000001
  1834.                50 AND 1 = 00000000
  1835.  
  1836.       If we used 51:
  1837.  
  1838.                      51 = 00110011
  1839.                       1 = 00000001
  1840.                51 AND 1 = 00000001
  1841.  
  1842.       51 AND 1 would return 1 !!! Cool, isn't it ?
  1843.  
  1844.     - Positive or negative ? (**)
  1845.  
  1846.       In the internal conception, integer numbers are represented with 15 bits
  1847.       for the value and 1 bit to act as a flag for positive/negative.
  1848.       So, knowing that the signal bit is the leftmost, we can use a method
  1849.       similar to the one we used to determine if the number was odd or even.
  1850.       For example:
  1851.  
  1852.                         -50 = 1000000000110010
  1853.                          -0 = 1000000000000000
  1854.  
  1855.       I know that -0 doesn't really exist, but in computer terms it exists.
  1856.       Notice this:
  1857.  
  1858.                          -0 = 1000000000000000
  1859.                               ||-------------|
  1860.                      Signal Bit   Value Bits
  1861.  
  1862.       So, continuing with the explanation:
  1863.  
  1864.                           -50 AND -0 = -0
  1865.  
  1866.       Getting a -0 means the number is negative. Otherwise the "function"
  1867.       returns a +0.
  1868.       Well, but in Pascal you can't use -0 as an operand, so how do you
  1869.       resolve the problem ?
  1870.       Well, as Pascal is a very flexible language , it can mix integers with
  1871.       words (unsigned integers), so integer (-0) is equal word 32768. So:
  1872.  
  1873.                       -50 = 1000000000110010
  1874.                     32768 = 1000000000000000
  1875.             -50 AND 32768 = 1000000000000000
  1876.  
  1877.       So, if the result is equal to 32768, then the number is negative...
  1878.  
  1879.       These are two of the many effects you can do with this kind of algebra.
  1880.       You can even compare numbers... But I'll leave that for next issue...
  1881.       I'm tired and I want to go to bed... Yawn...
  1882.       Just before I fall asleep in front of the keyboard,  I'll just tell you
  1883.       that this kind of think is called masking... You are masking bits
  1884.       (covering them up)... Just for the record, it was Scorpio who reminded
  1885.       me of these effects... Cya in another article...
  1886.  
  1887.  
  1888. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-
  1889.  
  1890.  
  1891.  10. Points of View
  1892.  
  1893.     God, I'm tired of this issue... It was a struggle to make this, because
  1894.   I'm in the middle of exams and all... But, finally it's over...
  1895.     This was the biggest of all issues of 'The Mag' so far... The Mag is
  1896.   growing... Next issue, the continuation of the Text Adventure tut, plus an
  1897.   article on how to make the flame effect (made by Scorpio... I'll just add
  1898.   somethings I was meaning to do for ages...). Also, I'll continue to do
  1899.   the articles on Sprites (transparency and moving over a background)... On
  1900.   the mode 13h tutorials, I'll teach how to convert the procedures to
  1901.   assembler... Yep, that's right... Full assembly procedures in next issue...
  1902.   I'll probably start basic 3d stuff, just to introduce a couple of effects
  1903.   that use standart 3d transformations... I'll also teach how to do 2d
  1904.   rotation of a sprite... Er... I think that's it... Maybe I'll remember
  1905.   something else to put there... Maybe I'll convince Scorpio to do some
  1906.   article (eheheh... Did you hear me, Scorpio ???? :)))) ).
  1907.     Well, That's all the time I've got for this issue... I'm already receiving
  1908.   death threat's by people complaining about the long delay on this issue...
  1909.   Oh, just a bit of publicity... Get the 'Infinity Mag, Issue 1'... That's a
  1910.   portuguese mag made by people in the portuguese demoscene, about the
  1911.   demoscene... Issue 2 is almost out, and both issues include my articles of
  1912.   'The Mag'... So, stay tuned and get it... It's pretty cool... Altough it
  1913.   requires a computer with VESA... But the look is great... Download it and
  1914.   see... You can get it at the ftp.cdrom.com site (it was in the
  1915.   pub/demos/incoming/mags directory when I last checked, but maybe Snowman
  1916.   moved it from there... If you can't find it in that dir, try the
  1917.   pub/demos/mags/1996... The filename is infy1.zip). You can also try the
  1918.   Realm Of Darkness BBS... The numbers are somewhere in this issue or in the
  1919.   info file... So, bye bye folks... See you in super-special-issue 10 !! :)
  1920.  
  1921.  
  1922. -x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x
  1923.  
  1924.  
  1925.  11. The adventures of Spellcaster, the rebel programmer of year 2018.
  1926.  
  1927.   Episode 9 - Living on a razor's edge (sometimes cut)
  1928.  
  1929.     As predicted by Deathripper, Kristie was back before supper, but she
  1930.   refuse to say even a word to me...
  1931.   - Let's get over the plan again... - I said, looking at the Gundsen Brothers.
  1932.   - Again ?!! - said finally Kristie, in a loud voice. - How many times will
  1933.     we have to go over that blasted plan ?!! I'm sick and tired of it !! You
  1934.     make it sound like it is complicated !
  1935.   - Hey, I just don't want any complications... - I responded in a calm voice.
  1936.   - Knock it off, you two ! - Karl said. - Can't you be a little time together
  1937.     without killing each other ?
  1938.   - It's not my fault that this no-good programmer keeps treating use like
  1939.     morons !!! - Excalibur said.
  1940.   - No good-programmer ? - I responded, in an angry tone. - Twice you called
  1941.     me a lammer, Kristie... If you keep up like this, I will walk away and you
  1942.     can both blow Comptel alone... If you can without me...
  1943.   - Well, mister Shitcaster... If that's the way you think, go right ahead...
  1944.     Good ridance... - Kristie said, looking me in the eyes... For a moment, I
  1945.   thought about swallowing my pride, and confess to her my love, but I
  1946.   restrained myself and I turn my back to both of them and walked away.
  1947.   - Good work, Kristie... - Gundsen said.
  1948.   - Well, Karl, what are we supposed to do now ?
  1949.   - He'll be back... - Karl said, with a smile on his face
  1950.     Kristie just looked at Karl, with a sad look on her face.
  1951.   - I don't think he hates you... - said Karl, laughing and turning his back
  1952.   on Kristie, who looked very embaressed...
  1953.  
  1954.     Again, Karl's previsions turned out right... In the morning after, the
  1955.   morning of the attack to the COHISS, I was waiting for them near the jetcar.
  1956.   - You came back ! - said Excalibur, excited, calming down in the next
  1957.   second. - I mean... It's about time ! - she continued in a stern voice.
  1958.   - I want to destroy Comptel... And I don't care who does it with me... - I
  1959.   answered.
  1960.   - So, let's go ! - Karl said, going into the car...
  1961.     Kristie led the car, until we arrived at 100 meters from the COHISS: a
  1962.   small shop, full of hitech equipment... A small fortress... One meter of
  1963.   solid cement, and lot's of guards with lazer machine-guns on the roof.
  1964.   - So, this is it... Are you sure you want to go throuht with this ? - I
  1965.   questioned.
  1966.   - Yep... - Karl said, without hesitating.
  1967.   - As damn sure as I'm gonna be... - Kristie answered, pressing the accelerator
  1968.   pedal to the ground, directly aimming the car at the COHISS's door...
  1969.     The guards on the roof opened fire, but the armour of our vehicle holded
  1970.   on, and we crashed against the door !
  1971.     The door collapsed with the strength of the Durallium block. We went off
  1972.   the car, with lazer riffles in our hands and we started to defend ourselfs
  1973.   from the guards, while Kristie loaded the car with the stuff we needed.
  1974.     In less than ten minutes, the vehicle was loaded, and we started to hear
  1975.   in the distance the sound of a airship comming in our direction. Almost
  1976.   twenty guards laid in front of us, dead, dripping blood from every wound.
  1977.   - Let's go !! Reinforcements are coming ! - Karl shouted, running for the
  1978.   car.
  1979.     Suddently, one of the dead guards wasn't so dead after all, and he lifted
  1980.   himself up, and he shot Karl right in the chest... He screamed and fell on
  1981.   the ground. I opened fire on the guard and hitted him in his head, making
  1982.   blood spill all over the place.
  1983.   - NOOOOOOOO !!!! - Me and Kristie shouted simultaneosly.
  1984.   - Shit ! - I said, running for Karl's body. He was still breathing... At
  1985.   some cost, I managed to pull him to the car, and then Kristie accelerated.
  1986.   We had killed almost every guard in the place, and running from the others
  1987.   was easy... Difficult was to outrace the above airship, but even that was
  1988.   simple, because we went into narrow streets, where it couldn't follow.
  1989.   - Hang on Karl... - I said, holding Karl's hand. He looked at me, with pain
  1990.   and agony visible in his eyes.
  1991.   - Take care of Kristie... - he said, almost in a whisper.
  1992.   - Hang on just a little bit more. - I said.
  1993.     Kristie continued to speed up the jetcar in the streets, trying to reach
  1994.   out headquarters, before it was too late...
  1995.  
  1996.                                              See you in the next issue
  1997.                                             Diogo "SpellCaster" Andrade