Copy Link
Add to Bookmark
Report
The Basix Fanzine issue 15
The BASIX Fanzine ISSUE #14 - MAY 1999
Text Issue Edited By David Groulx
------------------------------------------------------------------------
Contents
------------------------------------------------------------------------
Tutorials Modular Programming
Structured Programming
Your Text Line
Programs Text Stars
QBasic Sprite Editor v0.21
Misc & 2000 Website Awards
Credits Credits
Contact Info
------------------------------------------------------------------------
Tutorials
------------------------------------------------------------------------
---------------------------------------------------------
Modular Programming
By David Tordrup
---------------------------------------------------------
Contents:
1.0 Introduction
2.0 The basics of modular programming
2.1 Subs
2.2 Functions
2.3 Where to use SUBs and FUNCTIONs
3.0 Going into the details of subs
3.1 Getting data into modules
3.2 Global Variables
3.3 Module Interface
3.4 Commands you can't use in a sub
4.0 Going into the details of Functions
4.1 Using functions for calculations
5.0 Summary
6.0 Closing words
1.0 Introduction
When writing large programs in QBasic, it is vital for the debugging
process that the flow of the program can be understood easily. If GOTO and
GOSUB is used a lot, the program acquires a spaghettilike structure, making
it extremely hard to track down and remove bugs in the source. This is why
modular programming is such a good idea. This particular method of
programming lets you divide your program into smaller tasks that can be
solved individually, creating an easy-to-read source code, that also takes
less time to debug.
2.0 The basics of modular programming
First of all, a module is a chunk of code within a program that performs a
specific task, and typically has a name that describes that task. A module
that collects input from the user should therefore be called "GetInput" or
something along those lines, but all valid variablenames can be used.
Giving the module a descriptive name does however make it easier for
yourself and other people to understand the source. There are two types of
modules: SUBs and FUNCTIONs. These two terms will be used throughout the
tutorial, so here's a brief description of them:
2.1 Subs
SUB is short for 'Subroutine'. A SUB may contain most QBasic statements,
although some statements may be used only in the main code. SUBs are useful
when you need several lines of code to complete a single task, for example
when printing help-screens for the user, or creating windows. All variables
used in a SUB are local, which means they start at 0 or null every time the
SUB is called, unless they are dimensioned as global variables in the main
code (more on this later). You can also pass variables from the main code
to the SUB, or define the SUB as STATIC, which means the variables are not
reset when control is passed back to the main code. Don't worry if you
don't understand the above at this point, everything will be explained in
detail later.
2.2 Functions
Functions are just that. They are used in the same manner as 'built-in'
QBasic functions such as RND (random number), SQR (square root) or LEN
(length of a string). In general, if you can type Variable =
FunctionName(arguments) and get away with it, it's a function. This is
where FUNCTIONs are different from SUBs, a sub is called like a normal
QBasic statement, and functions are either assigned a variable (a% =
FunctionName) or used to determine the outcome of a true/false expression
(IF YesNo$ = "N" THEN...)
2.3 Where to use SUBs and FUNCTIONs
Subs and functions may appear alike, but their uses are completely
different. The following paragraph should give you some idea of when to use
subs and when to use functions. A sub is best suited for a routine that
requires little interaction with the main code, meaning that the outcome of
any calculation in the sub shouldn't bear any significance to the main
module. Functions on the other hand often provide a new value to a variable
in the main code, and are therefore best suited for long or complicated
calculations. In short, use subs for long routines and functions for long
calculations.
3.0 Going into the details of SUBs
A new sub is created by typing 'SUB SubName' whereafter you get a blank
screen with the words 'SUB SubName' and 'END SUB'. The statements in your
module go between these two lines of code. It is considered good
programming practice to add a few comment lines to the top of the sub
describing what it does and what variables from the main code are used
and/or changed.
3.1 Getting data into modules
As mentioned before, all variables used in subs and functions are local to
the module, which means they are reset when command is passed back to the
main code, unless the sub is defined as STATIC. If you want to use
variables/strings from your main code in the module, you have two options:
Global variables or the module interface.
3.2 Global Variables
If a variable or string is defined as global, it can be used and changed in
all levels of the program (in all modules). This kind of variable is
essential for large programs that use many variables for controlling the
programflow. A global variable is defined in the main code with the
following statement:
DIM SHARED Varible, Variable1..... AS TypeA, Variable2 AS TypeB
Where Type is the type of variable you are currently defining, e.g.
Integer, String, Single-precision etc. (See the QBasic help for more info
on variable types). When using global variables you are also forced to plan
your program ahead before typing the actual code. This often makes the
result of your efforts much better, as your code will be efficient and
easily readable if you have planned it properly. Constants defined with the
CONST ConstName = Value% statement are also global and may be used in all
subs and functions without any bother.
3.3 Module Interface
An alternative to global variables is using the module interface. This has
advantages over global variables in some cases, but naturally it is also
inferior on some points. If you are writing a sub that performs a task
based on the data passed to it from the main code, a box drawing routine
for example, you would use the module interface for data-interchange. This
way you could get away with typing something like 'DrawBox (1, 1, 20, 20)'
instead of having to give four global variables a new value and then
calling the sub. The module interface is created by adding a set of
paranthesis () to the 'SUB SubName' statement:
SUB SubName (Variable%, String$)
It is now possible to pass two variables to the module (an integer and a
string), by typing
SubName a%, s$
In the sub, the variables a% and s$ will be known as Variable% and String$
as stated in the SUB SubName statement. You can have any amount of
variables of any kind passed to a module. This does mean however, that
every time the module is called, the correct number of arguments must be
passed to it. This is partly where this method is inferior to global
variables. When calling a sub with arguments, you must make sure that you
type the arguments in the correct order, according to the declaration of
the sub, and that the arguments passed are of the right datatype (integer,
string etc).
3.4 Commands you can't use in a sub
There are certain commands and statements that won't work within a module.
This doesn't mean that there are some tasks that can't be solved with
modules, just that you need to add a little extra code to your main
program. The most commonly used statements that aren't allowed in modules
are as follows:
CONST - For defining global constants
DIM SHARED - For defining global variables
DECLARE SUB / FUNCTION - For initiating a module in a program
DATA - Data statements can only be put in the main code
You might very well encounter other statements and commands that are
illegal in modules, just try moving them into the main code.
4.0 Going into the details of Functions
A function is created in the same manner as a sub, by typing the following
statement: FUNCTION FunctionName (ArgumentList%) Same as before, you now
have a blank screen with the name of the function and the END FUNCTION
statement. There are two main uses for functions. The first is calculating
a new value based on the arguments passed to the function in the arguments
list, the second is reading or calculating a new value based on input from
the user or gathered from an input device.
4.1 Using functions for calculations
This is really practical if you need a fixed number of operations done on a
variable from the main code. You could have a function that converted a
string input from the user to all uppercase, no commas and no left/right
spaces. This could be useful in a text-adventure for example. You would do
this by typing the formatting code into your function module, using the
name of the module as your stringname:
FUNCTION ConvString$(Text$)
ConvString$ = UCASE$(ConvString$) ' Convert to uppercase
ConvString$ = LTRIM$(RTRIM$(ConvString$)) ' Remove left/right spaces
.....
END FUNCTION
In the main code you would put something like
INPUT Text$
Text$ = ConvString$(Text$)
Whereafter your string Text$ will be changed to whatever your sub does to
it. It saves a lot of space in the main module, and makes it easier to
read.
4.2 Using functions for getting new values
A function may also be used to create a new variable. This means that the
function collects input from the keyboard or another input device, and
gives it to the variable in question. For example, if we wanted to create a
function that waited for a single keypress from the user, printed it on the
screen, and passed control back to the main program, we would have
something like this:
FUNCTION GetChar$
DO ' Start reading-loop
Temp$ = INKEY$ ' Read the keyboard
LOOP UNTIL Temp$ <> "" ' Loop until key pressed
PRINT Temp$ ' Print the character on screen
GetChar$ = Temp$ ' Pass the value to the main program
END FUNCTION
And in the main code:
KeyPress$ = GetChar$
Again, this drastically shortens our code and makes it easier to get a
single character from the user. (Strictly, the above example is obselete as
it could be substituted with the INPUT$ function) It is vital that you
include the line 'FunctionName = Value' at some point in the function,
otherwise the main program will recieve the value 0 or null from the
function. In the example we have put GetChar$ = Temp$ to pass the value of
Temp$ to the main program.
5.0 Summary
We have (hopefully) learnt that modular programming has many advantages
over normal programming, the biggest being the improved readability of the
source code. In reality, all there is to modular programming is deviding
your program into tasks and figuring out the best way to interchange data
between the modules. Modules are written in the same manner as normal
QBasic programs, although some statements are illegal in modules (you'll
know when you try to use them).
6.0 Closing words
I hope this tutorial has given you some ideas for structuring your programs
in a functional way, modular programming really is the most important
aspect of structured programming. I encourage you to use modules in your
programs, as you will probably be able to use some of them again in later
projects. Eventually you will have built up your own set of QBasic commands
to be used over and over again, saving you precious time and letting you
concentrate on the important parts of your program. Thank you for reading
this tutorial.
Suggested reading: Structured Programming
---------------------------------------------------------
Structured Programming
By David Tordrup
---------------------------------------------------------
1.0 Introduction
There is more to good programming than learning the keywords and commands
of a language like QBasic, there is also a more abstract aspect known as
Structured Programming. If you want to write long and complicated code that
does more than print "Hello World!" on the screen, the spaghettistructure
of old fashioned BASIC just won't cut it anymore. People who have been
mucking about with GWBASIC and BASICA will know what I mean, but the days
of line numbering are over, say hello to Structured Programming!
QBasic is the prime version of BASIC and gives us quite a few advantages
over previous releases. For example, the source editor now consists of a
user- friendly text editor with many functions, a great modular level
interface and extensive on-line help. So why not use all this great stuff
to it's full extent? This tutorial will help you take advantage of the many
features QBasic offers to programmers today, so read on...
1.1 the project
During this tutorial a simple address database program will be constructed
step by step. With each step the necessary keywords and statements will be
introduced and explained, hopefully giving you a better picture of just how
everything works in reality. Thanks to Larry (nonnypa@gte.net) for
suggesting This approach.
2.0 The backbone of structured programming
One of the major elements of structured programming is planning your
program before you actually write it, e.g. you know in advance roughly what
your program will do, which variables and datastructures it will use, and
what the outcome will be. Starting to write a program without having at
least considered how it will work will often result in disasterous code.
Let's take a brief look at what our database program will contain:
* A datatype for our addresses
* Procedure to write new addresses to the file
* Procedure to retrieve addresses from the file
As you can see, an overview of the final program makes everything look very
simple and easily done. That's because it is, when you have a general plan
of your program worked out offhand, the rest is just a matter of applying
the correct QBasic commands and statements. To make this task even easier
we use pseudo code. This is basically a detailed version of the above
overview that let's us plan the structure of each individual routine in the
program before we get down and dirty with the QBasic commands. Some
programmers use a certain structure and layout for their pseudo code, the
official pseudo code, but this is not at all necessary as long as you know
what is going on in your preview. Consider the following code:
'{ Initialize Datatype }
'{ Allocate memory for records }
'{ Open random access file for read/write }
'
' Option 'Browse Records/Enter new record'
'
' Procedure 'Browse Records'
' { Get records from file }
' { Display records }
' end procedure
'
' Procedure 'Enter new record'
' { Get input for new record }
' { Append record to file }
' end procedure
'
'{ End program }
Make any sense? It should do, it's just a plain English version of the
QBasic program still to be written. We will use the above code in the
example source code (SPEX10.BAS) for the address database. Concluding this
short chapter, pseudo code can and should be used to create the framework
of your program before you indulge in the code writing process. It lets you
maintain a constant overview of your program, helps you target your
concentration on one task and gives you something to stick to when
everything goes wrong (and it will). In short, use it!
3.0 Creating userdefined datatypes
What is a datatype? Datatypes are things like Integers (16-bit numbers),
Long Integers (32-bit numbers), Single Precision digits etc, in general
just types of data. If you are working with arrays (stacks) of conventional
data, you will only be able to manipulate one array at a time, for example:
A%(2) = A%(1)
B%(2) = B%(1)
C%(2) = C%(1)
etc...
A bit unhandy if you have more than 2 or 3 arrays to manipulate. The is
where userdefined datatypes enters the scene. These datatypes can contain
several different kinds of datatypes all in one element, letting you easily
handle stacks of data:
TYPE variables
A AS INTEGER
B AS INTEGER
C AS INTEGER
END TYPE
DIM TestArray AS variables
TestArray(2) = TestArray(1)
The last line of code performed the exact same action as the 3 lines of
code in the previous example, pretty slick huh? This can save you a LOT of
time and bother and will even make your code look much neater. Building a
usertype like this involves three steps: finding out what kind of data you
need, making a type for it and allocating memory for that type. Pretty
simple. The TYPE statement that defines the usertype works like this:
TYPE TypeName
Element1 AS Type
Element2 AS Type
.....
END TYPE
TYPE is the actual QBasic statement, followed by the name of the datatype
you're creating. Next comes a list of all the elements in your datatype,
these elements can be:
INTEGER A 16-bit signed integer variable
LONG A 32-bit signed integer variable.
SINGLE A single-precision 32-bit floating-point variable.
DOUBLE A double-precision 64-bit floating-point variable.
STRING n% A fixed-length string variable n% bytes long.
And yes, you can have different types of data in your type, all types of
data will fit together in one type, great eh? This feature of QBasic can be
handy in a lot of situations that require moving and rearranging large
amounts of data, like in our database program. So guess what? We're going
to create a usertype for our database right now. As it's an adress database
we will of course want to store a persons name, addresse, city/state and an
optional phone number. Our datatype:
TYPE RecordType
Names AS STRING * 25
Address AS STRING * 25
City AS STRING * 25
PhoneNr AS STRING * 10
END TYPE
We could also have added other elements such as Integers for peoples ages,
but since most of your friends probably won't tell you how old they are
we'll leave this one out. So now we have a usertype for all our addresses.
Next step is to allocate data for the usertype. It's not a variable until
we take this step. We use the DIM statement for this (most people think DIM
is only for arrays, don't listen to them):
DIM Record AS RecordType
And voila! We now have a fully functional variable called Record to use in
our program, but the smart-alec reader will have noticed that we have four
elements in a variable with only one name. Correct, to get around this we
add a period (.) after Record followed by the name of the field we want to
access, and we have a regular variable: Record.Names is the name of the
first field, Record.Address is the second etc. That's all there is to know
about usertypes. You will notice when reading the source code of the
example program (SPEX10.BAS) that a whole record holding a persons name,
address, city and phone number can be written to a random access file with
one statement, beats using conventional arrays doesn't it?
4.0 Using loops
Loops stick the routines of your source together to a working program. It
is important to learn how to handle these loops in an efficient manner, as
well-structured loops make your program work better. There are several
kinds of loops available to us in QBasic, most of which you probably know
about already, but following this I will try to explain the in's and out's
of all the looping techniques you need in your programs.
4.1 DO .... LOOP
This looping method wasn't possible in early versions of BASIC that
required line numbers, so it is a fairly new addition to the language. This
kind of loop lets you handle conditional jumps in a very efficient manner,
as you can either add your condition to the DO or the LOOP statement. This
first example checks the status of the keyboard string before it even
starts the loop, so the loop may not even be executed once if a key has
been pressed prior to the routine:
DO UNTIL INKEY$ <> ""
PRINT "No key pressed"
LOOP
On the other hand, if you need your loop executed at least once you can add
the condition to the LOOP statement:
DO
PRINT "No key pressed"
LOOP UNTIL INKEY$ <> ""
Get the general picture? This is however not the only way of using the
DO...LOOP method, you can also use boolean (true/false) operators to
control your condition. Inserting AND between conditions will only
interfere with the loop if all the conditions are true (non-zero), using OR
will react when either one of your conditions is true:
DO UNTIL INKEY$ <> "" AND A% > 0
- This will exit the loop only when both
conditions after the UNTIL keyword are
true (non-zero)
DO UNTIL INKEY$ <> "" OR A% > 0
- This will exit the loop when either one
of the statements are true.
There is another keyword you can add to your condition, which is NOT. This
word reverses the meaning of the following boolean statement... confused? I
know I am, but let's try with an example:
DO UNTIL NOT INKEY$ <> ""
This breaks the loop when a key IS NOT pressed, e.g. when the conditional
statement is false rather than true. You can use several NOTs in your
condition:
DO UNTIL NOT INKEY$ <> "" AND NOT A% > 0
This will break the loop if no key is pressed and the variable a% is less
than or equal to zero. Alternatively, you can completely leave out the
condition at the front or end of the loop, and use EXIT DO within the loop
to terminate the routine:
DO
IF INKEY$ <> "" THEN EXIT DO
PRINT "No key pressed"
LOOP
I know all this must sound confusing, but then again you're not supposed to
learn everything from this tutorial. Once you have tried using the
different loops and conditions it will all make perfect sense to you, I
promise.
4.2 FOR ... NEXT
Most people starting off in QBasic have made that great program that prints
your name all over the screen 100 times, haven't you? If you did you
probably used a FOR ... NEXT loop, which is what we'll discuss here. This
kind of loop is what you use when you need a routine executed a fixed
amount of times (e.g. printing your name all over the screen). The syntax
is very simple:
FOR Variable% = LowerBound TO UpperBound [STEP Difference]
....
NEXT Variable%
Variable% is the variable that will be increased or decreased every time
the interpreter reaches the NEXT statement. Simple eh? For the names
program it works like this:
FOR a% = 1 TO 100
PRINT "Your Name Here"
NEXT a%
This example doesn't actually use the variable a% in it's operation, so
it's not necessary to do anything fancy like STEPping. The routine will
just loop one hundred times and pass control to the next statement. If, on
the other hand, you want to use this loop for something constructive, you
might need to use the STEP keyword. This let's you increase the variable by
more than one at every loop, or even make the counting go backwards. Let's
say we want to print a dot at every fifth position on the screen:
FOR Dot% = 1 TO 80 STEP 5
LOCATE 1, Dot%
PRINT "."
NEXT Dot%
That example is of course completely useless, but illustrates how to use
the STEP keyword. If you want to decrease your variable on every count,
STEP must be followed by a negative number and the low bound must be
greater than the high bound, for example...
FOR Dot% = 80 TO 1 STEP -5
LOCATE 1, Dot%
PRINT "."
NEXT Dot%
Another useless example that just prints the same dots on the screen
starting from the right instead of the left. But what if you want to fill
the whole screen with dots? Unlikely, but let's say you do. Writing 25
statements, one for each row, would take some time to accomplish, which is
why we can do nested loops:
FOR Row% = 1 TO 25
FOR Col% = 1 TO 80
LOCATE Row%, Col%
PRINT "."
NEXT Col%
Next Row%
This would execute the inner loop 25 times, one time for each row on the
screen. Beats copying and pasting the loop 25 times eh? You can nest as
many loops as you like, but keep in mind that QBasic can be rather slow in
it's calculations. The two loops in the example alone cause the interpreter
to loop a total of 25*80 = 2000 times, nesting an additional loop of 15
would make a total of 30.000 loops, and quite frankly that just wouldn't
work. This is also a part of structured programming, avoiding unnecessary
loops and moving time-critical operations outside of loops, but this will
all come clear to you when you try to calculate a sine value 2000 times in
a loop. (Ok, so we could have used the STRING$ statement instead of looping
an additional 80 times, but it's just an example, right? :)
You may also have noticed that the examples have extra blank spaces on the
left every time a new loop is started, this is not necessary but really
makes your code much easier to read, and not doing this will generally
cause programmers to frown at you, so make it a habbit!
5.0 Conditions and branching
Without conditions your program would output the exact same data every time
it was run, pretty boring, but fortunately we have two methods of branching
off to subroutines in our programs, making them a little more fun to work
with. Both methods have their advantages and disadvantages, so consider
which one you'll use in your program, it's a part of structured
programming!
5.1 IF .... THEN logic
You may have worked with this kind of condiditional branching previously,
it is quite simple and effective. Basically, using IF THEN logic will cause
your program to branch off to a subroutine when a condition is met, and
another if the condition is not met. This is IF THEN in it's simplest form,
and works like this:
IF Condition THEN .......
The condition works the same way as in DO LOOP loops (chapter 4.1). If the
condition is met, the statement after the THEN keyword is executed. Simple.
But what if it is not met? We could add a subsequent IF THEN statement with
the opposite condition, telling the program what to do in that case. Or
even better we can add the ELSE keyword to the existing statement:
IF Condition THEN ......... ELSE ......
The statement after the ELSE keyword will be executed if the condition is
not met. Now only one problem remains, what if we want to branch off to an
entire subroutine instead of just one statement? For this we have block IF
THEN's:
IF Condition1 THEN
......
ELSEIF Condition2
......
ELSE
......
END IF
These work basically the same way as the basic IF THEN statement, except
you can type several lines of code for each condition. The ELSEIF keyword
lets you specify multiple conditions in the same IF THEN block, handy for
things like menuhandling:
IF Choice% = 1 THEN
{ Subroutine for #1 here }
ELSEIF Choice% = 2 THEN
{ Subroutine for #2 here }
ELSEIF .......
ELSE
{ Subroutine for invalid choice here }
END IF
This brings us on to the next conditional jump method, CASE....
5.2 SELECT CASE
This is the modern and trendy version of IF THEN statements. This method
offers some advantages over IF THEN as you will discover in a short while.
The syntax for SELECT CASE is:
SELECT CASE Variable%
CASE Comparison1%
.......
.......
CASE Comparison2%
.......
.......
CASE ELSE
.......
.......
END SELECT
Variable% is of course the variable you will be handling in your
conditions. After each CASE keyword, a string or variable is placed that is
compared to the Variable%. If these two match, the routine after the CASE
keyword is executed. You can even specify several matches for the same
routine, for example you could make "A" and "a" do the same thing, which
would also be pretty handy for a letter-operated menusystem. This is done
by seperating the comparison variables with commas:
CASE "A", "a"
.......
Pretty simple too when you get the hang of it. Again, additional spaces
have been added to the left of the CASE keywords to make the code easy to
read and understand. SELECT CASE is typically used when there are several
possible subroutines that can be branched off to, it's simply more
convenient than a huge block of IF THEN's. And convenience is what we're
looking for.
6.0 Your comments please
Everybody knows what they are, but nobody uses them when they should. The
use of comments in your source can really speed up the understanding of the
code. At a glance you can see what all your routines do without having to
understand all the calculations and variables used within, pretty slick eh?
But then again we don't want to overdo it, placing a comment after a PRINT
statement saying 'This prints the text blablabla to the screen' is just a
waste of time. On the other hand, the following statement might need some
explaining:
a = (SIN(A1) * c) / SIN(C1)
When confronted with a statement like this you'd have to look back in the
program to find out what the variables represent, and then take a look at
the calculation itself to find out what the program is doing to them.
Adding a few comments, however, might ease the pain a little...
' Following routine calculates the side A of a triangle ABC, using
' the side c and the angles A1 and C1. Angles A1 and C1 are 124 and 10
' respectively, side c is 13...
a = (SIN(A1) * c) / SIN(C1) ' Calculate size of side a
The example wouldn't have been too hard to figure out without comments, but
routines consisting of several pages of code tend to get a bit harder, so
use comments when something needs explaining, other programmers will
respect you for it.
7.0 Name your Variables
This is a very short but important chapter. As you might have guessed, it's
all about naming your variables in a way that makes your code easier to
understand to yourself and others. We get to use as many characters as we
like (almost) in our variable names, but too much of anything isn't good
for the baby, the variable MainLoopIterationCounter is a legal variable,
but it takes time to type and is prone to misspelling. You get the idea.
Something like MainLoop would be sufficient and understandable.
You can also add little signs to the end of your variables indicating what
type of variables they are. As mentioned earlier, there are five types of
variables, and five corresponding signs:
Integers %
Long
Integers &
Single
Precision !
Double
Precision #
Strings $
So, the variable a% is an integer, a! a long integer etc. etc. Using these
signs saves memory and can make program execution faster, so by all means
get used to it, it's a good programming habit.
8.0 Closing words
Naturally you won't be a perfect programmer after reading this, it takes
days and weeks and months to learn how to structure your programs in the
most convenient manner, but throughout the document you've been introduced
to several important techniques and methods that could make programming
easier and a lot more fun in the future. I hope you enjoyed reading this as
much as I enjoyed typing it, please feel free to contact me if you have any
questions regarding anything with QBasic on dtordrup@mail1.stofanet.dk.
Thanks to Larry (nonnypa@gte.net) for helping out with the testing of this
tutorial, without his advice and constructive criticism it would be even
more confusing than it is now ;)
Suggested reading: Modular Programming
'
'
' Structured Programming Example
'
' By
'
' David Tordrup of The QBasic Team
'
' Copyright (C) 1997
'
'
'
' This code is very basic but functional. It could be improved in many
' ways, but as it only serves as an example of structured programming,
' the optimizing has been left out. You may however do to it whatever
' you like, use it in your own programs, whatever you see fit.
'
' This program was written in 6 minutes and debugged in 1 step.
'
' The pseudo code for our DataBase program:
'
'{ Initialize Datatype }
'{ Allocate memory for records }
'{ Open random access file for read/write }
'
' Option 'Browse Records/Enter new record'
'
' Procedure 'Browse Records'
' { Get records from file }
' { Display records }
' end procedure
'
' Procedure 'Enter new record'
' { Get input for new record }
' { Append record to file }
' end procedure
'
'{ End program }
'
'And the QBasic program:
TYPE RecordType
Names AS STRING * 25
Address AS STRING * 25
City AS STRING * 25
PhoneNr AS STRING * 10
END TYPE
DIM Record AS RecordType
OPEN "\RECORDS.DAT" FOR RANDOM AS #1 LEN = LEN(Record)
DO
CLS
INPUT "(N)ew / (D)isplay records / (Q)uit?", VD$
SELECT CASE VD$ ' Routines for viewing/displaying
CASE "N", "n"
CLS
INPUT "Name: ", Record.Names
INPUT "Address: ", Record.Address
INPUT "City/State:", Record.City
INPUT "Telephone: ", Record.PhoneNr
PUT #1, LOF(1) / LEN(Record) + 1, Record
CASE "D", "d"
FOR a% = 1 TO LOF(1) / LEN(Record)
GET #1, a%, Record
PRINT Record.Names
PRINT Record.Address
PRINT Record.City
PRINT Record.PhoneNr
NEXT a%
SLEEP
CASE "Q", "q"
Done% = 1
CASE ELSE
END SELECT
LOOP UNTIL Done%
------------------------------------------------------------------------
Your Programs
------------------------------------------------------------------------
---------------------------------------------------------
TEXT Programs
By Buzz
---------------------------------------------------------
--------------------------------------
Text Line
--------------------------------------
DECLARE SUB fastcharput (y!, x!, a$, vg!, ag!)
DECLARE SUB txtLINE (x1!, y1!, x2!, y2!, col!, res!)
' this is a routine of the first demo of BaseD
' it's supposed to be a txt LINE routine that has speed
' and has weird stuff
' demonstrate
RANDOMIZE TIMER
WIDTH 80, 50
' this cube rotation stuff is just copied of phobia's 3D_FLAM.BAS
' it wasn't such an important part of that program , so I ripped it
' i also added lots of extra stuff to it , such as the random movement
' and better support for text-mode.
xoff = 40 ' Center of the cube in x
yoff = 25 ' Same but in y
zoff = 25 ' Same but in z
xang = 0 ' Don't change these... they control the rotation.
yang = 0
zang = 0
xadd = .01
yadd = .01
zadd = .01
randiv = 8
points = 8 ' Amount of vertexes in the cube...
DIM cube(points * 3) ' All coordinates of the cube
DIM scr(points * 2) ' Coordinates of where the vertexes should be on the screen
FOR a = 1 TO points * 3
READ cube(a)
NEXT a
inc = 1 ' This is the current cube() pos the calculate
start! = TIMER
DO
' The following lines are matrixes (misspelled?), do NOT try to understand them...
REM ********** Rotate around z-axis **********
oldx = cube(inc)
oldy = cube(inc + 1)
xtemp = oldx * COS(zang) - oldy * SIN(zang)
ytemp = oldx * SIN(zang) + oldy * COS(zang)
cube(inc) = xtemp
cube(inc + 1) = ytemp
REM ********** Rotate around y-axis **********
oldx = cube(inc)
oldz = cube(inc + 2)
xtemp = oldx * COS(yang) - oldz * SIN(yang)
ztemp = oldx * SIN(yang) + oldz * COS(yang)
cube(inc) = xtemp
cube(inc + 2) = ztemp
REM ********** Rotate around x-axis **********
oldy = cube(inc + 1)
oldz = cube(inc + 2)
ytemp = oldy * COS(xang) - oldz * SIN(xang)
ztemp = oldy * SIN(xang) + oldz * COS(xang)
cube(inc + 1) = ytemp
cube(inc + 2) = ztemp
REM ***********************************
inc = inc + 3 ' Go to next x position
IF inc > points * 3 THEN
inc = 1
inc2 = 1
DO
' This do-loop calculates the screen coordinates of the cube...
y = (cube(inc) * 64) / (cube(inc + 2) - zoff) + xoff
x = (cube(inc + 1) * 64) / (cube(inc + 2) - zoff) + yoff
scr(inc2) = y
scr(inc2 + 1) = x
inc2 = inc2 + 2
inc = inc + 3
LOOP WHILE inc < (points * 3)
SCREEN 0, 0, 0, 1
frames = frames + 1
CLS
txtLINE scr(1), scr(2), scr(3), scr(4), 9, 0
txtLINE scr(1), scr(2), scr(5), scr(6), 9, 0
txtLINE scr(13), scr(14), scr(3), scr(4), 1, 0
txtLINE scr(13), scr(14), scr(5), scr(6), 1, 0
txtLINE scr(5), scr(6), scr(9), scr(10), 9, 0
txtLINE scr(1), scr(2), scr(7), scr(8), 1, 0
txtLINE scr(11), scr(12), scr(7), scr(8), 1, 0
txtLINE scr(15), scr(16), scr(11), scr(12), 9, 0
txtLINE scr(15), scr(16), scr(13), scr(14), 1, 0
txtLINE scr(15), scr(16), scr(9), scr(10), 9, 0
txtLINE scr(3), scr(4), scr(11), scr(12), 9, 0
txtLINE scr(7), scr(8), scr(9), scr(10), 1, 0
PCOPY 0, 1
zang = zang + zadd
IF zang < -RND / randiv THEN zadd = -zadd
IF zang > RND / randiv THEN zadd = -zadd
yang = yang + yadd
IF yang < -RND / randiv THEN yadd = -yadd
IF yang > RND / randiv THEN yadd = -yadd
xang = xang + xadd
IF xang < -RND / randiv THEN xadd = -xadd
IF xang > RND / randiv THEN xadd = -xadd
inc = 1
END IF
i$ = INKEY$
IF i$ = CHR$(27) THEN GOTO piep:
LOOP
piep:
finish! = TIMER
CLS
SCREEN 0, 1, 0, 0
WIDTH 80, 25: COLOR 7, 0: CLS
fps! = frames / (finish! - start!)
PRINT "Frames per second: ", fps!
END
REM *** Coordinates of the cube ***
REM x y z
DATA 5,5,5
DATA 5,5,-5
DATA 5,-5,5
DATA -5,5,5
DATA -5,-5,5
DATA -5,5,-5
DATA 5,-5,-5
DATA -5,-5,-5
SUB fastcharput (y, x, a$, vg, ag)
'duh ,
' YES it's faster
' NO it's not safe
' you must call DEF SEG=&HB800 before calling this routine
' that isn't done here 'cause of the speed loss.
' (c) buzz '96
' buzz@cyber-wizard.com
b = ((((INT(y) - 1) * 80) + INT(x) - 1) * 2)
a = ASC(a$)
col = (ag * 16) + vg
POKE b, a
POKE b + 1, col
END SUB
SUB txtLINE (x1, y1, x2, y2, col, res)
'translate :
' LINE (x1,y1)-(x2,y2),col
' res is the resolution ,
' if res=0 then calculate_resolution_needed_for_clear_line
' yeah , lower resolution , higher speed
' (c) buzz '96
' buzz@cyber-wizard.com
xdiff = x2 - x1
ydiff = y2 - y1
IF res > 0 THEN steps = res: GOTO prrr:
xsteps = xdiff
ysteps = ydiff
IF xsteps < 0 THEN xsteps = -xsteps
IF ysteps < 0 THEN ysteps = -ysteps
IF xsteps > ysteps THEN steps = xsteps ELSE steps = ysteps
prrr:
xd = xdiff ' uncomment all these | to add some sort of
IF xd < 0 THEN xd = -xd ' | lighting
yd = ydiff ' |
IF yd < 0 THEN yd = -yd ' |
IF yd > xd THEN d = yd ELSE d = xd ' v
IF d < 24 THEN col = 1 ELSE col = 9 ' helps you 2 see the perspective
xadd = xdiff / steps
yadd = ydiff / steps
xvar = x1
yvar = y1
DEF SEG = &HB800
FOR i = 1 TO steps + 1
yv = INT(yvar)
xv = INT(xvar)
char$ = "Û"
IF yv > 50 THEN yv = 50: char$ = " "
IF yv < 1 THEN yv = 1: char$ = " "
IF xv > 80 THEN xv = 80: char$ = " "
IF xv < 1 THEN xv = 1: char$ = " "
' LOCATE yv, xv 'bios gedoe
' COLOR col, 0
' PRINT char$;
fastcharput yv, xv, char$, col, 0 'sneller
yvar = yvar + yadd
xvar = xvar + xadd
NEXT i
DEF SEG
END SUB
--------------------------------------
Text Stars
--------------------------------------
DECLARE FUNCTION reverse$ (a$)
DECLARE FUNCTION randomkjoeps$ (size!)
DECLARE SUB update.stars (txt1$)
CLS : WIDTH , 50: SCREEN 0, 0, 1, 0
DIM SHARED stars(1000, 3)
RANDOMIZE TIMER
' 100 sterren , 3 locaties (1 = x , 2 = y , 3 = z)
FOR i = 1 TO 100
uc2ins:
a = INT(RND * 80) + 1
b = INT(RND * 35) + 7.5
IF a > 40 THEN c = a - 40 ELSE c = 40 - a
IF b > 25 THEN d = b - 25 ELSE d = 25 - b
' stelling van piet hazegras
e = SQR(c ^ 2 + d ^ 2)
IF e > 35 THEN GOTO uc2ins:
stars(i, 1) = a
stars(i, 2) = b
stars(i, 3) = RND / 4
NEXT
zippy:
READ z$
IF z$ = "|" THEN GOTO flip:
a$ = a$ + z$
GOTO zippy:
DATA "c o d e b y e d y o , b u z z "
DATA " "
DATA "g f x b y e d y o , b u z z "
DATA " "
DATA "d e m o m u s i c b y e d y o "
DATA " "
DATA "c r e d i t m u s i c b y b u z z "
DATA " "
DATA "i d e a s b y e d y o , b u z z "
DATA " "
DATA "g r e e t i n g s ( l e v e l o n e ) "
DATA " "
DATA "o m e g a / i n t r a"
DATA " "
DATA "b a n j o m a n i a / n o n e"
DATA " "
DATA "s a n d r o i d / n o n e"
DATA " "
DATA "d a r k g a b b e r / d a r k g r o u p"
DATA " "
DATA "g r e e t i n g s ( l e v e l t w o ) "
DATA " "
DATA "f a s t c h i c k e n / m y s t i c m i g h t"
DATA " "
DATA "t a s z / t h e c h a l l e n g e"
DATA " "
DATA "g r e e t i n g s ( l e v e l t h r e e ) "
DATA " "
DATA "d a r k g r o u p"
DATA " "
DATA "i n t r a"
DATA " "
DATA "g o t o 1 0"
DATA " "
DATA "a l l t e x t m o d e g r o u p s"
DATA " "
DATA "a l l b a s i c g r o u p s"
DATA "|"
flip:
a$ = SPACE$(40) + a$ + " "
tempy$ = randomkjoeps$(LEN(a$))
position = 1
pos2 = LEN(a$) + (40 + 30)
DO
update.stars t1$
PCOPY 1, 0
WAIT &H3DA, 8, 8
WAIT &H3DA, 8, 0
COLOR 4, 0
LOCATE 47, 40
PRINT MID$(a$, position, 40);
IF position - 36 > 1 THEN t1$ = MID$(a$, position - 36, 37) ELSE t1$ = SPACE$(37)
position = position + 1
IF position - 36 - 40 > LEN(a$) THEN position = 1
LOCATE 3, 40
IF position - 36 - 40 > 1 THEN PRINT reverse$(MID$(a$, position - 36 - 40, 40));
LOOP UNTIL INKEY$ <> ""
FUNCTION randomkjoeps$ (size)
temp$ = ""
FOR i = 1 TO size
a = RND
IF a > .5 THEN
b = 254
ELSE
b = 32
END IF
temp$ = temp$ + CHR$(b)
NEXT i
randomkjoeps$ = temp$
END FUNCTION
FUNCTION reverse$ (a$)
FOR i = LEN(a$) TO 1 STEP -1
r$ = r$ + MID$(a$, i, 1)
NEXT i
reverse$ = r$
END FUNCTION
SUB update.stars (txt1$)
LOCATE 26, 26
COLOR 1, 0
PRINT "c r e d i t ' n ' g r e e t z";
FOR i = 1 TO 100
IF INT(stars(i, 2)) > 0 AND INT(stars(i, 1)) > 0 AND stars(i, 2) < 50 AND stars(i, 1) < 80 THEN
LOCATE stars(i, 2), stars(i, 1)
PRINT " ";
END IF
xt = stars(i, 1)
yt = stars(i, 2)
xt = xt - 40
yt = yt - 25
stars(i, 1) = (xt * COS(stars(i, 3))) - (yt * SIN(stars(i, 3)))
stars(i, 2) = (xt * SIN(stars(i, 3))) + (yt * COS(stars(i, 3)))
stars(i, 1) = stars(i, 1) + 40
stars(i, 2) = stars(i, 2) + 25
SELECT CASE stars(i, 3)
CASE 0 TO .05
COLOR 8, 0
CASE .05 TO .2
COLOR 7
CASE .2 TO 2
COLOR 15
END SELECT
IF INT(stars(i, 2)) > 0 AND INT(stars(i, 1)) > 0 AND stars(i, 2) < 50 AND stars(i, 1) < 80 THEN
LOCATE stars(i, 2), stars(i, 1)
PRINT "ù";
END IF
NEXT i
COLOR 4, 0
FOR i = 0 TO 180 STEP 5
a$ = MID$(txt1$, INT((180 - i) / 5) + 1, 1)
x = (-22 * SIN(i / 57.2)) + 40
y = (22 * COS(i / 57.2)) + 25
LOCATE y, x: PRINT a$;
NEXT
END SUB
---------------------------------------------------------
QBasic Sprite Editor v0.21
By David Tordrup
---------------------------------------------------------
From: qbse021.zip (For the complete FILE go to the Basix Fanzine
website at http://www.come.to/basixfanzine)
--------------------------------------
Introduction
--------------------------------------
The QBasic Sprite Editor was written to aid programmers in easily creating
and incorporating graphics into their programs. The editor is written in
and for screen mode 7 (EGA), thus the graphics files ouput by the editor
will only display correctly in this mode. Future editions may include
options for other screen modes.
--------------------------------------
Using the editor
--------------------------------------
The editor is operated with the arrow keys and a number of function (F-xx)
keys. A self-explanatory menu is displayed to the right of the editing
window, describing the actions of the function keys.
Getting help
Pressing F1 at any time will bring up context sensitive help!
Changing colors
Pressing F2 will let you change the color you are currently using. Use the
arrow keys to highlight the desired color, and press ENTER to activate it.
When you return to the editor all tools will output the new color.
Selecting tools
Use the arrow keys to move the cursor in the editing window, and press
SPACE or ENTER to activate the drawing tool currently selected. The
available tools are displayed in a column to the right of the editing
window, with the active tool highlighted. To change tools press F3, and
select your new tool with the Up/Down arrow keys followed by ENTER.
Naming your file
Your sprite will be saved under the DOS filename stated above the editing
window. The default name is 'NONAME00.GFX'. This can be changed by pressing
F4 and typing in the new filename. The extension does not have to be GFX,
any valid filename will do.
Saving the sprite to disk
Once you have finished drawing your sprite, you can save it by pressing F5.
The sprite is saved under the filename stated above the editing window. Any
files with the same name as your sprite will be overwritten! All sprites
will be placed in the same directory as the editor itself.
Loading a sprite from disk
To continue work on a previously created sprite, press F6 and type in the
filename for the sprite. The sprite will be placed in the centre of the
editing window. Sprites over 115 x 100 pixels in size can not be loaded
into the editor.
--------------------------------------
Handling output files
--------------------------------------
Once you've drawn your sprite and saved it to disk, you might want to know
how to get it into your QBasic program. To do this, make a note of the size
of the sprite (the window below the menu), dimension an array of
integers in your program in that size, and use BLOAD to load the sprite:
DIM Array%(Size_goes_here) ' Dimension an array for the sprite
DEF SEG = VARSEG(Array%(0)) ' Start loading data at Array%(0)
BLOAD "FILENAME.EXT", 0 ' Load file at offset 0
PUT (1, 1), Array% ' Put the sprite on the screen
' to show that it works (leave out)
Use the above routine for all the sprites you need to load, just remember
to use individual names for the arrays. If you want to have multiple
sprites in one array, remember to change the index of the array in the DEF
SEG statement before you BLOAD again.
--------------------------------------
Afterword
--------------------------------------
You are free to create whatever you wish with this program, but please give
credit where credit is due. For more information on material by the QBasic
Team, visit the website or send e-mail to dtordrup@mail1.stofanet.dk
Suggested reading: "GETting and PUTting in QBasic" in Issue 14 of The Basix
Fanzine
------------------------------------------------------------------------
Miscellaneous & Credits
------------------------------------------------------------------------
------------------------------------------------------------
2000 BASIC Website Award
------------------------------------------------------------
I have yet to receive some submissions. Please send them in. Please note
that sites with pirated software posted on there webpage are not eligible.
Nominated your favourite BASIC site
You can vote for your favourite webpage or nominated on on the Basix
Fanzine homepage.
---------------------------------------------------------
Credits
---------------------------------------------------------
Thank you to those people who took time to send in articles for this issue.
Those people who contributed are:
David Tordrup dtordrup@mail1.stofanet.dk http://www.cybernet.dk/users/dtordrup/qbzone/
Buzz buzz@ddsw.nl http://huizen.ddsw.nl/bewoners/buzz/
---------------------------------------------------------
Contact Info
---------------------------------------------------------
ARTICLES Basix_Fanzine@yahoo.com
OTHER INQUIRIES ETC Basix_Fanzine@yahoo.com
WWW ADDRESS http://www.come.to/basixfanzine
--------------------------------------
Mailing List
--------------------------------------
Currently there are around 165 people on the list. Please note that I
assume no responsibility for any message that you receive or don't receive
from being on the list.
To join send an email to "Basix Fanzine" <ListProcessor@mindspring.com>
with the subject "SUBSCRIBE".
To unsubscribe send an email to "Basix Fanzine"
<ListProcessor@mindspring.com> with the subject "UNSUBSCRIBE".
---------------------------------------------------------
Next Issue
---------------------------------------------------------
As always I am always looking for articles, tutorials, comments, newsgroup
articles, etc to put in the Basix Fanzine. Please send them in.
------------------------------------------------------------------------
Edited By David Groulx
Copyright © 1999 The Basix Fanzine