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 |