issue #7

Want to print? Use this version

Back issues are in the Archive

Go back to the main site

Download a zip version

REM 'Letter from the Editor
Welcome to Issue 7 of Qbasic: The Magazine
ON KEY 'Hot List
News from the QB world
INPUT 'Your Stuff
Survey #5 results plus yer letters!
! mov 'asm Intro: part 4
The latest print of Petter's series...
LINE 'The Gallery
Our new feature with a shot of "Eternal Frost"!
PRINT '3d: Part II
MA SNART does rotation and translation in 3d
OPEN 'Hall of Fame: Entry II!
Who's the second entry into the hallowed hall?
REM 'ProjectRT Preview!!!
Project RT! Project RT! Project RRRRTTTT! ^_^
PLAY 'Midi Composing I
A new series by 19Day on MIDI Composition!
END 'Next Issue
What's up in our next issue?

 

Letter from the Editor

By ZKman

7 is the luckiest number of all! ^_^

S'up's, todos? It's already time for Issue 7, a mere 28 days since the last issue (wow!..heheh). So, I know you're dyin' to know what's new...firstly, we're doin' a Zip version now in addition to the normal printable issue! You guys begged for it and we brought it. The link to the demo version is in the Table of Contents. Also new this month is a feature called the Gallery. Here we bring you an exclusive screen or two from a hot new game. This month one of the titles featured is "Eternal Frost", a new and top-secret project by DarkDread himself! Guess what you guys get next month, too? How about a full preview of this hot ticket!

 

Zips, galleries, and things

Speakin' of the Future, we've talked with some of the leaders in qb development

That's not all though! We've also got the start of a new Composing series by 19day. No need to steal midi's for your game now that you'll know how to make your own, eh? Petter continues his award-winning (ok..so not really...but it SHOULD be award winning!) series on Assembler. MA SNART whips up the latest batch of 3d fun; as ya know, 3d is the future, so no whining about how you hate math! ^_^. Finally, Angelo and Petter rule, because this month, myself and them bring you the coolest preview of 1999's coolest and, up this point, most secret game! What's that, asks the guy whose been under a whole for the last year? Why, it's "Project RT"! On top of this, we get screens straight from Enhanced, and an interview about qbasic's future by Terminator_Z!

Finally, some major apologies are in order. First of all, the code from Petter's assembly article last month wasn't quite right. This was my fault, not his. I put up an early version of the article rather than the correct one, so if you're interested in getting the correct version (in a .txt file), you can grab it here or you can grab it down at with the new assembly part. Also, we mentioned that last issue (in the Index) that this was the second part of the assembly series. It was in fact the third (making this issue the fourth...wow!).

So, dig in and enjoy. Feast and rejoice. Blibber and blabber. Roast a pig in effigy. Or just read the issue...

See ya' next month!

zkman

 

Back to top

qbasic Hot List

Compiled by ZKman

 

What da ya MEAN "not for sale"?
Last month, we reported that the qbt50 had decided not to sell itself. Well, heheh, turns out Steve has changed his mind yet again. The qbt50 is now for auction at ebay.com, for a reported starting bid of $800 American, half of what it was goin' for! "Attention, KMART shoppers. We have a sale on aisle one..."

You can contact Steve at this address

quickbasic.com, Learn to Program QBASIC, the Qbasic Gazette, and more!

 

 

Interplay puts out a BASIC compiler!
Interplay, creators of some hugely important PC games, such as Fallout, Baldur's Gate, and many more, have released a new product called "Learn to Program BASIC". This product includes many movies to teach concepts such as LOOP's and Logic Statements, and also has "special" statements, such as BACKGROUND, which is a way of loading .bmp files direct into your program, and other special features such as new fonts, a new, easier way of doing GET-PUT, sound control and more! And it's even Windows based to boot, meaning you have easy access to tons more memory!

Sounds scrumdiddly-umptios, doesn't it? Unfortunately, it isn't. For one thing, many qb codewords aren't supported in LTPB. Also, the program can't even compile to .exe! It has a thing called a "runner" which will let it run games from the Windows desktop, like a Klik n Play file. However, at a street price of around $30, this is a good way for newbs to learn the basic concepts behind qbasic. Go to the LTPB site for more information, in addition to hearing .wav's from bigshots like Fallout and Sim City's lead coders talking about why BASIC is good! Very cool!

 

Qbasic Gazette launches
Tek from over at Neozones has begun printing his email-based qbasic gazette, called, appropriately, the "Qbasic Gazette". 1 issue has been put out, featuring a small amount of recent qbasic news, and a few short articles. However, we're assured that the next issue will go the full monty (uhh...sorta ^_^) with lots of qb:tm style articles and news to flood your email box periodically

Sign up for this new publication at Neozones today!

 

IF DarkDread <> RavenNight THEN "We're Wrong"
Last month, we told ya we were certain that RavenNight was in fact NOT DarkDread. Turns out we're wrong again. Through recent talks with Mr. RavenNight and some big shots in the qb community, we've determined that, (at least in our opinion), RavenNight is the same man as DarkDread. But that's not all! He's also got a new game in progress! Check upcoming Games for more...

 

Qlympics Voting Begins!
SonicBlue and Danny Gump's Qlympics program's preliminary part has ended, which means no more entries will be taken, which means it's time to vote! Wheeeeeeee! This year, voting will be done care of a PERL Script and all votes will be recounted by hand, to prevent the rampant cheating that occured in last year's Qlympics. Take your pick of the entries at the Qlympics.

 

uNESs Nintendo Emulator!
A group of qb'ers are developing (or, I should say, have developed, as an early version has already been released) a Nintendo Emulator! Dubbed "The World's Crappiest NES Emulator" because of it's slow speed, this is nonetheless very cool. The doc's report that the program can run many popular ROM's such as Super Mario World. The only downside (and I don't know why they made this design decision) is that the program has a 16-color maximum.

 

New library updates
Wow. Pretty slow month for the dynamic duo of Dash and DirecQB. First, the dqb updates. Nothing. ^_^. DirecQB stays at version1.42. No news on when Version1.5 will come, but we hope it's soon, because there are many reports of DirecQB's newest version not being compatible with some older code, and many coders reverting to the older (but more stable) version1.3. On the Dash front, a few new SUB's have been added, such as Collision Detection, DashX and Dash have merged, and Dash has been split into 10 different .obj files (much like what was done with dqb). Cheers to that!

 

