Classic Computer Magazine Archive 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.
  1. Write your machine code, and determine how much expansion you need.
  2. Print the contents of memory location 45.
  3. 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.
  4. 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.
  5. 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.
  6. Input and verify your code.
  7. Change your program to call machine code, test the program, and change the machine code if necessary.
  8. Delete your input routine and all unnecessary instructions.
  9. Save your program on tape or disk.