QB ON ACID [smalogo.gif] December 21st, 1999 Welcome to issue #4 of our lil online zine. We're HUGE this time...over 70K of text alone! Not counting images and whatnot of course...anyways, prepare for some serious coding examples and a lot of info in this issue...so let's shut up and dig in! _________________________________________________________________ LETTERS TO THE EDITOR From Blair Pyle (aka Big Nose) I'd like to take a moment to respond to Zkman's letter from last issue. In no way are the Phat Kids inexperienced. Just because we haven't had any earlier projects posted until recently (Holy Conquest) in no way means that we are inexperienced. Fat Phil and myself have been making games for almost 7 years. We started by modifying Nibbles and as we learned more we made somewhat better stuff. We didn't have access to libraries off of the internet or anything like that until Groovin got access to the Internet. That's when we first got access to libraries and other stuff. I really must say that calling the Phat Kids inexperienced is a very ignorant comment and I should expect it only from an individual who can't see past his own face. Thank you. Editor's Note: This is what we're all about...allowing the readers to state their opinions. If you have any responses to this letter, please feel free to write to us. From Wafn I be inna December QBoA, wee :) Anyway, er, I actually enjoyed this latest issue of QBoA, some very valid rants/articles, and ones that interested me, too. That's very hard to find nowadays, mm? I also wanted to give QbProgger a virtual cookie for finally writing something that makes sense >:) One thing I would really like in this e-zine.. is something that will teach. Too many of the articles are just talking about theory, ranting about things, and so on. Although this is quite entertaining, it needs something solid to supplement it :) Why not write an article on how to load GIFs properly in QB, since you, Nek, and QbP seem to be able to do this pretty good. This is a QB magazine, if the title of it is any indication of the language, and a QB magazine should have something directly relaring to QB, no? :) Ahh... well, great job, you'd better not discontinue this thing.. this 'EFnet diskmag' couldn't be much better ;) Editor's Note: Discontinue? Not a chance in hell at this point! But we'll try to find some people willing to do more teaching articles. Back 2 Top _________________________________________________________________ The News New NES Emulator? Yes! According to the creator, a new NES emulator, written in 100% QuickBASIC 4.5, is in development! According to Bloodknight, the author of this emulator, it will support all 160 mapper types and all 256 6502 opcodes. We got a chance to view a very early copy of the source code, and it is very very complex. This emulator will run in ModeQ, 256x256x256 colours, whether or not it will support sound is currently unknown. Of course, being pure QB will make it very slow, but this is just another example of how people are trying to break the rules in QB. Neozones...what are they DOING! For those who don't know, there now exists neozones.com! Yes, NeoZones, one of the largest online QB community centers, finally gets its rightful place in cyberspace. Apparently there was some miscommunication between Chuck of quickbasic.com and Spacehog, and old-school QBer, about domain issues, and in the mix, Enhanced Creations was lost. Well, it's back online at http://ec.neozones.com/ for the time being. Back 2 Top _________________________________________________________________ State Of The Scene The QB 'scene' has been quite pathetic and lame of recent times. First of all though, let me tell you that have no wanting of re-igniting any of these stupid childish feuds. I am simply ruminating over the semi-recent events. First of all was the almost dead "using libs means you are not writing your own program" argument, which was re-started by another feud, more on that one later. Of the ceaseless banter back and forth, Darkdread summed it up best. Why create your own gfx lib if someone has already done it? Using JFont means I cannot program? I do use GSLib for its blazing speed. I could program my own set, but it would not be nearly as fast and would be time consuming, so what would be the use? Look at C, you can't use the language without libs. Then there is the issue of using code without credit. Credit should definitely be given where due. In my own lil project (not an RPG), I have credited 10-15 people, even people whose code I just examined and learned from, later re-building from scratch for my own purposes. Then there was the issue of ripped gfx. I personally find it fine as long as it is not the final gfx, permission is asked, and credit is given. It is just really pathetic that ppl take gfx, and then call it their own. And lastly was the ripped web content. My own two sites (Simply Qbasic and QNN) were supposedly ripping content from Neozones. This showed the pathetic side. Some people had thought that I had ripped my files section from neozones, but not a SINGLE one of them told me that. 15k+ hits and I never knew. Once I did find out, I changed the layout. My other site was also said to have stolen the design from Neozones (to which I say stfu, if you have seen the site, you'll know why) and that my news was all sugar candy and sucking up. Its just news! Just letting other people know of some developments! And sucking up to whom btw? =) Lately, quickbasic.net also created a furor over ripping interviews and what not. Ripping an interview from LordQB to using other's code to old news and other site. Qb Sites permeate lameness ;-p Talking about qb sites, Qbasic top 50 and the other lists have all becomes jokes. How did quickbasic.com, down at the time, cone out on top? How does qbsource.com, which is still 'revamping' from 3 months ago, come on top of all those lists without any content? All the lists are screwed. Future Software had Nightwolf Productions suddenly from out from no where with 67 votes, and then suddenly jumped another 10. Top Ranked Sites also has a multitude of same IPs saying one site rocks, while the same IPs have been seen ranking other sites 1 multiple times with the always descriptive 'sucks.' There was also the bashing that Razor got. Ok, it was late, was skimpy, but instead of saying what shit it was and else, you guys could have told Terminator what to improve on. I have talked to speige, the new editor, about many improvements, and he said he would implement them. Looking at all this, this is an overall condescending view by the more 'elite' QB programmers (*ducks under the flood of hate mail). A while ago, BattleCraft99 posted on the Neozones qboard about some programs. Instead of constructive criticism (yes ppl, you can do it), he was just told how stupid and what a newbie he was. Gimme a break. You same ppl were never newbies? Came out of the womb all smart and intelligent? ;-p Also the stupid little argument over Nexus_13. Nek was overly harsh on his lib ;-). And this condescension is found throughout the 'scene.' Truly pathetic. I myself was around in '97 when Tsugumo came out of nowhere with TheGame, had his gfx ripped by Tek (I still remember that there was even a petition ;-]), and DarkDread got hacked and left. It was pitiful. I was called stupid and what not for not fully knowing the palette statement (Qbasic 1.1 help is awful ;-p), and left utterly disgusted. Then this spring '99, after learning some other languages, I decided to come back with my newfound 'intelligence'. Seeing Qbasic Top 50, Dark Ages, DirectQB, Monospace, and others, I thought things had finally changed. Damn was I wrong. As the number of qb programmers dwindle, people still insist on being assholes. Being one is ez mind you. And that about concludes my rant, and what a rant it is ;-). Btw, all flames, comments, bomb threats (mind you, we have nukes! Heheh) and anything else can be directed to: ahmedfq@hotmail.com By Ahmed Back 2 Top _________________________________________________________________ QB parameter passing (technical edition) Hey again to all our favourite readers. My friend Nodtveidt asked me to do another one of my special articles. This time im going to explain a bit about QB parameter passing, yes i know alot of you know this already, but for the newbies out there (who happen to know asm by heart :) read on. Assumptions: QuickBASIC 4.5 (no other compiler or version) MASM 6.11d (or newer, tasm is no good in my humble opinion) Lets start off with common variables. Normally, they are passed by reference, this is the same way QB does it internally between subs and functions. By reference Example program: BASIC: DECLARE FUNCTION MyInteger& (Integ%, Lon&) t& = MyInteger(123, 456) PRINT t& MASM: .model medium, basic .386 .code MyInteger proc public uses bx cx si di ds es Integ:word, Lon:word mov bx, Integ mov ax, [bx] ; ax now contains the value of Integ% (123) mov bx, Lon mov ax, [bx] ; ax now contains the lower 16 bit of Lon mov dx, [bx+2] ; dx now contains the upper 16 bit of Lon inc ax ret MyInteger endp end SCREEN OUTPUT: 457 Explanation: In the basic module, i pass the value 123 by reference. What happens is the value is moved to a temporary location in the common data segment, and i retrieve MyInteger's return value and print it. In the assembly module, i use a VERY handy way of referencing parameters. ".model medium, basic" tells the assembler i want a medium sized memory configuration which is identical to QB's (dont use any other, it wont do anything to make your modules smaller or faster, but only to make it crash). The basic parameter tells the assembler we are using BASIC parameter passing technique (its pretty much identical to PASCAL but they where nice enough to give it its own name). This way we dont have to count where in the asm routine's stack we keep the variables, they are automatically handled by masm, and BP is set up. Don't worry, the code is NOT slower or bigger than the hand coded technique, it is exactly the same. Down the line we find "uses", this tells the assembler that we use these registers, its only there to make the code look alot better, but you can use the regular way, if you like. I typed in all the registers QB wants you to save, of course if you dont use them all you could just remove those from the list. By reference means a 16bit OFFSET pointing to the actual variable. And you must NOT tamper with DS if you want to be able to reference them. It is slower to use this technique, but if speed is not an issue, it does make things look better for the programmer, and makes it more userfriendly, in my humble opinion. One advantage with by reference passing is your ability to alter the parameter variables. That way you can return more values. LONG variables are 32bit signed integers. These can be a bore to handle, since you need two 16bit registers. I usually use a 32bit register for them, makes things easier to load and store. If you use 32bit registers, you still have to save the lower 16bit of those registers. ** RETURNING VALUES are simple. If you define MyInteger to return an Integer then AX must contain the returning value. If you define MyInteger to return a Long then DX:AX must contain the returning value. Returning values are passed by value, not by reference. By value Example program: DECLARE FUNCTION MyInteger& (BYVAL Integ%, BYVAL Lon&) t& = MyInteger(123, 456) PRINT t& MASM: .model medium, basic .386 .code MyInteger proc public uses bx cx si di ds es Integ:word, Lon:dword mov ax, Integ ; ax now contains the value of Integ% (123) mov eax, Lon ; eax now contains the value of Lon& (456) inc ax ret MyInteger endp end SCREEN OUTPUT: 457 STRINGS are a bit tricky, a single little screw up and your program will crash. do not try to pass these byvalue, its not a good thing. A 16bit offset is passed instead, this points to a header that looks roughly like this: string_length word SIZEOF string_string string_offset word OFFSET string_string <....> string_string byte 'my string is here' A simple example here: in BASIC: declare function mystr$ (a$) a$="lala" b%=mystr(a$) print b% in MASM: .model medium, basic .386 .code mystr proc public uses bx cx si di ds es astr:word cld mov bx, astr mov cx, [bx] ; cx contains LENGTH of astr (a$) lds si, [bx+2] xor ax, ax lodsb ; ax contains ASCII value of first character in astr. ret mystr endp end Simple as that, there you got the string position and length, no sweat. Problems arise when you want to get a string and return a string, you also have to use complex segment declaration. And you have to DGROUP both _BSS and _DATA. You who know what im talking about, you can easily do it. You who doesn't, well, not my problem. Microsoft wont officially support ARRAY passing for QB4.5, and I wont either, but if you want to know how its done, just tinker around abit. Its really easy. Hint: myarray:word, myarray will point to header. By Sten Daniel Sørsdal Back 2 Top _________________________________________________________________ PlatForm Engines 101 To tell ya the truth, platform engines are really easy to code. A lot easier than what i thought it'd be. To start out, you use a regular pixel engine. Yep, that's right, a regular pixel engine. If you don't know what that is, it's basically a tile engine that can scroll using pixels. And if you don't know what a tile engine is, then you really don't need to read this, not to be mean or anything, but learn to create a tile engine, then I'll be glad to help with a pixel engine. First off, to create the pixel engine, start off with a tile engine, and add tile offsets. So lets say you have map offsets; Xoffset, Yoffset Those are our map locations for the upper lefthand tile. The tile engine would set the screen up like so: _ _ _ _ _ _ _ _ _ |a|_|_|_|_|_|_|_|_| If Xoffset and Yoffset = 1 then the block "a" is |_|_|_|_|_|_|_|_|_| where xoffset and yoffset are. Easy huh? |_|_|_|_|_|_|_|_|_| |_|_|_|_|_|_|_|_|_| Adding Tile offsets are just as easy. Lets say |_|_|_|_|_|_|_|_|_| tiles are 16x16, your basic tile. When scrolling |_|_|_|_|_|_|_|_|_| you add to the offset until it reaches 16 segments |_|_|_|_|_|_|_|_|_| then set it back to 0, and adding 1 to the map offset. Sounds simple enough right? In laymans terms: TileX = 0 SUB ScrollRight TileX = TileX + 1 'TileX is the tile offset for the X coords. IF TileX > 15 THEN 'starting TileX at 0 and adding 16 gives you 15 TileX = 0 'put TileX back on 0 (default for next tile) Xoffset = Xoffset + 1 'move the map X coords over 1 END IF END SUB I told you it was easy. Adding detection is pretty simple too, just check the Tile offsets against the map coords, and see if the tile is walkable. Yea, i know, this isn't REAL pixel by pixel, but i never said it was. =) Also, you may want to use a PUT routine that has clipping capabilities, which lets you place a tile half on the screen, most libraries allow this, i think, i don't use any except Qb13h. And i KNOW it does clipping. Ok, on to the platform. Platforms are the same, except you don't need to scroll up and down, or, at least you really don't need to. So maps can be longer. The way i do maps: DIM SHARED map%(1, 200, 15) This allows me to have a 2 layer map system that is 200 tiles long and 15 tiles high. Like i said, you don't NEED to scroll up and down, though you can if ya want. It's the same as what i just went thru above, using a pixel engine, just only able to scroll left and right. Only difference in them is the graphics that you use, instead of an RPG tileset, you'd use a side tile set. With cliffs and holes and things inside it. When placing a character into the engine, you just supply an X and Y coord for him to start. Usually on a ground level. While scrolling and finding where the character is, you check for map location, collision detection is better terms. I'm sure you are all familiar with it. You check the X and Y locations against the FIRST layer on the map, if it's a tile you can't walk on, the player can't move. If he CAN walk on it, then of course he can go across it. Also you would need to check above and below the player to check for holes and cliffs. IF map%(0, PlayerX, PlayerY + 1) < 0 THEN 'check below player for MakePlayerFall 'nonwalkable tile END IF It's simple to implement, well, simple to me, i know, and I'm tryin to explain it the best i can. One thing i forgot to mention. You will need to add character tile offsets that tell if the character is between tiles. It works the same way as the tile offsets. When scrolling right, you'd add 1 to the PlayerXTileOffset. After PlayerXTileOffset > 15 then you'd add 1 to the PlayerX coord, moving the player along. This is a MUCH easier way to compare map locations with the player coords. It saves lots of time and trouble later. When making the player jump, you want to use tile detection again, checking 1 tile above the player while he is moving up, then when he starts coming down, you check below him, all the while checking the 1 tile in front of the direction he is facing. Hope that wasn't too confusing. (Any questions just email me =) When showing the map, you just show the first layer, then show the player, then place the top map layer over top, so you get a "walk behind" effect. This effect adds really great looks to a good engine. While placing the second layer, to speed things up, make sure that you have a tile that is not placed. So that you won't need to go thru the whole loop of placing tiles. Say tile #17 isn't goin to get placed for the second layer, I would do something like: DIM SHARED tiles(200, 5) DIM SHARED map%(1, 200, 15) SUB ShowMap x = 0: y = 0 FOR b = Yoffset TO Yoffset + 13 'places 13 (16x16) rows on the screen FOR a = Xoffset TO xoffset + 20 'places 20 (16x16) cols on the screen Puttile (x, y), tiles(0, map%(0, a, b)) 'routine for placing tiles x = x + 16 'moves location over 16 pixels NEXT a x = 0: y = y + 16 'starts new row NEXT b PutPlayer x = 0: y = 0 FOR b = Yoffset TO Yoffset + 13 'places 13 (16x16) rows on the screen FOR a = Xoffset TO xoffset + 20 'places 20 (16x16) cols on the screen IF map%(1, a, b) <> 17 THEN '17 being the tile not going to get 'placed Puttile (x, y), tiles(0, map%(1, a, b)) 'routine for placing tiles END IF x = x + 16 'moves location over 16 pixels NEXT a x = 0: y = y + 16 'starts new row NEXT b END SUB That's usually what my map show sub looks like. Placing the first layer of the map (map%(0, x, y)) and then placing the player (PutPlayer), then placing the second layer of the map (map%(1, x , y)). Simple right? I hope so. Hopefully this article made sense, if not, email me and i will try to explain it a bit more in more detail. Thanks for your time! [sshot.gif] By Necrolyte Back 2 Top _________________________________________________________________ QUICKBASIC MAGAZINES: NO MORE, PLEASE! STOP THE INSANITY! (DISCLAIMER: I'm writing this to express that I think there should be only one mag, not so many. I'm not insulting anyone who makes a mag, cause it is hard work, and I respect you. I just think someone should establish a standard. So please don't get pissed at me :-) Intro Quick! Look to your left! It's a QB Mag! Look above you! It's a QB DiskMag! Ahh! To the right! Another mag! Now that your attention has been captured, lets continue. Why? Why do we need 2 million QB mags? Its stupid. Which one do I read to get the latest and greatest info? Which one has the best project reviewers? No one reads 4 newspapers a day so they won't miss anything, and no one has to. Most of the QB mags and diskmags have the same news, and some even rip others content. 12 mags all with the same content doesn't make any sense. So, here is an idea, JUST HAVE ONE, PLEASE! Two or three is all we need Why not just have 1 or 2 mags? If we had two then we could have one mag and one diskmag. One for people to read quickly and easily, and one for people to download and enjoy. Also, another idea is for one that is emailed instead of HTML or download(Tek is already doin this with The QB Gazette, which is cool).I beg for this option! A Standard One QB mag should be the main mag that is read and written by the QB community as a whole. This would be easier for readers and effective. It could be hosted on one of the more popular QB sites. This would eliminate ALL of the problems which have occured because of the mass number of QB mags. Let's Count... Lemme count the number of QB mags and Diskmags just from memory, lets see, there is Razor DiskMag, the late QBTM, QB on Acid, The QB Times, The QB Gazette, and another nameless QB diskmag that is just surfacing. And those are only the ones I can think of right now at almost 12:43 AM. Who the hell knows how many more there are? Now, lets take a second to think about what the hell is wrong with this picture. You thinking? Good. Lets see, there are TOO many mags and diskmags, almost all of them have the same content, the people who write for one of the mags usually writes for others too therfore dishing out the SAME content, and finally, IT'S FUCKING RIDICULOUS! Conclusion Well, as you can see, I hate having so many QB mags and diskmags. Its nonsense. Just have one no frills mag that takes care of it all, then the editors of the other mags can take a break and have a shower. Thanks for reading this, and be sure to submit any ideas for rants to brandon@nssoft.cjb.net! By Nova Back 2 Top _________________________________________________________________ Beginner's Tutorials: Chapter 1 - Beginning Commands This is a set of tutorials that will come out 1 each issue of QBoA. They are aimed to teach you how to use QB from the beginning. I will assume you have QBasic and know your way around it. Today we will talk about the commands PRINT, CLS, and INPUT. I will also introduce you to variables. ______________________________________________________________________ PRINT The PRINT command does what it says, it PRINTs. What does it PRINT to you ask? Not the printer, but the screen. It simply PRINTs the words you specify to the screen. The syntax for PRINT is PRINT "". Put what you want to show up on the screen inside the quotes. PRINT "Hello World!" Example Code CLS What happens when you write a program, run it, then add some more code to it and run it again? You still see what was on the screen the last time you ran it. To fix this problem put the command CLS in your program. CLS stands for CLear Screen. It does what it stands for, it clears the entire screen. The syntax for this is, of course, CLS. VARIABLES Variables are used to store information in your computers memory. Think of them as storage boxes in your computer. Variables are VERY useful. They are almost ALWAYS used in a program. There are five different types of variables. They are string, integer, long integer, single precision, and double precision. Different symbols must be placed at the end of variable names so QB knows what type of variable it is working with. Strings use $, integers use %, long integers use &, single precision uses !, and double precision uses #. The type of variables you will use most often are strings and integers. Strings store words, integers store numbers. To declare a variable just do this: ExampleVariable$ = "hello". That is a string variable(notice the $) that is now storing the word "hello". When declaring a string variable make sure to put its value inside quotes. To declare an integer variable just do this: IntegerVariable = 8. Notice that I didn't have a % at the end of the variable name. That's because with integers you don't have to have one. If you don't put a symbol at the end of the variable name, QB makes it integer by default. name$ = "Bob" score% = "1500" age = "15" Example Code INPUT Programs are no good if you can't accept input from the user, right? Well that is what this command does. Judging by its name, you probably knew that already. The syntax for INPUT is this: INPUT; "What is your name" (, or ;) variable. Lets break this down. First, you have the command. Next is a semicolon, which is optional. The semicolon right after INPUT keeps the cursor on the same line after the user presses enter. Next is prompt string. It is what shows up when the INPUT code executes. Next is either a comma or a semicolon. If you put a semicolon then a question mark appears after the prompt string. If you put a comma, then no question mark appears. Last is the variable you want to store the users input in. Now, about the Prompt String, some might not understand it, so I will talk about it. If your prompt string is "What is your name" then the words "What is your name" will appear on the screen and the program will wait for the user to input the answer. INPUT "Hello. What is your name"; usersname$ INPUT "Enter your Zip Code: ", zipcode INPUT; "Enter your age: ", age Example Code PRINTING VARIABLES So, you used the input command and stored some data from the user in a variable, but what good is the data if you can't display it? Well you can using the PRINT command. It's VERY simple, heres the syntax: PRINT variable. You just type PRINT then the variable name, easy stuff. Don't forget to include the variable type symbol. PRINT playername$ PRINT age PRINT score% PRINT enemyname$ Example Code YOUR HOMEWORK Your homework is to make a program that clears the screen, asks the user for his/her name, stores it in a variable, and prints out put his/her name on the screen. See you next issue! Written By: Nova - brandon@nssoft.cjb.net Back 2 Top _________________________________________________________________ Implicit Surface Polygonization This article, originally an e-mail to tek_hed and some others, will describe a method for polygonizing implicit surfaces. Metaballs are an example of these: the surface is not defined explicitly (as in polygons), or parametrically (as in spline grids), but rather, well, implicitly. Basically, you have a function f(x,y,z) which is 0 for any point on a closed surface, negative on the "inside" of that surface, and positive on the "outside". Now, to polygonize it, you create an array of points at an arbitrary resolution (100x100x25 would do fine in most situations), and evaluate the function for every point. Then, you connect all the points with the ones exactly 1 unit away, creating a gridwork between these points. Every one of those connections that crosses through the texture (ie, one endpoint has a different sign than the other), you do a binary search (or just approximate by averaging) to find a much closer point to the surface, and you "attach" that point to the two endpoints. Then, you go through all the points again, and for every one that has 3 attached points that are NOT coaxial (ie, one is offset only by X, one only by Y and one only by Z), you generate a poly connecting those points. You cover all combinations of 3 points that are not coaxial that are connected to the same grid point, and when you're done, you have a polygonal representation of the implicitly defined surface (which could be a metaball system or whatever). To refine the surface, look at every edge of the polys and find the angle between the polys on each side of the edge. Sort the edges by this angle, and resample the ones with angles exceeding a given threshold using a finer grid (ie, the same grid over a smaller volume). There ya go =D By logiclrd at http://logiclrd.cx Back 2 Top _________________________________________________________________ Infix -> ASM convertor plan, by logiclrd For every sum-of-terms, reorder it so that the longest terms come first, e.g.: 1 + 2*3 -> 2*3 + 1 1 + (4/2 - 6*8/3) - 3*4 -> (-6*8/3 + 4/2) - 3*4 + 1 Then, switch the order of dividends with their divisors (the reason for this will become clear later), e.g.: 4/3 -> 3/4 (6+3)/(2*x) -> (2*x)/(6+3) Look for terms in which the non-variable factors (ie, the coefficients) can be simplified, e.g.: 2*x/4 -> x/2 3*6 -> 18 Now convert the string to an expression tree by recursively selecting the last operation (by order of operations) of the current substring to be the new subtree's head. e.g.: 4*2+6 -> + -> + / \ / \ 4*2 6 * 6 / \ 4 2 Swap the left and right children of any node whose left child is a numeric constant and whose right child is not, e.g.: + -> + / \ / \ 6 * * 6 / \ / \ 3 x x 3 Swap the left and right children of any node whose left child is a greater power of two than its right child, e.g.: + -> + / \ / \ 8 * * 8 / \ / \ 4 x x 4 Swap the left and right children of any division node whose _LEFT_ child is a power of two (since we changed 4/3 to 3/4), and take the logarithm base 2 of the new right side, changing the node into a "shift right" operation: ( x/8 -> 8/x -> ) '/' -> shift right / \ / \ 8 x x 8 Now, parse the tree in postfix order to generate an RPN expression, e.g.: + -> (x, 4, *, 8, +) / \ * 8 / \ x 4 We're halfway there! Replace any constant followed by a + or - operator with a special code "add ", e.g.: (x, 4, *, 8, +) -> (x, 4, *, add 8) Replace any constant followed by a shift or bitwise operator into a single list entry in a similar manner, e.g.: (x, 3, &, 2, <<) -> (x, and 3, shift left 2) Replace any power of two followed by a * operator with a special code "shift left" e.g.: (x, 4, *) -> (x, shift left 2) DO Replace any "shift left" operation followed by a "shift right" operation with the corresponding single shift, e.g.: (x, shift left 3, shift right 1, shift left 4) -> (x, shift left 2, shift left 4) Replace any "shift left/right" operation followed by another similar "shift left/right" operation with the corresponding single shift, e.g.: (x, shift left 2, shift left 3, shift right 1, shift right 2) -> (x, shift lef t 5, shift right 3) LOOP WHILE (changes made) Here's the part that'll most likely require the most code: parsing the RPN string into ASM, keeping track of what registers are which stack entries. Treat the registers as though they were the RPN stack, using MOV EAX,[x] instead of PUSH [x]. This way, all PUSHes and POPs are virtual (unless you run out of registers, in which case you are forced to PUSH one of your registers anyway -- this is unlikely, though, unless you're dealing with a maniac of a coder!), and must be kept track of within your conversion routine. If you have an item which is implicated in a subtraction or addition and there is a multiplication or a division in between the term and it's operator (i.e., the '*' between '2' and '+' in (4, 2, 3, *, +)), then avoid using EAX or EDX for that term. If there is an item which has only shifts, special additions and special subtractions (not '+' or '-', just 'add 3'-type items) between it and the next multiplication or division, place that term into EAX. Keep in mind that modulus values can only be placed into EDX, while the actual division to be performed implicates EAX. These 'in-between' checks can be performed by going through the RPN list, keeping track of the stack height at every point. Also, watch out for changes in the order of stack variables. These are pretty much guaranteed to be the same for every operation of a given type. I've documented them below, to give examples. For divisions, divide EAX by the second-to-topmost item on the stack (this is why we changed 4/3 to 3/4: 3/4 becomes 3,4,/, 4 goes into EAX, and we can do DIV EBX). E.g.: (x, y, z, add 4, *, +) would become: MOV EBX, [x] ; because there is a '*' between 'x' and its '+' MOV ECX, [y] ; 'z' is between 'y' and '*', and ECX is next available register MOV EAX, [z] ; only 'add 4' between 'z' and '*' ADD EAX, 4 ; here's the special 'add 4' routine MUL ECX ; before operation, stack is: EBX->ECX->EAX, after: EBX->EAX ADD EAX, EBX ; before operation, stack is: EBX->EAX, after: EAX And finally, if the resulting value is not in EAX, move it there (unless you have intelligent condition parsing code). If your last operation is a commutative operation which uses EAX as an operand but not as the target, switch the operands around, e.g.: ADD EBX,EAX MOV EAX,EBX would become: ADD EAX,EBX thereby avoiding copying the value back into EAX. You're done! (but don't forget to keep track of which registers were destroyed, and don't forget to PUSH all the registers with values you need when doing function calls) By this method, this expression: (x / 8) & 63 + 64 * ((y / 8) & 63 would become: MOV EAX,[y] SHR EAX,3 AND EAX,63 SHL EAX,6 MOV EBX,[x] SHR EBX,3 AND EBX,63 ADD EAX,EBX which, I'm sure you'll agree, is pretty damn good output. By logiclrd Back 2 Top _________________________________________________________________ An Attack On Polfalk? Here's Polfalk's way of creating an RPG (in QBasic), and a direct quotation from LordQB's WebBoard that shows his total stupidity. LordQB's QuickBasic RPG site is at http://members.xoom.com/qbasic_rpgs/ Polfalk How to make a REAL QBasic Rpg!! Sun Dec 19 12:35:32 1999 Take some ripped graphic, use PLAY instead of MIDI and if someone complaints just say "At least it works on every computer". Make it so slow that everyone thinks it's P*P scroller, if you need some dialog (just in case), use PRINT. And, finally, if you must (and only if you must!!) make a storyline. Do not implented though, just say "It will be implented in the next demo" and never release "the next demo". --Polfalk btw, this is how I make my Rpgs, if you have any complaints... well, that's not my problem. Apparently, he proved this method by actually STEALING graphics from DarkDread's EGA color game, Secret of Cooey 2. My sources revealed to me later that he changed this, as this is embarassing and that is exactly why I pointed this out! It's embarassing! I don't think Polfalk would need any dialogue in his games due to the fact he makes piles of spelling errors which is demonstrated in his above post on LordQB's WebBoard. And he says, and I quote "btw, this is how I make my Rpgs, if you have any complaints... well, that's not my problem." . Apparently, this will be his problem since I'm publicly embarassing him and pointing him out to the QB Community as a fraud and a dirty bastard because he steals graphics of hardworking programmers and makes crappy games, which in turn makes the game which the graphics were stolen off of, look worse! I think you owe DarkDread a sincere, public, begging-for-forgiveness apology! And I think we all agree! YOU SUCK POLFALK, WORSE THAN THE PHANTOM LIGHTING VACUUM! AND IT HAS DUAL-CYCLONIC ACTION, EASY DISPOSING BINS, AND A UNIQUE STAIR-HUGGER DESIGN! Go scamper away, and produce an RPG worth looking at! Then, maybe you won't get large articles making fun of you! Article written by MAjIkO! By the way Polfalk, the opinions expressed in this article are solely mine, and a few others. We hate you, go away! My site is currently located at http://majiko.logiclrd.cx/, and will be moving to a better server soon, but the files and a nice archive will be kept there! So hang tight and hang ten! By Majiko Back 2 Top _________________________________________________________________ Game Review: Killers Remix Ahh, another game by Nekrophidius. Will it live up to the standards of his other games? Will the download size for this game be the size of the download for WOS? Stay tuned and find out! ______________________________________________________________________ Parental Advisories? [warning.gif] "A p0rn game? Not quite." The first thing you see when you start the game is this parental advisory screen. A p0rn game? Not quite. The only close to p0rn thing I see in here is short skirts. Oh yeah, and the girl wearing only a snake and shoes *cough*. Graphic violence? Not yet, only violent spin kicks to the lower jaw in the demo. The Graphics Ok, lemme quote from the exit screen of the program, "...We'll prolly just keep rippin the gfx from commercial games =)". Heh. I asked Nek about this and he confirmed he ripped them. He did however make the opening screens, the character's faces, and the logos. He also said that the backgrounds were public domain. The opening screens look good and the faces are also good. The graphics are very SNESish, which I happen to LOVE. It reminds of of all those summer days spent playing Street Fighter 2. The bloody sword health bars are cool, and the flashing when you are low on health helps you know when you need to start fighting harder or die. [screen1.gif] "The graphics are very SNESish..." Gameplay As I said above, this is a very SNES style fighter. Most QB fighters are stick fighters, so naturally the gameplay is going to better for this game than others because of full figures. This is a demo, so naturally everything isn't finished. The only two things you can do so far are high punch and high kick. The objective is the same as all other fighters: beat your opponet down to win. Nek has planned out VERY bloody scenes and GRUESOME deaths, heh, I like that. Your opponets are fairly smart, although I found out that if you just high kick them every time thet come close, you can easily win. The Controls As I said above the only actions in this demo are high punch and high kick. The keys for these actions are well place, being ALT for high kick and CTRL for high punch, no A for kick and shit like that. The controls to move your character are also well placed, being the arrow keys. It is very easy to use these controls together to play the game. They are all near each other so you won't have to stretch your hand. I had no problems with control while playing Killers Remix. Bottom line: controlling is good so far, just don't screw it up later Nek :p Sound Sound is always important in a game. Sound gives the game more meaning. Kicking a guy square in the face just isn't tha same without some fast paced music to go with it. The game has 16 music tracks in the demo, with double that amount planned for the final product. Overall Overall Killers Remix is a good, solid game. I found no bugs in it at all. Control is easy. The only problem I found (there are probably more) in KR is that the animation is a little too fast when you punch, kick, etc. Even for a demo, this a good game. I really like the SNESish feel of the game, takes me back to days gone past. [title.gif] Written By: Nova - brandon@nssoft.cjb.net Back 2 Top _________________________________________________________________ Product Review: QBP's Engine QbProgger recently sent me a copy of his RPG engine, and asked for a review of it. So, I agreed, and here's the results. The Good This RPG engine is VERY fast. It is a full p*p scrolling engine (although he coded it to move at 16 pixel increments, giving the appearance of a t*p). 8-way movement is present, which is nice. What really amazed me was the amount of onscreen moving NPCs...they were all over the place! They all looked the same though (two NPCs, a guy and a girl) but damn, they were everywhere. The Bad Although this is anything but your typical RPG scrolling demo, it's still just a scrollie. There is no game present. The movement is a little whacked, the character gets stuck a lot and there's no way of telling (due to the simplistic nature of the graphics) where the hell you are going. It does appear QBP's scripting engine isn't in place yet either, since the NPCs were all very silent (kind of annoying! ack) The Skinny Apply a game to this, and you have a masterpiece. Until that time, it's just a scrollie. Although a very impressive scrollie...but still just a scrollie. I know QBP's spent years trying to perfect the RPG engine, and you can certainly see the experience in this one, but there's more to a game than the engine...now it's time to add the rest. By Nekrophidius Back 2 Top _________________________________________________________________ Music Review: Syraphage Liquidex, the chief figure behind the Syraphage project, recently sent QBoA several pieces of music from the upcoming Build 5 of Syraphage, so that we may review the soundtracks. Of the three submitted, only two shall be reviewed here, since the first piece (the title) will not be used in the game anyways. So, here we go... Syraphage: Battle This is a semi-fast tune, with a driving beat and some interesting synths. You can definately hear a technoesque influence in this piece, however, the heavy guitar synth was very cool as well. The only real problem with this piece is that most of the channels are centered, and this tends to kill the quality in anything but Modplug Player. You can really get the sense of urgency in this piece, it will fit very well into a battle scenario. Syraphage: Guilden Wow...talk about contrast in styles. This is a very nice acoustic guitar piece. Flutes in the background, a nice rhythm, and an interesting progression. The theme tends to lend itself to redundancy however, as there's never really any break from the same progression of note structure. In a game you'd probably never notice this however. This piece will be used in towns, according to Liquidex. So there you have it. For the record, these pieces will be played through Alphx's own LiquidGL module player when Build 5 is released. By Nekrophidius Back 2 Top _________________________________________________________________ Interview with JQB45 (Jeff Crawford) on Rapid BASIC, formerly known as NextQB This really seems to be the next best thing on the market. In this interview with JQB45, i found out that there are great features involved in this compiler. It will be totally 100% compatible with QB4.5, which would make it very easy to use. The first demo should be out around Jan 20-25th for public use. Their are a only a handful of supported keywords in the first demo. This list can be found at Rapid BASIC's site under News. According to Jeff, Rapid BASIC translates BASIC to highly optimized Assembly, hence the name "Rapid BASIC." Jeff also states that executables created by Rapid BASIC seem to be 30 to 50 times smaller than the exact same source compiled by the QB45 compiler. This is great, seemin most of MicroSoft's products are TOTALLY bloated out of proportion. Speed for Rapid BASIC hasn't been determined yet, though Jeff says it should increase 30% to 50%. Also a good thing, that IS what everyone is looking for right? SPEED!!! Quoting Jeff, "Long integers have been increased in speed nearly 100 times over. Math routines use the FPU instead of emulation and should produce very nice results indeed." Doesn't this sound astounding? He also has added bitshift operators, along with true inline assembly, using an ASM...END ASM block. This will defeat the purpose of using Call Absolute and Call Interrupt. Inline ASM should produce a huge increase in speed also. It should also cut down on library usage and pointing programmers in the way of ASM, so their programs will be faster. For those of you wondering if you will still be able to use QuickBASIC libraries...yes, all current libraries are 100% compatible. This is also great, i mean, c'mon! Smaller EXE's and faster code, PLUS the use of libraries!? I'm amazed.... As for an IDE, Jeff says that several text editors with syntax highlighting and all the convience features are being developed. Maybe he should talk to War_Lord? (War_Lord is creating WinQB). These IDE's being developed my not be ready by the first release of RB's Demo. Don't worry, you can still use Rapid BASIC! You should be able to use QB45's IDE, or any other text editor like NotePad. For any other information not talked about, or if you want to see more information (if this inteview hasn't convinced you this WILL be a great compiler) you can goto http://24.26.150.46. 'till the next article i do, Necrolyte Back 2 Top _________________________________________________________________ QUOTE OF THE MONTH This issue's quote comes from an unknown newbie known only as TLQ "I'm making $1000 in January of 2000 doing stuff with QB, so I think if I wasn't good at it, they wouldn't pay me so much for my services. It is only maps that I'm having troubles with." This person posted this little wonder on LordQB's QBRPGs message board. After several posts proving his/her true ignorance in coding and trying to get other people to do his/her work for them, they decided to offer this in response to an accusation of rookieism. I do believe there's an old saying that goes sorta like this..."It's better to close your mouth and to be thought a fool, than to open it and remove all doubt." Back 2 Top _________________________________________________________________ THE GALLERY This time we have two games featured in the Gallery, since last time we didn't have ANY. So, the first one was submitted by xms, who is working on the game Cataclysm Eve (we reviewed the alpha earlier in the QBoA series). He's got some new screenshots, and take a look! [attack.gif] [field10.gif] [field5.gif] [menu.gif] [npcs2.gif] [LeftTown.gif] [castle1.gif] The second Gallery entry is a single pic, however, it looks mighty tasty. This is another Tetris clone, but this one's being coded by none other than "Zip Person" (that's the best name we could come up with! heheh) and is looking nice so far. [tetris3.gif] Now, what is this picture? According to rumour mill, this is actually a shot from the upcoming super-game SubShock...online capabilities? Can it be true? See for yourself! [winserv1.jpg] Back 2 Top _________________________________________________________________ Coding Info Block To get the VESA hi-resolution modes, you'd do this: mov ax, 4F02h mov bx, mode int 10h Where mode is a hex number between 100h and 164h, Goto: http://www.ctyme.com/intr/RB-0274.HTM for a list of most of the vesa modes. As for the rest of the lesser known vesa modes, here they are: 131h 320x240x256 132h 320x240x32K 133h 320x240x64K 134h 320x240x16M 141h 400x300x256 142h 400x300x32K 143h 400x300x64K 144h 400x300x16M 151h 512x384x256 152h 512x384x32K 153h 512x384x64K 154h 512x384x16M 161h 1152x864x256 162h 1152x864x32K 163h 1152x864x64K 164h 1152x864x16M I'm not sure yet, but there might be screen modes after 164h. By QB_AL Back 2 Top _________________________________________________________________ Neat Code Example Super-Fast Pure QB PUT DEFINT A-Z DECLARE SUB precalc () DECLARE SUB demo () DECLARE SUB tput (sprite%(), x%, y%) '==================================================== ' NEGPOKE3: 16x16 Screen 13 Clipped Sprite PUT demo ' Copyright 1999 Toshihiro Horie. '==================================================== ' Best Timings (compiled on Cyrix 6x86 100Mhz): ' NEGPOKE3: 27191 sprites/second (my routine) ' SPDTESTO: 17035 sprites/second (Pasco's lib) ' ' This negative segment and poke offset idea came from the ' distant past, when I was using Applesoft BASIC. BASIC ' used signed integers even then, and POKEing to negative ' positions meant writing to the memory address at ' the unsigned version of the binary representation of ' the negative address. DIM SHARED pokeseg(200) demo SUB demo precalc 'draw and save a sample sprite DIM sprite(130) SCREEN 13 FOR x = 0 TO 15 FOR y = 0 TO 15 PSET (x, y), x + y + 100 NEXT y NEXT x ' save into normal QB sprite array GET (0, 0)-(15, 15), sprite t1! = TIMER 'main test loop (do not make the max value 32767) FOR iter = -32768 TO 32766 tput sprite(), RND * 320, RND * 200 NEXT iter t2! = TIMER sps! = 65535 / (t2! - t1!) SCREEN 0: WIDTH 80: CLS PRINT sps!; "16x16 sprites/second" END SUB SUB precalc 'precalculates the correct starting segment 'for each scanline. This way we get 'NO overflows even if we use all integer calc. pokeseg(0) = -24576 FOR yp = 1 TO 199 pokeseg(yp) = pokeseg(yp - 1) + 20 NEXT yp END SUB SUB tput (sprite(), x, y) '16x16 clipping sprite PUT routine with no multiplies in sight! ' this assumes the GET sprite() array is ready for use, ' and that the given x, y coordinates are within screen range. 'QB4.5's PUT is faster than looped POKE for in-screen blits IF x < 304 AND y < 184 THEN PUT (x, y), sprite, PSET: EXIT SUB 'xd is the visible width of the sprite 'yd is the visible height of the sprite xd = 319 - x yd = 199 - y IF xd > 15 THEN xd = 15 IF yd > 15 THEN yd = 15 s = VARSEG(sprite(0)) r = 4 'array data start = 0 + (4 bytes of header skipped) 'This uses the loop unrolling optimization 'which also gets rid of extra segment switching. 'Of course, all those peek/pokes could easily be 'replaced by a simple memcopy function in asm, 'but this was for a Pure QB speed competition. 'There are rumors that the internal function 'B$ASSN may be abused as memcopy routine, but 'I decided against using it. FOR dy = 0 TO yd yseg = pokeseg(dy + y) SELECT CASE xd CASE 0 DEF SEG = s z0 = PEEK(r) DEF SEG = yseg POKE x, z0 CASE 1 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 CASE 2 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 CASE 3 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 CASE 4 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 CASE 5 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) z5 = PEEK(r + 5) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 POKE x + 5, z4 CASE 6 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) z5 = PEEK(r + 5) z6 = PEEK(r + 6) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 POKE x + 5, z5 POKE x + 6, z6 CASE 7 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) z5 = PEEK(r + 5) z6 = PEEK(r + 6) z7 = PEEK(r + 7) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 POKE x + 5, z5 POKE x + 6, z6 POKE x + 7, z7 CASE 8 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) z5 = PEEK(r + 5) z6 = PEEK(r + 6) z7 = PEEK(r + 7) z8 = PEEK(r + 8) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 POKE x + 5, z5 POKE x + 6, z6 POKE x + 7, z7 POKE x + 8, z8 CASE 9 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) z5 = PEEK(r + 5) z6 = PEEK(r + 6) z7 = PEEK(r + 7) z8 = PEEK(r + 8) z9 = PEEK(r + 9) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 POKE x + 5, z5 POKE x + 6, z6 POKE x + 7, z7 POKE x + 8, z8 POKE x + 9, z9 CASE 10 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) z5 = PEEK(r + 5) z6 = PEEK(r + 6) z7 = PEEK(r + 7) z8 = PEEK(r + 8) z9 = PEEK(r + 9) z10 = PEEK(r + 10) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 POKE x + 5, z5 POKE x + 6, z6 POKE x + 7, z7 POKE x + 8, z8 POKE x + 9, z9 POKE x + 10, z10 CASE 11 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) z5 = PEEK(r + 5) z6 = PEEK(r + 6) z7 = PEEK(r + 7) z8 = PEEK(r + 8) z9 = PEEK(r + 9) z10 = PEEK(r + 10) z11 = PEEK(r + 11) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 POKE x + 5, z5 POKE x + 6, z6 POKE x + 7, z7 POKE x + 8, z8 POKE x + 9, z9 POKE x + 10, z10 POKE x + 11, z11 CASE 12 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) z5 = PEEK(r + 5) z6 = PEEK(r + 6) z7 = PEEK(r + 7) z8 = PEEK(r + 8) z9 = PEEK(r + 9) z10 = PEEK(r + 10) z11 = PEEK(r + 11) z12 = PEEK(r + 12) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 POKE x + 5, z5 POKE x + 6, z6 POKE x + 7, z7 POKE x + 8, z8 POKE x + 9, z9 POKE x + 10, z10 POKE x + 11, z11 POKE x + 12, z12 CASE 13 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) z5 = PEEK(r + 5) z6 = PEEK(r + 6) z7 = PEEK(r + 7) z8 = PEEK(r + 8) z9 = PEEK(r + 9) z10 = PEEK(r + 10) z11 = PEEK(r + 11) z12 = PEEK(r + 12) z13 = PEEK(r + 13) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 POKE x + 5, z5 POKE x + 6, z6 POKE x + 7, z7 POKE x + 8, z8 POKE x + 9, z9 POKE x + 10, z10 POKE x + 11, z11 POKE x + 12, z12 POKE x + 13, z13 CASE 14 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) z5 = PEEK(r + 5) z6 = PEEK(r + 6) z7 = PEEK(r + 7) z8 = PEEK(r + 8) z9 = PEEK(r + 9) z10 = PEEK(r + 10) z11 = PEEK(r + 11) z12 = PEEK(r + 12) z13 = PEEK(r + 13) z14 = PEEK(r + 14) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 POKE x + 5, z5 POKE x + 6, z6 POKE x + 7, z7 POKE x + 8, z8 POKE x + 9, z9 POKE x + 10, z10 POKE x + 11, z11 POKE x + 12, z12 POKE x + 13, z13 POKE x + 14, z14 CASE 15 DEF SEG = s z0 = PEEK(r) z1 = PEEK(r + 1) z2 = PEEK(r + 2) z3 = PEEK(r + 3) z4 = PEEK(r + 4) z5 = PEEK(r + 5) z6 = PEEK(r + 6) z7 = PEEK(r + 7) z8 = PEEK(r + 8) z9 = PEEK(r + 9) z10 = PEEK(r + 10) z11 = PEEK(r + 11) z12 = PEEK(r + 12) z13 = PEEK(r + 13) z14 = PEEK(r + 14) z15 = PEEK(r + 15) DEF SEG = yseg POKE x, z0 POKE x + 1, z1 POKE x + 2, z2 POKE x + 3, z3 POKE x + 4, z4 POKE x + 5, z5 POKE x + 6, z6 POKE x + 7, z7 POKE x + 8, z8 POKE x + 9, z9 POKE x + 10, z10 POKE x + 11, z11 POKE x + 12, z12 POKE x + 13, z13 POKE x + 14, z14 POKE x + 15, z15 END SELECT r = r + 16 NEXT dy END SUB By Toshi Horie Back 2 Top _________________________________________________________________ The End! Well folks, this concludes the largest QBoA ever made! This became huge in a relatively short period of time, I was quite surprised at the number of people who submitted material. Anyways, we'll be back with another issue as soon as possible, so be sure to send us your letters, articles, reviews, screenshots, comments, questions, news, whatever! And watch for the next issue, date unknown! Send us Email