Exodus goin' to quickbasic.com
ChUcK's domain of www.quickbasic.com has been attracting some attention recently; namely from the fact that many of qbasic's biggest site's will be moving there! We've heard that Enhanced Creations, Neozones, Alternate Logic, Dark Elf (ChUcK's company), and possibly even qb:tm will be moving to the site! Fairly fab is that this means many of your favourite sites will no longer have annoying banner ads!

 

Qbasic.com owner returns
David Zohrob, aka Mallard aka The Owner of qbasic.com has returned! After an unannounced hiatus that has lasted since this summer, Mallard has returned as has begun re-updating the qbasic.com site and hopefully putting a stop to the hacking that has been occuring at the popular qbasic.com webboard.

 

Gif89a Player in development!
We've learnt this month that many a gif89a player are in development! As you know, the gif89a spec allows for Animated gif's that so often annoy you on various webpages! And why is this good news then? How about animated gif logo's and cutscenes? BSAVE'd cutscenes take up nearly 64k per frame, whereas a gif89a would use less than half that!

 

If ya thought an NES emu was good...
A program called FSH-Flash, which is used to make all of the kits (y'know, altered graphics so you can have different leagues like the Irish league) for the Fifa series, may have been coded in qbasic! I was glancin' at the credits the other day, and noticed YPI being mentioned for .bmp code, BWSB for the background music, and "Quick Basic 4.5 for cool functions and making my hair turn grey". This is an *excellent* program, featuring such things at 800x600 res, and I'll try and bring more information for the March issue...

 

Nekrophidius' Carnage
Besides being a wicked cool Wetspot II levelset, quite a debate was stirred over our less-than-favourable report on Wrath of Sona, nekrophidius' latest project, last month. Whether it was about the semi-nudity, or the quality of the game itself, a venerable war was waged! Anyways, you can see for yourself and come to your own conclusion by grabbing the WOS demo from the Kaddash homepage.

 

Jaws-V is the MAN!
Super-cool development team Jaws-V Soft, made up of Change-v and Jaws-v rule! A recent visit to their site turned up much more than the well-known trio of MiniRPG 2, MiniRPG 3 and Cookie Delivery, including, among others, a great-looking side-scrolling shooter! With Jaws-v recovering from his injury earlier this year, work on MiniRPG 4 is starting! Look for more news soon, and check out current offerings at Jaws-V Soft

 

Oops!
Heya. A few gremlins entered the press less month. They were, all, umm... computer errors! Yeah, that's it!

Firstly, I put up an early and incorrect version of Petter's asm series. Look to this month's episode for the correct version. Also, in the Table of Contents, it said this was Part 2 of the series, when in fact it was part 3.

Also, this issue is coming out 13 FEBRUARY, not January, as the Next Issue section said last month and WAY too many people told me about. ^_^

 

Dark Ages 2 Engine Demo, Puz, Eternal Frost and more!

Latest Game updates:
Big news on the DA2 front. A new engine demo was released this past month! Mike Hoopman wants to emphasize that this is NOT the "demo", but merely a quick view of how the great asm scroller works. And how does it work? Pretty friggin' nice! Running at 640x480 on a P166, it ran too FAST!, and this is with no noticable frameskip! Also, pixel-perfect layering is demostrated. Pick it up today at the DA2 Homepage

 
 

 
After a brief hiatus from work on MazeRPG, 19Day will again pick up his project this month. The early demo looked great. For those who do not know this game, it features 4 "areas", each of which is somewhat different; for instance, side scroller in one area, top-down RPG the next, etc.. Keep your eye on this one!

SonicBlue has been putting lotsa work recently into Space-a-Roo 2. This game features great graphics, and is a top-down 360 degree scrolling space shooter! On top of that, it features pixel-perfect collision detection, rather than easier methods such as Manhattan and circle collision.

Puz, an addictive finished (read: complete!) puzzler by The Brain came out recently, and it rules! Although it's not a graphical masterpiece, it gets the job done in that department, featuring a cool fade-in when you rotate the numbers. It also has 3 mood-setting background midi's. The gameplay is high point though, with the objective being to get a collection of numbers into the right order and facing the right direction. Check this month's survey to see just how popular this game is, and possibly expect at least a mini-review in next month's issue!

A C+C style game in development that's lookin' pretty cool is called "Shell Shock". The graphics aren't the most amazing, but they do the job, the start of an attack program is already in the demos, and there are plans for ore mining, infantry, etc. Qbasic history does not bode well for C+C clones, though, as (to my knowledge), both Low Radiation and Woodlands have both been cancelled.

ChUcK and the guys over at Dark Elf Productions, one of qb's largest programming groups, currently housing the Fox team and others in addition to the groups main project, Medieval Muddle 3. Not much is known about the gameplay at this time, although it looks somewhat RPGish, but the graphical quality is great; right up there with Sypharage and Eldim...

This month in the shorts section: Eldim has begun prelim engine work on Bubble Fighters, his artistical-style driven fighting game...A demo of Dudes with Attitude has been released by Pickle Productions. This game looks similar to Wetspot 2 in screen layout, but plays in a novel and interesting way: The object is to "bump" your character off walls and collect crystals, but not bump into the enemies. The WII connection is not surprising, considering Pickle's head man, Wafn, was the creator of many of the more popular Wetspot II worlds. Pick it up at Wafn's World... No need to even print any Project rt news here: You can read all about it in the Project RT full preview in the articles section!...Darkdread has re-emerged, and his new title, Eternal Frost, should possibly have a full beta out by next month's issue! I've played an alpha copy, and it's great: Legend of Lith 2 style engine, but much better and more innovative. Look for a full preview next month, and check the screenshot in the gallery!...Speaking of re-emerging, what's up with Pasco's slew of titles? 3 of the most interesting looking qb titles in Soccer, Groov 2, and "Vertical Scroller" have had nothing new come out the stables in recent months! Come on, Pasco! We want those games! ^_^...Lbt1st is working on a "1.1" version of SFB2. Not merely a patch, this will improve the AI, fix up some particle effects, and possibly include new levels!

That's all the latest news for this month. Remember, if you have any news or rumours, send them along to Qbasic: The Magazine

 

Back to Top

Input: Your Stuff

By ZKman

 

 

Here's what you had to say about last month. Be sure and send us more letters soon!

hehe. The new issue should be coming 13 FEBRUARY, not January, as printed in the "Next Issue" section. Fabulous issue, thanks for taking the time to put it out.

MagnaUnum

AAAARRRGGGGGHHHH! ok! I know now that I put up the wrong month in the Last issue section. The oodles of email I recieved will help me from makin' the same mistake twice ^_^.

 

 

First off, the format is looking better; No more 44 billion frames cluttering up my screen and making my monitor fall on the floor. Good. But now you've got this crazy layout of the magazine itself. Bad. It looks... Drunk. It's really hard to differentiate between the sections, cause everything's just swimming left and right randomly... No. Bad. I don't like it.

Second, some more information wrong:
"SFB2: One of only 2 fighters in QB (the other being Sphere Fighter) this newly released game is Super-fab." One of only 2 fighters in QB? What the hell? Are you DRUNK? There are HUNDREDS of fighters out there in QB... SFB ONE for instance (you don't think LBt1st just pissed himself and said that was the first game, do you?). Then, there's some smaller, crappier ones... Smallness and crappiness does not preclude fighterness, though. No! I'll name some...Hexx... Stick something-or-other (sorta like SFB1 but it wasn't)... Spar... Others upon others, really... I can think of SO MANY QB fighters, I can't even list them.

There's just this big freaky cloud: "Lianne...in the Dark Crown: ...Many hours of gameplay...". 2 things: It's Lianne in... The Dark Crown. Put your ...'s in the right place! Second off, in what strange world did it have many hours of gameplay? Was there a later demo I never saw? The only one I've seen just had one overworld map where you killed bunnies then fought a tree and some guy... That didn't take hours, unless you fell asleep or something. What is wrong with you?

Things I agree with...Wrath of Sona being crappily dappily: Well, I can't say... The thing's too frickin huge to download! His server quits on me every frickin time I try! I mean, 13 megs, for a QBASIC game??? That's not big; That's just plain ineffecient. I mean, what is wrong with him? 6 megs is music, apparently... Okay, then, that's fine, you can choose not to DL that part (you can, you really can)... But that's still 7 MEGABYTES of stuff... I mean, is he using 320x200 JSprite graphics or what? I don't know; I don't want to know... After failing 3 times at downloading it, I'm never GONNA know. That nazi!

DarkDread != RavenNight... I agree with that too, but what's with the C inequality operator? <>, damn you, if it's gonna be a QBasic magazine! I got nothing against C, mind you, it's a great language and I enjoy using it almost as much as BASIC... But when you have a magazine called QBasic: The Magazine, you use QBasic's inequality operators, damnit! Oh, and the trends were pretty on the mark, though I think some were pretty obvious (ie, libs... They already dominate, and continue to grow...)... Of course, that doesn't mean I LIKE where the community is headed. I fear the game progger's will soon be drowning in libs. It'll be glorious for people who like lib-progging, mind you, they get to do all the fun stuff. But for any reasonable person (or, as you mentioned, PEOPLE) who wants to get something done that people will actually LOOK at, they're gonna have to go all-out using every damned lib available. QB Progging will turn into little more than putting together bits and pieces... Not by inadequacy, but by neccessity. And that's kind of sad, in my opinion. QB is losing it's low-quality romance: I'll miss it. Oh well. So it goes.

Oh yeah, and what's with the lack of mention of Last of the Legends(LotL)? It's one of the most damned promising QB games out there... Are you looking over it just because of the poor graphics or what? I mean, yeah, there are games out there with SVGA and umpteen other crappy effects, but do they have working, playable battle engines? A complex, versatile scripting language? None that I've seen. LotL certainly isn't the best QB RPG out there - Not yet, at least - But, damnit, it's worth a mention (especially when you're STILL talking about the long-dead Eldim, which was really little more than a glorified tile engine)

(Don't worry, I asked Enigma if I could say that. He said "If you...want...". I'll take that as a yes!)... I think that's all I have to say. Woo. Oh, and I bet you're wondering why there's a midget in my hat...

Skywise

whew! hehe, that's one long letter! Anyways, lots of stuff to comment on. Firstly, about the new layout. We've heard many compliments on it (see Seav's letter), so it's gonna stay "drunk"! I don't understand how it's hard to follow, as each article is either left or right: I don't switch in the middle!

On SFB2, I've fixed where it said that it was one of only 2 fighters! That section was written months ago, and I was probably half-asleep when I wrote it ^_^. About your comments on Lianne's playing time, it took me a couple hours! heh, but I suck at RPG's, so...

Wrath of Sona is either ya like it or ya don't, so I won't get in that discussion again ^_^. As you can see, we're also using qb's inequality operators now ^_^. Yeah! Qb! The trends probably should be obvious...I'm not Nostradamus over here! But your comments on the libs being a bad thing: I don't use any libs, but I probably will in the future without remorse. Romero and Carmack use libs in their games! If it improves the quality of qb games, I'm for it. What I don't like is newbies starting out with dqb the week they start qb. That's probably not the best way to learn to code ^_^.

Finally, you asked why we don't cover more on LotL. I try to get in lots of projects under Upcoming Games, but only about 10 games a month out of hundreds in dev means I can't mention everything every month! I've only mention my own game once in 7 issues!

 

 

 

I felt I had to respond to some things...

Yes, you should make an email version [of qb:tm].

Why is everyone looking forward to Project RT? It's just an engine for any type of 3d game at the moment - the reason Angelo and Petter are keeping it secret is that they dont' know what game they're going to turn it into.

Steve

hmm...we were considering (and have added) a .zip version of qb:tm. But there was never talk of an email version! Y'see, our issues weigh 50 to 100k, but free email lists have a max of about 10k, so...

About Project RT: Think ya missed some info there! To see what the fuss it about, read the preview down in the articles section! Yeah! Project RT!

 

 

 

Hey, zkman

I really like the new layout. It looks like it took a lot of effort to make it presentable. And it's easier to read the articles! Keep up the good work!

BTW, Maybe you meant that the next issue will be presented on *February* 13. Not January. Also, would you consider dividing each issue into pages? Hmm....

I think a zip version would bee a good idea. Something like VSNews of VirtuaSoft.

About the labyrinth: I'm planning to release another demo by early February. This time it will have some resemblance of gameplay.

SEAV

Thanks! I like the new layout much better too...you don't even have to move your head to read the articles anymore! ^_^. AAARRRGGGGHHHH! Yes! We get the picture! February! ^_^.

On dividing into pages, I've thought of that (or of doing a .pdf version), but have decided not to, primarily cuz it's a lot of work ^_^. Maybe sometime in the future, though.

I hadn't seen the new demo posted as I was formatting this issue, but go see for yourself at Seav Softwares if the new Labyrinth is up.

 

 

 

Where can I get hold of the previous assembler tutorials by Petter Holmberg??

Thanks for your help!

Tom Sor

Oh! For readers who didn't know of it, back issues are stored in the Issue Archive. You can also read old news there. In the case of Petter's articles, they were in Issue 4, 5, 6, and this one. Check the Index for a complete listing of all articles, what issue they appeared in, and printable versions of each issue!

 

 

qbasic: the magazine reserves the right to edit any letter recieved for purposes of clarity and space. But you already knew that.

 
 

Here's the results of the Issue 7 survey from Qbasic: The Magazine! We need more people to Vote! So do it!

 Favourite Game     |  Last Month  | Change    
 1. Wetspot 2              1           <> 
 2. Dark Ages (t)          2           <> 
 2. Mono. Shooter (t)      --          --  
 4. Puz                    --          --
 5. SFB2 (t)               3(t)        D2 
 5. Groov Buggies (t)      --          --
   
 Comments: Monospace Shooter jumps up to a
   tie with perennial number two Dark Ages!
   Puz performs well, and SFB2 slips a bit.

 Favourite Utility  |  Last Month  | Change
 1. DirectQB               1           <>
 2. QMIDI                  2(t)        <>
 3. SpriteShop (t)         --          --
 3. PP256 (t)              2(t)        D1
 5. Dash                   2(t)        D3

 Comments: Dash takes a huge hit this month
  in the polls! QMIDI and dqb both do very 
  well, and 2 tile editors make the list.

 Best Upcoming      |  Last Month  | Change
 1. Dark Ages 2 (t)        1           <> 
 1. "Project RT" (t)       2           U1
 3. MOTG Project           --          --
 4. LotL (t)               --          --
 4. Vampira (t)            --          --
 4. Adv. in Rulon (t)      --          --
 4. dqb version 1.5 (t)    --          --
    
 Comments: DA2 and Project RT controlled 
   the voting again, with Project RT coming 
   into a tie at the top with DA2! MOTG did
   fairly well, and a slew of titles rounded
   out the list.

Vote today by emailing your votes for:
favourite game
favourite utility
Game/utility that you're most looking forward to.
Send your vote here

 

You need this stuff!

Must Downloads returns for another showing. This month, a new game was added! Puz, the puzzler by the brain, has qualied for Must Downloads! Also, a tile editor called SpriteShop, also by The Brain, came very close. Everyone knows that there are a god-awful amount of qb games out there, but what's worth downloading? These, my friend. Here, you'll find a list of the best of the best in QB progs. See something that should be here? Tell us and we'll check it out. You HAVE to have this stuff!

 
Puz
A puzzle game by The Brain where you must flip numbers in rectangular groups until you get them in order. Decievingly addictive!

Absolute Assembly
Petter Holmberg of Enhanced Creations's assembly converter. By typing in normal asm, this proggy will convert the asm into goods that qb will understand. Super-spiffy!

Dark Ages
One of the most engaging QB games ever, as well as one of the only complete rpg's. This was featured in PC Gamer! Check it out!

Groov Buggies
The best QB racer ever. Although it has some control problems and some clipping glitches, this wireframer set a new standard.

Lianne...in the Dark Crown
Darkdread best and most complete game. Many hours of gameplay, and featuring a battle system that's been imitated in countless qb projects since. Not to mention you get cat food from the enemies!

Monospace Shooter
Gradius' 2 color side-scrolling space shooter. Featuring very detailed enemies, flickerless animation and a devious AI, this game is a classic

Wetspot & Wetspot 2
Wetspot, the bubble-bobble like block-pushing action game was one of the best QB games when it came out, but W2 is just incredible. Super midi sound, great fast graphics, tons of variety, an insane number of levels...everything you could want. GET this game. Now.

SFB2
The BEST qbasic fighter. Ever. Even though it's wireframe, it has cool particles and smooth animation as well as rippin' gameplay.

PP256
Called the best tile editor in QB ever, PP256 has loads of tile-editing options at your disposal. If you use tiles in your game, you can't live without this.

DirectQB/DashX/Blast!
These 3 sets of libraries take QB graphics to the extreme. They all have strenghts and weaknesses, but you should check them all out before you start a big project. You'll save a lot of coding plus get a big speed increase (and in DQB's case, save memory). Try these now.

QMIDI4.1
This is the best version of Qmidi. Play .mid's in your game! The new qmidi4.1 rules! It has tons of features and a "light" version. Get it now!

Pb3d
Marko Dokic's 3d routines. Phong shading, gourard shading, environment mapping, you name it- it's here. If you want to see how to do good 3d, come here!

 

Sneukeule's RPG Page
 

This February, our site of the month in one immense Qbasic RPG Heaven. Modeled after The QuickBasic RPG Page, which is now run by LordQB, takes everything that page has done and does it doubly good. Updates and screenshots for nearly every qbasic game in development today, a complete collection of the greatest qbrpg's of yesterday, and a great tutorial section top it off.

Of course, this site is none other than the (relatively) new Sneukeule's RPG Page, whose name is incredibly hard to spell. Cheers to qb:tm's fifth Site of the Month.

RPG's, tuts, and a hard to spell name...

Back to Top

assembly tutorial

By Petter Holmberg

This time: Controlling Program Flow

(Editor's Note: I put up the wrong version of Petter's Part III assembly series. The code that I posted was incorrect. I apologize. You can grab a correct .txt version of the article right here.)

Hello! The fourth part of my assembly tutorial is here! The last part was really huge and covered many ways to manipulate registers and QB variables. I hope you've done some test programs during the last month to test what you've learnt. Now it's time for something new!

Until now, we've only seen simple assembly programs that basically only could manipulate numbers in different ways. As interesting this may be, it would be great to know a little more, wouldn't it?

 

Grab Absolute Assembly 2.1

 
Controlling the program flow:
In QBASIC, we're used to instructions that can control the way that snippets of code are run. I'm talking about instructions like GOTO, IF, FOR/NEXT and DO/LOOP. These kinds of instructions are essential in all program languages. Naturally, they also exist in assembler.

Many professional programmers don't like BASIC because of one particular instruction. I guess most of you knows what I'm talking about... GOTO! If you don't know the sad story of GOTO, here it is:

Before QuickBASIC was created, no BASIC compiler was procedural, i.e. they didn't support the creations of SUBs and FUNCTIONs. You had to build your program around a messy structure of jumps back and forth through the code. Some common routines could be called with GOSUB/RETURN which made it a little easier to keep up a good program structure, but sooner or later you still ended up with a really messy program that was hard to debug and update unless you planned the program very well. The term "spaghetti code" is often used to describe program code that is really messy, with jumps between lines all over the place. Most old BASIC programs looked like that, so naturally the BASIC language wasn't the choise of professional programmers. When you're coding in QBASIC/QuickBASIC you should never use GOTO and, if you can avoid it, not GOSUB/RETURN either. With a good procedural structure you will never need them, and the program won't turn into spaghetti. In assembler, you don't have that luxury. You MUST use instructions similar to GOTO and GOSUB if you want to get anything done!

Let's begin by explaining the eqivalent to GOTO:

GOTO in asm:
Whenever you need to make an unconditional jump in an assembly program, you use the instruction JMP. (JMP stands for JuMP, as you probably guessed). The general syntax is:

JMP offset

Where offset is a number that describes the offset in bytes to the byte where the machine language equivalent of the JMP instruction is located. The number can also be in a register or at a specific position in the memory. Does it sound complicated to you? Yes, I thought so! Actually, it's a pain to use JMP like this. If you want to make a correct jump, you must go through all of the asm code between the JMP instructions and the destination instruction and count the number of bytes they take up. And if you need to insert new assembly instructions in the middle of a program using JMP, you'll mess up everything and you have to recalculate all the offsets.

In DEBUG, you can always see the the memory position of every assembly instruction, so in order to make it easier to use jumps, you just type the memory position of the instruction you want to jump to, and DEBUG translates this into an offset for you. This makes it easier to use JMP, but you cannot say it has become very much easier.

One of my primary concerns when writing Absolute Assembly 2.0, the first really useful version, was to make it much easier to use JMP. So I included the support for line lables, just like the ones you use in QBASIC. Absolute Assembly and DEBUG together takes care of the translation to offset numbers for you. With Absolute Assembly, JMP is as easy to use as GOTO is in BASIC. Consider this very short program:

LineLabel: MOV AX, 1
JMP LineLabel

This program moves a 1 into AX, at a line labelled LineLabel, and then the JMP instruction makes the program go back to that line again. 1 is moved to AX again, and the same jump is performed again. As you easilly can understand, this program would result in an infinite loop if executed. Since I didn't spend too much time perfecting the label feature of Absolute Assembly, there are some limits to the ways that you can use them. Read the notes in the beginning of the program source for more information. You should use JMP carefully in order to avoid spaghetti code. Now when we're at it, let's look at another feature of Absolute Assembly:

Comments:
It's hard to understand source that other people have written. Most of the time it's also hard to understand the code that you've written yourself after a couple of weeks. Understanding assembly code, even if you wrote it yourself only an hour ago can be a nightmare! Just look at the routine that we did in the end of the last part of this tutorial series:

PUSH BP
MOV BP, SP
MOV BX, [BP+A]
MOV AX, [BX]
MOV BX, [BP+8]
MOV CX, [BX]
ADD AX, CX
MOV BX, [BP+6]
MOV [BX], AX
POP BP
RETF 6

 
 

Do you remember what it did? Can you instantly explain how it works? I can't. Due to this problem, I knew that it was necessary to allow comments in Absolute Assembly. In BASIC, the "'" sign, or the older REM instruction can be used for commenting. In the most popular assembler's, like Microsoft's MASM and Borland's TASM, (I'll get back to them in another part of this tutorial series) the semicolon, ";", is used for comments, so I made this an Absolute Assembly standard too. Commenting assembly code is as simple as commenting BASIC code. Let's try:

; Assembly routine that adds two integer variables together:
PUSH BP ; Allow the reading of variables from QBASIC:
MOV BP, SP
MOV BX, [BP+A] ; Move variable 1 into AX:
MOV AX, [BX]
MOV BX, [BP+8] ; Move variable 2 into CX:
MOV CX, [BX]
ADD AX, CX ; Add AX and CX together and put the result in variable 3:
MOV BX, [BP+6]
MOV [BX], AX
POP BP ; Get back to QB:
RETF 6

Wow! What a difference a few comments can make, right? Now the purpose and the basical functions of the routine are clearly explained. The details of each operation can now be understood by examining very few lines of code. This commented version of the addition routine would be correctly handled by Absolute Assembly. It just ignores everything written on a line after a semicolon has been encountered. Now, let's get back to those jumps!

CALL and RET:
Instead of using only JMP to jump around in your asm code, you can use the assembly instructions CALL and RET. They are the asm equivalents to GOSUB and RETURN in BASIC. So if you have a routine that needs to be used several times in your code, you can put it in a subroutine in the end of your asm code and use CALL to get there. The syntax for CALL is:

CALL address

Just like with MOV, you would have to calculate the memory offset to the asm instruction that you want to jump to, but with Absolute Assembly all you have to do is to specify a line label.

When a CALL is executed, some things happen that you maybe will find interesting: First of all, the CPU pushes the offset address of the asm instruction after CALL on the stack, thus reducing the SP register by two. This is important to know if you're using the stack in the program. Then, the IP register, which always contains the offset address of the current machine language instruction being executed, is changed to the offset address of the assembly instruction you want to jump to. The IP register cannot be changed manually. The only way to modify it is to use JMP, CALL or similar instructions.

When you've jumped to a subroutine using CALL and want to get back again, you must use the instruction RET, short for RETurn. RET will pop the address of the instruction after the CALL instruction back from the stack and change IP. The next assembly instruction to be executed is the one after CALL. The syntax for RET is simply:

RET number

The number can usually be left out, but if you have pushed additional numbers on the stack inside the subroutine, you can let the RET instruction pop them away for you.

If you look at the example program used in the description of JMP above, you can see the instruction RETF 6. RETF works just like RET, but with the difference that it returns from a FAR call, i.e. a call that has been made from another segment address in the memory. It's possible to specify a full memory address, containing both segment and offset after CALL, but we won't need to do that in Absolute Assembly programs. However, I strongly suspect that QBASIC executes such a CALL instruction when you use CALL ABSOLUTE. Far calls require that both the segment and offset of the next asm instruction are pushed on the stack, so also the CS register, containing the segment address of the instruction currently being executed, is pushed. That's four bytes instead of two, and that's why you have to use RETF instead of RET. The number 6 after RETF pops away 6 extra bytes from the stack. That's the three integer variables that was passed from the BASIC program.

This program is a simple, useless example of using CALL and RET:
(The numbers to the right specify in which order the instructions are executed)

PUSH BP ; 1
MOV BP, SP ; 2
MOV AX, 1 ; 3
CALL Subroutine ; 4
MOV CX, 3 ; 7
POP BP ; 8
RETF 2 ; 9

Subroutine:
MOV BX, [BP+6] ; 5
RET ; 6

Just as a reminder: The four lines that was presented to you earlier in this tutorial series as a base for all your assembly routines are not necessary if you don't pass any BASIC variables to the routine. Thus, three of the four lines below won't be necessary:

PUSH BP
MOV BP, SP
POP BP
RETF x

The only important instruction is RETF. You don't need the others. All right, now on to something else.

Conditional jumps:
It's very likely that your assembly routines sometimes has to do different things depending on, say, the numbers you've put in the variables you pass to them. In QBASIC, you frequently have to use IF/ELSE or SELECT CASE to control such things. How can we do this in assembler? The answer is: Through conditional jumps!

If you want to control the program flow depending on input data, you can use the assembly instruction CMP, which is short for CoMPare. The syntax is:

CMP destination, source

 

Earlier parts of Petter Holmberg's assembly series can be found in the Archive. They are in issues 4, 5, and 6.

The destination and source can be registers, immediate numbers or memory pointers.

CMP actually works a little like SUB. It subtracts the source from the destination. The difference is that it doesn't store the result in the destination like the SUB instruction would do. What's the use of it then? Well, something very important actually happens, but you can't see it. Now I have no choice but to present another new feature of assembler to you: The flags.

The flags are a very important part of assembly programming, even though it's something you rarely have to worry about. The flags are all located in a register, and that register is simply called the FLAGS register. This register is different from all of the others, because its individual bits all have separate tasks, and they are very important for the execution of a program. Each bit in the FLAGS register is called a flag, and they all have names. I'm not going to present them to you here because you'll never need to use most of them, but the important thing to know is that, almost every assembly instruction modifies some of the flags in different ways. One example is SUB. There's one flag called the Sign Flag, SF, and it will be set to 1 if the result of the subtraction gets negative or 0 if it gets positive. One of the few times you really use of the FLAGS register is when you push or pop it. PUSH FLAGS and POP FLAGS are valid instructions, and they're used when you need to preserve the state of the flags to a later time. So, even though CMP won't store the result of a subtraction, it will modify the flags in the same way that SUB would do. What use can we have of this then? Well, here comes the answer:

There are a number of assembly instructions that can be used to perform conditional jumps in the code. Their names all begin with a J, for Jump.

Here are the most common ones:

Name: Description:
----- ------------
JB    Jump if Below
JBE   Jump if Below or Equal
JE    Jump if Equal
JAE   Jump if Above or Equal
JA    Jump if Above
JL    Jump if Less (signed)
JLE   Jump if Less or Equal (signed)
JGE   Jump if Greater or Equal (signed)
JG    Jump if Greater (signed)
JNB   Jump if Not Below
JNBE  Jump if Not Below or Equal
JNE   Jump if Not Equal
JNAE  Jump if Not Above or Equal
JNA   Jump if Not Above
JNL   Jump if Not Less (signed)
JNLE  Jump if Not Less or Equal (signed)
JNGE  Jump if Not Greater or Equal (signed)
JNG   Jump if Not Greater (signed)

How do these instruction work then? Well, the general syntax for all of them is:

Jxxx linelabel

The linelabel thing works just like it does with JMP.

The idea is that you should use CMP to compare two operators, and then use one of the conditional jump instructions to go where you want to go depending on the state of the flags that a CMP between the two operators changed. Let's try an example!

Suppose you have two numbers in AX and BX. If the number in AX is greater than the one in BX, CX should be set to 1. If not, CX should be left unchanged. Then you could use this code snippet to test it:

.
.
.
CMP AX, BX ; Compare AX against BX.
JBE NotGreater ; IF AX is below or equal to BX; skip the next line.
MOV CX, 1 ; Set CX to 1. (This line is only executed if AX > BX)
NotGreater: ; The label used for the skipping of the prevoius line.
.
.
.

Get it? Now CX will only be changed if AX is greater than BX, because if it's below or equal to BX, one line will be skipped.

As you can see, I've put a space before the MOV CX, 1 instruction. I usually do this when writing conditional jump code in asm, just to make it look more like in QBASIC where you often do this between IF and END IF. This is just one of my tricks to make asm code more readable so you don't have to care about it.

There's another thing you may be wondering about. In the list of jump instructions above, some of the descriptions have the comment "(signed)" in them. As I mentioned briefly in the previous tutorial, a signed number is the same as a negative number. I'll wait with the explanation of how negative numbers are stored, but it's important that you know when to use what jump instruction. If you want to compare two numbers where one or both of them are negative, you must use a conditional jump instructions that can handle signed numbers. So instead of using JA (Jump if Above), you use JG (Jump if Greater) and so forth. The JE and JNE instructions works with all types of numbers. I promise to explain the nature of signed and unsigned numbers later, and then you'll understand why you need so many different jump instructions.

Let's try another example just for the sake of clarity: Consider the following code snippet:

.
.
.
CMP AX, 0
JL Negative
MOV BX, CX
JMP EndOfTest
Negative:
MOV BX, DX
EndOfTest:
.
.
.

What this code snippet does is the following: It first tests if AX is 0. If it is less than zero, i.e. if it's negative, BX will be set to the value in DX. If it wasn't negative, no jump will occur and BX will get the value in CX instead. But in order to avoid setting BX to DX right after that, which would destroy everything, an unconditional jump to the code after that line must be made. It's a bit ugly, but that's the only way to do it. If it makes you feel any better you can think of the "JL Negative" instruction as IF, the "Negative:" label as ELSE, and the "EndOfTest:" label as END IF.

There's another assembly instruction that can be used for conditional jumps: TEST. The syntax for TEST is:

TEST destination, source

TEST is used exactly like CMP and for the same purpose. The difference is that CMP performs a subtraction between the two operands, but TEST performs an AND between them. This can be useful if your conditional jumps depends on the bit settings of the operands instead of the value of the whole operand.

Loops:
Another important feature of QBASIC and other high-level languages is the ability to execute a code snippet repeatedly, a feature knows as looping. In QBASIC, you have the FOR and NEXT instructions for loops that you want to perform a certain number of times, and DO/LOOP and WHILE/WEND for loops that should run until something special occurs. Slow loops are one of the major reasons of slow program execution, and one of the major reasons to use assembler in QBASIC is to speed up things. Therefore, you'll soon discover that the most important parts of your program to rewrite in assembler often are the loops.

You already know how to perform DO/LOOP type of loops in assembly. You can use JMP together with conditional jumps, like this:

.
.
.
XOR AX, AX ; Set AX to 0.
StartOfLoop:
INC AX ; Increase AX.
CMP AX, 10 ; If AX is 10: Get out of the loop.
JE EndOfLoop
JMP StartOfLoop ; Jump back to the start of the loop.
EndOfLoop:
.
.
.

This example would increase AX ten times and then continue the program. Note how I use three spaces before the instructions inside the loop here, just like loops are usually written in QBASIC. This is just another way of making the code more readable. Feel free to invent your own tricks if you don't like mine. (This article is formatted to html, which unfortunately makes 3 blank spaces a wicked pain to code, which is why you'll notice only one space- editor)

