|
 |
 |
7
Borrowing From
BASIC
BASIC is a collection of
ML subroutines. It is a large web of hundreds of short, ML programs.
Why not use some of them by JSRing to them? At times, this is in
fact the best solution to a problem. How
would this differ from BASIC itself? Doesn't BASIC just create a
series of JSR's when it RUNS? Wouldn't using BASIC's ML routines in
this way be just as slow as BASIC? In
practice, you will not be borrowing from BASIC all that much. One
reason is that such JSRing makes your program far less portable,
less easily RUN on other computers or other models of your computer.
When you JSR to an address within your ROM set to save yourself the
trouble of re-inventing the wheel, you are, unfortunately, making
your program applicable only to machines which are the same model as
yours. The subroutine to allocate space for a string in memory is
found at $D3D2 in the earliest PET model. A later version of PET
BASIC (Upgrade) used $D3CE and the current models use $C61D. With
Atari, Texas Instruments, Sinclair and other computers as
exceptions, Microsoft BASIC is nearly universally used in personal
computers. But each computer's version of Microsoft differs in both
the order and the addresses of key subroutines.
Kernals And jump
Tables To help overcome this lack of portability, some
computer manufacturers set aside a group of frequently used
subroutines and create a Jump Table, or kernal, for them. The idea
is that future, upgraded BASIC versions will still retain this
table. It would look something like this:
FFCF 4C 15 F2 (INPUT one
byte) FFD2 4C 66 F2
(OUTPUT one byte) FFD5
4C 01 F4 (LOAD something) FFD8 4C DD F6 (SAVE
something)
This example is part of the
Commodore kernal. There is a trick to the way
this sort of table works. Notice that each member of the table
begins with 4C. That's the JMP instruction and, if you land on it,
the computer bounces right off to the address which follows. $FFD2
is a famous one in Commodore computers. If you load the accumulator
with a number (LDA #65) and then JSR FFD2, a character will be
printed on the screen. The screen location is incremented each time
you use it, so it works semi-automatically. In other words, it also
keeps track of the current "cursor position" for you.
This same "output" routine will work for a printer or a
disk or a tape - anything that the computer sees as an output
device. However, unless you open a file to one of the other devices
(it's simplest to do this from BASIC in the normal way and then SYS,
USR, or CALL to an ML subroutine), the computer defaults to
(assumes) the screen as the output device, and FFD2 prints
there. What's curious about such a table is
that you JSR to FFD2 as you would to any other subroutine. But
where's the subroutine? It's not at FFD5. That's a different JMP to
the LOAD code. A naked JMP (there is no RTS here in this jump table)
acts like a rebound: you hit one of these JMP's in the table and
just bounce off it to the true subroutine.
The real subroutine (at $F266 in one BASIC version's $FFD2's
JMP) will perform what you expect. Why not just JSR to F266
directly? Because, on other models of Commodore computers – Original
BASIC, for example - the output subroutine is not located at F266.
It's somewhere else. But a JSR to FFD2 will rebound you to the right
address in any Commodore BASIC. All Commodore machines have the
correct JMP for their particular BASIC set up at FFD2. This means
that you can JSR to FFD2 on any Commodore computer and get
predictable results, an output of a byte. So,
if you look into your BASIC code and find a series of JMP's (4C xx
xx 4C xx xx), it's a jump table. Using it should help make your
programs compatible with later versions of BASIC which might be
released. Though this is the purpose of such tables, there are never
any guarantees that the manufacturer will consistently observe them.
And, of course, the program which depends on them will certainly not
work on any other computer brand.
What's
Fastest? Why, though, is a JSR into BASIC code faster
than a BASIC program? When a BASIC program RUNS, it is JSRing around
inside itself. The answer is that a program written entirely in ML,
aside from the fact that it borrows only sparingly from BASIC
prewritten routines, differs from BASIC in an important way. A
finished ML program is like compiled code; that is, it is ready to
execute without any overhead. In BASIC each command or instruction
must be interpreted as it RUNS. This is why BASIC is called an
"interpreter." Each instruction must be looked up in a table to find
its address in ROM. This takes time. Your ML code will contain the
addresses for its JSR's. When ML runs, the instructions don't need
the same degree of interpretation by the computer.
There are special programs called compilers which take a BASIC
program and transform ("compile") it into ML-like code which can
then be executed like ML, without having to interpret each command.
The JSR's are within the compiled program, just as in ML.
Ordinarily, compiled programs will RUN perhaps 20 to 40 times faster
than the BASIC program they grew out of. (Generally, there is a
price to pay in that the compiled version is almost always larger
than its BASIC equivalent.) Compilers are
interesting; they act almost like automatic ML writers. You write it
in BASIC, and they translate it into an ML-like program. Even
greater improvements in speed can be achieved if a program uses no
floating point (decimal points) in the arithmetic. Also, there are
"optimized" compilers which take longer during the translation phase
to compile the finished program, but which try to create the
fastest, most efficient program design possible. A good compiler can
translate an 8K BASIC program in two or three
minutes.
GET And
PRINT Two of the most common activities in a computer
program are getting characters from the keyboard and printing them
to the screen. To illustrate how to use BASIC from within an ML
program, we'll show how both of these tasks can be accomplished from
within ML. For the Atari, $F6E2 works like
BASIC's GET#. If you JSR $F6E2, the computer will wait until a key
is pressed on the keyboard. Then, when one is pressed, the numerical
code for that key is put into the accumulator, and control is
returned to your ML program. To try this, type:
2000 JSR $F6E2 2003
BRK
Then run this program
and hit a key on the keyboard. Notice that the code number for that
letter appears in the accumulator. Another location within Atari's
BASIC ROM will print a character (whatever's in the accumulator) to
the next available position on the screen. This is like PUT#6. Try
combining the above GET# with this:
2000 JSR $F6E2 (get the
character) 2003 JSR $F6A4
(print to the screen) 2006 BRK
Using $F6A4 changes the numbers in the X and Y registers
(explained below). For the Apple, there are
BASIC routines to accomplish these same jobs. Apple Microsoft
BASIC's GET waits for user input. (Commodore's GET doesn't wait for
input.)
2000 JSR $FD0C
(GET a byte from the keyboard) 2003 RTS
(the character is in the
accumulator)
This address, $FD0C, will
wait until the user types in a character. It will position a
flashing cursor at the correct position. However, it will not print
an "echo," an image of the character on the screen.
To print to the screen:
2000
LDA # 65 (put "a" into the
accumulator) 2002 JSR $FBFD
(print it)
For Commodore computers (VIC, 64,
and PET/CBM) which also use Microsoft BASIC, the two subroutines are
similar:
2000 JSR
$FFE4 (GET whatever key is being
pressed) 2003 BEQ 2000
(if no key is pressed, a zero is in the accumulator,
so you BEQ back and try for a character again) 2005 RTS
(the character's value is in the
accumulator)
The $FFE4 is another one of
those "kernal" jump table locations common to all Commodore
machines. It performs a GET. An ML routine
within your BASIC which keeps track of the current cursor position
and will print things to the screen is often needed in ML
programming. The VIC, 64, and PET/CBM use the
routine called by $FFD2. Apple uses $FDED. Atari uses
$F6A4. You can safely use the Y register to
print out a series of letters (Y used as an index) in any BASIC
except Atari's. You could print out a whole word or block of text or
graphics stored at $1000 in the following way. (See Program
7-1.)
Atari's BASIC alters the X and Y
registers when it executes its "print it" subroutine so you need to
keep count some other way. Whenever you borrow from BASIC, be alert
to the possibility that the A, X, or Y registers, as well as the
flags in the status register, might well be changed by the time
control is returned to your ML program. Here's one way to print out
messages on the Atari. (See Program 7-2.)
If you look at Appendix B you will see that there are hundreds
of freeze-dried ML modules sitting in BASIC. (The maps included in
this book are for VIC, PET, Atari, and Commodore 64. Appendix B
contains information on how to obtain additional maps for Apple and
Atari.) It can be intimidating at first, but
disassembling some of these routines is a good way to discover new
techniques and to see how professional ML programs are constructed.
Study of your computer's BASIC is worth the effort, and it's
something you can do for yourself. From time to time, books are
published which go into great detail about each BASIC routine. They,
too, are often worth studying.
Program 7-1.
|
0010
|
; COMMODORE &
APPLE VERSION
|
|
0020
|
|
.BA
$2000
|
|
0030
|
|
.OS
;(OUTPUT SOURCE
CODE)
|
|
0040
|
COUNTER
|
.DE $55
;(WILL HOLD INDEX)
|
2000- 53 55
50
|
0050
|
STRING
|
.BY 'SUPERDUPER' ; STORE THIS
TEXT STRING
|
2003- 45 52
44
|
|
|
|
2006- 55 50
45
|
|
|
|
2009- 52
|
|
|
|
|
0060
|
LENGTH
|
.DE 11
; STRING IS 10 CHARS.
LONG
|
|
0070
|
;
|
|
|
0080
|
PRINTIT
|
.DE $FFD2
;(COMMODORE)
|
|
0090
|
;
|
|
|
0100
|
; (FOR APPLE USE
$FDED)
|
|
0110
|
;
|
|
200A- A0
00
|
0120
|
LOOP |
LDY
#$00
|
200C- B9 00
20
|
0130
|
|
LDA STRING
,Y
|
200F- 20 D2
FF
|
0140
|
|
JSR
PRINTIT
|
2012- C8
|
0150
|
|
INY
|
2013- CO
OB
|
0160
|
|
CPY #LENGTH
;(NOTE LENGTH IS PLUS ONE.
|
2015- DO
F5
|
0170
|
|
BNE
LOOP
|
2017- 60
|
0180
|
|
RTS
|
|
0190
|
|
.EN
|
ENDPASS
|
|
|
|
--- LABEL FILE:
---
|
|
|
COUNTER
=0055
|
LENGTH
=000B
|
LOOP
=200C
|
PRINTIT
=FFD2
|
START
=200A
|
STRING
=2000
|
Program 7-2.
|
0010
|
; ATARI
VERSION
|
|
0020
|
|
.BA
$0600
|
|
0030
|
|
.OS
;(OUTPUT SOURCE
CODE
|
|
0040
|
COUNTER
|
.DE $55
;(WILL HOLD INDEX)
|
0600- 53 55
50
|
0050
|
STRING
|
.BY 'SUPERDUPER' ; STORE THIS
TEXT STRING
|
0603- 45 52
44
|
|
|
|
0606- 55 50
45
|
|
|
|
0609- 52
|
|
|
|
|
0060
|
LENGTH
|
.DE 11
; STRING IS 10 CHARS.
LONG
|
|
0070
|
;
|
|
|
0080
|
PRINTIT
|
.DE $F6A4
;(ATARI)
|
|
0090
|
;
|
|
060A- A9
00
|
0100
|
START
|
LDA #00
|
060C- 85
55
|
0110
|
|
STA *COUNTER
; (ANY FREE ZERO PAGE)
|
060E- A0
55
|
0120
|
LOOP
|
LDY
#COUNTER
|
0610- B9 00
06
|
0130
|
|
LDA
STRING,Y
|
0613- 20 A4
F6
|
0140
|
|
JSR
PRINTIT
|
0616- E6
55
|
0150
|
|
INC
*COUNTER
|
0618- A9
0B
|
0160
|
|
LDA
#LENGTH
|
061A- C5
55
|
0170
|
|
CMP
*COUNTER
|
061C- D0
F0
|
0180
|
|
BNE
LOOP
|
061E- 60
|
0190
|
|
RTS
|
|
0200
|
|
.EN
|
ENDPASS
|
|
|
|
--- LABEL FILE:
---
|
|
|
COUNTER
=0055
|
LENGTH
=000B
|
LOOP
=060E
|
PRINTIT
=F6A4
|
START
=060A
|
STRING
=0600
|
Return to Table
of Contents | Previous
Chapter | Next
Chapter |
| |
|