7
Beyond Basic
Merging Machine Language into BASIC
Fred Pinho
Merging machine language subroutines can be a time-consuming task. The program offered here will allow you to add machine language to a BASIC program as a string or as DATA statements.

You've just bought your Assembler Editor cartridge, and you're starting to get into machine language programming. Hold it, before you go any further. If you haven't already heard, your assembler manual is chock full of errors. Run, don't walk, to the Atari hot line to request their errata sheets. It will save you grief and headaches, especially if you are cassette dependent.

After writing and debugging your first machine language program with your Assembler Editor cartridge, you can now save it to cassette or disk as a binary file. You can also load it back into the computer and run the machine language program directly. But what if you want to combine this routine with a BASIC program? This is the objective of a majority of beginning machine language programmers. If you look on pages 66-67 of the Assembler Editor manual, you will find a merger program. However, the program is clumsy and unwieldy, especially in its handling of problem code values (such as the one which is the ATASCII equivalent of quotation marks).

To overcome this problem, I've provided Program 1. This program will take your machine language and automatically convert it into a complete BASIC subroutine. This can then easily be added to your BASIC program. The subroutine is complete within itself. It requires only:

This will allow you to reuse the subroutine variables in your program if you wish. Also the DIMension statements will be declared at the start of the program.

Your Options

This utility program has a great deal of flexibility built-in. You can choose to store your machine language in a variety of ways:

The program, as written, sits in slightly less than 5K bytes of RAM after DIMensioning of arrays and strings. I've run it through the "Masher" program from the Atari Program Exchange. However, this saves only about 500 bytes. The program also then becomes very difficult to follow. So I've kept it as is. Type the program in and LIST it to disk or cassette. Don't SAVE it.

Using the Utility

To use the utility program, first store your machine language to disk or cassette as a binary file. If your source program is in RAM, this can be done through the assembler with this command: ASM,,#D:<filename> for disk or ASM,,#C: for cassette.

Note that what you wrote was the source code, not the actual object code which is the machine language program. Once you've done the above, turn off the computer to wipe out the source program. Then remove the Assembler cartridge, insert the BASIC cartridge, and boot the DOS into memory.

If your program has already been assembled (converted to machine language) and the final machine language resides in RAM, then do the following:

  For Disk
  SAVE #Disk File<starting address<end of routine address
Example: SAVE #D:PROGRAM.OB]<1400<17FF
For Cassette
  SAVE #C:<start address<end address
Note that all addresses are in a hexadecimal.

Again, shut off the computer and replace the Assembler with the BASIC cartridge.

To use this utility both the utility and the machine language program must be in RAM. The utility program occupies about 5K bytes of memory. Thus you must be careful to locate your machine language program so that it does not interfere with the BASIC program. You can locate the machine language either in page 6 or high up in memory just below the display list. To help you with the second method, the tables below define usable and safe living space for your machine language program.

Table 1. Disk-Based System

Computer
RAM
Installed
Suggested Safe Memory
Decimal Hexadecimal
From To From To
8K Not enough memory
16K 12750 15390 31CE 3C1E
24K 12750 23582 31CE 5C1E
32K 12750 31774 31CE 7C1E
40K 12750 39966 31CE 9C1E
48K 12750 39966 31CE 9C1E
Note: Assumes that you are in GRAPHICS 0, that the BASIC cartridge is installed, and that the first part of DOS 2.0S (mini-DOS) is loaded. The mini-DOS occupies 5628 bytes.

Table 2. Cassette-Based System

Computer
RAM
Installed
Suggested Safe Memory
Decimal Hexadecimal
From To From To
8K 7100 7198 1BBC 1C1E
16K 7100 15390 1BBC 3C1E
24K 7100 25382 1BBC 5C1E
32K 7100 31774 1BBC 7C1E
40K 7100 39966 1BBC 9C1E
48K 7100 39966 1BBC 9C1E
Note: Assumes that you are in GRAPHICS 0 and that the BASIC cartridge is installed.

Machine Language to BASIC