Although this is an acceptable way of performing loops in asm, there is a special loop instruction that does the job even better. Let's take a look at it!

The special loop instructions I'm talking about are made for the FOR/NEXT type of loops, i.e. loops that you want to run a certain number of times. The CX register plays an important role here. It's the register used to store the number of times a loop should be executed.

The instruction used to perform loops is... LOOP! The general syntax is:

LOOP Label

The Label operator works the same as with other jump instructions. Here's an example of using LOOP:

.
.
.
MOV CX, 10 ; Loop ten times.
StartOfLoop: ; This label specifies the start of the loop.
ADD AX, 4 ; A useless asm instruction.
SUB BX, DX ; Another useless asm instruction.
LOOP StartOfLoop ; Jump back to the StartOfLoop label.
.
.
.

This example demonstrates how you use the CX register and LOOP to make a set of asm instructions execute a certain number of times. You just put the number specifying how many times you want to execute the loop in the CX register. When the LOOP instruction is executed, the number in CX is decremented by one (without touching the stack), and if the result is above zero, a jump back to the specified label is performed. This means that the lines between the line label you jump to and the JUMP instruction will be executed the same number of times as the number you set CX to before the loop. The only bad thing with this loop technique is that the CX register is occupied as long as you're inside the loop. You can use CX for other stuff, but then you would have to save its value somewhere else (the stack for example) and restore the value before the LOOP instruction.

