In this "issue":
This "issue" will be a lot shorter than normal, due to the fact that 4.04 is due release any day now and we are all rather busy. Another issue will be forthcoming after release when we can "chill out" a little.
What's Happening?
Normally, I'd say "Quite a lot" here, but this week, that'd be one hell of an understatment! If you've looked in the "News" section, you'll know that PF4.04 (and 68k Fantasm 4.04) is due any day now - I'd say probably "sometime before Sunday 16th". As I speak we are in testing.
I have to say it, but 4.04 is probably the best version we've ever produced. Why? Well it's the first version that has had full time work done on it. This gives us a lot of continuity, both in development and testing. From a stability point of view, 4.04 is light years ahead of 4.02., as is Eddie 1.20 bundled with both versions. To go with the release of 4.04 Rob has written an Advanced Fantasm article, showing practical uses of Fantasms directives - for example how to use For-next loops in your source, leading to less bugs, and more readable problem domain based code.
This time round, what is foremost in my mind is testing, and naturally, thought it a good idea to "discuss" the point here. Please note that the following discussion is not platform based - it is a discussion on debugging in general.
What should always be a big question in your mind, when writing a program, is "How am I going to test this?". Good question, and one that is often a most neglected area of development.
So, you've written a program, of moderate proportions and it seems to work as you think it should. Now you have to test it right? Wrong! :-)
What you should be doing now is compatibilty and final testing. You're basic testing should have been done during development. If you know what I'm talking about, then skip the next few paragraphs, if not, read on...
How to test when writing? Two steps you need to do. Firstly, when you write a routine, you should at the very least step through each line of the routine in a low level debugger such as Macsbug(tm) and make sure everything is working as you expect. Here's a classic example that we could've fallen into when writing the CD controller for Eddie1.20.
The CD driver returns status information, such as current playing time in Binary Coded Decimal (B.C.D.). We needed to display the track remaining time which means we have to subtract the current track time from the total track time in BCD maths. Now this would have been a doddle apart from the fact that you have can have a miximum of: 99 minutes, 59 seconds and 75 75ths of one second. What throws it is the seconds and 75ths, as BCD naturally works to a maximum of 99 in one byte - but I digress.
When doing BCD maths with the SBCD instructions, the Z bit is taken into account. This means that at the start of the subtract routine we must clear the Z bit, else the first SBCD could produce an error. It's an obvious statement, but something like this is so easy to miss, specially when you think you're good enough not to have design the routine, but just type it out!
After the routine was written, we pushed some test data through it, and monitored the steps in Macsbug, thus reassuring ourselves that all was well.
Next after the dry running comes the real-time running. Now in our CD display case we had a display we could watch, and make sure it was working ok. What if you've written a routine that has no visible indication that all is well?
This brings me to step 2 of the development testing process, which is to write your own debug module. In any large project it is always worthwhile writing such a piece of code. For example, in all our programs we have a debug window we can switch on simply by setting an equate, which coupled with a conditional IF assembles the debugging code.
Into this window we can display any internal variable on every event loop or whenever we want, simply by calling the debugging code, along with a text string explaining what the variable is doing, and what part of the program we are in. Generally it is a good idea to display the variable on going into it's processing routine AND coming out. Taking this further, you can press a pre-defined key, which freezes the program, modify a variable, then continue running.
It may seem like a lot of aggro to spend time writing a debugger for your program, but it will pay off on any large project. The time you spend writing such code is certainly not "non-productive". (As a side note, you may come across programmers that say they have no need to write debugging code - everything they do always works first time - to them, I raise my index finger and say "spin").Taking this even further, in our own game work, we "remote debug" via a serial link. The great advantage of this is that your debugging code need only be small, and if the machine the program is running on crashes, you don't loose the data produced from the "run". FYI, our remote debugger runs on ST's (cause we've lots of them), is very flaky and needs custom code in the program being debugged - but when it's working it's great :-)
But, what if you are writing a full screen game, can't remote debug, but still need debugging info displayed somewhere? In this case overlay the debugging text on top of the graphics being displayed. It is important to use the XOR drawing mode for the debugging text, otherwise, at a crucial time, the debugging info you need will be on a block of colour that is exactly the same colour - guaranteed!. To keep speed up, use a custom font and blitter - for example the custom font supplied with Fants libraries is ideal for this as it's 8*8 which means very fast movement to memory.
Sometimes you don't need debugging text - for example in any 3d game there is a lot of processing which dictates which objects are to be processed, and which are not - sometimes called "filtering". In this case all you may need is some simple feedback - such as how many objects are being processed, or even which objects are being processed? In this case, for speed you can access video memory directly, and in the debugging routine, just scan the objects, and if on, draw a short green line (8 pixels), and if off, draw a red line.
So, you've done all the good stuff, tested routines, checked out the real time behaviour (which seldom has little to do with the static behaviour when stepping, specially in recursive routines) with your custom debugger, and the program is finished. All you have left to do is make sure it all works as you think (not always easy!), then finally make sure it runs on a variety of machines, video cards, keyboards, Operating Systems, Extensions....the list is endless :-)
The good news, for you games writers, is that IMHO it is easier to test a game than any other type of program. (Harder, harder! :-)) Why? Well you have defined the game world right? So you control what the user can and cannot do, and hence can hopefully plan for all eventualities. The worst form of program to write (and maintain) is any program that lets the user type...You never know what you're going to get (or if your stack is big enough :-))!
The very final thing in this section today is parameter checking
to prevent crashes. I'll be honest, if one was to check every
single parameter on every routine, the thing would crawl along!
There is no need. If you have an algorithm, split over six routines,
do you need to parameter check at the start of every routine?
Well maybe not. If you can guarantee that what the main routine
passes to the other routines will not "go out of bounds",
then you are simply wasting time checking the parameters in the
sub routines. This means that you have to think about what you're
doing, but hey, turnips don't wrote programs right? (God, the
range of jokes springing to my mind is horrendous!). It is however
easy to make a mistake, and assume that subroutine x will always
pass subroutine y good data, when in fact it does not.
"The choice is yours.".
How do I do it...no chance!.
Just two other items I want to briefly touch on, the first is spotting "hot spots", and the second is interupt debugging.
Saint Tropez?
Hot spot = area of the program that if more optimised would drastically
improve performance.
Hot spots can generally be spotted when writing - if you are writing
a sprite based game, then your sprite print routine is a hot spot.
Because it is always in use, and is fundamental to the game's
mechanics, if you can optimise it by 1 instruction less, you will
get a practical speed increase.
Sometimes you have hot spots you don't know about, and need to improve performance. There are two things you can do - either write a timer for your debugger routine (which we do a lot) or write a "pc sampler", sometimes called a lot of other things, but this name explains exactly what it does.
For this you need to set up a high priority timer interupt running at a high frequency. The sampler has an array of "PC buckets". When the sampler is run from the interupt it knows the current PC (as it will have hopefully interupted your program), and puts it in the first bucket. When next run, the new PC is either put in a new bucket, or if the same as the first PC, added to the first bucket. When entered again, the buckets are searched, and if the PC isn't found, it goes in a new bucket etc etc etc.
When you stop the sampler, it sorts the buckets and you can view them. If you have a hot spot, you will spot it quite easily by examining the buckets. A hot spot will stand out. Now change the sampling frequency and run again. If you get the same hot spot address, then its probably a hot spot!
When writing this type of sampler, it is sometimes worth while varying the interupt frequency (jittering), just in case it harmonises with the repetition rate of one of your loops.
Now you're probably thinking, "Heck thats a lot of work just to maybe get a minor speed up?" - my answer is that it's up to you. This is the kind of thing we do. Do you want to write a killer game, or a mundane one? Besides it always makes a nice "intermission" half way through a project to sit down and put in some real debugging code. It is sometimes very hard getting through the middle of a project.
You will not write a game overnight - it is a long term commitment, possibly years - design, foundations, modularity - all words that should spring to mind in the early stages.
The second method of finding hot spots is to get each routine to call debugging timer start and timer end routines. Your debugging window/overlayer/host can then display these items as you perform various actions in your program.
Interupt debugging.
Why are you debugging interupts! No, I don't mean it... But seriously,
most large games almost have their own Operating Systems. I've
never written any game code without using at the very least timer
interupts, and more often VBL/HBL/Sound etc interupts. Even the
good old mundane Divide by Zero trap needs patching, cause you
generally get a lot of them and it's sometimes better to trap
than slow down the code with a test or compare when most of the
time the data will be good!
Unfortunately, sometimes you crash in interupt code - this is where a good low level debugger like good old Macsbug is invaluable. Depending on the processor there will almost always be a crash area of memory that will contain the machine state prior to the crash. Check the processors handbook and check out the crash dump.
More often than not, the interupt will have crashed because you caused the stack/pseudo stack to go awry, or because you did not manage the interupt well. Depending on the system, you may need to manually stop any other interupts coming in whilst you are processing the interupt. Others hardware set the interupt mask (or whatever), and it's up to you to enable the interupts again on exit from the current interupt processing.
Getting debugging info from an interupt routine is generally a case of finding your global variables, and modifying the debugging ones accordingly.
In conclusion, there are as many ways to debug a program, as there are ways to write one (cliche attack - argghh!) I hope that this small discussion has given you food for thought...
General
Well, I'm now well into my second week of full time work for Lightsoft. I can tell you that it is hard work. Not the holiday I envisaged! Generally it involves working 10-16 hours a day, but I can work those hours when I feel like, and not when my boss says :-)
I guess with the release of 404 looming, this is a bad time to get a feeling of what it's like. It should be better next week. Apart from picking my daughter up from school, i really haven't seen much of her, or Cath, my wife, for that matter.
I am looking forward to the holidays in July. Programming is great, but these long days can take their toll on one. At the moment I am starting work about 5 pm, and finishing some time in the early morning. This I find agreable, as I need absolute concentration most of the time, and the night time is generally the best for that. Luckily, I tend to work in "spurts", and so am not nocturnal contrary to what a lot of people think. It may mean working solid for three weeks, but I like to pick on something, and just do it. This means I can live, eat and sleep (bugger, another C!) the problem - for me there is no other way to do it.
Rob has spent an inordinate amount of time writing up "Random
Rob", to go hand in hand with the release of 4.04. He explains
in details, and with source code, how to use high-level constructs,
for example for-next loops, in Fantasm. Do give it a try. This
is one of my favourite pieces of Fant code:
T_SWITCH check_hands,`hand_controller_output T_CASE_RANGE check_hands,#hand_actions_start,#hand_actions_end,NOT,#hand_actions_face_start,#hand_actions_face_end,HANDS_ACTION T_CASE_RANGE check_hands,#hand_on_hip,#hand_in_pocket,hands_not_busy T_DEFAULT check_hands,hes_picking_nose T_ENDSWITCH check_hands
Hopefuly we'll get 404 (both Fant and PowerFant) out this week sometime - everybody please upgrade to it. We'll make no money out of it, but at the very least it'll save you some aggro, and us some tech support!
Code on!
Stu.