-IMPORTANT NOTE---------------------------------------------------------------- NOTE: Issues 1-8 of the Basix Fanzine were edited by Peter Cooper. Issues 9-13 of the Basix Fanzine were edited by Alex Warren. The new editor of the Basix Fanzine is David Groulx and the addresses for the fanzine are: ARTICLES: Basix_Fanzine@yahoo.com OTHER ENQUIRIES ETC.: Basix_Fanzine@yahoo.com WWW ADDRESS: http://www.come.to/basixfanzine Please use these addresses in place of the out-of-date addresses in the Fanzine below. David Groulx April 1999 ------------------------------------------------------------------------------- The Basix Fanzine issue 11 · july 1998 email: basix@dewarr.globalnet.co.uk www: http://come.to/basixfanzine Welcome to issue 11 of the Basix Fanzine! The fanzine is now available for download in a much more sophisticated HTML format, but here is cutandpaste-over text format version for those of you who want it. Soon, however, the fanzine may not become available in a text format version at all and will ONLY be available in HTML. Note that this text version may contain mistakes and ommissions, though hopefully it doesn't as (believe it or not) I have compiled it carefully and proof-read it.... So, what's in this issue? Here's the rundown: Get the info on the new PB/CC from PowerBasic Inc.... read how to do long filenames in your programs.... find out about your copyright rights.... find out why most online tutorials suck in Rude John's new regular editorial column.... read PCX files.... use the modem.... speed up input and output.... and more besides. Enjoy! ------------------------------------------------------------------------------- CONTENTS Levels are represented in bold by B, I or A for Beginner, Intermediate and Advanced respectively, or a combination. They aren't clear-cut definitions though, so you might find something useful labelled (B) even though you've been programming for 10 years, for example. Unless otherwise stated, the articles apply to all major breeds of BASIC. News: - BASIX FANZINE AWARDS 1998 - POWERBASIC CONSOLE COMPILER FOR WINDOWS - PROPOSAL FOR NEW POWERBASIC NEWSGROUP Articles: - NEW! Rude John's editorial: WHY (MOST) ON-LINE PROGRAMMING TUTORIALS SUCK! - LONG FILENAMES FROM DOS by Alex Warren (I/A) (not QBasic) - GRAPHICS IN QBASIC TUTORIAL (PART 2) by "Hacker" aka Damian Nikodem (I) - TESTING MATRIX SOFTWARE, by Rude John (A) - HIT-OR-MISS INTEGRATION, by Rude John (A) - YOUR PROGRAMS - YOUR RIGHTS, by Alex Warren From the newsgroups: - INACCURACY IN GRAPHICS TUTORIAL IN ISSUE 10 - UNLOCKING GWBASIC PROGRAMS - SORTING RANDOM NUMBERS - FAST KEYBOARD INPUT - CLEARING THE KEYBOARD BUFFER - POKE-ING CHARACTERS TO THE SCREEN - TREE/PLANT FRACTALS - USING THE MOUSE IN QBASIC - QBASIC COMMUNICATIONS Internet Resources How to contribute Final words ------------------------------------------------------------------------------- NEWS *** BASIX FANZINE AWARDS 1998 Do you have a good BASIC website? Or have you seen one recently? If so, why not nominate the site for an award? To nominate a site for an award, just send an email to basixawards98@dewarr.globalnet.co.uk and include the name of the site and the URL. If you want to suggest a category the site could go in, include that as well. Alternatively, you could fill in the form at http://www.users.globalnet.co.uk/~dewarr/basix-awards98.htm The deadline for nominations has been moved back again (in the original Usenet post I wrote about this, I suggested the end of May 1998 I think, and recieved about five nominations) to the end of September 1998. After the deadline, I will post a complete list of nominations the the BASIC newsgroups and also include the list in the issue of the Basix Fanzine which will be released after that date. Votes will be taken in and the winning site will recieve a prominent position on the Internet Resources page, much publicity in the Basix Fanzine, a lovely-looking GIF file to display on the site, and much praise and other nice-ness. Good luck! Nominations accepted for all non-commercial DOS BASIC-oriented websites. The editor reserves the right to disallow a nomination, for example if adult content appears on a nominated site. *** POWERBASIC CONSOLE COMPILER FOR WINDOWS In a recent newsgroup post Dave Navarro hollered the following (slightly edited for size, as tragically, I'm not paid for this kind of thing): "Ever wished for a 32-bit BASIC compiler? One with the straightforward DOS interface but megabytes and megabytes of memory? Ever wished for a 32-bit Windows compiler that's truly easy to use? Well, now it's here. Today. The PowerBASIC Console Compiler for Windows. With PB/CC, it's a whole new Windows! Text mode for Win95, Win98, and WinNT! The Console is a text mode interface connected right to the heart of 32-bit Windows. With a console application, there's no fluff, no animated puppets, just intense computing power. Port existing Basic code from DOS to Windows today! Access a flat memory model that's practically unlimited. Boost performance with true 32-bit code... PB/CC supports PRINT, LOCATE, LINE INPUT, INKEY$, INSTAT, CLS, COLOR, and LPRINT... then we added more. CURSOR, INSHIFT, PAGE, PCOPY, WAITKEY$ and WAITSTAT... mouse handling routines... All the text tools you'll ever need... PB/CC's high degree of compatibility with PowerBASIC, QuickBasic, GW-BASIC and BASICA means that you can port existing DOS applications to Win32 with ease... creating an array that uses 50 megabytes of RAM is as simple as: DIM x%(0 to 26214400) Create CGI applications for your web server... Get complete executables as small as 10K! With no run-time requirements of any kind... Access to the Win32 API, or any 32-bit DLL, is a breeze! Including ODBC, Winsock, and the Internet controls. There's an inline assembler, support for threads... PB/CC Features: - Array Sort, Scan, Insert, Delete - Bit Shift, Rotate, Test Set, Reset and Toggle - Built-in 32-bit assembler - Code pointer variables (create your own callbacks) - Compile to true machine code - Complete access to the entire Win32 API, including ODBC and Winsock - Conditional compilation ($If/$EndIf) - Currency variables with two levels of precision - Data pointer variables - Extended-precision (80-bit) floating point variables - Native support for I/O redirection (STDOUT and STDIN) - Native support for multi-threading - Peek/Poke, Peek$/Poke$ for memory access - User-Defined Types and Unions - Unsigned byte, word, and double word variables - 64-bit quad-word integers System Requirements: - Personal computer with a 386 or higher processor - Windows 95/98 or Windows NT 3.51 or later - 2 MB of available memory - One 3.5" high-density disk drive - A hard disk with 3.0 MB available for installation" Dave Navarro replied to a later post from Ernie Deel that: "PB/CC is much more than "PB/DLL plus console support". I'm saddened that you have no faith. The console compiler is the start of a new "genre" of compilers. ...Wouldn't you like to write your program on a single platform and simply re-compile it for other platforms? Without having to spend hours (or days or months or years) converting between API calls, calling conventions, etc... With the console compiler, we provide a BASIC language which will work on all platforms supported in the future. PRINT, LOCATE, COLOR, CLS, LPRINT, etc. can be supported on all platforms and we will do all of the low-level work so that you can concentrate on just writing code. Of course, if you *WANT* to take advantage of platform specific features (such as linking to DLLs in Windows) we don't prevent you from doing that. It's your choice. Certainly with PB/DLL you can never write code which will work on Linux, DOS, or other platforms because the API/OS calls won't be the same on each platform... As for PB/DLL itself, we're certainly not done with that. We've been collecting suggestions from thousands of users and together with some of our own ideas, we'll certainly be updating it." Later support for OS/2 is a "possibility" but they're "not going to commit to it." Apparently PowerBasic "plan to release many new BASIC compilers and BASIC programming products over the next 6 to 7 months". So watch this space. A demo of PB/CC is available at ftp://ftp.powerbasic.com/pub/pbcc/ PB/CC's RRP is $149 (not including shipping & handling). For more details, check their website at http://www.powerbasic.com/products/pbcc/ or email sales@powerbasic.com *** PROPOSAL FOR NEW POWERBASIC NEWSGROUP Marc van den Dikkenberg sent a formal Request For Discussion (RFD) to the news.announce.newgroups newsgroup, proposing a new PowerBasic newsgroup in the comp.lang.basic.* hierarchy, to be called comp.lang.basic.powerbasic - this would be carried by more news servers and be more "official" than the existing alt.lang.powerbasic. Marc said in the RFD that: "By now, well over a hundred people have already expressed their desire for a widespread newsgroup specifically aimed at PowerBasic products. Newsgroups in the comp.* hierarchy are carried by most newsservers, as opposed to a lot of alt.* newsgroups. As such, the comp.* hierarchy is preferable since it guarantees a much wider distribution or your questions / answers." If you want to join in the discussion, or read the full RFD including the proposed charter for the group, visit the news.groups newsgroup. After the discussion phase, a Call For Votes (CFV) will be issued if there is enough support (judging by the response so far, there seems to be plenty). This will be your chance to vote for the creation of the new group - 100 more "yes" than "no" votes will be needed and more than two thirds of the votes need to be in favour. So, keep your eyes peeled for the CFV - it will be posted in the news.groups newsgroup as well as the regular BASIC ones, with full instructions for what to do. While I'm on the subject, the editor of this fanzine recently sent out an RFD for some more Visual Basic newsgroups to split up the current mess of comp.lang.basic.visual.misc - the discussion can be found in the news.groups newsgroup. At the moment, there doesn't seem to be much interest though... :( *** Got a BASIC news article? Want to announce a new product that will benefit BASIC programmers? Then send it to basix@dewarr.globalnet.co.uk NOW! [The editor reserves the right to edit or omit articles] ------------------------------------------------------------------------------- ARTICLES *** LONG FILENAMES FROM DOS, by Alex Warren · alexwarren@writeme.com One of the very nice things that Microsoft did for us with Windows 95 was add support for ridiculously long filenames, up to 256 characters in length, if I remember correctly. No longer do we have to put up with cryptic filenames such as "LETMRB01.DOC" - instead we be can more creative and save files with names like "Letter to Mr Brown draft 1 about the boiler that exploded last March which still hasn't ruddy well been fixed yet even though I've phoned them three times I mean I despair I really really do something ought to be done about it.doc" and other such silly things. At least... almost. We can use long filenames from our Windows apps, but what if we want to use long filenames from our QuickBasic programs? If we try OPEN "THE SILLY LONG FILENAME.TXT" FOR INPUT AS #1 we get a "Bad Filename" error. But if you look at the version of EDIT included with DOS 7, it's clearly a DOS program... but look! Look at all those lovely long filenames! Isn't it amazing! Isn't it? Oh well... Yes, it is possible - we can access long filename functions from DOS (well, a Windows 95 DOS session anyway). But it's not very easy to do in QuickBasic - though it can be done. Really. If you look in the ABC Packets you'll hopefully see that I posted some code a few months back which will do just that, from QB. A newer version of the program (v1.5) is contained within this fanzine, in ' LONG FILENAMES FROM DOS DEMONSTRATION v1.5 ' by Alex Warren, June 1998 ' Released into the Public Domain ' This program is intended to show you how to use the long filename ' functions. It's not particularly neat but it should be good enough to ' show you how to incorporate long filenames in your applications without ' having to use the incredibly messy method of using SHELL. ' New in version 1.5 - new neater screen layout, "Create File" added (was ' in menu in 1.0 but forgot to implement it ) DECLARE SUB drawwindow (x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, col AS INTEGER) DECLARE SUB centreprint (y AS INTEGER, s$) DECLARE SUB setdirectory () DECLARE SUB getfileattribs () DECLARE SUB showdirectory () DECLARE SUB createfile () DECLARE SUB createdirectory () DECLARE SUB getvolumeinfo (drive$) DECLARE FUNCTION truncate$ (s$) DECLARE FUNCTION getcurrentdir$ () DECLARE SUB int21 () '$INCLUDE: 'qb.bi' DIM SHARED inregs AS RegTypeX, outregs AS RegTypeX DIM SHARED filesystem$, casesensitive%, preservecase%, unicode%, lfnfunctions%, compressed% DIM SHARED searchhandle%, returnedlfn$, returnedsfn$ DO CLS drawwindow 5, 1, 75, 5, 0 centreprint 2, "Long Filenames from DOS Demonstration, by Alex Warren" centreprint 3, "version 1.5, June 1998" centreprint 4, "Released into the Public Domain" drawwindow 15, 12, 65, 22, 9 centreprint 12, "[ MENU ]" centreprint 13, "Current directory is " + getcurrentdir$ centreprint 15, "1 - Create directory" centreprint 16, "2 - Change to directory" centreprint 17, "3 - View directory" centreprint 18, "4 - Create file" centreprint 19, "5 - Change drive" centreprint 20, "6 - Exit" ' Not included but worth investigating are: ' - Remove directory (service 713Ah) ' - Delete file (7141h) ' - Move/Rename file (7156h) ' - Get/Set file attributes (7143h) ' +++ others on Ralf Brown's Interrupts List DO: a$ = INPUT$(1): LOOP UNTIL a$ >= "1" AND a$ <= "6" selected = VAL(a$) COLOR 7, 0: CLS SELECT CASE selected CASE 1 createdirectory CASE 2 setdirectory CASE 3 showdirectory CASE 4 createfile CASE 5 INPUT "Enter drive letter: ", driveletter$ driveletter$ = driveletter$ + ":" SHELL driveletter$ CASE 6 END END SELECT LOOP SUB centreprint (y AS INTEGER, s$) x% = 40 - LEN(s$) / 2 LOCATE y, x%: PRINT s$ END SUB SUB createdirectory DIM pathname AS STRING * 255 INPUT "Enter FULL path of sub-directory to create: "; p$ pathname = p$ ' inregs: ' .ax: service - 7139h ' .ds: segment of pathname ' .dx: offset of pathname inregs.ax = &H7139 inregs.ds = VARSEG(pathname) inregs.dx = VARPTR(pathname) int21 END SUB SUB createfile DIM createfilename AS STRING * 255 INPUT "Enter name and path of file to create: ", f$ createfilename = f$ ' inregs: ' .ax: service - 716Ch ' .bx: access mode and sharing flags ' .dx: action (bits: 0=open; 1=truncate; 4=create) ' .ds: segment of pathname ' .si: offset of pathname inregs.ax = &H716C inregs.dx = (2 ^ 4) inregs.ds = VARSEG(createfilename) inregs.si = VARPTR(createfilename) int21 ' When the file has been created you could get its short filename and ' write to it. I think that you may need to close the file using an ' interrupt before you do that though... I don't know what it is. Sorry! END SUB SUB drawwindow (x1 AS INTEGER, y1 AS INTEGER, x2 AS INTEGER, y2 AS INTEGER, col AS INTEGER) COLOR 7, col FOR i% = y1 TO y2 LOCATE i%, x1 PRINT STRING$(x2 - x1, " ") NEXT i% ' draw corners LOCATE y1, x1: PRINT CHR$(218) LOCATE y1, x2: PRINT CHR$(191) LOCATE y2, x1: PRINT CHR$(192) LOCATE y2, x2: PRINT CHR$(217) ' draw edges LOCATE y1, x1 + 1: PRINT STRING$(x2 - x1 - 1, 196) LOCATE y2, x1 + 1: PRINT STRING$(x2 - x1 - 1, 196) FOR y% = y1 + 1 TO y2 - 1 LOCATE y%, x1 PRINT CHR$(179); NEXT y% FOR y% = y1 + 1 TO y2 - 1 LOCATE y%, x2 PRINT CHR$(179); NEXT y% END SUB FUNCTION getcurrentdir$ DIM thedir AS STRING * 255 ' inregs: ' .ax: service - 7147h ' .dx: 0=default, 1=A:, 2=B:, 3=C:, 4=D: etc. ' .ds: segment of buffer ' .si: offset of buffer inregs.dx = &H0 inregs.ax = &H7147 inregs.ds = VARSEG(thedir) inregs.si = VARPTR(thedir) int21 ' This line truncates thedir up to the first null character: d$ = truncate$(thedir) ' This line adds the backslash if not present, as the backslash may or may ' not be present already. If you don't want the backslash there ever, modify ' this line: IF RIGHT$(d$, 1) <> "\" THEN d$ = d$ + "\" getcurrentdir$ = d$ END FUNCTION SUB getvolumeinfo (drive$) ' inregs: ' .ax: service - 71A0h ' .ds: segment of rootname ' .dx: offset of rootname ' .es: segment of buffer for file system name ' .di: offset of buffer for file system name ' .cx: size of file system name buffer DIM rootname AS STRING * 4 DIM filesysname AS STRING * 255 rootname = drive$ + CHR$(0) inregs.ax = &H71A0 inregs.ds = VARSEG(rootname) inregs.dx = VARPTR(rootname) inregs.es = VARSEG(filesysname) inregs.di = VARPTR(filesysname) inregs.cx = 255 int21 IF outregs.ax = &H7100 THEN BEEP PRINT "LFN functions are not supported; exiting program..." END END IF filesystem$ = truncate$(filesysname) IF outregs.bx AND 2 ^ 0 THEN casesensitive% = 1 ELSE casesensitive% = 0 IF outregs.bx AND 2 ^ 1 THEN preservecase% = 1 ELSE preservecase% = 0 IF outregs.bx AND 2 ^ 2 THEN unicode% = 1 ELSE unicode% = 0 IF outregs.bx AND 2 ^ 14 THEN lfnfunctions% = 1 ELSE lfnfunctions% = 0 IF outregs.bx AND 2 ^ 15 THEN compressed% = 1 ELSE compressed% = 0 END SUB SUB int21 CALL INTERRUPTX(&H21, inregs, outregs) END SUB SUB setdirectory DIM pathname AS STRING * 255 INPUT "Enter FULL path of sub-directory: "; p$ pathname = p$ ' inregs: ' .ax: service - 713Bh ' .ds: segment of pathname ' .dx: offset of pathname inregs.ax = &H713B inregs.ds = VARSEG(pathname) inregs.dx = VARPTR(pathname) int21 END SUB SUB showdirectory ' get volume label DIM vollable AS STRING * 500 DIM filespec AS STRING * 4 filespec = "*.*" + CHR$(0) inregs.ax = &H714E inregs.ds = VARSEG(filespec) inregs.dx = VARPTR(filespec) inregs.es = VARSEG(vollable) inregs.di = VARPTR(vollable) inregs.cx = &H808 inregs.si = &H0 int21 vol$ = truncate$(MID$(vollable, &H2D, 260)) ' truncate string to null char. vol$ = LEFT$(vol$, 8) + RIGHT$(vol$, 3) ' get rid of the dot COLOR 9: PRINT "Volume label is "; vol$; "." COLOR 7 quitloop = 0 ' What's done: ' first, call 714Eh to find first matching file, then call 714Fh to find ' next matching file and so on. Use 71A1h to stop the search. ' Attributes masks: ' Bit: 4 = directory ' 3 = volume label ' 2 = system ' 1 = hidden ' 0 = read-only ' ax=&h714E (service) ' ds:dx = filespec, e.g. *.* ' es:di = finddata record (see below) ' cl = allowable attributes mask, set here to F7, i.e ALL attributes allowed ' except for volume label. ' ch = required attributes mask, set here to 00 so NO attributes required DIM finddatarecord AS STRING * 500 ' filespec must be DIMmed as STRING * x, this is already done above. filespec = "*.*" + CHR$(0) inregs.ax = &H714E inregs.ds = VARSEG(filespec) inregs.dx = VARPTR(filespec) inregs.es = VARSEG(finddatarecord) inregs.di = VARPTR(finddatarecord) inregs.cx = &HF7 inregs.si = &H0 int21 filefindhandle = outregs.ax numfiles = 1 DO ' finddatarecord$ is now filled with loads of information about the file ' we've retrieved (info from Ralf Brown's Interrupts List): ' offset 00h bits 0-6 are standard DOS file attribs ' 04h QWORD file creation time ' 0Ch QWORD last access time ' 14h QWORD last modification time ' 1Ch DWORD file size (high 32 bits) ' 20h DWORD file size (low 32 bits) ' 2Ch 260bytes ASCIZ full filename ' 130h 14bytes ASCIZ short filename ' If required, you could interpret all that stuff but we're only interested ' in the filenames for now: longfilename$ = truncate$(MID$(finddatarecord, &H2D, 260)) shortfilename$ = truncate$(MID$(finddatarecord, &H131, 14)) ' NOTE that shortfilename$ will contain nothing if the short filename ' is *exactly* the same as the long file name. PRINT longfilename$; TAB(30); shortfilename$ ' If longfilename$ is null, we've reached the end of the list. IF longfilename$ = "" THEN PRINT "*** END OF LIST; PRESS A KEY ***": EXIT DO ' reset finddatarecord finddatarecord = STRING$(500, 0) inregs.ax = &H714F inregs.bx = filefindhandle int21 IF numfiles MOD 20 = 0 THEN COLOR 14 PRINT ">> Press a key <<" COLOR 7 a$ = INPUT$(1) IF a$ = CHR$(27) THEN quitloop = 1 END IF numfiles = numfiles + 1 LOOP UNTIL quitloop = 1 IF quitloop = 0 THEN a$ = INPUT$(1) ' It's important to reset the filefind handle: inregs.ax = &H71A1 inregs.bx = filefindhandle int21 END SUB SUB showinfo getvolumeinfo ("C:\") PRINT "File system is: "; filesystem$ PRINT PRINT "Searches are case sensitive:"; casesensitive% PRINT "Preserves case in directory entries:"; preservecase% PRINT "Uses unicode chars in file and dir names:"; unicode% PRINT "Supports DOS LFN functions:"; lfnfunctions% PRINT "Compressed volume:"; compressed% END SUB FUNCTION truncate$ (s$) ' Truncates s$ to first null character truncate$ = LEFT$(s$, INSTR(s$, CHR$(0)) - 1) END FUNCTION If you run this rather un-user-friendly program from Windows 95 (it won't work with QBasic, you'll need QB4.5 or better loaded with /L) you'll see that it works. How? Well, it's all quite simple really, though it took me a while to figure it out - maybe because I'm stupid, I dunno... anyway: basically, all each subroutine does is call interrupt 21h (that means the hexadecimal number 21) with the required service (specified by inregs.ax) and the required tags (specified by the other inregs thingies). Usually these tags include a string, and to pass a string to an interrupt you need to use a pointer to that string. A pointer is basically a number referring to a specific part of the computer's memory, and is a concept that will familiar to the C++ programmers amongst us, though it's not often used in BASIC unless you're writing more complex programs using C++ libraries and such. If you look at the information for interrupts you see in books and on Ralf Brown's list, you'll see things such as "DS:DX - ASCIZ pathname". This means a string where DS (i.e, inregs.ds) specifies the segment in memory of the string, and DX specifies the string's location within that segment. ASCIZ just means a string with a null character at the end - you can get one of these in BASIC by using CHR$(0). In QB, we can get the segment of the string using VARSEG and the string's location within that segment with VARPTR. This appears to work for fixed-length strings only - if you want to do the same with a variable length string you must use SADD in place of VARPTR. For example, if pathname is a string containing a pathname, we can pass it to the interrupt using this: inregs.ds = VARSEG(pathname) inregs.dx = VARPTR(pathname) Note that other services may want the pathname/filename to be in other places instead of DS:DX, so check your documentation. (If you have the time you can download the entire of Ralf Brown's interrupts list from his website - it's about 8mb). So, this program should be enough to get you started using long filenames from QB. It wouldn't be too hard to make a library from it if you wanted to, and it would be quite a useful library to have. However, these long filename functions obviously won't work in anything less than Windows 95. There are equivalent functions though, that work with short filenames. It would be a good idea to make a library that detected whether LFN functions were available, and if not then replace the LFN services with the equivalent SFN service. This is (almost) really easy - the equivalent of an LFN service 71xxh is xx00h, so all you would need to do hopefully would be to add that to the int21 subroutine. (To detect whether LFN functions are available, look at the program - this detects for LFN functions when it starts). That would keep users of Windows and users of DOS (however rare they are these days) happy. Also, bear in mind that LFN functions require Windows to be running - if you are running in MS-DOS mode (not in an MS-DOS window from Win95) then the LFN functions won't work. Have fun! *** GRAPHICS IN QBASIC TUTORIAL (PART 2), by "Hacker" aka Damian Nikodem · Hacker@alphalink.com.au For todays lesson we will be covering PCX images! Now because we are covering a FILE FORMAT I will split the Tutorial into Diffrent Sections. The sections needed for PCX images are: HEADER IMAGE DATA PALETTE HEADER: The header is ALWAYS 128 bytes and most of the data you will not even use so I will not cover it here. But if you really want it you can goto: www.alphalink.com.au/~hacker/pcxinfo.htm for all of the information but trust me its not really worth the download time but there is a slight bit of information that is very useful and that is the X and Y sizes of the image. But that would make the program a LOT larger than I would want to send (I have a VERY slow conection to my mail server) but here is the formula for figuring out the size of the image: ' Start CODE TYPE pcxheader MAN AS STRING * 1: VER AS STRING * 1: ENC AS STRING * 1: bit AS STRING * 1 xls AS INTEGER: yls AS INTEGER: xms AS INTEGER: yms AS INTEGER HRE AS INTEGER: VRE AS INTEGER: col AS STRING * 48: RES AS STRING * 1 PLA AS STRING * 1: BYT AS INTEGER: pal AS INTEGER: FIL AS STRING * 58 END TYPE DIM dat AS pcxheader file$ = "Image1.pcx" OPEN file$ FOR BINARY AS #1 GET #1, , dat PRINT dat.XMS - dat.XLS + 1; " Is the size of the X plane" PRINT dat.YMS - dat.YLS + 1; " Is the size of the Y plane" ' End CODE All of that just to see what the size of the image is. This wouldnt be to hard to implement into a normal pcx loader but the problem is that my loader writes to memory (like I explained in part 1) and because it is only designed to handle 320x200 it works a LOT faster. IMAGE DATA: The data in the image is easy to handle A pcx image is almost a direct screen to memory dump with 1 single execption. If the number that is read is over 192 and under or equal to 255 then it starts a small loop and this lets us get a small compression ratio (1/1.2) on the average image without much work being done. Well heres the code to load a 320x200 image: ' START CODE h$ = string$(128,32) file$ = "IMAGE1.PCX" 'Image you want to load screen 13 DEF SEG = &HA000 OPEN file$ FOR BINARY AS #1 GET #1, 1, h$ ' Gets rid of header (We dont need it) FOR c = 1 TO 64000 ' Starts a loop with 64000 (the number of pixels) repeats GET #1, , dat$ ' Gets image data IF ASC(dat$) > 192 AND ASC(dat$) <= 255 THEN ' If data > 192 continue lps = ASC(dat$) - 192 ' Take 192 away from number we got store number in LPS GET #1, , dat$ ' Get more data VALUE = ASC(dat$) ' Convert data into number FOR cnum = lps TO 1 STEP -1 ' start loop with LPS repeats POKE x, VALUE ' put 1 pixel to the screen at position X x = x + 1 ' add 1 to X NEXT cnum ' goto start of loop if its not done ELSE ' If the first data we got was < 192 then we get put here POKE x, ASC(dat$) ' Put that first data to the screen at position X x = x + 1 'increment X END IF ' End If (duh!!) NEXT c ' goto start of loop if its not done 'PAUSE CODE Now what have you notaiced about that code apart from you cant make heads or tails of it??? If you run it the colors off. Do you remember what I said in part one about the palette and how you can redefine the colors well you can now put it into action! PALETTE: The palette in a PCX file is not all ways the exact same palette as the standard so the last 768 bytes of a pcx image are just palette data. The format for the pallete data is quite simple. it contains 256 * 3 ascii charecters. Each charecter is one part of the color so the first charecter is the red value for color 0 the second is the Green value for the color 0, the third charecter is the blue value for color 0,and so on this is repeated until color 255 is reached. Here is the final piece of code for the PCX loader: ' START CODE h$ = string$(128,32) file$ = "IMAGE1.PCX" 'Image you want to load screen 13 DEF SEG = &HA000 OPEN file$ FOR BINARY AS #1 GET #1, 1, h$ ' Gets rid of header (We dont need it) FOR c = 1 TO 64000 ' Starts a loop with 64000 (the number of pixels) repeats GET #1, , dat$ ' Gets image data IF ASC(dat$) > 192 AND ASC(dat$) <= 255 THEN ' If data > 192 continue lps = ASC(dat$) - 192 ' Take 192 away from number we got store number in LPS GET #1, , dat$ ' Get more data VALUE = ASC(dat$) ' Convert data into number FOR cnum = lps TO 1 STEP -1 ' start loop with LPS repeats POKE x, VALUE ' put 1 pixel to the screen at position X x = x + 1 ' add 1 to X NEXT cnum ' goto start of loop if its not done ELSE ' If the first data we got was < 192 then we get put here POKE x, ASC(dat$) ' Put that first data to the screen at position X x = x + 1 'increment X END IF ' End If (duh!!) NEXT c ' goto start of loop if its not done GET #1, LOF(1) - 768, dat FOR c = 0 TO 255 GET #1, , dat r& = INT(ASC(dat) / 4) GET #1, , dat b& = INT(ASC(dat) / 4) GET #1, , dat g& = INT(ASC(dat) / 4) OUT &H3C8, c OUT &H3C9, r& OUT &H3C9, g& OUT &H3C9, b& NEXT c CLOSE 'END CODE Now you know how to load PCX images in basic! Keep this code (The entire loader) because next issue I will be discussing RANDOMIC GRAPHICS. The jpg loader is being postponed until I fully understand the format this may be a few months ( or years have you seen the size of the DOCS) NOTE: Also go down to the store and BUY quake 2 it is well worth it! if you dont have the money at least download the shareware (11 Mb) from www.quake2.com I will be using screen shots from the game in the next part also - Hacker hacker@alphalink.com.au *** YOUR PROGRAMS - YOUR RIGHTS, by Alex Warren · alexwarren@writeme.com I've written this article to explain to you about copyright laws and how they affect you and your programs - I hope it will be of help to some of you. I'm not an expert on this subject - all information has been gathered from various copyright FAQs I've researched. If you require more information I suggest you visit one of the copyright newsgroups, or better, consult your lawyer. Copyright: EVERY program you have already written yourself is copyrighted - to you. You do not need to register it with anyone - you already own the rights to it. Copyright registration is unneccessary and is only a good idea in circumstances where you're likely to need to prove copyright ownership, which isn't often. So, the quick and easy step to ensure that your software is copyrighted in all countries of the world is to do nothing! OK, so a sentence such as "Copyright © 1998 Whatever Software" isn't going to hurt - in fact, it's a good idea to add a statement as it reinforces the applicable laws by demonstrating copyright ownership. Most countries in the world share copyright laws. If you're a resident of the US, your software is still protected even when used in the UK, or France, for example. So, the message is: Your software, and in fact anything that you write or compose or draw, is already your own intellectual property. You have rights to it already. Public domain and Freeware: Just because you distribute something as freeware does not mean that you've abandoned your rights to it. If you give away your applications for free, it is still illegal for others to distribute them without permission. This makes it technically illegal to distribute even a freely distributed piece of software such as QBasic, if Microsoft has not given permission for you to do so (permission could take the form of a letter to you, or a statement in the help file giving permission to distribute the software free of charge, etc.) If you want your application to be distributed freely by others then it's usually a good idea to put in a phrase such as "This software may be distributed freely on provision that no charge is made for it", etc. "Public domain" software is different. You can release something into the public domain by saying something like "This software is hereby released into the public domain" somewhere in the software or in its documentation. If you put something in the public domain then you abandon all your copyright rights to it. So, don't say "public domain" when you really mean "freeware" - you won't be able to stop people selling it, giving it away, etc. - other people will have just the same copying rights to it as you do. If you ever see a notice on some software you've downloaded saying "This software is released into the public domain for non-commercial use", then don't believe it. That sentence contradicts itself. By donating the software to the public domain, the programmer has abandoned all rights to the software, making it theoretically possible for anybody to sell it on for trillions of dollars/pounds/etc. (not that anybody is neccessarily going to buy it, though). So, always remember - there is a massive difference between "public domain" and "freeware". The two terms are not inter-changable. So, even though QBasic is given away freely, it is not "Public domain". Copyright expiration: Copyright doesn't last forever. In the case of individuals, it expires 75 years after the death of the creator (I think - though I did read somewhere that it was only 70 years within the EC so I may well be completely wrong). This means that, in 1998, anything written by anybody who died before 1923 is now in the public domain, and anything written by anybody who died after 1923, or is still alive, is still under copyright. The case is different with computer programs, however - this expires 50 years from the date of creation, if my sources are correct. This means that you will be able to distribute QuickBasic freely and legally any time after the year 2038, whether or not Mr. Gates is still around... still, I'm sure you'll be able to find a way to pass the time. *** WHY (MOST) ON-LINE PROGRAMMING TUTORIALS SUCK! Disclaimer The opinions expressed by RudeJohn do not necessarily reflect the opinions of the Basix Fanzine or its staff. In fact, they probably disagree with me entirely. Everyone else does. Forward Regardless of the title, this editorial is not an attack upon any person or persons. Rather, it is an attack upon the ill-conceived approach to writing on-line programming tutorials which the internet continues to engender. It's Geek to Me! If I had a nickel for every Usenet post that said something like this: "I've never programmed before, but I want to learn how to program in . Can anyone tell me if there is a free tutorial on the internet that will teach me how to program in ?" Didn't Yoda warn Luke that the dark side of the force was "easier, more seductive"? Well, that's the warning I would give anyone who thought that they could learn how to program from a language-specific tutorial. Although the reader will no doubt learn something about programming in , he may never appreciate the simple fact that learning to program, and learning to program "in", are just not the same thing. It's been said that the most famous algorithm in history is Euclid's algorithm for calculating the greatest common divisor of two integers. Correct me if I'm wrong, but I don't think that Euclid knew the difference between an Intel processor and a 3-pack of rubbers. Yet, without benefit of MMX technology or prophylactics, Euclid formulated an idea which today exemplifies the nature of procedural programming. Obviously, an algorithm is not a computer program. An algorithm is a description of a method, and a program is an implementation of that method. While programming languages are limited by their syntax , an algorithm is limited by nothing less than the imagination. Learning to program "in" burdens the hobbyist with all of the baggage peculiar to a specific language. On the other hand, learning to program in spite of a specific language enables the hobbyist to recognize what different languages have in common. Once that commonality is established, it becomes far easier to take advantage of the differences between programming languages because those differences become features rather than obstacles. It's unlikely that any hobbyist would want to learn programming from a purely theoretical point of view. Who can resist the lure of writing and running a working program? Nonetheless, a little practical application is good for the ego. The sensible approach lies somewhere between the extremes of theory and application. IMHO, a healthy dose of theory should proceed a first attempt at programming "in", while further doses of theory should be endured at regular intervals as the hobbyist becomes more facile with his language(s) of choice. End Game IMHO, learning to program should not be sacrificed for the sake of learning to program "in." If the reader is going to depend upon language-specific tutorials to teach him how to program, then he is -- as far as I am concerned -- in very deep fertilizer. Every concept that a language-specific tutorial presents must be filtered through the syntax of that programming language. As a result, the tutorial will discourage the reader from pursuing any idea which is beyond that language's ability to express. Q: How would you illustrate an idea using a programming language which was designed to ignore that idea? A: Badly. C'ya, RudeJohn "I'm rude. It's a job." *** TESTING MATRIX SOFTWARE Disclaimer This paper is the result of an elementary exercise in combinatorics. This program (aka 11-ctln.bas) was play-tested in the QB 4.5 IDE: DECLARE FUNCTION Cat# (n AS INTEGER) DIM Number AS INTEGER CLS LOCATE 2, 1 PRINT STRING$(40, "=") PRINT " Calculate the Catalan number for n" PRINT STRING$(40, "-") LOCATE 18, 1 PRINT STRING$(40, "-") PRINT " Enter 0 to Exit" PRINT STRING$(40, "=") VIEW PRINT 6 TO 16 DO INPUT "Enter n: ", Number SELECT CASE Number CASE IS <= 0 END CASE IS = 1 PRINT "You're a business major, right?" PRINT CASE IS = 2 PRINT "How's the lobotomy working out?" PRINT CASE ELSE PRINT "Cat("; Number; ") ="; Cat#(Number) PRINT END SELECT LOOP FUNCTION Cat# (n AS INTEGER) DIM Denom AS DOUBLE DIM Numer AS DOUBLE DIM Result AS DOUBLE DIM I AS INTEGER ' Cat(n) was derived from the following identity: ' ' (2n - 2)! (n + 1)(n + 2) ... (2n - 2) ' ----------- = --------------------------- ' (n - 1)! n! (n - 1)! ' First, we calculate the numerator. Numer = n + 1 FOR I = (n + 2) TO ((2 * n) - 2) ' Condition: n >= 3 Numer = Numer * I NEXT I ' Next, we calculate the denominator. Denom = 1 FOR I = 2 TO (n - 1) ' Condition: n >= 3 Denom = Denom * I NEXT I ' Taking a/b directly for large integers is inefficient. However, ' it's good enough to make the point that a brute-force approach ' is inadequate to the task of finding the best case for chained ' matrix multiplication. Result = Numer / Denom ' Obviously, using floating-point numbers is going to cause a ' problem so we filter Result accordingly. IF (Result - INT(Result)) >= .499999999999998# THEN Cat = INT(Result) + 1 ELSE Cat = INT(Result) END IF END FUNCTION Notation Let A[x,y] represent a matrix of dimensions x and y. If A and B are matrices, then AB represents their product such that (A[x,y])(B[y,z]) = (AB)[x,z]. By the Numbers For any two integer matrices A[x,y] and B[y,z], the direct method of calculating (AB)[x,z] requires y multiplications for each element of AB. Since AB has x*z elements, the total number of multiplications required to calculate AB is given by x*y*z. Matrix multiplication is associative, i.e., A(BC) = (AB)C. If we multiply four matrices then the product may be grouped in five different ways: ((AB)C)D (AB)(CD) (A(BC))D A((BC)D) A(B(CD)) The question is, does the order in which this operation is carried out have a significant effect upon efficiency? It's obvious that for square matrices of the same dimension the total number of multiplications required for ABCD is the same no matter how we group the product. For argument's sake, let's try: A[ 4,40] B[40, 2] C[ 2,11] D[11,20] The total number of multiplications required for each grouping is shown in the following table. Grouping Complexity = 1 ((AB)C)D (4*40*2) + (4*2*11) + (4*11*20) 1,288 2 (AB)(CD) (4*40*2) + (2*11*20) + (4*2*20) 920 3 (A(BC))D (40*2*11) + (4*40*11) + (4*11*20) 3,520 4 A((BC)D) (40*2*11) + (40*11*20) + (4*40*20) 12,880 5 A(B(CD)) (2*11*20) + (40*2*20) + (4*40*20) 5,240 Grouping 4 requires 14 times as many multiplications as grouping 2. Evidently, the order in which the multiplication of several matrices is carried out can have a considerable effect upon efficiency. Under the right circumstances, this simple observation might be put to the test as a measure of a matrix software package's sophistication. Catalan Numbers The number of different groupings for a product of n matrices is called the Catalan number for n, and is generated by the following formula: Catalan(n) = ((2 * n) - 2)! / ( (n - 1)! * n! ) where x! (read "x factorial") = 1 * 2 * ... * (x - 1) * x. The program 11-ctln.bas generates the Catalan number for n. If you run the program you will see why it is not practical to use a brute-force approach for finding the best, or even the worst, case of a matrix product as n increases. C'ya, RudeJohn "I'm rude. It's a job." *** HIT-OR-MISS INTEGRATION [note: this article contains squared-signs which in the HTML are superscript from the text. This is obviously not possible in plain old text format so some of the formulae may be hard to read here :( ] Warning: All code was play-tested in the QB 4.5 IDE. Introduction This article discusses the simplest type of probabilistic numerical integration which is sometimes referred to as the hit-or-miss Monte Carlo method. This approach may not compete with deterministic methods in general, but it's something to think about when you wake up in a cold sweat at 4 a.m. I guess. Parabolas Consider the definite integral of x 2 from x = 0 to x = 1. Anyone who's had an introduction to calculus will remember that the indefinite integral of x 2 is given by (1/3) x 3 + C, where C is a constant. It follows that the value of the definite integral is equal to [(1/3) 1 2 + C] - [(1/3) 0 2 + C] = 1/3. In other words, the magnitude of the area under the graph of y = x 2 from (0,0) to (1,1) is equal to 1/3. That said, let's find the value of the definite integral in a very different fashion as outlined by the following algorithm. For the sake of discussion, random is an ideal random-number generator over [0,1] while num represents the number of randomly-generated points. procedure estimate_area area = 0 loop from 1 to num {num > 0} x = random y = random if y 2 then (area = area + 1) end loop return (area / num) end procedure If all goes well, this algorithm should return an estimate to within 99% accuracy or better. This is not significant in terms of absolute error, but what do you expect? Fortunately, the rationale is quite simple. (If it wasn't, I'd have to find someone who could explain it to me.) For a sufficiently large number of randomly-generated points, the number of points in one unit-area should be nearly equal to the number of points in any other unit-area. This means that any area A n is proportional to P n the number of points it contains. So, if A 1 = k P 1 and A 2 = k P 2 then A 1 / A 2 = ( k P 1 ) / ( k P 2 ) or A 1 / A 2 = P 1 / P 2. See 11-x2.bas: DIM Point2D AS DOUBLE DIM Number AS DOUBLE DIM Hits AS DOUBLE DIM Diff AS DOUBLE DIM Estimate AS DOUBLE DIM Accuracy AS DOUBLE DIM StartTime AS LONG CLS LOCATE 2, 1 PRINT STRING$(40, "=") PRINT " Hit-or-Miss Estimate of y = x^2" PRINT STRING$(40, "-") LOCATE 21, 1 PRINT STRING$(40, "-") PRINT " Enter 0 to Exit" PRINT STRING$(40, "=") VIEW PRINT 5 TO 20 INPUT "Number of Points: ", Number WHILE Number > 0 RANDOMIZE (INT(INT(TIMER) * 4369 / 5760) - 32768) ' Scale the seed ' for 24 hours. Hits = 0 FOR Point2D = 1 TO Number IF RND <= RND ^ 2 THEN Hits = Hits + 1 END IF NEXT Point2D PRINT PRINT "Area"; TAB(15); 1# / 3# Estimate = Hits / Number PRINT "Estimate"; TAB(15); PRINT USING "#.################"; Estimate Diff = (1# / 3#) - Estimate PRINT "Difference"; TAB(15); PRINT USING "#.################"; Diff Accuracy = 100 * (1 - ((1# / 3#) - Estimate) / (1# / 3#)) PRINT "Accuracy"; TAB(13); PRINT USING "###.##%"; Accuracy PRINT INPUT "Number of Points: ", Number WEND END The reader is invited to generalize the above algorithm for y = f(x) over the interval [a,b]. Is this practical? What do you need to know about f(x) over [a,b] before applying our hit-or-miss method? Pi a la Rude Consider a circle of diameter D inscribed within a square of area D 2. The ratio of the area of the circle to the area of the square is pi / 4. As with the previous example, we will use randomly-generated points to find an approximate value for pi. If D = 1 and the center-point of the circle is (0.5,0.5), then our test for points which lie on the disk is (x - 0.5) 2 + (y - 0.5) 2 2. See 11-pi2d.bas: DIM Point2D AS DOUBLE DIM Number AS DOUBLE DIM Hits AS DOUBLE DIM Diff AS DOUBLE DIM Estimate AS DOUBLE DIM Accuracy AS DOUBLE DIM StartTime AS LONG CONST Pi = 3.141592653589793# CLS LOCATE 2, 1 PRINT STRING$(40, "=") PRINT " 2-Dimensional Estimate of Pi" PRINT STRING$(40, "-") LOCATE 21, 1 PRINT STRING$(40, "-") PRINT " Enter 0 to Exit" PRINT STRING$(40, "=") VIEW PRINT 5 TO 20 INPUT "Number of Points: ", Number WHILE Number > 0 RANDOMIZE (INT(INT(TIMER) * 4369 / 5760) - 32768) ' Scale the seed ' for 24 hours. Hits = 0 FOR Point2D = 1 TO Number IF ((RND - .5#) ^ 2 + (RND - .5#) ^ 2) <= .25# THEN Hits = Hits + 1 END IF NEXT Point2D PRINT PRINT CHR$(227); TAB(15); Pi Estimate = 4# * (Hits / Number) PRINT "Estimate"; TAB(15); Estimate Diff = Pi - Estimate PRINT "Difference"; TAB(15); PRINT USING "##.###############"; Diff Accuracy = 100# * (1# - Diff / Pi) PRINT "Accuracy"; TAB(13); PRINT USING " ###.##%"; Accuracy PRINT INPUT "Number of Points: ", Number WEND END Funny thing is, if we restrict our randomly-generated points to the line-segment from (0,0) to (1,1), the ratio of the total number of points on the line-segment to the number of points on the circle's diameter approaches 2 1/2. Why? By now, you must be wondering if this experiment can be extended to three dimensions. Well, of course it can! This time, we'll need a sphere of diameter D within a cube of volume D 3. If Vs and Vc represent the volume of the sphere and the cube, respectively, then Vs / Vc = pi / 6. Our test for points that lie within the ball is (x - 0.5) 2 + (y - 0.5) 2 + (z - 0.5) 2 2. See 11-pi3d.bas: DIM Point2D AS DOUBLE DIM Number AS DOUBLE DIM Hits AS DOUBLE DIM Diff AS DOUBLE DIM Estimate AS DOUBLE DIM Accuracy AS DOUBLE DIM StartTime AS LONG CONST Pi = 3.141592653589793# CLS LOCATE 2, 1 PRINT STRING$(40, "=") PRINT " 3-Dimensional Estimate of Pi" PRINT STRING$(40, "-") LOCATE 21, 1 PRINT STRING$(40, "-") PRINT " Enter 0 to Exit" PRINT STRING$(40, "=") VIEW PRINT 5 TO 20 INPUT "Number of Points: ", Number WHILE Number > 0 RANDOMIZE (INT(INT(TIMER) * 4369 / 5760) - 32768) ' Scale the seed ' for 24 hours. Hits = 0 FOR Point2D = 1 TO Number IF ((RND - .5#) ^ 2 + (RND - .5#) ^ 2 + (RND - .5#) ^ 2) <= .25# THEN Hits = Hits + 1 END IF NEXT Point2D PRINT PRINT CHR$(227); TAB(15); Pi Estimate = 6# * (Hits / Number) PRINT "Estimate"; TAB(15); Estimate Diff = Pi - Estimate PRINT "Difference"; TAB(15); PRINT USING "##.###############"; Diff Accuracy = 100# * (1# - Diff / Pi) PRINT "Accuracy"; TAB(13); PRINT USING " ###.##%"; Accuracy PRINT INPUT "Number of Points: ", Number WEND END This time, if we restrict our randomly-generated points to the line-segment from (0,0,0) to (1,1,1), the ratio of the total number of points on the line-segment to the number of points on the sphere's diameter approaches 3 1/2. Why? Hyperspace The next logical(?) step is to generalize our experiment using n-dimensional hyperspatial objects, i.e., the hypersphere and hypercube. Let Vs represent the "volume" of an n-dimensional hypersphere of diameter D. Let Vc = D n represent the "volume" of an n-dimensional hypercube. If n is even, Vs / Vc = pi m / ( m! 2 n) where m = n / 2. If n is odd, Vs / Vc = ( m! pi m ) / n! where m = (n - 1)/ 2. The reader is invited to write a program that will use our probabilistic calculation of Vs / Vc to estimate pi in the general case. Keep in mind that the formula for an n-dimensional hypersphere of radius R is given by x12 + x22 + ... + xn2 = R 2. Everyone who submits an entry to the Basix Fanzine will receive a lifetime subscription absolutely free! First prize is a lovely string of multi-colored paper-clips. Really. Extra Credit If we restrict randomly-generated points in the hypercube to the line-segment from (0,0,...,0) to (1,1,...,1), what can we say about the ratio of the number of points on the line-segment to the number of points lying on the hypersphere's diameter? Addendum Deterministic algorithms can sometimes be hybridized to include probabilistic methodology. For example, the so-called "trapezoidal rule" may be improved(?) as follows. Rather than using the mean of f(x) taken at the endpoints of an interval [a,b], or [f(b) - f(a)]/2, we take the mean of f(x) for n randomly-generated values of x over the interval [a,b], i.e., [ f(x 1) + f(x 2) + ... + f(x n)] / n for a i Once again, the reader is encouraged to submit either an algorithm or example program employing this technique to the Basix Fanzine for future publication. Let's face it, if you had anything better to do then you wouldn't be reading this! Addendum 2 Okay, an Addendum to the Addendum may be a bit much, but I just remembered something. The mathematical mean of a series of values can be extremely misleading if a few of those values are unusually high or low in comparison to the rest of the series. In response to this problem, somebody came up with the idea of the sample median. To find the sample median, first sort the series of values in question. If n is odd, the central element of the series is our sample median. If n is even, our sample median is the mathematical mean of the two values at the center of the series. I would very much like to see someone incorporate the use of the sample median into a hit-or-miss Monte Carlo program. As usual, it would make a smashing article for a future issue of the Basix Fanzine! C'ya, RudeJohn "I'm rude. It's a job." PS: If it looks as though I've been a bit heavy-handed in my constant solicitation of articles for the 'zine all I can say is "Glad you noticed!" Now why don't you write something up and send it in? Got an idea, an opinion, a story, or even a good joke? Send it in! Heck, think of the 'zine as a multi-newsgroup bulletin board. Advertise your website, your proggies, and even your latest game! Please, talk to us. We're so lonely. ------------------------------------------------------------------------------- FROM THE NEWSGROUPS *** INACCURACY IN GRAPHICS TUTORIAL IN ISSUE 10 From: kraken@mbox3.singnet.com.sg (Rory) Newsgroups: alt.lang.basic Subject: inaccuracy in Graphics Tutorial BASIX fanzine #10 Date: Mon, 30 Mar 1998 16:54:38 GMT in the aforementioned tutorial by 'Hacker' aka Damian Nikodem, he claims that POKEing straight to video memory is faster than Qbasic's PSET routine. he said, quote, "a LOT faster". hearing everyone say Qbasic's PSET is way slow for setting pixels, and that writing direct to the video memory is faster, i decided to investigate this myself. i came up with this code: 'code to benchmark speed of PSET and 'POKEing straight to video memory SCREEN 13 PRINT "Press any key to begin test." SLEEP 'fill screen pixel by pixel with PSET startPSET# = TIMER FOR col% = 0 to 15 FOR y% = 0 to 199 FOR x% = 0 to 319 PSET(x%, y%), col% NEXT x% NEXT y% NEXT col% endPSET# = TIMER 'fill screen pixel by pixel with POKE DEF SEG = &HA000 startPOKE# = TIMER FOR col% = 0 to 15 FOR y% = 0 to 199 FOR x% = 0 to 319 POKE 320& * y% + x%, col% NEXT x% NEXT y% NEXT col% endPOKE# = TIMER DEF SEG = 0 'print results PRINT "Time taken with PSET:"; endPSET# - startPSET# PRINT "Time taken with POKE:"; endPOKE# - startPOKE# ok, the times i got on my P100 for the POKE routine were never more than 1.5 seconds faster than the PSET routine. most of the time it was only 0.2 seconds faster. so POKEing direct to the video memory isn't "a LOT faster" than using PSET. what annoys me more about the article is that he never specified his variables to be of any type, therefore Qbasic defaults to type double i think, which makes the POKE routine run ridiculously slow. it took about 2.5s for the PSET routine with everything specified as short integer, and 30s for the POKE with no specifier!! does ayone see something wrong about my test code? are my conclusions correct? the only thing i see wrong is that POKE is not as fast as it can be, because i have to put an ampersand after the 320 in this line POKE 320& * y% + x%, col% otherwise Qbasic spews out an overflow error. because of the long, it's not as fast as if i was able to keep everything short. if it was possible to do this, then i believe that POKEing would be much faster the PSET. comments anyone? From: SimpleSi Newsgroups: alt.lang.basic Subject: Re: inaccuracy in Graphics Tutorial BASIX fanzine #10 Date: Mon, 30 Mar 1998 18:46:09 +0100 It all depends on the processor and graphics card that you have. With a fast processor, the multiplication is quicker, so poke is not slowed down. On my lowly P90, poke was twice as fast as pset. From: pb@excelsior.xs4all.nl (Marc van den Dikkenberg) Newsgroups: alt.lang.basic Subject: Re: inaccuracy in Graphics Tutorial BASIX fanzine #10 Date: Mon, 30 Mar 1998 19:48:11 GMT On my P200, the code you nclosed here takes 2.5 seconds for PSET, and 1.6 seconds for POKE. That's more then 1.5 times as fast... I think that qualifies as 'a lot', when things are time-critical... From: kraken@mbox3.singnet.com.sg (Rory) Newsgroups: alt.lang.basic Subject: Re: inaccuracy in Graphics Tutorial BASIX fanzine #10 Date: Tue, 31 Mar 1998 15:05:30 GMT ok, another run today gave me 7.367s for PSET and 6.695s for POKE. that's a difference of about 1.1 for me. well, since my code is just filing the screen, i shall take your word for it now, that 1.5 times is significant enough to be a LOT faster. i will try out both methods in a real world program (probably a game of some sort) and post results when i have time. unless someone would like to beat me to it. From: Krogg Newsgroups: alt.lang.basic Subject: Re: inaccuracy in Graphics Tutorial BASIX fanzine #10 Date: Mon, 30 Mar 1998 17:28:13 -0500 in qbasic on my p75 under win95 12.30 for pset 9.77 for poke in powerbasic 3.5 on my p75 under win95 Note:i used screen 7 in pb3.5 for pset and mode 13h for the poke part. Time taken with PSET: 13.2370438659709 Time taken with POKE: 1.48298831693683 looks damn faster here is a test using dim absolute scrn(319,199) as byte at &ha000 for a video buffer overlay ploting a point ="scrn(%x,%y)=%col" Time taken with PSET: 13.0722673863129 Time taken with POKE: 2.85612564742769 still faster than pset but with some inline asm code instead of the poking like ! mov ax, &ha000 ! mov es, ax ! mov ax, 320 ! mul y% ! mov bx, x% ! add bx, ax ! mov al, col% ! mov es:[bx], al ya get Time taken with PSET: 12.5230124541195 Time taken with ASM: .823882398297428 Now thats fast! now with another asm code for the poking not much diff but uses byte for color insteat of integer ! MOV AX , 320 ! MUL y% ! MOV DI , AX ! ADD DI , x% ! MOV DX , &hA000 ! MOV ES , DX ! MOV AL , col% ! STOSB Time taken with PSET: 12.4680869608928 Time taken with ASM: .823882398290152 as you can see,its allmost the same. I hope this helps someone By the way,the asm routines i got from Robert Seidel. I havent got the code handy but a faster asm code uses shifts instead of muls somehow but i will leave that for someone else to test. *** UNLOCKING GWBASIC PROGRAMS From: pb@excelsior.xs4all.nl (Marc van den Dikkenberg) Newsgroups: alt.lang.basic Subject: Re: Unlocking old BASIC programs Date: Mon, 30 Mar 1998 19:48:09 GMT On 30 Mar 1998 15:13:32 GMT, eppersond@aol.com (EppersonD) wrote: >Does anyone know how to unlock old BASIC programs. Seems like it used BLOAD >and BSAVE to do it. I have it in a manual somewhere but I moved recently and >have not been able to find it. When you try to list a program it says "Illegal >Function Call". If you're talking about protected GW-Basic programs, here are two ways to do it. It's something I found on the internet quite some time ago... Here's a trick that often works:- 1. run GWBASIC. 2. LOAD the protected program. 3. type NEW and hit return. 4. type 0'* (The * is a placeholder for CHR$(15) - so that's zero,apostrophe, ALT+15) and hit return. 5. you can now LIST and SAVE the file normally. 6. don't ask ME why it works. Failing that write your own UNPROT.BAS program as follows:- 1.Run DEBUG and type in the following E100 FF 1B RCX 2 NUNPROT.BAS W Q with a carriage return at the end of each line of course. There will be some responses from DEBUG which can be ignored except that RCX will present you with a colon input prompt instead of the usual hyphen. That gets you a file which contains only two bytes, FF and 1B. Now... 2. Run GWBASIC. 3. LOAD the program you want to unprotect. 4. LOAD UNPROT.BAS. 5. You will now find you can LIST (etc.) the original program. 6. Don't ask me why that works either. I take no credit (or blame) for either. Both are old, old tricks which have been around for so long that, it seems, they may have been forgotten. *** SORTING RANDOM NUMBERS From: crbates@cdmnet.com (Curt Bates) Newsgroups: alt.lang.basic Subject: Re: Sorting random numbers? Date: Mon, 25 May 1998 23:23:57 GMT "Retribution" wrote: >Thanks Ian but the problem lies in the generation of the numbers. I'm >generating them one at a time so no two are the same. The numbers have >already been generated by the time I need to sort them rather than >generated in a DIM statement. >So I have six numbers, no two alike that need to be sorted. The routine you >sent was however handy...thanks! > >Ian Blakeley wrote in article ><35632d1a.18028890@news.btinternet.com>... >> Hope the attached helps, some of its from a book can't remeber which >> and some I probably modified. 100 random nubers are generated, sorted >> and displayed. For six number you can use a simple bubble sort. Like this: [Ed's note - I've modified this slightly to work properly in QB, and to print the results at the end] RANDOMIZE TIMER DIM n(6) CLS ' generate 6 randem numbers FOR x = 1 TO 6 n(x) = INT(RND * 40) NEXT ' sort the numbers s = 1 WHILE s = 1 s = 0 FOR x = 1 TO 5 IF n(x) > n(x + 1) THEN t = n(x) n(x) = n(x + 1) n(x + 1) = t s = 1 END IF NEXT WEND FOR x = 1 TO 6 PRINT n(x) NEXT x Hope this helps. *** FAST KEYBOARD INPUT From: pb@excelsior.xs4all.nl (Marc van den Dikkenberg) Newsgroups: alt.lang.basic Subject: Re: HOW CAN I MAKE FAST KEYBOARD INPUT ?? Date: Tue, 12 May 1998 21:21:39 GMT On Tue, 12 May 1998 18:17:15 +0200, Jellyfish wrote: >I was wandering how I could get fast keyboard input for a game in >GW-Basic ?? I'm not sure if GW-BASIC supports this too, but... Try: A=INP(&H60) This will read a character directly from the in-port, and is a LOT faster then inkey$. It will take you some time to figure out the values it returns, though, since it's a lot less 'friendly' then INKEY$. you may need to insert A$=INKEY$ in your loop as well, though, since the method shown above won't delete the characters from the keyboard buffer. If you don't do this yourself, your computer will be beeping continuously because the buffer is full... Advantages: no delays whatsoever, extremely fast repeat rate, detect key press, and key release -- keep track of multiple keys hold down simultaneously. Disadvantages: more difficult to keep track off, and you have to find the scancodes/release codes of the various keys somewhere first. *** CLEARING THE KEYBOARD BUFFER From: c1284j@aol.com (C1284J) Newsgroups: alt.lang.basic Subject: Re: Annoying beeps Date: 22 Apr 1998 23:36:24 GMT >>anyone know how to be rid of that annoying beeping >>when one holds down a key too long in qbasic? > >That's not QBasic, it's the operating system complaining that the >keyboard >buffer is full, and all those extra keystrokes are being lost. Just let go of >the key when you hear the first beep. And here's the code to clear the keyboard buffer: DEF SEG = &H40 POKE &H1A, PEEK(&H1C) POKE &H1B, PEEK(&H1D) DEF SEG 'switch to BIOS data segment 'make the buffer head pointer 'equal then buffer tail pointer 'back to default data segment *** POKE-ING CHARACTERS TO THE SCREEN From: "Judson McClendon" Newsgroups: alt.lang.basic,comp.lang.basic.misc Subject: Re: POKE to screen? Date: Wed, 22 Apr 1998 08:33:36 -0500 KKJOHN2 wrote: >A while back someone posted code to POKE and PEEK characters to the >screen. I want to sqeeze the last bit of speed out of my text boxes >without using alot of advanced programing. >If you can help, please email me below, I sometimes can't follow these >groups. THANKX Don't know if this is faster than PRINT, but here are routines to POKE characters and strings to the screen: ' ************************************************** ' * * ' * POKE.BAS * ' * * ' * Print Character or String * ' * to screen using POKE * ' * * ' * Judson D. McClendon * ' * Sun Valley Systems * ' * 329 37th Court N.E. * ' * Birmingham, AL 35215 * ' * 205-853-8440 * ' * * ' ************************************************** ' DECLARE FUNCTION BufAdd% (Lin%, Col%) DECLARE SUB PokeByte (Lin AS INTEGER, Col AS INTEGER, Byte AS STRING) DECLARE SUB PokeString (Lin AS INTEGER, Col AS INTEGER, Str AS STRING) CLS CALL PokeString(12, 34, "Hello World!") END FUNCTION BufAdd% (Lin AS INTEGER, Col AS INTEGER) BufAdd% = (Lin - 1) * 160 + (Col - 1) * 2 END FUNCTION SUB PokeByte (Lin AS INTEGER, Col AS INTEGER, Byte AS STRING) DEF SEG = &HB800 POKE BufAdd%(Lin, Col), ASC(Byte) DEF SEG END SUB SUB PokeString (Lin AS INTEGER, Col AS INTEGER, Str AS STRING) DIM I AS INTEGER DEF SEG = &HB800 FOR I = 1 TO LEN(Str) POKE BufAdd%(Lin, Col + I - 1), ASC(MID$(Str, I, 1)) NEXT DEF SEG END SUB *** TREE/PLANT FRACTALS From: "Ali Afshar" Newsgroups: comp.lang.basic.misc Subject: Re: DRAW for plants in fishtank program Date: Fri, 24 Apr 1998 02:27:40 +0100 Steve Rush wrote in message >It's been a while since I saw any code, but search for "fractal" and "plant". >There are iterative processes that can create very realistic shapes with a few >equations. Dear Gloria I'm not sure whether this will meet your needs but here's a simple iterative SUB for creating a different number of tree/plant fractals using different initial input parameters. Any problems with the code, get in touch. REM The SUB Tree uses the initial parameters (x,y,l,a,ang,lim%,m,c) REM x is the start x REM y is the start y REM l is the initial branch length REM a is the initial angle REM ang is the angle of rotation for each successive branch REM lim% is the branch length lower limit REM m is the denominator for successive branch length REM c is the colour SUB tree (x, y, l, a, ang, lim%, m, c) IF l > lim% THEN LINE (x, y)-(x + l * COS(a), y + l * SIN(a)), c LINE (x, y)-(x + l * COS(a), y + l * SIN(a)), c CALL tree(x + l * COS(a), y + l * SIN(a), l / m, a + ang, ang, lim%, m, c) CALL tree(x + l * COS(a), y + l * SIN(a), l / m, a - ang, ang, lim%, m, c) END IF END SUB DECLARE SUB tree (x!, y!, l!, a!, ang!, lim%, m!, c!) REM A simple tree fractal by Ali Afshar SCREEN 13 CONST pi = 3.141593 WINDOW (-200, 0)-(200, 400) REM This example draws a big white tree CALL tree(0, 0, 50, pi / 2, pi / 6, 5, 1.3, 15) REM This example draws 4 trees all at a pi/3 angle in random colours FOR a = -100 TO 100 STEP 50 CALL tree(a, 0, 20, pi / 3, pi / 12, 5, 1.4, RND * 15) NEXT a END Cheers, Ali ** USING THE MOUSE IN QBASIC From: steverush@aol.com (Steve Rush) Newsgroups: comp.lang.basic.misc Subject: Re: Mouse in Qbasic? Date: 10 Apr 1998 00:42:30 GMT >In QBasic (not QB45), the ONLY way to call an interrupt is to >write a little machine language program and then: > > 1) call ABSOLUTE to > 2) call the machine language program, which > 3) calls the interrupt, which > 4) sends your message to the mouse driver. I use QuickBasic 4.5, but I a have experimented with the CALL ABSOLUTE method of calling the mouse driver. You don't need a machine-language program. You can call the mouse driver directly with CALL ABSOLUTE if you know where to call. You can PEEK this address out of the interrupt table at 0000:(&H33 * 4). The first two bytes at this address are the segment, followed by the offset. It takes a little fiddling to combine the two one-byte PEEK values if the segment or the offset is greater than &H7FFF, because the obvious MouseSeg% = PEEK(TablePtr%) + PEEK(TablePtr% + 1) * 256 MouseDvr% = PEEK(TablePtr% + 2) + PEEK(TablePtr% + 3) * 256 will produce an integer overflow. It should work if MouseSeg and MouseDvr are LONG integers, but I think you have to convert them to 16-bit integers for use by DEF SEG and ABSOLUTE. You then use DEF SEG MouseSeg CALL ABSOLUTE (m1, m2, m3, m4, MouseDvr) Where m1..m4 are the four parameters to the mouse driver. You always have to supply all four, and they should be variables, not runtime expressions, because many mouse functions return data by overwriting the parameters. *** QBASIC COMMUNICATIONS From: xxx.spagtime@compuserve.com [Ed: poss. anti-spam measure? Maybe spagtime@compuserve.com ?] (Spag) Newsgroups: comp.lang.basic.misc Subject: Re: Qbasic Communications Date: 29 Mar 1998 08:34:09 GMT "Michael Beck" wrote: > I've just spent 5 hours trying to get two computers some what talking > to each other. I would really like to know how to write modem or > serial games in qbasic. If anyone would please direct me to a good > communications tutorial, I would greatly appreciate it. > Thanks for anything. Have a go with this, hope it helps Plumb the 2 machines together with a 'null modem' cable, then run the following Qbasic prog on each. (it helps if you can see both monitors!!) CLS OPEN "COM2:9600,N,8,1,lf,cs,ds,cd,op,rs" FOR RANDOM AS #1 COM(2) ON ON COM(2) GOSUB rxflag DO key$ = INKEY$ IF key$ <> "" THEN PRINT #1, key$ IF rxflag = 1 THEN GOSUB getmessage LOOP UNTIL key$ = "!" END rxflag: rxflag = 1 RETURN getmessage: rxflag = 0 DO UNTIL EOF(1) rxchar$ = INPUT$(1, #1) IF ASC(rxchar$) < 32 OR ASC(rxchar$) > 128 THEN rxchar$ = "" PRINT rxchar$; LOOP RETURN ------------------------------------------------------------------------------- INTERNET RESOURCES GETTING THE FANZINE: - Website The Basix Fanzine: http://come.to/basixfanzine The "Basix Fanzine Interactive Library" at GeoCities has been taken down. The site no longer exists at all and may soon be reoccupied by somebody else. - Newsgroups The Basix Fanzine is posted when it is released to alt.lang.basic, alt.lang.powerbasic, comp.lang.basic.misc and microsoft.public.basic.dos. If you want it posted to any other BASIC newsgroups then please let me know. - Mailing List To get the Fanzines as they are released, join Tony Relyea's mailing list by sending an email to the new address of fanzine@vt.edu with subject "subscribe basix-fanzine". This mailing list is completely separate from the Basix Fanzine so I cannot accept responsibility for any emails you do - or don't - get when you join the mailing list. USEFUL BASIC WEBSITES: Compilers -PowerBASIC (also FirstBasic, PB/DLL and PB/CC): http://www.powerbasic.com -EMS (old MS compilers): http://www.wdn.com/ems/oldtools/ms.htm -Provantage (old MS compilers): http://www.provantage.com BASIC code NEW LINK! The Beginners Basic Homepage: http://www.users.globalnet.co.uk/~basic/ -QBasic.com: http://www.qbasic.com -The Programmer's Page: http://www.professionals.com/~peterp/ -ABC Packets (bi-monthly collections of useful BASIC code): http://come.to/abcpackets -PowerBasic Archives: http://pitel-lnx.ibk.fnt.hvu.nl/~excel/pb.html -Tim's QuickBasic Page: http://www.geocities.com/SiliconValley/Heights/8967/ -QBasic Programming Corner: http://www.geocities.com/TheTropics/9964/qbasic.html -The QBasic Site: http://hem.passagen.se/hedsen -DMAPLAYH.BAS (WAV player): http://www.ocf.berkeley.edu/~horie/project.html -The QuickBasic Enhanced Programming Page: http://www.geocities.com/SiliconValley/Lakes/7303/ -Blood 225's BASIC stuff: ftp://users.aol.com/blood225 -Alex Warren's BASIC page: http://www.users.globalnet.co.uk/~dewarr/basic.htm Libraries -Zephyr Software (SVGA Library): http://www.zephyrsoftware.com -MOD Library: http://www.fortunecity.com/millenium/celesteville/23/index.html -QMIDI (MIDI player): http://www.primenet.com/~merlin57/qmidi/ Reference -Ralf Brown's Interrupt List: http://www.ctyme.com/rbrown.htm NEW LINK! Programmers' File Formats Collection: Europe: http://www.wotsit.demon.co.uk · US: http://wotsit.simsware.com NEW URL Basix Fanzine: http://come.to/basixfanzine -PCGPE (PC Games Programmers' Encyclopaedia): ftp://x2ftp.oulu.fi/pub/msdos/programming/gpe Want to nominate one of these sites - or your own - for an award? See the Basix Fanzine Awards 1998 News Article in issue 11. Add your site to this list by sending an email to basix@dewarr.globalnet.co.uk. Also, if any of the sites disappear or move address, please send an email so I can update this page when I release the next issue. USEFUL BASIC NEWSGROUPS: alt.lang.basic alt.lang.powerbasic comp.lang.basic.misc microsoft.public.basic.dos soon... perhaps comp.lang.basic.powerbasic - see the news article in issue 11. GETTING HOLD OF BASIC: QBasic is free and available on DOS disks (versions 5.0 and up), the Windows 95/98 CDROMs, and within http://www.microsoft.com/windows/download/olddos.exe You can order new and used copies of QuickBASIC, PDS 7.x, VBDOS, etc. online at http://www.wdn.com/ems/oldtools/ms.htm and http://www.provantage.com And you can buy PowerBasic, FirstBasic and other products from http://www.powerbasic.com. If you want to compile very few programs which have been made using QBasic, and don't want to shell out $110 for PB, you can pay $5 a time for compiling at http://members.aol.com/qbasicnow. Email qbasicnow@aol.com for more information on this. QB4.5 can be also be downloaded illegally from various websites. The Basix Fanzine does not accept responsibility for any damage caused to your computer or your life through downloading illegal software. If you know of any other compilers or would like to advertise yours here, send an email to basix@dewarr.globalnet.co.uk, or post a message on one of the BASIC newsgroups and hope that I see it. ------------------------------------------------------------------------------- HOW TO CONTRIBUTE The Basix Fanzine relies on the contributions it recieves from ordinary people like you - so, if you have an article or some source code you'd like to sumbit, please email it to basix@dewarr.globalnet.co.uk - all contributions very gratefully recieved, so thanks in advance. If you have any questions about submitting an article - for example, if you want to make sure that your article will be suitable - please do not hesitate to ask! Contributions are accepted in a number of formats. The preferred format is HTML although you can submit documents in Word format, or just in plain old text format if you prefer. Feel free to include graphics - the new format of the fanzine means it is now easy to accommadate them, provided they are not too big! Please note that some of the formatting in your article may be changed to keep it consistent with the rest of the fanzine. In some cases files may be included separately (i.e. articles may not be pasted straight in to the issue's HTM file, they may be just put in with a hyperlink) It helps me a lot if the font you use in articles is Verdana and any code is indented in Courier New, in the same way as in exisiting HTML issues. Please also refrain from ASCII art as it tends to bugger up when converted to HTML! Articles and source code submitted becomes the joint copyright property of the author and the Basix Fanzine. This means that both the author of the article and the Basix Fanzine have the right to redistribute the article/source. Remember - no articles, no fanzine :( Please note that the editor reserves the right to edit or omit articles and source code recieved for inclusion in the Basix Fanzine. ------------------------------------------------------------------------------- FINAL WORDS Thanks to the following for contributing articles and source code for this issue: - "Hacker" aka Damian Nikodem - Rude John, aka "The Tripods". Though who knows what his real name might be... ...and to the following for their newsgroup posts that I've reproduced here: - Rory - "SimpleSi" - Marc van den Dikkenberg - "Krogg" - Curt Bates - "C1284J" - Judson McClendon - Ali Afshar - Steve Rush - "Spag" NEXT ISSUE: Well, who can tell... Rude John's column will be back... more informative posts from the newsgroups... plus the latest news - all in a new, brighter, more enlightening form guaranteed to bring you ever-lasting joy and happiness. Probably. I hope to release the next issue as soon as I get enough contributions - hopefully before October 1998. If you'd like to see it sooner, why not contribute an article? Go on - you know you want to. I hope you've enjoyed the issue and have found it useful. Hopefully the new HTML format has made the issue easier to read and easier to use code from. Until next time... goodbye! Alex Warren, 27th June 1998