Limits of jumps:
All of the asm instructions performing jumps (JMP, CALL, LOOP and so on) have a limit: The jumps cannot be too long. What I mean is that you can't jump across too many assembly instructions. Or actually, the limit depends on the number of bytes the machine language equivalents to the assembly instructions takes up. The limit is between 128 bytes upwards to 127 bytes downwards. It's not easy to know how many bytes your assembly instructions occupy, but it generally varies between one and five. An instruction such as PUSH AX only takes up one byte, but an instruction like MOV [BP+10], BX needs three bytes. Usually this limit is nothing to worry about. 100 bytes are a lot of assembly code. I've never had a problem with this limit.

Memory transfers fast and easy:
I thought we should make a really cool example of assembler in QBASIC this time. But for that example, we need to know three new assembly instructions. There are lots of asm instructions for handling strings. I'm not going to discuss all of them here, but there are three of them that are really usefull: LODS, STOS and MOVS. Their respective syntaxes are:

LODSx
STOSx
MOVSx

As you can see, there are no operands or anything. The "x" should be replaced with either a B for Byte or a W for Word. The first instruction, LODS, works like this: If you use LODSB, a byte from the address pointed out by DS:SI will be loaded into AL, and SI will be increased by one. (Actually it may be decreased instead if the Direction Flag, DF, is set.) LODSW works in the same way except that a whole word (two bytes) will be loaded into the entire AX register and SI will be increased (or decreased) by two. STOS is the opposite of LODS: It copies the value of AL/AX to the address pointed out by ES:DI, and increases/decreases DI by one or two depending on if you use STOSB or STOSW.

