COMPUTE! ISSUE 37 / JUNE 1983 / PAGE 200
Easy VIC Machine
Language Saves
Poul Christensen
The VIC and other Commodore machines
allow you to place machine language routines next to your BASIC
program. Once you know how, the method is simple and makes your program
shorter and easier to load. This method is demonstrated with a simple
example and a step-by-step description.
When you write in BASIC on your VIC, you will sometimes find that the
resulting program isn't fast enough. With imagination and rewriting you
can often make it faster, but sooner or later you may reach the point
when only machine language will help.
Where do you place the machine code, and how do you
load it in with its BASIC program as a single entity?
Placing
And Loading
The most common method is to choose some unused area such as the tape
buffers or the memory below the screen image. Of course, you cannot put
your coding there directly, so you must write your machine code in DATA
statements and include a routine to read your data and POKE the values
in place during the program RUN.
If your only problem is speed, this method may work
fine. It takes a little longer to load the program, and it takes time
to POKE the machine code into the computer, but the main part of your
program will run faster. But
what if you also have memory constraints?
You have extra DATA statements and extra code, so you are using up even
more memory than before.
Fortunately, there is an easier and better way.
If you look in memory locations 45 and 46, you will
find the "start of data" register. This is also the "end of the BASIC
program" address. It's the address right after the last BASIC
statement. (You can get the decimal number of the address in RAM where
your program ends by: ?PEEK(45) + PEEK(46)*256.) When you save your
program on tape or disk, this
"register" determines how much you are
saving and, therefore, how much you will load when you read your
program in again.
Tricking The VIC
We can make VIC believe that the program extends past the last BASIC
statement, and we can use the extra space for a machine program.
Although we still have the problem of getting the machine language
there in the first place, once it is there it will be saved with the
program, so it becomes a permanent part of the program. If we add,
delete, and change lines, we will change the length of the BASIC
program, but our machine code will
stay right where it belongs, next to
the last statement.
A Practice Program
Let's put the theory into practice. This program has no serious
purpose, but it serves well as a demonstration. The program simply
shows three eight-letter words on the screen and, every three seconds,
moves the words around. You'll see why we want to use machine language,
and how we go about it.
10 PRINT"{CLEAR}{06
DOWN}";TAB(7);"ROTATI
ON"
20 PRINT TAB(7);"CONFUSES"
30 PRINT TAB(7);"THE MIND"
40
PRINT"{WHT}";TAB(7);"XXXXXXXX{BLK}"
50 TI$="000000"
60 IF TI$<>"000003" THEN 60
70 GOSUB 100
80 GOTO 50
100 FOR I=8 TO 1 STEP -1
110 POKE 7818+66+I,PEEK(7818+I)
120 POKE 7818+I,PEEK(7818+22+I)
130 POKE 7818+22+I,PEEK(7818+44+I)
140 POKE 7818+44+I,PEEK(7818+66+I)
150 NEXT
160 RETURN
When you run the program, you see the characters
move. Let's speed up the program by programming the subroutine in
machine language:
|
|
Hex
|
|
|
Decimal
|
|
LDX
|
#8
|
A2
|
08
|
|
162
|
8
|
|
LDA
|
7818,X
|
BD
|
8A
|
1E
|
189
|
138
|
30
|
STA
|
7884,X
|
9D
|
CC
|
1E
|
157
|
204
|
30
|
LDA
|
7840,X
|
BD
|
A0
|
1E
|
189
|
160
|
30
|
STA
|
7818,X
|
9D
|
8A
|
1E |
157
|
138
|
30
|
LDA
|
7862,X
|
BD
|
B6
|
1E |
189
|
182
|
30
|
STA
|
7840,X
|
9D
|
A0
|
1E |
157
|
160
|
30
|
LDA
|
7884,X
|
BD
|
CC
|
1E |
189
|
204
|
30
|
STA
|
7862,X
|
9D
|
B6
|
1E |
157
|
182
|
30
|
DEX
|
|
CA
|
|
|
202
|
|
|
BNE
|
*-25
|
10
|
E5
|
|
16
|
229
|
|
RTS
|
|
60
|
|
|
96
|
|
|
We will first see where the program ends, so we
PRINT PEEK(45) and PRINT PEEK(46); we should have 44 and 17, which
means that the pro gram ends at 17 x 256+44 or address 4396 (or hex
address 112C). We will add 30 characters to the program, so we POKE 45,
74.
We now have 30 bytes available for the program, so
we could start POKEing: POKE 4396,162; POKE 4397,8, etc.
This is not a very easy method, so let's add some
lines to the program to read and POKE. But when we add lines, we change
the location, so we must recompute the address.
1 OC=PEEK(46)*256+PEEK(45)-30
2 FOR I=0 TO 29
4 INPUT Q%
6 POKE OC+I,Q%
8 NEXT
9 STOP
Now we run the program, and input the 30 bytes as
they are prompted. This little routine is good enough for our purpose,
since we want to write only a small program. If you make an error, just
start over. But if you have longer programs, you will probably want to
add embellishments to your program so you can verify and correct your
input.
When the program stops with a "break in 9," your
program is in and, you hope, correct (otherwise, you would run the
program again). Now is the time to delete all superfluous statements.
We must leave line 1, but delete lines 2, 4, 6, 8; 9, and line 100 and
on. Finally, change line 70 to:
70 SYS OC
Instant Changes
Now run the program, and you will see the difference in speed; the
screen changes instantaneously.
Stop the program and PRINT OC; you should get 4284,
so your program ends at 4314. Not only did we make the program faster,
but we also saved 82 bytes.
You can now save the program, and when you load it
again you will see that everything, machine language subroutine
included, is still intact.
You can, of course, use the same method to place
constants at the end of your program. That's useful if you want to
write a melody or generate your own character set.
Two Hints
When you expand the program, be sure to allocate enough space - a few
extra bytes at the end won't hurt you, and they'll make it much easier
for you to change the machine language without having to make more
changes in your program. In this example, I would normally expand the
program by at least 40 bytes.
Make sure your program is relocatable. That means
that you should make the program less than 128 bytes long and use
branch commands only, not jumps.
If you have more than one machine language routine,
you should create a branch table at the start of it and call your
routines by SYS OC; SYS OC + 2; SYSOC + 4; etc. This also makes it
easier to change your code.
It is easier to place your input routine at the end
of your program and use a command like RUN900 to call it. That way you
won't inadvertently end up in your input routine when you test your
program, and you can leave the routine until the program is correct. Be
sure to place a STOP between your program and the input routine.
Finally, let's recapitulate the steps.
- Write your machine code, and determine how much expansion you
need.
- Print the contents of memory location 45.
- Add the length of your routine (plus a little bit extra) to the
contents of 45, and POKE this value into 45, provided the sum is less
than 256.
- If the sum is 256 or more, subtract 256 from the sum and POKE it
into 45; read 46 and POKE a new value (1 higher) into 46.
- Write an input routine at the end of your program. Make sure you
precede the input routine by a STOP command, and that the first
instruction computes the location of your expansion area. Also, compute
the location of the expansion area in your main program.
- Input and verify your code.
- Change your program to call machine code, test the program, and
change the machine code if necessary.
- Delete your input routine and all unnecessary instructions.
- Save your program on tape or disk.