To convert your machine language to BASIC, proceed as follows:

  1. Load your machine language subroutine into its safe area. If from disk, first load the second part of DOS and then use option L (Binary Load). Then go back to BASIC. If you have a cassette, be careful. Page 65 of the Assembler Editor manual tells you to CLOAD your machine language. Trying that can give you a headache. The errata sheets from Atari give you a routine for cassette loading.
  2. ENTER the utility program which has previously been LISTed to disk or cassette (see step 6 if you are using cassette).
  3. RUN the program. The program will ask for the starting and ending addresses of your machine language routine in RAM. Answer in decimal only! All keyboard inputs for this program must be in decimal form.
  4. The program will then ask which method you desire for storage of your machine language. If you wish string storage, you will be prompted for the string name. You will also be asked if you wish a printout of the data to be inserted into the string. If so, you will be prompted to tum on the printer.
  5. If you desire to store machine language at a specific location, you will be asked if you wish storage at the same memory location as specified in step 3. Alternatively, you can store it at a different location.
  6. Finally you'll be asked if you wish to make any additional conversions. If yes, the program will loop back. If not, the computer will CLOSE all files and END. Your BASIC subroutine will be stored on disk as a file labelled MLR.LST. If you are using a cassette, see Program 2 for required program modifications.
  7. After you're done, erase the utility program via NEW Now enter your BASIC program. Finally, merge your machine language into your program by:
    For Disk
      ENTER "D:MLR.LST"
    For Cassette
      See Program 2
  8. Now that the two programs are merged, type in a GOSUB statement to reference the first line number of MLR.LST (or the equivalent cassette file).

And that's it; you're ready to go.

Storing Machine Language in Strings

I'd like to make some comments on storage of machine language in a string format. First, to do it correctly, you must write routines which are relocatable. That is, they must not contain any JMP or JSR instructions to a specific memory location within the program. Since the string can be located nearly anywhere in memory, nonrelocatable code will almost surely crash the computer. It's best to store your subroutines and data tables in page 6 of memory. These permanent addresses can then be safely called from within your routine.