MOVS is a combination of LODS and STOS. A byte or a word is loaded from DS:SI, but it doesn't go to a register. Instead it is copied to ES:DI and both SI and DI are incremented. MOVSB/MOVSW can therefore be used to transfer bytes from one position in the memory to another without touching the reguisters, something you cannot do with the standard MOV. We're going to use MOVS in the example routine I'm now going to present to you.

An example program:
To summarize this part of my assembly tutorial, we're going to make a really cool example program. You may not have realized it, but you already have a great knowledge of assembly programming. If you don't believe me, you'll only have to look at this example:

What we're going to do is an assembly version of the popular QBASIC instruction PUT. I'm not talking about the file handling version, but the graphics instruction used to put a sprite on the screen.

We're only going to use SCREEN 13 for this example, since this screen mode is really easy to use. The whole screen is built up by a 320 * 200 pixel bitmap with 256 possible colors. Each pixel occupies one byte in the VGA memory, and that memory starts at the address A000:0000h. The first byte contains the index of the pixel at coordinates 0,0, the second one of the pixel at 0,1, the 320th one of the pixel at 1,0 and so forth.

The sprite is stored in a QBASIC array, where the first word specifies the width of the sprite times 8. The second word specifies the height of the sprite, and then the pixel indexes comes as bytes stored in the same way as the screen 13 bitmap.

It would be nice with a routine that was a little better than PUT. Of course it will be faster than PUT, but let's add another feature. Many times you wish that you could draw sprites with an "invisible" color. This means that one of the colors in the sprite won't be drawn on the screen. With this feature, your sprites can have irregular edges and "holes" in them because a certain color will be skipped when the sprite is drawn on the screen. We'll use color 0 as the "invisible" color.

Before we start writing the asm code, we'll make a demo program in QBASIC to test it with:

' Demonstration of using assembler in QBASIC to put a sprite in SCREEN 13
' much faster than with PUT and with an invisible color.
SCREEN 13
' Initialization of the assembly routine:
' (Here we're goint to put stuff later on.)
DIM testsprite%(513)
DIM background%(513)
LINE (0, 0)-(31, 31), 32, BF
LINE (4, 4)-(27, 27), 0, BF
LINE (8, 8)-(23, 23), 40, BF
LINE (12, 12)-(19, 19), 0, BF
GET (0, 0)-(31, 31), testsprite%
' Demonstrate the routine:
CLS
LINE (0, 24)-(319, 199), 32
LINE (0, 199)-(319, 24), 40
spritex% = 144
spritey% = 96
COLOR 40
PRINT " Demo of PUT routine in assembler:"
COLOR 32
PRINT "Use the cursor keys to move the sprite."
PRINT "Pressing Escape exits the demonstration."
GET (spritex%, spritey%)-(spritex% + 31, spritey% + 31), background%
' (Here we're going to make a call to the asm PUT routine later.)
DO
key$ = INKEY$

SELECT CASE key$
' Up:
CASE CHR$(0) + "H":
PUT (spritex%, spritey%), background%, PSET
IF spritey% > 30 THEN spritey% = spritey% - 10
GET (spritex%, spritey%)-(spritex% + 31, spritey% + 31), background%
' (Here we're going to make a call to the asm PUT routine later.)
' Down:
CASE CHR$(0) + "P":
PUT (spritex%, spritey%), background%, PSET
IF spritey% < 158 THEN spritey% = spritey% + 10
GET (spritex%, spritey%)-(spritex% + 31, spritey% + 31), background%
' (Here we're going to make a call to the asm PUT routine later.)
' Left:
CASE CHR$(0) + "K":
PUT (spritex%, spritey%), background%, PSET
IF spritex% > 10 THEN spritex% = spritex% - 10
GET (spritex%, spritey%)-(spritex% + 31, spritey% + 31), background%
' (Here we're going to make a call to the asm PUT routine later.)
' Right:
CASE CHR$(0) + "M":
PUT (spritex%, spritey%), background%, PSET
IF spritex% < 278 THEN spritex% = spritex% + 10
GET (spritex%, spritey%)-(spritex% + 31, spritey% + 31), background%
' (Here we're going to make a call to the asm PUT routine later.)
END SELECT
LOOP UNTIL key$ = CHR$(27)

That's it! Let's save this code in the file PUTDEMO.BAS.

Now we need to figure out what input values the asm routine needs:

First of all we need to pass to the assembly routine the x and y coordinates of the screen where we want the sprite to be drawn. The asm routine also need to know where in the memory to find the sprite. The sprite should be stored in an array, and all arrays start with the offset address 0 in the memory, so we just need to pass the offset address. The asm routine also need to know the dimensions of the sprite, but that's stored in the sprite data so we can obtain these values inside the assembly routine itself. So, let's make the call to it look something like this:

CALL ABSOLUTE(BYVAL x%, BYVAL y%, BYVAL VARSEG(testsprite%(0)), SADD(asmput$))

Now we can start typing in the asm code in our favourite text editor. Let's take it step by step! First we want to allow the reading of QBASIC variables:

; A PUT routine with clipping:

PUSH DS ; Push DS and BP and move SP into BP.
PUSH BP
MOV BP, SP

As you can see, we do not only push BP, but also DS. It's necessary to preserve the value of DS in all assembly routines called by QBASIC if you're going to use it, or else your computer will crash.

Now we need to point DS:SI to the start of the sprite in the memory: This requires that we get the segment address of it from QBASIC. Where in the stack can we find it? Well, first we remember from the last part of this tutorial series that QBASIC first pushes the variables we passed to the routine in left to right order and then pushes four extra bytes on the stack, so the computer knows how to get back to the BASIC program. Then we push DS and BP on the stack ourselves. Since the stack grows downwards, this means that the last variable would be found at BP+2+2+4=BP+8, the middle one at BP+2+2+4+2=BP+10 and the first one at BP+2+2+4+2+2=BP+12. Since DEBUG treats all numbers as hexadecimal, we will have to define the stack offsets of the variables in the following ways:

