Saturday, February 12, 2011

LEARN QBASIC PROGRAMING

 What Is QBasic?      

   Microsoft QBasic (Quick Basic) is a DOS based programming language developed in the 1980's to let people program their own games, small business software and much, much more, very easily (Hence the name)! QBasic was widely used throughout the U.S. and some U.S. companies. However it had a short life. In the late 1980's it died. Then in the early to mid '90s it was resurrected. Microsoft decided to keep QBasic around so they included a copy with Windows 95/98/NT 4.0

Getting Started



First you need QBasic on your system. If you do not have it click Here.
(IMPORTANT NOTE: when downloading QBasic remember to insert it in drive 'C:\' no folders!)
Next you will need to open QBasic. To do this go to your DOS Prompt, then type 'cd\' followed by enter. This will ensure that you are in drive 'C:\' and nothing else. now type in 'QBASIC' and press enter. Congratulations you have now opened QBasic. Proceed by pressing the escape key, you now have a blue screen with a white blinky cursor. To discover the use of this blue screen and the blinky cursor goto the section titled ' Essentials '

.
Essentials First you need to know the basics of QBasic programming. The QBasic language is made of instructions called statements. The statements must be written according to what is called "QBasic syntax rules". Consult the help menu in QBasic for further information on syntax rules.
 
One highly important syntax language is "The QBasic Alphabet" also known as the ASCII (As·key) Table. The ASCII table is a whole bunch of three number combinations used to show small characters. To display an ASCII Code you hold in the Alt key on your keyboard, and while holding that button down enter the 3 digit code found on an ASCII table.
 
Included in this section's software pack is a program called 'ASCII.BAS' (The BAS representing BASIC).
 
Now to open this you will need to find the 'File' menu inside QBasic and select 'open'. You should now select the directory that you put this section's package in. On the file list choose 'ASCII.BAS'. If you are using your keyboard, highlight the file and press enter. If you are using a mouse, double click on the file or highlight the file and press the open button. You now have a page of strange symbols in front of you. These symbols are your QBasic programming. To put them into action press the 'F5' key on your keyboard.
There are some special characters used for simple functions so you don't have to type out commands. An example is typing ' ?" ' instead of the print statement which would generally look like this: ' PRINT " '. Then there are characters that define categories such as input and output.
In QBasic there are close to two hundred keywords. Keywords do not serve as a function but rather as a modifier to the statements.
If you wanted to print 'QBasic' out on the screen in blue...
        You'd probably want your program to look like this:

CLS
COLOR 1
PRINT "QBasic"
The 'CLS' statement tells the computer to clear the screen.
The 'COLOR' statement tells the computer the the number following it (for example the 1) is the color the text on the proceeding line needs to be.
The ' PRINT " ' statement and it's following text tell the computer what text to display on the screen. Your computer can do math operations as well!
To solve a math problem on your screen you can use a program like this:
CLS
PRINT 5 + 5 Notice how on the 'PRINT' statement for an equation I left out the quotation marks. In doing this it will allow the computer to solve the problem and display it on your screen.

Input and Output
    In this section we will teach you how to write I/O (Input / Output) programs. We will teach you how to enter data using your keyboard, and then print it on your display or to your printer.
    To start off we will demonstrate displaying a name on the screen and then choosing output types.

CLS
INPUT "What is your name" ; NAME$
CLS
PRINT "Output Type"
PRINT "(P)rinter"
PRINT "(S)creen"
INPUT "Choose One" ; OUTPUT$
IF OUTPUT$ = "P" THEN LPRINT NAME$
IF OUTPUT$ = "S" THEN PRINT NAME$
END
In this program the 'INPUT' statement tells the computer to accept text.
The text in quotation marks, is the question requiring input.
the semicolon followed by a word and a '$' is what labels the answer to the question.
The line 'IF OUTPUT$ = "P" THEN LPRINT NAME$' says that if the answer to the second question is a 'P' then to send it to the printer; the following line signifies it's needing to be sent to the monitor if the answer is 'S'.     If you want a numerical response in a program you would probably want your input to look like this:
CLS
INPUT x
The 'x' tells the program that it can only accept numerical answers.
If a non-numerical answer is received it will display 'Redo From Start' and state the question again     The write statement, can be used in place of the 'PRINT' statement:
WRITE d1, d2, d3 …
WRITE inserts commas inbetween items, and quotation marks around strings. It also removes the space before positive numbers and after all numbers. Here is an example of the WRITE statement compared to the PRINT statement:
CLS
a = 123
b = 4.56
C$ = "END" WRITE a, b, c$
PRINT a; b; c$
PRINT a, b, c$
END
The output of the program is:
123,4.56,"END"
123 4.56 END
123        4.56        END
In the program we defined the variables of a, b, and c$ so that all the WRITE and PRINT statements had to do was print the values of the a, b, and c. If you would like your output to start at a certain point on the screen use the LOCATE statement to set the cursor:
CLS
LOCATE 12, 38
PRINT "Test"
END This program shows the use of the LOCATE statement to center the cursor in the screen and type 'Test'. However you can change the '12' and '38' to any desired numbers to place the cursor in different screen positions.

Device Control
    In this section we will go over the following items
· Data Conversion functions
· Arrays
· Modem usage
· Events and manipulating the mouse
· Shelling out
    We're going to start this section off with Indexed variables and arrays. If you have a group of variables that contain the same type of data, and feature the same characteristics, you would previously implement them like this:

month1$ = "January"
month2$ = "February"

month12$ = "December"
This could get complicated if you want to deal with a set of related data where the elements number considerably more than 12. So we need to group these, however still have them singly accessible. For this we will need Arrays. Each array is an indexed variable. This is a variable which, in addition to it's name, is characterized by a set of indices which explicity define it's position in the array. The indices are placed in parenthesis after the variable name, which is the same as the name of the array.
Now before we show you an example, let's  discuss the index base of arrays. Array indexes in QBasic are ,by default, counted up from zero, so the first element in the list of months would need to be zero.
If you would like to control your keyboard the following code will help you greatly:
WHILE Keypress$ = " "
        Keypress$ = INKEY$
        IF Keypress$ <> " " THEN PRINT Keypress$
WEND END
In this program the INKEY$ statement says that when a key is pressed it will tell the computer.
The variable titled Keypress$ is just an input statement and can be changed without harming the program. You can use programs such as the following to assign a certain key on the keyboard a task :
IF INKEY$ = "Q" THEN END
This tells the computer that if someone presses the Q key on the keyboard, to end the program immediately. With the current demand for the internet, computer communications have become a very important area. To communicate over long distances a device called a modem is used. A modem uses your telephone line to transmit and receive special signals which untranslated brings to your display the graphics, text, downloads and so forth used in this site's (and other site's) creation.
QBasic does allow modem usage... one example would be the OPEN COM statement which opens and initializes your communications channel for input and output. This program example below shows how the modem is readied, initialized, and connected using an RS232 interface :
OPEN "COMn: optlist1 optlist2" [FOR mode] AS [#]filenum% [LEN=reclen%]
If you would like to use the mouse within a program, you can use a quick, easy program like the following example:
DIM SHARED a%(34) DECLARE SUB MouseInit ()
DECLARE SUB MouseReset ()
DECLARE SUB MouseHide ()
DECLARE SUB MouseShow ()
DECLARE FUNCTION MouseX% ()
DECLARE FUNCTION MouseY% ()
DECLARE FUNCTION MouseButtons% ()
DECLARE FUNCTION MouseChoice% ()
DECLARE SUB DrawMenu (current%, selected%)
'Loading machine codes for working with the mouse
DEF SEG = VARSEG(a%(0))
FOR i% = 0 TO 63
  READ d%
  POKE VARPTR(a%(0)) + i%, d%
NEXT i%
DEF SEG
DATA 00,00
DATA 00,00
DATA 00,00
'    Mashine codes        :        Assembler commands
DATA &HB8,00,00           :       'mov    ax,000h
DATA &HCD,&H33            :       'int    33h
DATA &H3D,&HFF,&HFF       :       'cmp    ax,0FFFFh
DATA &H75,&H0D            :       'jne    @@notPresent
DATA &H0E                 :       'push   cs
DATA &H07                 :       'pop    es
DATA &HBA,&H24,&H00       :       'mov    dx,offset MouseHandler
DATA &HB9,&HFF,&HFF       :       'mov    cx,0FFFFh
DATA &HB8,&H0C,&H00       :       'mov    ax,000Ch
DATA &HCD,&H33            :       'int    33h
     : '@@NotPresent:
DATA &HCB                 :       'ret
DATA &HB8,00,00           :       'mov    ax,0000h
DATA &HCD,&H33            :       'int    33h
DATA &HCB                 :       'ret
DATA &H2E,&H89,&H0E,00,00 :       'mov    [cs:mouseX],cx
DATA &H2E,&H89,&H16,02,00 :       'mov    [cs:mouseY],dx
DATA &H2E,&H89,&H1E,04,00 :       'mov    [cs:mouseButtons],bx
DATA &HCB                 :       'ret
DATA &HB8,01,00           :       'mov    ax,0001h
DATA &HCD,&H33            :       'int    33h
DATA &HCB                 :       'ret
DATA &HB8,02,00           :       'mov    ax,0002h
DATA &HCD,&H33            :       'int    33h
DATA &HCB                 :       'ret
'Inicial menu and mouse
SCREEN 9: CLS
CALL DrawMenu(1, 1)
CALL MouseInit
CALL MouseShow
curChoice% = 1
curSel% = 1
doRedraw% = 0
LOCATE 1, 1: PRINT "Press Esc to exit ..."
'Mouse and menu active work
DO
  IF (MouseChoice% <> 0) THEN
    IF (curChoice% <> MouseChoice%) THEN
       curChoice% = MouseChoice%
       doRedraw% = 1
    END IF
    IF (MouseButtons% = 1) AND (MouseChoice% <> curSel%) THEN
      curSel% = MouseChoice%
      doRedraw% = 1
    END IF
    IF (doRedraw% = 1) THEN
      CALL MouseHide
      CALL DrawMenu(curChoice%, curSel%)
      CALL MouseShow
      doRedraw% = 0
    END IF
  END IF
   LOCATE 7, 35:  PRINT "Operation 1"
   LOCATE 9, 35:  PRINT "Operation 2"
   LOCATE 11, 35: PRINT "Operation 3"
  'Select menu operations
  IF MouseButtons% = 0 THEN
   LOCATE 14, 30
   SELECT CASE curSel%
     CASE 1: PRINT "This is Operation One  "
     CASE 2: PRINT "This is Operation Two  "
     CASE 3: PRINT "This is Operation Three"
   END SELECT
   END IF
LOOP UNTIL INKEY$ = CHR$(27)
CALL MouseReset  'Reset mouse
SCREEN 0
SUB DrawMenu (selected%, current%)
  IF current% = 1 THEN
    LINE (222, 78)-(418, 103), 2, BF
  ELSE
    LINE (222, 78)-(418, 103), 3, BF
  END IF
IF selected% = 1 THEN
    LINE (220, 76)-(420, 105), 10, B
    LINE (221, 77)-(419, 104), 10, B
  ELSE
    LINE (220, 76)-(420, 105), 15, B
    LINE (221, 77)-(419, 104), 15, B
  END IF
  IF current% = 2 THEN
    LINE (222, 108)-(418, 133), 2, BF
  ELSE
    LINE (222, 108)-(418, 133), 3, BF
  END IF
  IF selected% = 2 THEN
    LINE (220, 106)-(420, 135), 10, B
    LINE (221, 107)-(419, 134), 10, B
  ELSE
    LINE (220, 106)-(420, 135), 15, B
    LINE (221, 107)-(419, 134), 15, B
  END IF
  IF current% = 3 THEN
    LINE (222, 138)-(418, 163), 2, BF
  ELSE
    LINE (222, 138)-(418, 163), 3, BF
  END IF
  IF selected% = 3 THEN
    LINE (220, 136)-(420, 165), 10, B
    LINE (221, 137)-(419, 164), 10, B
  ELSE
    LINE (220, 136)-(420, 165), 15, B
    LINE (221, 137)-(419, 164), 15, B
  END IF
END SUB
FUNCTION MouseButtons%
  MouseButtons% = a%(2)
END FUNCTION
FUNCTION MouseChoice%
  MX% = MouseX%: MY% = MouseY%
  IF (MX% < 220) OR (MY% < 76) OR (MX% >= 420) OR (MY% >= 166) THEN
    MouseChoice% = 0
  ELSEIF (MY% < 106) THEN
    MouseChoice% = 1
  ELSEIF (MY% > 136) THEN
    MouseChoice% = 3
  ELSE
    MouseChoice% = 2
  END IF
END FUNCTION
SUB MouseHide
  DEF SEG = VARSEG(a%(0))
  CALL ABSOLUTE(VARPTR(a%(0)) + &H3A)
  DEF SEG
END SUB
SUB MouseInit
  DEF SEG = VARSEG(a%(0))
  CALL ABSOLUTE(VARPTR(a%(0)) + 6)
  DEF SEG
END SUB
SUB MouseReset
  DEF SEG = VARSEG(a%(0))
  CALL ABSOLUTE(VARPTR(a%(1)) + &H1E)
  DEF SEG
END SUB
SUB MouseShow
  DEF SEG = VARSEG(a%(0))
  CALL ABSOLUTE(VARPTR(a%(0)) + &H34)
  DEF SEG
END SUB
FUNCTION MouseX%
  MouseX% = a%(0)
END FUNCTION
FUNCTION MouseY%
  MouseY% = a%(1)
END FUNCTION
 
  This program will display a mouse cursor on your screen allowing you to click on the buttons labeled operation 1-3. When you click on the button you notice how the name of that button is displayed right below them.
Shelling out can be used if you would like to open a program from the DOS Prompt that is not QBasic Syntax. In order to do this you would use a line of programming like this:
CLS
SHELL "PROGRAM.EXE"
However this statement should be modified, by changing the text 'PROGRAM.EXE' with the program's name that you would like to run. To leave this DOS Prompt you simply type in 'EXIT' followed by the enter key. Own great media type nowadays is the CD-ROM drive. Now if we wanted to we could very easily write a program to open specific data off of a CD, but instead we are writing a program that will play music from a CD. However this program uses the Gold Star CD-ROM drive which some may have. If you are not an owner of this drive or it's software adjust the program names as needed:
CLS
PRINT "Insert compact disk, press any key,"
PRINT "run CD-Player and press Power (off)"
F$ = INPUT$(1)
SHELL "C:\CDROM\gsaudio.exe"
PRINT "10 Seconds play!"
SLEEP 10
SHELL "C:\CDROM\eject.exe"
CLS
PRINT "END" END
Did it work? Do you have a Gold Star Drive? If it didn't work and you have a Gold Star Drive please E-mail :  (link disabled) .


Program Design The larger and more abstract a program is the the more bugs and errors the program will have. In this section we are going to talk about how to organize your programs so that you can very easily find bugs that may be hiding inside your program to be fixed. The first way to organize your program is by using SUBs these handy tools can be used by typing in the following:
  SUB name
You should replace 'name' with the actual name of your SUB program.
When you press enter QBasic will bring up a screen looking similar to this:
SUB name END SUB
Inbetween the two lines 'SUB name' and 'END SUB' type any valid QBasic source, such as a graphic's source. Then when you run the program if there are any defects with that graphic you know where to go. Now to get back to your main program screen press the 'F2' key on your keyboard and go up to the first option which should be your program's name, and press enter. Now if you run your program whatever you typed in the SUB program boxes doesn't work. In order to use them you must return to your main program and put in a statement like the following one :
DECLARE SUB name ( )
Another way of organizing a program is to insert an apostrophe like so:
'CHEESE
PRINT "CHEESE" 'PEANUT BUTTER
PRINT "PEANUT BUTTER"
Now this program is not very complicated, but if it were and you had a problem with the cheese area then you would know to go write to the part labeled 'CHEESE. You can also have programs make reference to one part of the program labeled with an apostrophe by using the following program.
'CHEESE
PRINT "CHEESE" 'PEANUT BUTTER
PRINT "PEANUT BUTTER"
END
GOTO PEANUT BUTTER
Without the END statement after PEANUT BUTTER the program would go on forever printing PEANUT BUTTER (Notice the loop without the END statement?).

Graphics and Animation
Table of contents: (1) what can QBasic do?
                            (2) using DATA to create graphics
                            (3) GETting and PUTting graphics
                            (4) so how does this work?
 
    QBasic was not meant to be a great environment for graphic-oriented
applications. Instead, it was meant to be a learner's language for struc-
tured programming as well as basic programming techniques. However, using some of its functions, we can achieve some level of graphic quality while keeping in the language's power.
 
    The best screen mode for this is SCREEN 7, a 320 x 200 16-color
screen mode. While this may seem primitive, the mode is fast and has mul-
tiple "pages" to eliminate any flicker whatsoever. VGA graphics can be
done, but without a very fast computer, huge amounts of flicker occur.
Of course, without this tutorial, you can use LINEs, CIRCLEs, and
PSETs, but this method is far faster, better, and more professional.
All methods explained in this document are MY OWN, developed by myself
over the years.
Using the DATA statement to create graphics
Graphics using this method are created beforehand using the GRAPHICS
editor written by myself specifically for this tutorial, or using the DATA
statement to read in color values. I'll start by explaining how to use the
DATA statement to create graphics.
First, you have to start off by deciding how large your graphic is
going to be. For instance, you might choose 10 pixels by 10 pixels. To
make the graphic, start by creating a table like this:

DATA 00,00,00,00,00,00,00,00,00,00DATA 00,00,00,00,00,00,00,00,00,00
DATA 00,00,00,00,00,00,00,00,00,00DATA 00,00,00,00,00,00,00,00,00,00
DATA 00,00,00,00,00,00,00,00,00,00DATA 00,00,00,00,00,00,00,00,00,00
DATA 00,00,00,00,00,00,00,00,00,00DATA 00,00,00,00,00,00,00,00,00,00
DATA 00,00,00,00,00,00,00,00,00,00DATA 00,00,00,00,00,00,00,00,00,00
Then, changing the QB editor from insert to overwrite using the
INSERT key on your keyboard, change the color value of black (your back-
ground color as 00) to the color you desire. The color table is as follows:
 
        00 - black              08 - dark grey
        01 - dark blue        09 - light blue
        02 - dark green      10 - light green
        03 - dark cyan       11 - light cyan
        04 - dark red         12 - light red
        05 - dark purple     13 - magenta
        06 - orange brown  14 - yellow
        07 - grey                 15 - bright white For instance, to create a graphic of a fire, use this set of DATA statements:
DATA 00,00,00,04,00,00,04,00,00,00
DATA 00,00,04,04,00,04,04,04,00,00DATA 00,00,04,04,04,04,04,04,00,00
DATA 00,04,04,12,12,04,04,04,00,00DATA 00,04,04,12,12,04,04,00,00,00
DATA 00,04,04,12,12,04,04,00,00,00DATA 00,04,12,12,12,04,04,04,00,00
DATA 00,04,12,14,14,12,04,04,00,00DATA 04,04,12,14,14,14,12,04,00,00
DATA 04,04,12,14,14,14,12,04,00,00
Note that these DATA statements do nothing without using the method
explained below to READ these color values into usable form. Before going
on, however, try making some graphics on your own of varying sizes to try
out using the following method.
GETting and PUTting graphics
Now you should have a set of DATA statements that you think will look
like an object of some sort. What next? You'll have to use this routine
to read the values into an array and then place them on the screen. Make
sure you've already used the SCREEN 7 statement first.
xlength = 10 'replace 10 with the length of your picture
ylength = 10 'replace 10 with the width of your picture
FOR y = 1 to ylength FOR x = 1 to xlength READ z
PSET(x, y), z NEXT NEXT
This should give you what your graphic looks like. If you're satis-
fied with it, go on. If not, alter it and try loading it again.
Now that you've got a good picture, you'll need to put it into memory
so the program doesn't have to redraw it every time. This saves a LOT of
computing power. To do this, you'll have to DIM an array and then GET the
graphic into the array. Here's how:
DIM graphicname(xlength * ylength) 'replace graphicname with a good name
'for your graphic GET (0,0)-(xlength, ylength), graphicnameCLS
Okay! You've got your picture saved in the computer's temporary
memory. To view the graphic, use the PUT statement like this:
PUT (x, y), graphicname 'replace x & y with where you want
'the graphic & change graphicname
That's it for this section. You can now do simple graphics. For
a good way to create smooth animation, read on to the next section.
So how does this work?
You now know how to create graphic data using the DATA statement,
how to read that data and place it on the screen, and how to GET and PUT
the graphic itself. However, creating graphics that move is a bit more
complex, though not terribly difficult.
What you'll have to do is set the number of *screen pages* you'll
be using. When you use the SCREEN statement, you're only using one page-
page 0. However, with modifications of the SCREEN statement, you can
create graphics on a different "page" and then copy them to the screen.
This eliminates CLS'ing all the time.
First off, your SCREEN 7 statement should be changed to SCREEN
7,0,1,0. This means that you'll be using page 1 to do graphics and page
0 will be what the user sees. Once you've done this, all your graphics
work WON'T be shown to the user until you use the PCOPY 1,0 statement.
SCREEN 7,0,1,0 '- using page 1 as work page & page
'0 as visual. .... '- insert the code you used to create
'your graphic using the method above
CLS
WHILE x < 270
PUT (x, 100), graphicname '- replace graphicname w/ the name of
PCOPY 1,0 'your graphicCLSx = x + 1WEND The code above can be modified to whatever your needs.


Game Development Now for the best part of the whole web page, games! In our chapter we are going to discuss the following :
· Storing dynamic data
· Traditional game design
· Game control techniques
· Manipulating sprites
· Advanced graphic tips
· Program optimization
· Smoothing out the wrinkles in your programs
You've probably played GORILLA.BAS where two apes throw exploding bananas at eachother, or NIBBLES.BAS which is a fun classic game where a worm eats numbers and grows longer, but even though they are fun they are probably the poorest QBasic games. Many excellent games can be written in QBasic. Example Battle Craft <--Link which is a spoof of WarCraft™ and shows a great resembelance. Or games such as Lith2 <--Link which is very close to the game The Legend Of Legaia™ for the Sony™ PlayStation®.
So QBasic may not be fast like C++, and it might not be as sophisticated as Visual Basic or Delphi, but it makes up for that by being a whole lot easier to learn. As long as you know what you want to make, and you have it all planned out it should give you a very quick, very nice program.
In QBasic we can write many types of games. We can make strategy games, action games, puzzle games, and RPG (Role Playing Games). They may not have the greatest graphics or the best sounds, but most of all they're fun!
Text Adventures are games that don't have any graphics but instead it is all text. Included in this sections software pack is a game titled ADVENTUR.BAS, this is a text adventure where using commands such as HELP ME, GO EAST, RUN NORTH, CLIMB UP, and PICK UP KEY can be used to play. A line titled 'Objects Present :' tells you what you can use the 'PICK UP' command for. Text adventures are a very simple concept and are very fun and addictive.
You'll notice here that the source for aventur.bas is really just an INPUT statement and many IF, THEN statements.

DECLARE SUB RESTART ()
DECLARE SUB RestartConfirm ()
DECLARE SUB DrawHeader ()
DECLARE SUB Help ()
DECLARE SUB QuitFunction ()
DECLARE SUB IntroScreen ()
DECLARE SUB QuitScreenConfirm () REM ADVENTURE
REM Written by A.Sill for Wrox Press
REM In GWBASIC
REM (c) 1995
IntroScreen
220 REM ########## INITIALIZE ###########
Objects% = 3: Rooms% = 5
DIM Word$(200), PlaceName$(Rooms%), PlaceDescription$(Rooms%)
DIM Direction$(Rooms%), Barrier$(Rooms%), ObjectKey$(Objects%)
DIM ObjectDescription$(Objects%), ObjectInfo$(Objects%)
DIM SHARED Reply$
DIM UnknownResponse$(15)
CurrentLocation% = 1
FOR a = 1 TO Rooms%: READ PlaceName$(a), PlaceDescription$(a), Direction$(a), Barrier$(a): NEXT a
DATA "The Path", "This wide gravel path leads from the locked gate at the foot of the hill and    winds up steeply to the front door of the castle.  The hedges that are adjacent to the path are impenetrable.", "00020000", "06020604"
DATA "The Lobby", "You are in the hallway inside the main doors.   Doors lead off in different     directions.   The room looks to have been used very little.", "05000401", "00050002"
DATA "The Dungeon", "The dungeon also couples as a torture chamber.   It is very dark, smelly and    and damp drips from green fronds on the ceiling.", "00000005", "05050500"
DATA "The Bedroom", "The bedroom is definately the best kept room in the castle.   Decorated with    extreme amounts of freshly coloured drapes, it is such a contrast!", "02000000", "00050505"
DATA "The Kitchen", "This is where all the castles food is prepared.   Functional furniture dots     the dusty long-unused room.", "00030200", "05000005"
FOR a = 1 TO Objects%: READ ObjectKey$(a), ObjectDescription$(a), ObjectInfo$(a): NEXT a
DATA "Cross", "This is a large wooden well-crafted cross.   There are signs of extensive       hammering and stains of a dark liquid on the base.", "0304"
DATA "Key", "A heavy and well-worn brass key.", "0101"
DATA "Lantern", "Dull, brown, grubby cob-webbed lantern thats obviously not worked properly for years. Probably needs a good clean.", "0401"
FOR a = 1 TO 15: READ UnknownResponse$(a): NEXT a
DATA "Pardon?", "Come again?", "I don't understand", "Sorry, what was that?"
DATA "Eh?", "Speak English boy", "Sorry, I didn't quite catch that"
DATA "Are you okay?", "Don't want to", "Give me a proper command"
DATA "Say it again", "I seem to have a little problem understanding you"
DATA "What?", "Slow down, slow down, what was that?", "Stop messing me about"
350 REM ######## SCREEN LAYOUT #########
DrawHeader
480 REM ######## START INTERFACE #########
490 LOCATE 7, 1: PRINT STRING$(79, " ")
500 LOCATE 7, (80 - (LEN(PlaceName$(CurrentLocation%)))) / 2: COLOR 6: PRINT PlaceName$(CurrentLocation%)
510 LOCATE 9, 1: PRINT STRING$(79, " "): LOCATE 10, 1: PRINT STRING$(79, " "): LOCATE 11, 1: PRINT STRING$(78, " ")
520 LOCATE 9, 1: COLOR 7: PRINT PlaceDescription$(CurrentLocation%)
530 LOCATE 13, 1: PRINT STRING$(79, " ")
535 LOCATE 22, 1: PRINT STRING$(79, " ");
536 LOCATE 23, 1: PRINT STRING$(79, " ");
540 LOCATE 13, 1: COLOR 2: PRINT "Exits : "; : COLOR 7
550 flag% = 0
560 IF VAL(MID$(Direction$(CurrentLocation%), 1, 2)) <> 0 THEN PRINT " North";
570 IF VAL(MID$(Direction$(CurrentLocation%), 3, 2)) <> 0 THEN PRINT " East";
580 IF VAL(MID$(Direction$(CurrentLocation%), 5, 2)) <> 0 THEN PRINT " South";
590 IF VAL(MID$(Direction$(CurrentLocation%), 7, 2)) <> 0 THEN PRINT " West";
600 LOCATE 15, 1: PRINT STRING$(78, " ")
610 flag% = 0: LOCATE 15, 1: COLOR 2: PRINT "Objects present : "; : COLOR 7
620 FOR a = 1 TO Objects%
630 IF VAL(LEFT$(ObjectInfo$(a), 2)) = CurrentLocation% THEN IF flag% = 1 THEN PRINT ", ";
640 IF VAL(LEFT$(ObjectInfo$(a), 2)) = CurrentLocation% THEN PRINT ObjectKey$(a); : flag% = 1
650 NEXT a
660 IF flag% = 1 THEN PRINT "." ELSE PRINT "None"
670 LOCATE 18, 1: PRINT STRING$(78, " ")
680 LOCATE 20, 1: PRINT STRING$(78, " ")
REM ########### GET TEXT INPUT ##############
COLOR 7: LOCATE 18, 2: PRINT "INPUT :": COLOR 2
X = 10: Y = 18: MAX% = 50: EntryString$ = "": a$ = ""
StartTextEntry:
a$ = "": WHILE a$ = "": a$ = UCASE$(INKEY$): WEND
SOUND 137, .1
IF a$ = CHR$(13) AND EntryString$ <> "" THEN GOTO StartAnalysis
IF a$ = CHR$(8) AND EntryString$ <> "" THEN EntryString$ = LEFT$(EntryString$, LEN(EntryString$) - 1): LOCATE Y, X: PRINT EntryString$; "     ": GOTO StartTextEntry
IF (ASC(a$) < 64 OR ASC(a$) > 91) AND (a$ <> " " AND a$ <> "-" AND a$ <> "." AND a$ <> ",") THEN GOTO StartTextEntry
IF LEN(EntryString$) > MAX% - 1 THEN GOTO StartTextEntry
EntryString$ = EntryString$ + a$
LOCATE Y, X: PRINT EntryString$ + " "
GOTO StartTextEntry
REM ########## INPUT STRING ANALYSIS ############
StartAnalysis:
NumberOfWords% = 0: NumberOfSentences% = 0: WordLength% = 0
FOR a = 1 TO 20: Word$(a) = "": NEXT a
  IF RIGHT$(EntryString$, 1) <> "." THEN EntryString$ = EntryString$ + "."
FOR a = 1 TO LEN(EntryString$)
  B$ = MID$(EntryString$, a, 1)
  IF B$ = "." THEN NumberOfSentences% = NumberOfSentences% + 1
  IF B$ = " " AND WordLength% = 0 THEN GOTO 910
  IF (B$ = " " OR B$ = "(" OR B$ = ")" OR B$ = "?" OR B$ = "!" OR B$ = CHR$(34) OR B$ = "." OR B$ = "," OR B$ = "" OR a = LEN(EntryString$)) AND WordLength% > 0 THEN GOSUB GetWord: GOTO 910
  WordLength% = WordLength% + 1
910 NEXT a
REM ############# ACTION ###############
Reply$ = UnknownResponse$((RND * 14) + 1)
WHILE a$ <> "": a$ = INKEY$: WEND
W1$ = LEFT$(Word$(1), LEN(Word$(1)) - 1)
IF Word$(2) <> "" THEN W2$ = LEFT$(Word$(2), LEN(Word$(2)) - 1)
IF Word$(3) <> "" THEN W3$ = LEFT$(Word$(3), LEN(Word$(3)) - 1)
IF Word$(4) <> "" THEN W4$ = LEFT$(Word$(4), LEN(Word$(4)) - 1)
IF Word$(5) <> "" THEN W5$ = LEFT$(Word$(5), LEN(Word$(5)) - 1)
IF W1$ = "DROP" AND W2$ <> "" THEN GOSUB DropObject
IF W1$ = "DROP" AND W2$ = "" THEN Reply$ = "Drop what?"
IF (W1$ = "GET" OR W1$ = "GRAB" OR W1$ = "TAKE") AND W2$ <> "" THEN W4$ = W2$: GOSUB GetObject
IF (W1$ = "GET" OR W1$ = "GRAB" OR W1$ = "TAKE") AND W2$ = "" THEN Reply$ = "Try again - what do you want?"
IF (W1$ = "GO" OR W1$ = "RUN" OR W1$ = "WALK" OR W1$ = "CRAWL" OR W1$ = "HOP") AND (W2$ = "NORTH" OR W2$ = "SOUTH" OR W2$ = "EAST" OR W2$ = "WEST") THEN GOSUB MoveRoutine
IF (W1$ = "GO" OR W1$ = "RUN" OR W1$ = "WALK" OR W1$ = "CRAWL" OR W1$ = "HOP") AND W2$ = "" THEN Reply$ = "After exercising on the spot, you get tired and give up."
IF (W1$ = "H" OR W1$ = "HELP") AND W2$ <> "ME" THEN Reply$ = "Help who?"
IF (W1$ = "H" OR W1$ = "HELP") AND W2$ = "ME" THEN Help
IF W1$ = "INVENTORY" OR W1$ = "RUMMAGE" OR W1$ = "INV" THEN GOSUB ListInventory
IF W1$ = "OPEN" AND W2$ = "DOOR" THEN GOSUB OpenDoor
IF W1$ = "PICK" AND W2$ = "UP" AND W3$ = "" THEN Reply$ = "Pick up what?"
IF W1$ = "PICK" AND W2$ = "UP" AND W3$ <> "" THEN W4$ = W3$: GOSUB GetObject
IF W1$ = "QUIT" OR (W1$ = "LOG" AND W2$ = "OUT") OR (W1$ = "LOG" AND W2$ = "OFF") THEN QuitFunction
IF W1$ = "RESTART" THEN RESTART
IF W1$ = "UNLOCK" AND W2$ = "" THEN Reply$ = "Unlock what?"
IF W1$ = "UNLOCK" AND W2$ <> "" THEN GOSUB UnlockDoor
COLOR 15: LOCATE 20, 1: PRINT Reply$
WHILE INKEY$ = "": WEND
GOTO 490
MoveRoutine:
REM ########## 'GO' SUBROUTINE RESPONSE SEEKER #####
1330 IF W2$ = "NORTH" THEN MOVE% = 1
1340 IF W2$ = "EAST" THEN MOVE% = 3
1350 IF W2$ = "SOUTH" THEN MOVE% = 5
1360 IF W2$ = "WEST" THEN MOVE% = 7
IF VAL(MID$(Barrier$(CurrentLocation%), MOVE%, 2)) = 2 THEN Reply$ = "The door is locked."
IF VAL(MID$(Barrier$(CurrentLocation%), MOVE%, 2)) = 5 THEN Reply$ = "You cannot walk through that wall."
IF VAL(MID$(Barrier$(CurrentLocation%), MOVE%, 2)) = 6 THEN Reply$ = "You cannot penetrate the impenetrable bushes."
REM #### Check Move Type
IF VAL(MID$(Barrier$(CurrentLocation%), MOVE%, 2)) = 0 THEN CurrentLocation% = VAL(MID$(Direction$(CurrentLocation%), MOVE%, 2)): Reply$ = "YOU " + W1$ + " " + W2$
RETURN
ListInventory:
REM ############ LIST INVENTORY OBJECTS #############
LOCATE 22, 1
FOR a% = 1 TO Objects%
IF LEFT$(ObjectInfo$(a%), 2) = "00" THEN PRINT ObjectKey$(a%); " ";
NEXT a%
Reply$ = "You are holding : "
RETURN
GetObject:
REM ############ PICK UP AN OBJECT ############
flag% = 0
FOR a% = 1 TO Objects%
  IF UCASE$(ObjectKey$(a%)) = W4$ THEN flag% = a%
NEXT a%
IF flag% = 0 THEN Reply$ = "I can't see a " + W4$: GOTO EndGetObject
IF VAL(LEFT$(ObjectInfo$(flag%), 2)) = 0 THEN Reply$ = "You're already carrying it! ": GOTO EndGetObject
IF (VAL(LEFT$(ObjectInfo$(flag%), 2)) > 0) AND (VAL(LEFT$(ObjectInfo$(flag%), 2)) <> CurrentLocation%) THEN Reply$ = "It isn't here!": GOTO EndGetObject
IF VAL(RIGHT$(ObjectInfo$(flag%), 2)) = 4 THEN Reply$ = "After a-heaving and a-hoing, you decide it's far too heavy to even lift.": GOTO EndGetObject
ObjectInfo$(flag%) = "00" + RIGHT$(ObjectInfo$(flag%), 2)
Reply$ = "You've picked up the " + W4$
EndGetObject:
RETURN
DropObject:
REM ############# DROP AN OBJECT ###############
flag% = 0
FOR a% = 1 TO Objects%
  IF UCASE$(ObjectKey$(a%)) = W2$ THEN flag% = a%
NEXT a%
IF flag% = 0 THEN Reply$ = "I can't see a " + W2$
IF VAL(LEFT$(ObjectInfo$(flag%), 2)) <> 0 THEN Reply$ = "You don't have it!": GOTO EndDropObject
temp$ = RIGHT$(STR$(CurrentLocation%), LEN(STR$(CurrentLocation%)) - 1)
IF LEN(temp$) = 1 THEN temp$ = "0" + temp$
ObjectInfo$(flag%) = temp$ + RIGHT$(ObjectInfo$(flag%), 2)
Reply$ = "You've dropped the " + W2$
EndDropObject:
RETURN
 
GetWord:
REM ############ SUBROUTINE ##########
NumberOfWords% = NumberOfWords% + 1
flag% = 0
'WHILE A - WordLength% > .9: WordLength% = WordLength% - 1: WEND
Word$(NumberOfWords%) = MID$(EntryString$, a - WordLength%, WordLength% + 1)
WordLength% = 0
RETURN
InputStats:
REM ############ INPUT STATS ##########
LOCATE 11, 1: COLOR 7: PRINT "Number of words : "; NumberOfWords%
PRINT "Number of Sentences : "; NumberOfSentences%
PRINT "Average sentence length was : "; (NumberOfWords% / NumberOfSentences%); "words long"
LOCATE 17, 1: FOR a = 1 TO 8: PRINT Word$(a); "   "; : NEXT a
WHILE INKEY$ = "": WEND
RETURN
UnlockDoor:
REM ############ UNLOCK DOOR ############
flag% = 0: flag2% = 0
FOR a% = 1 TO 8 STEP 2
  IF MID$(Barrier$(CurrentLocation%), a%, 2) = "02" THEN flag% = flag% + 1: flag2% = a%
NEXT a%
IF flag% = 0 THEN Reply$ = "There are no doors here!"
IF flag% > 1 AND W2$ = "DOOR" AND W3$ = "" THEN Reply$ = "Which door?"
IF flag% > 1 AND W2$ = "DOOR" AND W3$ <> "" THEN Reply$ = "Sorry, my head hurts with all these doors!"
IF flag% = 1 THEN MID$(Barrier$(CurrentLocation%), flag2%, 2) = "03": Reply$ = "Door is now unlocked"
RETURN
OpenDoor:
REM ############# OPEN DOOR ###############
flag% = 0: flag2% = 0
FOR a% = 1 TO 8 STEP 2
  IF MID$(Barrier$(CurrentLocation%), a%, 2) = "03" THEN flag% = flag% + 1: flag2% = a%
NEXT a%
IF flag% = 0 THEN Reply$ = "There are no doors here!"
IF flag% > 1 AND W2$ = "DOOR" AND W3$ = "" THEN Reply$ = "Which door?"
IF flag% > 1 AND W2$ = "DOOR" AND W3$ <> "" THEN Reply$ = "Sorry, my head hurts with all these doors!"
IF flag% = 1 THEN MID$(Barrier$(CurrentLocation%), flag2%, 2) = "00": Reply$ = "Door is now open"
RETURN
SUB DrawHeader
360 CLS
370 FOR a = 1 TO 79
380 LOCATE 1, a: COLOR 4: PRINT "Û"
390 LOCATE 2, a: COLOR 12: PRINT "±"
400 LOCATE 4, a: COLOR 12: PRINT "±"
410 LOCATE 5, a: COLOR 4: PRINT "Û"
420 NEXT a
430 LOCATE 3, 1: PRINT "Û"; : COLOR 12: PRINT "±±±"
440 LOCATE 3, 76: PRINT "±±±"; : COLOR 4: PRINT "Û"
450 LOCATE 2, 1: PRINT "ÛÛ": LOCATE 2, 78: PRINT "ÛÛ"
460 LOCATE 4, 1: PRINT "ÛÛ": LOCATE 4, 78: PRINT "ÛÛ"
470 COLOR 14: LOCATE 3, 25: PRINT "TEXT ADVENTURE : SAVE THE PRINCESS"
END SUB
SUB Help
LOCATE 22, 1: PRINT "HELP QUIT GO RUN WALK CRAWL HOP GET TAKE GRAB 'PICK UP' INVENTORY RUMMAGE"
LOCATE 23, 1: PRINT "DROP RESTART UNLOCK OPEN"
Reply$ = "All I can offer is this list of keywords"
END SUB
SUB IntroScreen
SCREEN 9: CLS
a$ = "TEXT ADVENTURE : SAVE THE PRINCESS"
RAND = 0
COLOR 2: LOCATE 2, (80 - LEN(a$)) / 2: PRINT a$
COLOR 10: LOCATE 5, 1
PRINT "ADVENTURE is a text adventure where you type in simple commands to complete"
PRINT "the game.   You are the local hero, and must save the princess from the clutchesof the evil witch.   The evil witch lives in a castle and has imprisoned the"
PRINT "princess in a dungeon.   You must save her.   Use commands like GO NORTH or     TAKE FOOD or KISS FROG to control your character."
LOCATE 20, 33: COLOR 12: PRINT "Press any key"
a$ = ""
WHILE a$ = "": a$ = INKEY$: RAND = RAND + .1: WEND
RAND = RAND - VAL(TIME$)
RANDOMIZE RAND
END SUB
SUB QuitFunction
LOCATE 22, 1: PRINT "Are you sure you want to quit this game (Y)es or (N)o ?"
WHILE a$ = "": a$ = UCASE$(INKEY$): WEND
IF a$ = "Y" THEN QuitScreenConfirm
Reply$ = "Quit action cancelled"
END SUB
SUB QuitScreenConfirm
LOCATE 22, 1: PRINT "Are you REALLY sure that you want to quit my game (Y)es or (N)o ?"
a$ = "": WHILE a$ = "": a$ = UCASE$(INKEY$): WEND
IF a$ = "Y" THEN CLOSE ALL: SYSTEM
END SUB
SUB RESTART
LOCATE 22, 1: PRINT "Are you sure you want to restart this game (Y)es or (N)o ?"
WHILE a$ = "": a$ = UCASE$(INKEY$): WEND
IF a$ = "Y" THEN RestartConfirm
Reply$ = "Quit action cancelled"
END SUB
SUB RestartConfirm
LOCATE 22, 1: PRINT "Are you REALLY sure that you want to restart my game (Y)es or (N)o ?"
a$ = "": WHILE a$ = "": a$ = UCASE$(INKEY$): WEND
IF a$ = "Y" THEN RUN
END SUB

This Picture shows how you should set up a text adventure. The current Location Box has lines jutting out showing all possible directions of travel. Gray boxes show where you are not permitted to go.
Now you have some idea of how to make text adventures. However if you make your first game and spend lot's of time doing it, you don't want to have to worry about people pirating the program or altering it. So to prevent this the following code can be inserted at the beginning of the program:

CLS
XVal% = INT(RND * 100) + 1
YVal% = INT(RND * 5) + 1
LOCATE 5, 5: PRINT "Enter security code for entry : "; XVal%; ", "; YVal%
LOCATE 30, 5: INPUT ":"; code$
IF VAL(code$) <> STATS%(XVal%, YVal%) THEN PRINT "INCORRECT!": SYSTEM
LOCATE 30, 6: PRINT "CORRECT!"
When this program is run you will get a screen like this:
Enter security code for entry : 70, 1
 
The user of the program will need to be supplied with a list of these codes (ex : 70, 1) to find the corresponding password. Earlier we went over keyboard usage, but we will now learn about the pressakey command in the following code:
WHILE pressakey$<>"X"
pressakey$ = " "
WHILE pressakey$ = " ": pressakey$=UCASE$(INKEY$) : WEND
IF pressakey="T" THEN moveup
IF pressakey="G" THEN movedown
IF pressakey="F" THEN moveleft
IF pressakey="H" THEN moveright
WEND
Computer games are not much fun with the keyboard, or for the keyboard either. If your really engrosed in a game and you rapidly strike the keys they're bound to brake eventually. So this is where the Joystick fits in. There is a special command in QBasic made just for the joystick and that would be STRIG. The format for using the STRIG statement is as follows: STRIG(n%) ON                Enables Joystick event trapping.
STRIG(n%) OFF               Disables Joystick event trapping.
STRIG(n%) STOP            Suspends Joystick event trapping.
n%        Trigger
0            Lower trigger, Joystick A
2            Lower trigger, Joystick B
4            Upper trigger, Joystick A
6            Upper trigger, Joystick B
The following program will detedt when a joystick's trigger has been activated :
CLS
ON STRIG(0) GOSUB Handler
STRIG(0) ON
PRINT "Press Esc to Exit"
DO UNTIL INKEY$ = CHR$(27) : LOOP
END Handler :
    PRINT "Joystick Trigger has been pressed"
RETURN
Program's CH12_3.BAS and CH12_4.BAS contain more code for the joystick. We hope this section has given you plenty of knowledge on QBasic Programming.
 



 Sound
Introduction
Soon after the introduction of the Personal Computer, people began to realize its possibilities for playing games. The PC was from itself equipped with a relatively adequate graphics system, but the sound capabilities were very limited. This changed with the introduction of special addon sound cards, such as the AdLib card and the SoundBlaster card from Creative Labs. Nowadays, the majority of PCs has a sound card installed, and most of them are more or less compatible with the SoundBlaster card.
In this document, we will look at using a SoundBlaster compatible card with QBasic. Such a card actually combines different methods or systems for producing sound. These methods may include waveform sound, FM systhesizer music, sound from the Creative Music System (not used a lot nowadays), MIDI compatibility and wave table sounds. Also, many sound cards are capable of digitizing sound. In this document, we will limit ourselves to discussing waveform and FM sound, because these two methods are the most widespread. With the experience gained from working with these methods and the specifications of the system, you will be able to work out how to control other sound systems.
Controlling a sound card in QBasic is done by sending data to hardware ports, by means of the OUT statement. Hardware ports are the connections between the computer and peripheral devices, such as printers or modems. Data can also be send the other way, from the device to the computer. In QBasic, this data is read in using the INP function.
The SoundBlaster card can be configured to use different sets of hardware ports, identified by their base address, which is the number of the first port in the set. For instance, if the card is set to use hardware port numbers 220h to 233h, then its base address is 220h. To control the sound card, you have to find out its base address. Usually, the install program that comes with the sound card, has set up an environment variable containing the base address. If you type the command
SET
at the DOS command line, a list will be printed with all the environment variables. There should be an item in the list that looks like this:
BLASTER=A220 I5 D1 T3
The number just after the 'A' is the base address, in hexadecimal notation, in this case 220h. The other numbers specify the interrupt request number, the DMA channel number and the version number, but these don't concern us. The programs in this document assume 220h to be the base address, so if your card is set to another address, you will have to change the value BaseAddr in the programs.
To save the programs discussed in this document to your hard disk in standard ASCII format, click on the name with the righthand mouse button and select 'Save Link As...' from the menu.
 
FM synthesized music
A very different form of sound output is FM systhesis. This section applies also to AdLib sound cards, but AdLib owners should change the base address in the programs below to 380h. In this section we will look at how to produce sound using the FM system, and we will experiment with some of the parameters used to define the sound.
FM stands for frequency modulation. The sound is formed by having a carrier sound being modulated by a modulator sound. We can define up to nine 'instruments', each consisting of a carrier and a modulator. We can let these nine instruments play different notes together, producing complicated tunes. The instruments are defined by a lot of parameters, from which we will discuss only a few.
The FM chip on the sound card is programmed by setting registers in the chip to certain values. There are 224 of such registers, so you'll understand that we won't discuss every one of those. To set a register to a value, we send the number of the register to the Register Port, whose address is base+8. Then, we send the desired value to the Data Port, with address base+9. The carrier and modulator of each instrument both have four registers in which parameters are placed. Since there are nine channels (instruments), that already gives us 2 x 4 x 9 = 72 registers to program! Of course, we don't have to use all nine channels. The register numbers for the carrier of channel 1 are given in table 2.
Table 2: FM register numbers for carrier of channel 1 Number Function
20h Amplitude modulation/Vibrato/EG type/Key scaling/Octave shift
40h Key scaling level/Output level
60h Attack rate/Decay rate
80h Sustain level/Release time
 
The functions given in table 2 will be explained below. To find the other 68 register numbers, add the offset numbers from table 3 to the base numbers in table 2.
Table 3: FM register offset numbers for carrier and modulator of channels 19 Channel Offset for carrier Offset for modulator
1 00h 03h
2 01h 04h
3 02h 05h
4 08h 0Bh
5 09h 0Ch
6 0Ah 0Dh
7 10h 13h
8 11h 14h
9 12h 15h
 
For example, to find the register number for the Attack rate/Sustain rate of the modulator of channel 6, add 0Dh to 60h to find 6Dh.
To define an instrument, values should be assigned to parameters. As you can see in table 2, two or more parameters are combined into one register. Each register is eight bits wide, so the register values can range from 0 to 255. These eight bits are devided over two or more parameters, so each parameter has less than eight bits available. For instance, if a parameter has three bits available, its values will range from 0 to 7. The total register value is found by combining the values for the different parameters using the appropiate coefficients.
We will now look at what the parameters mean. We will look at the registers for the carrier of channel 1, i.e. 20h, 40h, 60h and 80h, but the same goes of course for the other channels.
Register 20h looks like this: Bit 7 6 5 4 3 2 1 0
Parameter AM Vib   Octave shift
The value (ranging from 0 to 15) in bits 03 specifies whether the octave a note is played at should be changed from the specified value. If set to 0, the note is played one octave lower than specified. If set to 1, there is no change. If set to 2, the note is shifted one octave up. There are other values possible, but we will not discuss those here. We will also not discuss the function of bits 4 and 5. Setting bit 6 applies vibrato to the sound. Setting bit 7 causes amplitude modulation in the sound. The depth of the amplidute modulation and vibrato is specified for all channels through one register, BDh: Bit 7 6 5 4 3 2 1 0
Parameter AM Vib
If bit 6 is set, the vibrato (if applied) is set to 14 percent. If clear, the vibrato is 7 percent. If bit 7 is set, the amplitude modulation depth (if applied) is 4.8 dB. If clear, it is 1 dB. We will leave the other bits of register BDh for what they are.
Register 40h looks like this: Bit 7 6 5 4 3 2 1 0
Parameter Scaling Output level
The output level value, bits 05, ranges from 0 to 63. 63 corresponds to the lowest level, 0 dB, and 0 to the highest level, 47 dB. Bits 67 (ranging 03) specify how quickly the output level rises if the pitch of the sound goes up. 0 is no rise, 1 is 1.5 dB/octave, 2 is 3 dB/octave and 3 is 6 dB/octave.
Before we look at registers 60h and 80h, we first need to know a little bit more about how a note played on an instrument is built up. We distinguish four phases in the note. First, there is the 'attack'. This is the fast rise in level at the beginning of the note. Then follows the 'decay'. This is when, after reaching peak level, the sound volume drops till a certain level. This level is called the 'sustain' volume. The sound stays at this level until the 'release' time is reached, at which point the sound stops. Look at figure 2 to see a graphical representation of this idea.
 
 Now, the attack rate defines how quickly the sound level initially rises, and the decay rate specifies how quickly it drops again to the sustain volume. The release time controls how long the sound stays at the sustain volume. These four parameters can be varied producing different sound 'shapes'. A number of these shapes is given in figure 3.
 Figure 3: Schematic results of varying attack rate, decay rate, sustain level and release time. a: high attack rate, low decay rate, low sustain level, short release time. b: low attack rate, high decay rate, high sustain level, long release time. c: high attack rate, high decay rate, low sustain level, long release time. d: high attack rate, high decay rate, high sustain level, short release time.
 
 The attack rate and decay rate are specified by register 60h: Bit 7 6 5 4 3 2 1 0
Parameter Attack rate Decay rate
Bits 47 specify the attack rate, a value between 0 (slowest) and 15 (fastest).
The sustain level and release time are controlled by register 80h, which look like this: Bit 7 6 5 4 3 2 1 0
Parameter Sustain level Release time
Bits 03 specify the release time, from 0 (longest) to 15 (slowest). Bits 4 7 specify the sustain level, from 0 (loudest) to 15 (softest).
As you can see, defining an instrument is not the simplest of tasks. There are still more registers, but I'm sure you've had enough for a while by now.
Now we will have a look at how to actually use the instrument we have just learnt to define. To hear a note play, we have to specify the note and the octave. We have eight octaves at our disposal, numbered 07. The notes, normally written down as letters, have gotten numbers. These can be found in table 4:
Table 4: FM note representations Note Number
C# 16Bh
D 181h
D# 198h
E 1B0h
F 1CAh
F# 1E5h
G 202h
G# 220h
A 241h
A# 263h
B 287h
C 2AEh
As you can see, these numbers take up ten bits, and because we have only eightbits registers, the numbers have to be split into two parts. The eight least significant bits go into registers A0h (for channel 1) to A8h (for channel 9). The two most significant bits go as bits 0 and 1 into registers B0h (for channel 1) to B8h (for channel 9). Register A0h looks like this:

Bit 7 6 5 4 3 2 1 0
Parameter Eight LSB of note number
while register B0h looks like this: Bit 7 6 5 4 3 2 1 0
Parameter Unused Switch Octave Two MSB
Bits 24 specify the octave the note is played at. Bit 5 turns the channel on and off. When bit 5 is set, the note starts playing. When it is cleared, the sound stops, and a new note can be played. Registers A0h and B0h are for channel 1, but the procedure is of course the same for the other channels (registers A1hA8h and B1hB8h). Now we're ready to play some music. The following Program shows how to play a simple tune, using channels 1, 2 and 3:
Title this program : FMTUNE.BAS
DECLARE SUB SetReg (Reg%, Value%)
CONST BaseAddr = &H220 'Change if your sound card uses another base address CONST RegAddr = BaseAddr + 8, DataAddr = BaseAddr + 9
DEFINT AZ
FOR i = 0 TO 224
  SetReg i, 0 'Clear all registers
NEXT i
SetReg &H20, &H1 'Plays carrier note at specified octave ch. 1
SetReg &H23, &H1 'Plays modulator note at specified octave ch. 1
SetReg &H40, &H1F 'Set carrier total level to softest ch. 1
SetReg &H43, &H0 'Set modulator level to loudest ch. 1
SetReg &H60, &HE4 'Set carrier attack and decay ch. 1
SetReg &H63, &HE4 'Set modulator attack and decay ch. 1
SetReg &H80, &H9D 'Set carrier sustain and release ch. 1
SetReg &H83, &H9D 'Set modulator sustain and release ch. 1
SetReg &H21, &H1 'Plays carrier note at specified octave ch. 2
SetReg &H24, &H1 'Plays modulator note at specified octave ch. 2
SetReg &H41, &H1F 'Set carrier total level to softest ch. 2
SetReg &H44, &H0 'Set modulator level to loudest ch. 2
SetReg &H61, &HE4 'Set carrier attack and decay ch. 2
SetReg &H64, &HE4 'Set modulator attack and decay ch. 2
SetReg &H81, &H9D 'Set carrier sustain and release ch. 2
SetReg &H84, &H9D 'Set modulator sustain and release ch. 2
SetReg &H22, &H1 'Plays carrier note at specified octave ch. 3
SetReg &H25, &H1 'Plays modulator note at specified octave ch. 3
SetReg &H42, &H1F 'Set carrier total level to softest ch. 3
SetReg &H45, &H0 'Set modulator level to loudest ch. 3
SetReg &H62, &HE4 'Set carrier attack and decay ch. 3
SetReg &H65, &HE4 'Set modulator attack and decay ch. 3
SetReg &H82, &H9D 'Set carrier sustain and release ch. 3
SetReg &H85, &H9D 'Set modulator sustain and release ch. 3
READ NoOfNotes
FOR i = 1 TO NoOfNotes
  time! = TIMER
  FOR j = 0 TO 2 'Voices 0, 1 and 2
    READ octave
    READ note$
    SELECT CASE note$
    CASE "C#"
      SetReg &HA0 + j, &H6B 'Set note number
      SetReg &HB0 + j, &H21 + 4 * octave 'Set octave and turn on voice
    CASE "D"
      SetReg &HA0 + j, &H81
      SetReg &HB0 + j, &H21 + 4 * octave
    CASE "D#"
      SetReg &HA0 + j, &H98
      SetReg &HB0 + j, &H21 + 4 * octave
    CASE "E"
      SetReg &HA0 + j, &HB0
      SetReg &HB0 + j, &H21 + 4 * octave
    CASE "F"
      SetReg &HA0 + j, &HCA
      SetReg &HB0 + j, &H21 + 4 * octave
    CASE "F#"
      SetReg &HA0 + j, &HE5
      SetReg &HB0 + j, &H21 + 4 * octave
    CASE "G"
      SetReg &HA0 + j, &H2
      SetReg &HB0 + j, &H22 + 4 * octave
    CASE "G#"
      SetReg &HA0 + j, &H20
      SetReg &HB0 + j, &H22 + 4 * octave
    CASE "A"
      SetReg &HA0 + j, &H41
      SetReg &HB0 + j, &H22 + 4 * octave
    CASE "A#"
      SetReg &HA0 + j, &H63
      SetReg &HB0 + j, &H22 + 4 * octave
    CASE "B"
      SetReg &HA0 + j, &H87
      SetReg &HB0 + j, &H22 + 4 * octave
    CASE "C"
      SetReg &HA0 + j, &HAE
      SetReg &HB0 + j, &H22 + 4 * octave
    END SELECT
  NEXT j
  READ duration!
  DO
  LOOP WHILE time! + duration! > TIMER 'Wait as long as duration
  FOR j = 0 TO 2
    SetReg &HB0 + j, 0 'Switch voices off
  NEXT j
NEXT i
END
DATA 15: REM Number of notes
'Data below: octave1, note1, octave2, note2, octave3, note3, duration
DATA 4,B,4,G,4,D,.5
DATA 4,B,4,G,4,D,.5
DATA 4,B,4,G,4,D,.5
DATA 4,B,4,G,4,D,.5
DATA 5,D,4,B,4,F#,.25
DATA 4,C,4,A,4,E,.25
DATA 4,C,4,A,4,E,.25
DATA 4,B,4,G,4,D,.25
DATA 4,A,4,E,3,C,1
DATA 4,A,4,F#,4,D,.5
DATA 4,A,4,F#,4,D,.5
DATA 4,B,4,G,4,E,.5
DATA 4,C,4,A,4,F#,.5
DATA 5,D,4,A,4,F#,1
DATA 5,G,5,D,4,B,.5
SUB SetReg (Reg, Value)
  OUT RegAddr, Reg
  OUT DataAddr, Value
END SUB
  First, the SUB SetReg is declared. This SUB puts the specified value into the specified register. Then, all registers are cleared. The registers for the first three channels are set to three identical instruments; some kind of electronic piano sound. The octaves and notes are read from the DATA statements, and the SELECT CASE statement chooses the correct number for the note. The octave and note numbers are put in their respective registers, and the note starts playing. We wait for a time specified by the duration variable using the TIMER system variable, and then registers B0h, B1h and B2h are set to zero, and bit 5 with them, to switch the channel off. The DATA statements at the end of the program describe the tune. The first DATA statement specifies the number of notes, and the statements that follow specify the octave and note for each channel, and the duration of the note in seconds.
Working out the correct values for an instrument can be a long and tedious process. However, there are ways of making this easier. For your convenience, I have made the program FMLAB.BAS. This program lets you play with four of the parameters: the attack rate, the decay rate, the sustain level and the release time. When you run this program, a screen is printed as depicted in figure 4.
 
 The parameter currently chosen is highlighted. You can adjust the value for this parameter using the up and down arrow keys. You can choose another parameter with the left and right arrow keys. Press Enter to hear the note you have just defined. Esc ends the program. This program demonstrates very well the effects the different parameters have on the sound. You could expand this program to include the other parameters as well. When you are satisfied with the sound, you could use the values in a program similar to FMTUNE.BAS.
The program FMLAB.BAS doesn't introduce new SoundBlaster programming techniques, so we won't have a detailed look at it. We hope that this document has given you some idea on SoundBlaster programming, and has encouraged you to perform some experminents of your own.
In this section we have looked at how to talk to a SoundBlaster compatible sound card, using OUT and INP statements. We have looked at digitizing sound from an external sound source. We have seen that the sound can be plotted, stored and played back. We have seen how we can read a WAV file and how to play it back. We have looked at a number of FM registers for specifying instruments, notes and octaves. We used this knowledge to program a simple tune using three channels. Finally, we have experimented a bit with four of the parameters: attack rate, decay rate, sustain level and release time.

User Interfaces
A user interface is a fairly importantpiece of a program. The user interface allows for interaction and adjustments within a program. A user interface should be very easy to use, you should assume that someone has no clue what your program is about and build on that. A good effective user interface should contain the following items :
· Menus
· Fields for data entry and editing
· Illustrations, text, messages, and references
· The results of data processing
· Selection fields - lists of objects, from which the user chooses one (one selection) or several (multi-choice selection) objects.
· Pause fields which output messages and wait for a key to be pressed.
· Program titles and stages of execution, labels or headings of data groups and entry fields.
· Explanation of control and function keys
One good example of a user interface is the QBasic program. It contains a main action bar, panel title, program entry and edit field, 'immediate' field, function and control keys, pop-up dialog window, pop-up window title corresponding to menu item, field titles, entry field, selection field, pop-up window menu, and a selection cursor. All of these attributes make a good user interface.
First we're going to teach you how to insert a pointer to select items. This program will allow you to use your arrow keys to select an option on the menu and enter to execute it. The source for doing so would be this:

DECLARE SUB Triangle2 ()
DECLARE SUB Triangle3 ()
DECLARE SUB Triangle1 ()
DECLARE SUB Menu (X0!, Y0!, IM$, NT%)
DECLARE SUB Triangles ()
DECLARE SUB Circle1 ()
DECLARE SUB Trapesium ()
DECLARE SUB Rectangle ()
'------------------------------------------------------------
DO
 CALL Menu(10, 15, "Main", MNT%)
 SELECT CASE MNT%
 CASE 1
  CALL Rectangle
 CASE 2
  CALL Trapesium
 CASE 3
  CALL Triangles
 CASE 4
  CALL Circle1
 CASE 5
  SCREEN 0
  CLS
  WIDTH 40
  LOCATE 10, 13
  PRINT "G O O D - B Y E"
 END SELECT
LOOP UNTIL MNT% = 5
END
'------------------------------------------------------------
DataMenuMain:
DATA 5, " A R E A S ", Rectangle, Trapesium, Triangle
DATA Circle, Exit
'------------------------------------------------------------
DataMenuTriangles:
DATA 4, "D A T A :", "Three sides", "Base and height"
DATA "Two sides and angle", "Main menu"
'============================================================ SUB Circle1
'   *********************************
'   * Calculation of area of circle *
'   *********************************
SCREEN 2
CLS
CIRCLE (70, 40), 50
LINE (70, 20)-(70, 60), , , &HAAAA
LOCATE 6, 8
PRINT "D"
LOCATE 12, 1
PRINT "Enter parameter:"
PRINT
INPUT "D = ", D
S = D ^ 2 * 3.141593 / 4
PRINT
PRINT "S ="; S
Pause$ = INPUT$(1)
END SUB
SUB Menu (X0, Y0, IM$, NT%)
'   ******************************************
'   *                 Menu                   *
'   *   Items are selected using a pointer   *
'   ******************************************
' X0, Y0 - coordinates of upper left corner of menu
' IM$ - selection of data block for menu
' NT% - number of selected option
SCREEN 0
CLS
WIDTH 40
Wide% = 40        'screen width
SELECT CASE IM$
CASE "Main"
 RESTORE DataMenuMain
CASE "Triangles"
 RESTORE DataMenuTriangles
END SELECT
READ N%, Title$   'number of menu items and a title
' *** Centering and outputting title ***
T = (Wide% - LEN(Title$)) / 2
LOCATE X0 - 2, T
PRINT Title$
' *** Output of menu onto screen ***
FOR I = 1 TO N%
 READ B$
 LOCATE X0 + I - 1, Y0
 PRINT B$
NEXT I
' *** Selection of item ***
X = X0: Y = Y0 - 2    'current position of pointer
NT% = 1
DO
 LOCATE X, Y
 PRINT "->"
 DO
  Key$ = INKEY$
 LOOP WHILE Key$ = ""
 SELECT CASE Key$
  CASE CHR$(0) + CHR$(72)   'Up
   NT% = NT% - 1
   IF NT% < 1 THEN NT% = N%
  CASE CHR$(0) + CHR$(80)   'Down
   NT% = NT% + 1
   IF NT% > N% THEN NT% = 1
  CASE CHR$(13)             'Enter
   EXIT DO
 END SELECT
 LOCATE X, Y: PRINT "  "
 X = X0 + NT% - 1
LOOP
END SUB
SUB Rectangle
'   ****************************************
'   *   Calculation of area of rectangle   *
'   ****************************************
SCREEN 2
CLS
LINE (20, 20)-(190, 60), , B
LOCATE 2, 14
PRINT "a"
LOCATE 5, 26
PRINT "b"
LOCATE 12, 1
PRINT "Enter parameters:"
PRINT
INPUT "a = ", A
INPUT "b = ", B
S = A * B
PRINT
PRINT "S ="; S
Pause$ = INPUT$(1)
END SUB
SUB Trapesium
'   **************************************
'   *  Calculation of area of trapesium  *
'   **************************************
SCREEN 2
CLS
LINE (50, 20)-(180, 20)
LINE -(240, 60)
LINE -(20, 60)
LINE -(50, 20)
LINE (50, 20)-(50, 60), , , &HAAAA
LOCATE 2, 14
PRINT "a"
LOCATE 9, 14
PRINT "b"
LOCATE 6, 8
PRINT "h"
LOCATE 12, 1
PRINT "Enter parameters:"
PRINT
INPUT "a = ", A
INPUT "b = ", B
INPUT "h = ", H
S = (A + B) / 2 * H
PRINT
PRINT "S ="; S
Pause$ = INPUT$(1)
END SUB
SUB Triangle1
'   ****************************************************
'   *  Calculation of area of triangle by three sides  *
'   ****************************************************
SCREEN 2
CLS
LINE (50, 20)-(240, 60)
LINE -(20, 60)
LINE -(50, 20)
LOCATE 5, 19
PRINT "c"
LOCATE 9, 14
PRINT "a"
LOCATE 5, 4
PRINT "b"
LOCATE 12, 1
PRINT "Enter parameters:"
PRINT
INPUT "a = ", A
INPUT "b = ", B
INPUT "c = ", C
ABC = A > 0 AND B > 0 AND C > 0
IF A < B + C AND B < A + C AND C < A + B AND ABC THEN
 P = (A + B + C) / 2
 S = SQR((P - A) * (P - B) * (P - C) * P)
 S = CLNG(S * 100) / 100    'rounding off to 0.01
 PRINT
 PRINT "S ="; S
ELSE
 PRINT
 PRINT "Such a triangle does not exist!"
END IF
END SUB
SUB Triangle2
'   ********************************************************
'   *  Calculation of area of triangle by base and height  *
'   ********************************************************
SCREEN 2
CLS
LINE (50, 20)-(240, 60)
LINE -(20, 60)
LINE -(50, 20)
LINE (50, 20)-(50, 60), , , &HAAAA
LOCATE 9, 14
PRINT "a"
LOCATE 6, 8
PRINT "h"
LOCATE 12, 1
PRINT "Enter parameters:"
PRINT
INPUT "a = ", A
INPUT "h = ", H
IF A > 0 AND H > 0 THEN
 S = A * H / 2
 PRINT
 PRINT "S ="; S
ELSE
 PRINT
 PRINT "Such a triangle does not exist!"
END IF
END SUB
SUB Triangle3
'   *****************************************
'   *    Calculation of area of triangle    *
'   *  by two sides and angle between them  *
'   *****************************************
SCREEN 2
CLS
LINE (50, 20)-(240, 60)
LINE -(20, 60)
LINE -(50, 20)
LOCATE 9, 14
PRINT "a"
LOCATE 5, 4
PRINT "b"
LOCATE 7, 6
PRINT "C"
LOCATE 12, 1
PRINT "Enter parameters:"
PRINT
INPUT "a = ", A
INPUT "b = ", B
INPUT "C = ", C
IF A > 0 AND B > 0 AND C > 0 AND C < 180 THEN
 Pi = 3.141593
 S = A * B / 2 * SIN(C * Pi / 180)
 S = CLNG(S * 100) / 100    'rounding off to  0.01
 PRINT
 PRINT "S ="; S
ELSE
 PRINT
 PRINT "Such a triangle does not exist!"
END IF
END SUB
SUB Triangles
' *************************************************
' *   Selection  of source data for calculation   *
' *              of area of triangle              *
' *   Demonstration of use of second-level menu   *
' *************************************************
DO
 CALL Menu(10, 14, "Triangles", NT1%)
 SELECT CASE NT1%
 CASE 1
  CALL Triangle1
 CASE 2
  CALL Triangle2
 CASE 3
  CALL Triangle3
 CASE 4
  EXIT DO
 END SELECT
 Pause$ = INPUT$(1)
LOOP
END SUB
Now this is a good example of a user interface, but it's just not good enough. The program CH3_10.BAS is a really good example. It is a database for the 'Holders of the Blue Ribbon of the Atlantic'. The unique thing about this user interface is that it does not only have text fields, multiple choices, function keys, and the ability to save data to a file titled LINERS.DBF. The source code cannot be displayed on our site for it is too big, but the file can be downloaded with this section's software pack. another good example with the above attributes would be CH13_12.BAS.  

3 comments:

  1. IPhone stress test: how did it do? As you swipe through the list and highlight different
    items, the map moves around -- in other words, you can check out my other hub.
    Phosphorus from phytate is unavailable to humans, as we continued waking up each and every day.



    my web page: a diet that works

    ReplyDelete