Another problem lies in proofreading your string. If you load your data into a string and then PRINT it to the screen, you will see many weird and wondrous things. What is happening is that the screen editor is interpreting the function of the printed graphics symbols and carrying out the function. For example, if the graphics symbol in your string is that for a "delete character," the computer will slavishly do it. Thus the string symbols seen on your screen are not correct (unless you're lucky). To check your string, use the following routine in direct mode. (First RUN your program to DIMension and initialize your string):

L=LEN(string name$):FOR X=1 TO L:?ASC(string name$(X,X));",";:NEXT X

This routine prints the actual value of each byte stored in the string.

Another serious problem with string storage of data is the occurrence of values of 34 or 155. The value 34 is the ATASCII representation of quotation marks. The value 155 is the ATASCII for RETURN. The presence of either will cause the screen editor to prematurely truncate your string and give you an error message. Thus the program does the following when it encounters either value:

  1. It inserts a space character in the string and notes the position in the string.
  2. It then writes the BASIC subroutine statements so that the values are inserted into the string without going through the screen editor. It uses the CHR$ function for this purpose.
  3. As presently set up, the program can handle up to 15 values of the quotes and of the RETURN characters. It checks for the total occurrence of these and warns you if there are too many.

There you have it. I hope this program makes the difficult world of machine language a little more enjoyable.

Table 3. Variables in Program 1

A$   Used to receive yes or no responses
BATOP Top memory location of utility program
D0$ Holds name of string used to store machine language (ML)
F Flag. Zero if string storage requested. Set to one if storage at a specific address is requested
I,S,T,X,Y Loop counters
L Length of string required to store ML
LS,LF Initial and final position in string to be filled with data
LN Line number of subroutine to be written for string storage
LNO Line number of DATA statements to be written for ML storage at a specific address
LR Remaining length of string after subtracting 80
N Input value for choice of ML storage
QT Total number of values of 34 in ML
QUOTE() Holds position in string of ATASCII values of 34
RT Total number of values of 155 in ML
RETRN() Holds position in string of ATASCII values of 155
S Temporary value for ML address
SF,FF New starting and final address of ML
SO,FO Initial starting and final address of ML
V Counter to indicate number of routines to be stored at specific addresses
X Indicates cell in array RETRN()
Z Indicates cell in array QUOTE()

Program 1. Merging ML into BASIC Disk Version
10 CLR :GRAPHICS 0:POKE 752,1:POKE 756,209:? "{5 SPACES}MACHINE LANGUAGE CONVERTER":GOSUB 600:POKE 756,224
20 DIM A$(1),D0$(3),QUOTE(14),RETRN(14)
30 D0$="{3 SPACES}":TRAP 580:GOSUB 740:V=0:OPEN #3,8,0,"D:MLR.LST":LNO=32050:LN=31000:F=0
40 ? :? :? "INPUT STARTING ADDRESS OF CODE":POKE 752,0:GOSUB 590:INPUT S:SO=S:SF=S
50 ? "INPUT FINAL ADDRESS OF CODE":GOSUB 590:INPUT S:FO=S:FF=S:GOSUB 640
60 ? "STORAGE METHOD FOR ROUTINE7":? "{3 SPACES}1.AT SAME ADDRESS":? "{3 SPACES}2.WITHIN A STRING"
70 ? "{3 SPACES}3.AT NEW ADDRESS?"
80 GOSUB 590:? :? "PLEASE TYPE NUMBER PLUS RETURN!":INPUT N
90 IF (N<>1 AND N<>2 AND N<>3) THEN ? "{BELL}WRONG RESPONSE!TRY AGAIN!":GOSUB 600:GOTO 60
100 IF N=3 THEN ? :? "NEW STARTING ADDRESS FOR ROUTINE?":GOSUB 590:INPUT S:SF=S
110 IF N=3 THEN ? "NEW FINAL ADDRESS FOR ROUTINE!":GOSUB 590:INPUT S:FF=S
120 IF N=3 THEN IF FF-SF<>FO-SO THEN ? "{BELL}INCORRECT FINAL ADDRESS! TRY AGAIN!":? :GOTO 110
130 IF N=1 OR N=3 THEN F=1:V=V+1:GOTO 180
140 L=FO-SO+1:GOSUB 680:GOSUB 610
150 ? "DO YOU WISH AN ASCII PRINTOUT":? "OF YOUR STRING DATA!":GOSUB 590:INPUT A$
160 IF A$="Y" THEN N=4:? "HIT RETURN WHEN THE PRINTER IS ON!":GOSUB 590:INPUT A$:OPEN #2,8,0,"P:"
170 GOTO 260
180 ? #3;LNO;" DATA ";SF;",";FF:LNO=LNO+10
190 ? #3;LNO;" DATA ";
200 FOR I=0 TO 19
210 IF SO+I=FO+1 THEN POP :IF I THEN ? #3;",";-1:? #3:LNO=LNO+10:GOTO 490
215 IF SO+I=FO+1 THEN IF I=0 THEN ? #3;-1:? #3:LNO=LNO+10:GOTO 490
220 IF I THEN ? #3;",";
230 ? #3;PEEK(SO+I);
240 NEXT I:? #3:LNO=LNO+10:SO=SO+20:GOTO 190
260 IF N=4 THEN ? #2:? #2;"**DATA FOR ";D0$;"**"
270 LS=1:Z=0:W=0:? #3;LN;" DIM ";D0$;"(";L;"):";
280 IF N=4 THEN FOR I=0 TO L-1:? #2;PEEK(SO+I);:IF I<L-1 THEN ? #2;",";
290 IF N=4 THEN NEXT I
300 LR=L-80:IF LR<=0 THEN LF=LS+L-1
310 IF LR>0 THEN LF=LS+80-1:L=LR
320 ? #3;D0$;"(";LS;",";LF;")=";:? #3;CHR$(34);:FOR I=LS TO LF
330 IF PEEK(SO+I-1)=34 THEN ? #3;" ";:QUOTE(Z)=I:Z=Z+1:GOTO 360
340 IF PEEK(SO+I-1)=155 THEN ? #3;" ";:RETRN(W)=I:W=W+1:GOTO 360
350 ? #3;CHR$(PEEK(SO+I-1));
360 NEXT I:IF LR>0 THEN LS=LS+80:? #3;CHR$(34):? #3:LN=LN+10:? #3;LN;" ";:GOTO 300
370 ? #3;CHR$(34):? #3:LN=LN+10
380 QT=0:RT=0:FOR I=0 TO 14:IF QUOTE(X) THEN QT=QT+1
390 IF RETRN(X) THEN RT=RT+1
400 NEXT X:IF QT=0 AND RT=0 THEN 490
410 ? #3;LN;"RESTORE ";LN+20:LN=LN+10
420 IF QT THEN ? #3;LN;" FOR X=1 TO ";QT;":READ Z:";D0$;"(Z,Z)=CHR$(34):NEXT X":LN=LN+10
430 IF QT THEN ? #3;LN;" DATA ";:FOR Y=0 TO QT-1:? #3;QUOTE(Y);:IF Y AND Y<QT-1 THEN ? #3;",";
440 IF QT THEN NEXT Y:? #3:LN=LN+10
450 IF RT THEN ? #3;LN;" FOR X=1 TO ";RT;":READ Z:";D0$;"(Z,Z)=CHR$(155):NEXT X":LN=LN+10
460 IF RT THEN ? #3;LN;" DATA ";:FOR Y=0 TO RT-1:? #3;RETRN(Y);:IF Y AND Y<QT-1 THEN ? #3;",";
470 IF RT THEN NEXT Y:? #3:LN=LN+10
490 GOSUB 740:? "ALL DONE":GOSUB 590:INPUT A$
500 IF A$="N" THEN D0$="{3 SPACES}":CLOSE #2:GOTO 40
510 IF F=0 THEN 570
520 ? #3;"32000 W=0:V=";V;":RESTORE 32050"
530 ? #3;"32010 READ X,Y:FOR I=X TO Y:READ Z:POKE I,Z:NEXT I"
540 ? #3;"32020 READ Z:IF Z<>-1 THEN ?";CHR$(34);"ERROR IN CODE! CHECK DATA STATEMENTS!";CHR$(34);":END"
550 ? #3;"32030 W=W+1:IF W<V THEN 32010"
560 ? #3;"32040 RETURN"
570 CLOSE #2:CLOSE #3:END 
580 CLOSE #2:CLOSE #3:TRAP 40000:? "{BELL}ERROR ";PEEK(195);" AT LINE ";PEEK(186)+256*PEEK(187);"!":END 
590 FOR T=10 TO 6 STEP -1:FOR S=8 TO 0 STEP -1:SOUND 0,15-S,10,T:NEXT S:NEXT T:SOUND 0,0,0,0:RETURN 
600 FOR T=1 TO 400:NEXT T:RETURN 
610 ? :? "INPUT TWO CHARACTER STRING NAME":? "PLUS THE $":GOSUB 590:INPUT D0$
615 IF LEN(D0$)<3 THEN GOSUB 750:GOTO 610
620 IF ASC(D0$(1,1))>90 OR ASC(D0$(1,1))<65 OR D0$(2,2)="$" OR D0$(3,3)<>"$" THEN GOSUB 750:GOTO 610
630 RETURN 
640 IF SO<1792 THEN RETURN 
645 IF FO>(256*PEEK(106)-1000) THEN ? "{BELL}I DON'T HAVE THAT MUCH MEMORY!":POP :GOTO 40
650 BATOP=PEEK(144)+256*PEEK(145):IF BATOP>SO-100 THEN ? "{BELL}CAUTION! THIS PROGRAM MAY HAVE ":GOTO 670
660 RETURN 
670 ? "OVERRUN YOUR CODE! CHECK YOUR RESULTS!":GOSUB 600:RETURN 
680 QT=0:RT=0:FOR I=0 TO L-1:IF PEEK(SO+I)=34 THEN QT=QT+1
690 IF PEEK(SO+I)=155 THEN RT=RT+1
700 NEXT I:IF RT<16 AND QT<16 THEN RETURN 
710 ? "{BELL}WARNING! YOUR CODE CONTAINS":? "MORE THAN 15 ATASCII VALUES FOR":? "RETURN OR QUOTES!"
720 ? "THUS I CANNOT PROCESS IT!":? "PLEASE MAKE ANOTHER CHOICE!":GOSUB 600:GOSUB 600
730 POP :GOTO 60
740 FOR I=0 TO 14:QUOTE(I)=0:RETRN(I)=0:NEXT I:RETURN 
750 ? "{BELL}WRONG RESPONSE! TRY AGAIN!":RETURN 

Listing. Merging ML into BASIC Disk Version.
Download (Saved BASIC) / Download (Listed BASIC)

Program 2.  Changes for Cassette Users
  (see notes below)
30 D0$="{3 SPACES}":TRAP 580:GOSUB 740:V=0:LNO=32050:LN=31000:F=0
35 OPEN #3,8,0,"C:":? #3;"1 DATA ";:FOR I=0 TO 59:? #3;"0,";:NEXT I:? #3;"0";:? #3
Note: Line 35 writes a dummy line of DATA. This is needed because of a bug in the operating system. After the cassette handler is OPENed, the cassette motor will not stop running until a record is written to it. RUN the program and record the subroutine on tape. Then, before you enter your BASIC program, ENTER the subroutine from cassette. DELETE line 1. Then ENTER your BASIC program. Now type in GOSUB to the utility subroutine and you're ready to go.

Listing. Merging ML into BASIC Disk Version.
Download (Saved BASIC/full program) / Download (Listed BASIC/changes only)


Return to Table of Contents | Previous Section | Next Section