x% = BP+0C
y% = BP+0A
VARSEG(testsprite%(0) = BP+08

Calclulating stack offsets like this may seem a little tricky, but you'll get the hang of it after a while. Now, let's load the address of the sprite into DS:SI!

MOV BX, [BP+08] ; Get the segment address of the sprite.
MOV DS, BX
XOR SI, SI ; Set SI to 0.

Now we know where to begin fetching the data. But where should we put it? In order to determine the correct position on the screen, we need to know the pixel co-ordinates of the upper left corner of the sprite. These numbers should've been passed to the routine and pushed on the stack. It's just to go and get them:

MOV DX, [BP+0C] ; DX = X coordinate.
MOV AX, [BP+0A] ; AX = Y coordinate.
MOV BX, AX ; BX = AX = Y coordinate.

And why do I load the y co-ordinate into two registers, you ask? Since the screen pixels are stored in the memory from left to right, row by row from the top to the bottom, the correct memory position to begin moving data to depending on the coordinates x and y is: A000h:y * 320 + x. As you can see, the offset calculation includes a multiplication. Even though this only needs to be calculated once per call to the routine, making the use of MUL despite its slowness acceptable, we're going to use another method which is much more interesting. Remember from the last part of this tutorial that you could use shift instructions for multiplications and divisions if the number to multiply or divide by was in the series of numbers defined as 2^x, like 2, 4, 8, 16, 32, 64, 128, 256 and so on. 320 isn't one of these numbers, but it can be expressed as the sum of two of them: 320 = 256 + 64. This suggests that if we multiply the y coordinate first with 256 and then with 64 and add the results together, it will be the same as if we multiplied it with 320 in the first place. And that is certainly true! So with two shifts and one addition, we can perform a multiplication with 320 really fast:

MOV CL, 8 ; Multiply y with 256.
SHL AX, CL
MOV CL, 6 ; Multiply y with 64.
SHL BX, CL
ADD AX, BX ; Add the results together.

Now you can see why the y coordinate needed to be copied to more than one register. We need the value twice.

Now we have y * 320 stored in AX and x stored in DX. Now we only need to add them together to get the correct memory offset for the sprite. The segment address should be set to A000h, and then we have the correct destination memory address in ES:DI:

MOV BX, A000 ; ES = A000h.
MOV ES, BX
ADD AX, DX ; DI = y * 320 + x.
MOV DI, AX

All right. Now we have DS:SI pointing at the start of the sprite data and ES:DI pointing at the screen memory position where the data should be written.

Now it would be nice to know the width and height of the sprite. This is stored in the beginning of the sprite data. First comes the width of the sprite. We load the first two bytes of the sprite data into AX, using LODSW. The number we get is eight times bigger than the actual width. Therefore we need to do a division by 8 to get it right. Luckilly, this division can be handled with a shift instruction. Then we move the value to BX, where it will be stored:

LODSW ; Get width info from sprite data.
MOV CL, 3 ; Divide the number by 8 to get correct width.
SHR AX, CL
MOV BX, AX ; Store the width in BX.

Next comes the height. It's simple to retrieve it from the sprite data, since it comes after the width and is stored in the correct form. We won't need any shifts to correct it. We won't support sprites higher than 200 pixels, so only one byte needs to be stored. Let's keep the height in AH. AL must be left free for later:

LODSW ; Store the height in AH MOV AH, AL

Finally, we need to know what 320 - the sprite width is, because this number will be used in the drawing loop. This is simple to do. We can use DX to store this number, and the sprite width is already in BX. So all we need to do is this:

MOV DX, 140 ; DX = 320 - Sprite width
SUB DX, BX

Now we have loaded all the data we need. We still haven't put anything in CX, and that's good because we're going to need it in the drawing loop. AL will also be used, since we use LODSB in the code.

Let's take a look at the registers and see how many we have left:

DS = Source segment
SI = Source offset
ES = Destination segment
DI = Destination offset
AH = Sprite height
AL = Reserved for drawing loop
BX = Sprite width
CX = Reserved for drawing loop
DX = 320 - Sprite width

Phew! We made it without having to use the stack to stuff away data. All of the basic registers are used. If we would have had to load more information, using the stack would have been inevitable.

OK! Now it's time for the main loop. Actually it has to be two loops inside each other. I'll show it first and explain it later:

Yloop: CMP AH, 0 ; Stop drawing if y is zero.
JE EndOfDrawing
MOV CX, BX ; CX = Sprite width.
XLoop:
LODSB ; Load a pixel from DS:SI into AL.
CMP AL, 0 ; Is the pixel color 0?
JE SkipPixel
STOSB ; No: Copy the pixel in AL to ES:DI.
DEC DI
SkipPixel:
INC DI ; Yes: Increase DI by one.
LOOP XLoop
ADD DI, DX ; Move screen memory pointer to next line.
DEC AH ; Decrease height.
JMP YLoop
EndOfDrawing:

POP BP ; Return to QBASIC.
POP DS
RETF 6

All right! What this loop does is the actual drawing process. First we test if the height of the sprite (a number located in AH) is 0. If it is, we will instantly jump out of the loop and return to QBASIC since there's nothing to draw. If it's over 0, CX will be loaded with the width of the sprite. Now we can load the first pixel from the sprite into AL, using LODSB. Remember that this also increases SI by 1. We test to see if this pixel is 0, the invisible color. If it is, we increase DI by 1, thus skipping a pixel on the screen. If it isn't 0, we use STOSB instead to copy the pixel to the screen and decrease DI by 1 to compensate for the increse that comes below it. Then we use LOOP to repeat this process. Each time CX will be decreased until it reaches 0. When this first happens, the first column of the sprite has been drawn. Now we continue below the LOOP instruction, where 320 - the sprite width is added to DI. This ensures that the next column will be drawn in the correct position on the screen. Finally we decrease AH containg the sprite height by 1 and return to the start of the outer loop. This process will of course continue until AH is 0, and the sprite drawing will be complete! All that's left to do is to exit from the routine, returning us to the demo program.

Now paste together the pieces of code we've collected and save it in a file called ASMPUT.ASM. Then run Absolute Assembly. Make ASMPUT.ASM your assembly source file, PUTDEMO.BAS your QBASIC destination file, make sure it appends the code to PUTDEMO.BAS instead of erasing its original contents and skip the adding of call absolute code. We can do that ourselves. Now start QBASIC and make sure the asm code was added to the program. Take the added code and move it up to the beginning of the demo program, replacing the line saying: ' (Here we're going to put stuff later on.) Then, you look up the five places saying: ' (Here we're going to make a call to the asm PUT routine later.) and replace them with the following lines:

DEF SEG = VARSEG(asmput$)
CALL ABSOLUTE(BYVAL spritex%, BYVAL spritey%, BYVAL VARSEG(testsprite%(0)), SADD(asmput$))
DEF SEG

And that's it! Test the program and see the asm code we've written in action!

There are some limits to this sprite drawing routine though: It won't work in any other screen mode than SCREEN 13, because the screen memory works differently for the other modes, and the routine lacks border checking, i.e. If you put the sprite on a position where parts of it is "outside" the screen borders, it will not be drawn correctly. If you feel like it, you can try writing a sprite routine that "clips" the sprite correctly at the screen edges. But anyway, it draws sprites in a way that the standard PUT cannot do, and it's much faster too! Neat, huh? :-)

Oh no! I've done it again! These tutorial parts are growing for each new month :-) Last time I promised to cover a bit more than I actually did in this part, but the things that you've just read are enough for now. Now go and experiment with assembly flow control on your own and see if you can come up with something cool! We've come a long way now, but basically we still only know how to do clever calculations and memory transfers in asm. This can be used for much, but there's still more to learn. The next time I will introduce some ways to communicate with the different parts of your computer in assembler. This is the part of programming that is usually refered to as... I/O!

Happy hacking everyone!

 

Back to Top

Gallery

By Zkman

 

Hey, everybody! Check out our new section, the gallery! Here, we'll do an exclusive game from some big name game every month. This month, we have the first screen straight from DarkDread's Eternal Frost. Check it out!

 

Eternal Frost

 

Back to Top

3d: Part II

By MA SNART

This time: translation and rotation across 2d planes...

Introduction to Translation and Rotation...
Welcome back to part 2 of my 'Introduction to 3D graphics' series of articles. Last time I told you of the different 'spaces' that exist in the typical 3D graphics system: Object, World and Camera. I also wrote that 3D models, residing in Object space, must be 'transformed' to World space, and then further 'transformed' into Camera space in order to view them. However, I didn't cover how to perform the 'transformation'. So, that is what I will explain in this article. However I won't cover a real 3D type system this time, but rather a 2D system like that of the 'Asteroids' game. This is because I feel that the 3D calculations would overly confuse many of you. Fear not, though, as I will use what I present here as a foundation to build off of when it's time to add the other 2 axis into the equation.

 
 

First off I will need to describe what it is I'm trying to do here:

Do you remember the old arcade game 'asteroids'?. Well, guess what? That is exactly what I hope you could make after reading this article! Well, not exactly; as I'm only going to get you going with how the 'player's ship' works. You'll have to work out the rest. So the code that follows [once you type it into the IDE] will display a small ship that you can rotate and move around, just like the 'Asteroids' ship :). Please NOTE that I wrote the code without testing [so it may not work :( ] and it isn't optimized and is very un-structured...Sorry!

Because this is only [for the time being] going to be 2D, we will only need to concentrate on 2 dimentions: The X and Y [just like regular sprite graphics :)]. What this also means is that all of our rotations will be along the Z-axis: there isn't going to be a test on this, but keep this in mind for later articles. Our points are in the X and Y plains and we are rotating along the Z-axis.

The beginning
Fire up QB1.1 (or 4.5) and type the following in:

DEFINT A-Z
SCREEN 7,0,1,0
WINDOW (-100,-100)-(100,100)

First of we are defining all variables beginning A-Z as integers [helps speed things up!]. We are then going to enter into SCREEN 7 mode [moan all you want...I can't hear ya] and the extra 0,1,0 stuff tells QB that we are using the default setting [the first 0] and the active page is number 1 [this is where all the drawing will happen] and finally that the visible page is number 0 [this is what you will see on the screen]. I hope none of that was very new to you :)

The last line will put the display into a 'window', or what you can see, on screen, is what is visible within the range of numbers passed. Basically what this does is re-number the screen. If you were to PSET a pixel at 0,0 it would now show up at the center of the screen; likewise, if you PSET to -100,-100, it would show up in the upper-left corner [as if you had PSET at 0,0]. This is very handy in that it turns the screen into a cartesian graphing system: From the screen center -Y travels up, +Y down, -X travels left and +X right. This is the same type of system used in real 3D graphics [It also has the side benefit of negating the need to 'transform' World space to Camera space...more on this later].

Now enter this into QB:

TYPE pntdat
x AS INTEGER
y AS INTEGER
END TYPE

TYPE lndat
v1 AS pntdat
v2 AS pntdat
col AS INTGER
END TYPE


TYPE entdat
loc AS pntdat
vect AS pntdat
ang AS INTEGER
END TYPE

These are going to be our custom variable classes...
The first one 'pntdat' stands for POINT DATA. It contains the location of a point by measuring along the X and Y axis. This type will be used by the others [so pay attention!] Type 'lndat' stands for LINE DATA. These have v1 and v2 [both meaning VERTACY] of 'pntdat' type data. These represent the end points of a line. 'col' stands for COLOR...er...what color the line is...[each line is used in Object space]. Finally, we have type 'entdat' which means ENTITY DATA. It contains 'loc' [LOCATION] a 'pntdat' type that indicates the current position of the ENTITY. 'vect' [VECTER] indicates in what direction the ENTITY is moving [also a 'pntdat' type]. 'ang' [ANGLE] indicates at what angle the entity is facing [remember ENTITIES exist in World space...and they use Objects to define them].

Next stuff:


DIM lns(4) AS lndat
DIM player AS entdat

Now we're getting to the meat of the proggy. 'lns' [LINES] stores the model of the ship, and 'player' stores our only active ENTITY...

Now enter:


FOR i=1 TO 4

READ LNS(i).v1.x
READ lns(i).v1.y
READ lns(i).v2.x
READ lns(i).v2.y
READ lns(i).col

NEXT i


DATA 0,-9,4,4,15
DATA 4,4,0,1,7
DATA -4,4,0,1,7
DATA 0,-9,-4,4,15

What this will do is enter our space ship into the space provided by the 'lns' array. The ship model, of course, exists in the DATA statements [all values are in relation to Object space's reference point [or 0,0]].

The main-loop:
Now type in:

DO

LINE (-100,-100)-(100,100),0,BF

First we set-up our DO/LOOP routine. Then we draw a black box to erase the screen [remember that the top-left corner of the screen is at -100,-100 and the bottom right is at 100,100]

Now comes the fun part....

co! = COS( player.ang * 3.141593 / 180)
si! = SIN( player.ang * 3.141593 / 180)

What we do here is get the SINE and COSINE of the angle indicating the direction that the player is facing. In order to do that we must convert the integer angle measurement into radians [ you do that by multiplying by PI then dividing by 180 :) ].

Here we go again...

FOR i = 1 TO 4

rx1 = lns(i).v1.x * co! + lns(i).v1.y * si!
ry1 = lns(i).v1.y * co! - lns(i).v1.x * si!

rx2 = lns(i).v2.x * co! + lns(i).v2.y * si!
ry2 = lns(i).v2.y * co! - lns(i).v2.x * si!

There you have it: the rotation. Each end-point of the line [indicated by v1 and v2] is rotated around the reference point of Object space [again at 0,0]. This formula is actually the same as a z-axis rotation in a true 3D system. The formula explained:

rotated_x = ( original_x * cosine_of_angle ) + ( original_y * sine_of_angle )

rotated_y = ( original_y * cosine_of_angle ) - ( original_x * sine_of_angle )

The reasoning behind how it works isn't a subject for this article, but at this point in time, rotated_x and y are STILL in Object space. If it was transferred to World space it would always be located at the World space reference point [0,0]. So what we will do is add the VECTOR indicated by the player's location to each end-point:

type away:

fx1 = rx1 + player.loc.x
fy1 = ry1 + player.loc.y

fx2 = rx2 + player.loc.x
fy2 = ry2 + player.loc.y

Now at this point the line has been 'transformed' to World space, but because the Camera doesn't move and the World space has already been transformed by the WINDOW statement earlier, we can draw the line now.

So type:

LINE (fx1,fy1)-(fx2,fy2),lns(i).col

NEXT i

Alright!...there you go...one Object transformed to World space!...

Now type in:

SELECT CASE INKEY$

CASE CHR$(27)
quit = 1

CASE CHR$(0)+CHR$(75)
player.ang = player.ang-3

IF player.ang < 0 THEN player.ang = player.ang + 360

CASE CHR$(0)+CHR$(75)
player.ang = player.ang+3

IF player.ang > 360 THEN player.ang = player.ang - 360

CASE " "

vx! = (-.3) * si!
vy! = (-.3) * co!

player.vect.x = player.vect.x + vx!
player.vect.y = player.vect.y + vy!

END SELECT

Okay that's our 'get key press' routine. The 'ESC' key will quit the demo, the left arrow will cause the angle to decrease, the right will increase the angle, and 'SPACE BAR' will make the ship thrust forward [I will explain this formula at a later time].

Now type:

player.loc.x = player.loc.x + player.vect.x
player.loc.y = player.loc.y + player.vect.y

IF player.loc.x > 100 then player.loc.x = player.loc.x - 201
IF player.loc.x < -100 then player.loc.x = player.loc.x + 201

IF player.loc.y > 100 then player.loc.y = player.loc.y - 201
IF player.loc.y < -100 then player.loc.y = player.loc.y + 201

This will enable the ship to have 'zero gravity' effects. If you constantly add the ENTITIES vector back into it's location, then the next bit of code will 'wrap' the ship around to the other side of the World space.

Finish the proggy with:

PCOPY 1,0

LOOP UNTIL quit = 1

END

Okay that SHOULD give you a little white ship to move around on the screen [and hopefully a bit of knowledge of translation and rotation]. Just so you can remember: To 'transform' from Object to World space, you rotate THEN transform [try switching the rotate and translate order to see what happens :) ]. This is NOT a rule written in stone however, BUT for this series of articles, keep it in mind!

Next time I'll add multiple ENTITIES to the mix and transform the World to Camera space as well...see ya!

 

Back to Top

Hall of Fame

By Zkman

 

A
n
g
e
l
o

Welcome back! You all remember that Darkdread was the first electee to the hallowed halls of the Qbasic: the Magazine Hall of Fame back in Issue 4. It's come time to select another entrant. After much deliberation, one worthy candidate has come through.

This man has been around the qb scene for a few years now, and has stunned us all ever since his first game, Wetspot, hit the scene. Although earlier projects such as EDraw, a fairly popular Sprite Editor, has come before it, Wetspot's great gameplay and great graphics made it a must download. After you beat the 21st level, you would get a message: "Coming Soon: The Full Version".

After much anticipation, in March of 1998, Wetspot II was released and immediately heralded. Since then, Angelo Mottola has put out directqb, Worlds of Wetspot, SoundLab, and FMTracker; surely making him the most proficient qbasic coder ever.

For his achievements, Angelo Mottola of Enhanced Creations is awarded the second entry into the Qbasic: the Magazine Hall of Fame. Congratulations!

M
o
t
t
o
l
a

Back to Top

prt preview

By Zkman
Interview by Terminator_Z
Thanks to Angelo and Petter

  Hey! This month, Angelo Mottola, Petter Holmberg, Terminator_Z and myself (zkman) are very proud to present a full preview on the game currently referred to as "project rt"! The buzz over this game has been in high order over the last few months, ever since the first news on Enhanced Creations making a new game came out (in fact, such a great buzz that "Project rt" tied Dark Ages 2 for best upcoming project in this month's survey!), but any info has remained a secret...until now.

The Shots, the Interview, and more!

 

To help blow out this incredible engine/game, Terminator_Z (programmer on FoX and member of Dark Elf) interviewed Angelo on his past, present, and future. The guys at Enhanced were also generous enough to send us some early shots of the amazing engine that will make Project rt a reality, and they also sent over some information on the project. So, here you have it: the earliest news on 1999's biggest game...

"Project rt" no more...
First off, "project rt" is not the final name that will be used for this game. First used quite a few issues of qb:tm ago (probably Issue 4), "project rt" has gained widespread use, and it stands for RealTime RaycasTer, hence the "rt". There has been no final name for the game as of yet (nor for the engine on which it runs), so qb:tm will continue to use project rt in reference to the project until we get the real name from Enhanced. Trust us, it'll be good...

What you've been waitin' for
So, what is project rt anyways? We've just told you that it's a raycasting engine, and not only that, it will be the backbone for the 3d game Enhanced in developing. Right now, the graphic engine, which is boosted in assembly, is nearly ready. In it's un-optimized form, this baby can crank out 256 objects in a large area with no slowdown! And these results can be attained on a 486-66 at optimal performance. Qbasic's boundaries are being pushed again!

The engine in it's basic form has the ability to support 256 objects at a max size of 64x64 for each, but Angelo tells us that by smartly using the map handler, this maximum could be avoided without much of a hassle. There's native support in the map engine to handle textures for the walls, the ceiling, and the floor, of course. So, how do Angelo and Petter attain this high speed while still keeping room in memory for this large amount of data? By using a few techniques not even before attempted with qbasic.

 

Here y'are! The first rt screen!

 
 

XMS, drivers, and Windows
As Angelo says in Terminator_Z's interview, a DOS language is inherently faster than a Windows program. Why is this? When running windows, there are many TSR's (Terminate and Stay Resident...press Ctrl-Alt-Del to see just how many your 'put has open right now!) that run at the same time as your code. Which means they use system resources that your code should have access too, meaning cycles that in a DOS proggie would be all for yourself are lost to these TSR's. So, basing on this thought, a way to speed up your code would be to have as few TSR's and Drivers (such as SBMIDI) running alongside your code.

All programs using dqb (and, by using dqb, have the EMM386 EMS driver open) are slowed down by this basic fact. How to get around it? Attempt the fairly difficult task of storing all your data in the very quick and very large part of memory called XMS. More complicated to access than EMS, (sort of the difference between writing a .gif loader and writing a .jpg loader), XMS does have many pluses, mainly that it is fast. Take a guess where the rt engine puts it's textures (and all of the objects, which are treated as textures)?

The rt engine features a fully functional 2d scaler, which appopriately makes all the textures object grow and shrink as they come closer and further. Unlike in Legend of Lith, which uses multiple tile sets for each distance, a Raytracer (the graphical technique responsible for Doom and Wolfenstein 3d) uses one copy of each object, and just changes the object's size. This creates a very convincing world where you can fully rotate the camera just like a polygonally created world. The way that the Project rt engine does it's graphical drawing, it also has the side effect of creating a zero flicker world.

 

Imagine THIS at 60fps!

 
 

But what is the GAME about???
In actuality, final touches are still being added to the engine. Firstly is support for doors, which is basically polygon support, since the doors are being drawn out as 4-sided polys. Also, a couple of bugs need to be worked out, and some more optimisation continues (More optimisation? Wow!). Once the entire engine is ready to go, coding will begin on the game.

Unfortunately, the exact subject of the game is still being worked out, although a 3d rpg (a la Ultima Underworld) is the highest possibility right now. Angelo and Petter are certain that the game will not be a conventional DOOM style shooter. They believe qb has the capacity for innovative games, not clones.

Although an RPG is definitely up there on the list, this may not end up as the final game. Y'see, it would require tons of sprites, and, as Angelo puts it, "we're coders, not artists!". The issue of how much time it will take for an rpg to be completed is not an issue, though. They "know it will take a lot of time for [project rt] to be completed, and we accept this fact. We don't care about the time."

As always, we'll continue to bring you the latest on the backstory/genre of Project rt as it becomes available. For now, check out this interview by Terminator_Z, as it provides many clues as to the future of Enhanced, and Angelo's thoughts on qb in general. Enjoy!

Q: Tell us briefly about yourself...

A: Well, my name is Angelo Mottola (a lot of people spells it wrongly; this is my right name!) and I'm 21. I study electronical engineering at my university, and I live in Rome, Italy.

Q: When and where did you get your start in the QB Community? What got you interested?

A: Before having an internet connection, my knowledge in programming (QB as well as other languages) was very limited; when I got it, I understood the huge amount of informations that can be found over the web, and my experience grew quickly a lot... I've been online for about 3 years, and I know famous sites as Neozones and qbasic.com since the first time I got the connection; one of my first searches was on "qbasic", and I got and followed the qbasic.com link, that brought me inside the QB community. At first, I found that QB was not used to all its potential, so I promised myself to prove that it can do more, and this still remains my main goal, what brings me to keep coding...

Q: What do you think is your best piece of work?

A: I like all my works. As my experience grew, I coded more and more complex programs; needless to say, the last two ones (WetSpot 2 and DirectQB) are the most advanced, thus they cannot be compared to the others, like WetSpot 1, since it was coded in 1995-96! The same W2 itself cannot be even compared to DQB on the technical point of view: the first uses many routines done by others, to play sounds, to handle the graphics, etc., while DQB is entirely coded in assembly by myself... Do I need to say more? Just that W2 was a lot funnier to make than DQB!

Q: Obviously, MSDOS based programs won't be around forever, yet, there will always be people wanting to push the limits of QB, their technical and artistic skills. What do you think will be the outlet for these people in the future? Do you intend to move on to another platform?

A: I still think QB can do more. With the boost of assembly, I believe we'll see many cool programs in the future, made in QB to work under DOS. Remember that DOS programs will always be faster than Windows based ones; in addition, I like to have the total control over the machine, and this in Windows is not possible. I know nowadays "real programmers" do not code under DOS, and I'll probably learn more and more how to program under Win95/98. I already know a bit of Visual C and Visual Basic, but I still need years to handle these languages as I handle QB now. But, again, I think Windows based programming is not as fun as QB programming, and I also have a lot of projects in mind that I want to complete, before I can move to another platform...

Q: What are your views on the use of libraries? Should people use them even if they don't understand the principles behind them?

A: What a hard topic! Let me say just one thing: libraries are a natural step towards the future. Wait! I mean that without them, everyone should spend years to learn how to code the same routines someone already made; in addition, think about Windows programming: in VC or VB, you always call library functions someone else did for you. You don't have to know how to handle the TCP/IP, as winsock does it for you; you don't have to worry about how to code a MIDI player, as there are functions that automatically play MID files for you, and this doesn't mean you must know the MIDI specifications!!!

Everyone should worry about the principles of programming (the same for all the languages), the concepts behind parallax scrolling and these kind of things, before worring about how to code a screen scroller in assembly (as a stupid example). I think that if someone understands the concepts of a parallax scroller, there're no problems if he/she uses a library to achieve a cool effect, even if he/she doesn't know how to code the scrolling routine. BTW, if this guy doesn't have good programming skills, his work will never be really cool, with or without the library, so why bothering?

Q: What are your ambissions for the future?

A: Coding something in 3d. As you'll see in the "Project RT" preview, I'm already moving towards this direction, and I hope to achieve soon good results.

Q: Would you ever consider a career in programming? If so, what field would it be in, application or entertainment?

A: As I'm studying electronical engineering, of course I'll work with computers. I'll learn how to build them, AND how to code them; I know the next years I'll study how networks work, and I can't wait! Anyway it's still too early to say something on this topic, but basically I think I'll go where I find a good job...

Q: What upcoming QB productions are you most looking forward to?

A: There are a lot of interesting projects in the air: Dark Ages II seems to be something really special, as well as Xeno and Bubble Fighter, as noone ever tryed something like them in QB. The Labyrinth is also promising, like Dude With Attitudes (I've tryed the demo), Peanut Patrol, MazeRPG, and a some others I can't remember now... Also our own game using the RT engine looks really promising, and I can't wait to complete it together with Petter.

Q: Tell us about "Project RT", what is it, who's idea was it and when should we expect to see it complete?

A: The original idea is by Petter. More infos about it are on the preview!

Q: Smile for the camera. Any final words?

A: ^_^ It looks like the 1999 will be the year of QB... There are so many cool projects that will be hopefully completed soon, and the overall impression is that we'll see less and less those horrible little games using INPUT! Power to QB!

"It looks like 1999 will be the year of Qb"

Back to Top

Midi Composing I

By 19Day

Be Mozart, thanks to 19day

Some muddled history:

Okay. First things first, what is MIDI. Well, in the 80's, it was Musical Instrument Digital Interface. What it allowed people to do is to record music into their computer by plugging their synth into the MIDI port (or whatever MIDI complient instrument they had) and play a peice of music to the computer, that would store the keypresses, velocities, tempo's and all that stuff. But rather than store the actual sound like in the WAV format, it just stores that data, which is very small in comparison to WAV's or MOD's (more on those later) because that's all there is, a bunch of numbers in a file. How does one play it? Well, back then, because it was for musicians, it played out the instrument they had recorded it with.

 

Download WinJammer!

Now, a year or two later, some guys who were making games (I dunno, some company probably started it) decided, hey, these MIDI things are really small, and they store all the information needed to play the music except for the sounds, so if we make a interpreter for the sounds, then we can get really small music, that sounds good.

I'm not sure whether the first one was in a sound card, or whether it was some software interpreter, regardless, MIDI was being used for that. And people began to forget the true meaning of MIDI, and use it like it was supposed to be used, but who cares, you can use it in your games.

Sizable choices:
Well, most of you have probably heard of BWSB, and it's abilities to play MOD-like music. Well, you can still use some of the idea's out of this series to help you, because MIDI sequencing and MOD sequencing are very similar, a MOD is basically a MIDI like file, but with the sound samples inside of the MOD as well, as opposed to being stored in the soundcard or some other system dependant place.

Now, the only real problem with MIDI's, is that they can sound very different depending on what kind of sound card they are being played on. Although the rest of the music is the same, playing "old MacDonald" that at one point sounded like a penny whistle on one computer, can sound like "old MacDonald" played on some sort of Spanish Bagpipe. Others will argue that the quality of music is not as good too, but really that's a matter of preference, I think the quality is reasonable on my soundcard set to the standard MIDI port, but when I kick in my WaveTable software based MIDI's, MOD quality, here we come.

Now, on my computer, I can make a MIDI that sounds like a MOD, and they can be the same with one exception, the MIDI is 1k and the MOD is 100k, if you want to have system independent music with constant quality and huge downloads, use MOD, if you want system dependant (to a point) reasonable quality and very small downloads, use MIDI.

Conversely, the tools we use to play these in QB are also a consideration. While QMIDI (the tool to play MIDI's in QB) can crash your computer more easily than work if you do it wrong, BWSB (tool for playing MOD's in QB) uses EMS that conflict with other EMS using libs, and can be hard to understand.

Choosing the sequencer:
Well, as far as I'm concerned, there are only 2 MIDI sequencers (midi makers) that I know of, WinJammer and CakeWalk.

I use WinJammer, the reason I don't use CakeWalk is that one night I decided to Download it, and it took forever, cause their server was slow, and when I installed it, it crashed Windows and cause a Illegal Operation in Explorer whenever I booted up, so it went bye bye and I stuck with WinJammer.

WinJammer's shareware version is identical to that which you would have for registering it, but when it's unregistered, all the NAME fields in the midi change to WINJAMMER DEMO, which, if you used that area to remember which staff was your drum and which was the melody, you'd be pissed, but you can type that stuff in the INSTRUMENT field, which does not get overwritten.

Basics of MIDI:
Each midi has a few things in it. Usually, the main bit is a staff, or a track. It is basically a music sheet area where you can plot notes for a specific instrument, keeping them separate from other instruments.

An instrument is known as a patch. There are quite a few in the GENERAL MIDI set and lots more in the other sets (soundcard dependant), but I always stick with the General set to make sure that everyone will at least hear SOMETHING akin to what I wrote.

The NAME is something you have to provide to a track to even let you mess around with patches and stuff, so make a new track by clicking the empty field in the NAME column and typing in a name.

Now, you have to assign the track a channel, basically, each track has it's own channel (10 is a special one, so don't use it yet) and that's how the soundcard understands what patch to use with which set of notes. So make that track's channel 1 or something, don't mess with PORT, and the last one, PATCH, is the instrument selector.

There are a few ways you can look at your music. You can look at it as events, as notes on a scale and as notes on a traditional musical staff. The only ones I find useful are the event and scale views. Now, if you wanted to make a simple (REALLY SIMPLE) song, then you should just pick a nice patch to start with, say, recorder (it's pretty whiny, so I wouldn't stick with it a lot).

Now, after setting up the track, click on the button that looks like a bit of a synth keyboard, and a new window will come up. This is where you start composing.

By default, WinJammer sets up things 4/4 and, and in the MIDI, each new segment, marked with a number, is separated into the 4 beats, and each beat is separated into 120 sub-bits (I don't know what they're called)notes

You have quite a few notes available to you, whole, half, quarter, all the way to thirty-second note, now click on the pencil icon (meaning WRITE) and click on the quarter note, and in the first quarter position, under the first segment, (that is, the first possible note on the sheet) move the cursor up and down and look at the bottom of the window (not the background window, the keyboarding window) and you'll see that it tells you the note positions, like A and D and such, so in the first quarter, put a note at A 4, then in the next quarter, a note at B 4, and another at D 5, and another at C 5.

Now, that should fill up that entire segment, so, with all the defaults in place, you can play your midi (you may have to set up WinJammer to your card, but it tells you how to do that)

Well, it sounds sorta nice, doesn't it, but very short, and not very interesting. So let's lengthen it. You can't COPY and PASTE notes like you might expect (why, I don't know) but there is a command called replicate, under the track menu. What you have to provide is a starting place for the COPY, ending place for the COPY, starting point for the paste and number of copies, in this case, the numbers are:

1.1:000 starts at first possible place
1.4:119 ends RIGHT before 2nd segment (otherwise it would be off by .001)
2.1:000 Paste at start of seg 2
3 make 3 copies, thus filling segments 1, 2, 3 and 4.

Play, well, it's longer, but kinda silly and monotonous, so lets make it a bit nicer, shall we. Just from listening to it, you might expect the 2nd segment to sound higher or lower, well, just different; I pick higher, so lets move a few notes around.

In segment 2, move the first note (click and drag) to G4, next note E 5, A 4 and F 5. The last segment should sound different too, lets say this was our entire song, well, usually songs have a feeling of closure about them, usually they do that by ending low. So in segment 4, move note 1 to A 4, 2 to F 4, 3 to D 4, last to F 4.

play? Well, sounds better, sort of haunting, needs a background... Something equally haunting. Make a new track, making channel 2 and the patch, make it String Ensemble 2. Now, click on the keyboard button (or on the window if you didn't close it) and chose the Whole Note, and the pencil again, and now, in the first segment, make 2 notes, one in F 4 and one in D 4, they should be parallel and be the same lengths. Segment 2 has notes B 3 and G 3, segment 3 has F 4 and D 4 and segment 4 had B 3 and G 3. Now, play...

Hmmm, spookier, but, here, try this, in that second track (string ensemble 2) take out all the lower notes, the D4's and the G3's, and play it.

One of the problems with midi is that too many notes can quickly make a song sound like gibberish, and make it sound like the notes are fighting to be heard. So we took out those overlapping notes, well, it sounds less full, but is a little clearer.

This is where preference comes in, where does body become less important that sound, some patches interact better than others, and some just sound horrible together. Experiment. And see how it goes...

The Right and Wrong Time for Midi Making:
So, you got your sequencer of choice loaded, you've sat yourself down and you're ready to make some Midi's for the next 5 hours, and they'll be great, and nice and the best damned thing ever sequenced... no. I've found that the technique of forcing one's self to be artistic is just plain counter-productive (I'm assuming everyone here is just an amateur at music; I mean, if you're a friggin' Mozart then go ahead, but if you're like me (no musical experience AT ALL) then you'll want to at least try this)

When you least suspect it, or when you're feeling cheery or sad, or when you don't feel like doing anything, try to sequence. If you're not paying attention, sometimes really nice things come out.

When I finally heard that fateful day that Milo had passed away, I was annoyed, angry and generally upset, so I decided to make a Midi that very minute (well, it took longer than that) and, well, it wasn't a sad midi, it was pretty angry, which reflected my dominant feelings at the time. Although it isn't my best, it did have a good beat and I might work on it again, but that's where the secret lies...

Random pieces:
Really, the big thing to ANY midi is some sort of either a) constant sounding theme OR b) a catchy bit that repeats in different instruments/pitches.

Now, you wouldn't believe how many of the latter I got just by placing random notes down, listening, trying to make it less harsh and then playing it for someone. (This is what happened for my SBH4.MID, that Tofu listened to) Tofu was able to pick out this one little bit that he thought sounded nice, so I redid my whole midi around that bit, added in supporting bits and, Voila, a nice sounding midi (well, nice enough for me).

So never underestimate what you can do with some random emotion and a shaky mouse-hand.

In the next installment, I will discuss some other musical styles and patch combos that work nicely, how to use QMIDI and how to use other events.

 

Back to Top

next issue

By Zkman

 

Hey! Thanks for checkin' out the newest issue! Next month's ish, due out on March 13 (see, I remembered the month this time ^_^), will be nice and funktified, featuring a full preview of DarkDread's Eternal Frost, more 3d, assembly and midi action, as well as some super-special stuff that we're not tellin' right now! ^_^. Also, don't forget to register your QB company with the Visionaries Exchange. Till next time... END

What's up in March?

Back to Top