Classic Computer Magazine Archive COMPUTE! ISSUE 73 / JUNE 1986 / PAGE 90

Converting IBM ML
To BASIC DATA


Mark Russinovich With Dennis Moul

This short utility converts object code created with a machine language assembler into DATA statements ready to be merged with a BASIC program. It works on any IBM PC, PCjr, or compatible with DOS 2.0 or higher.


An efficient way of speeding up crucial parts of BASIC programs or performing operations not possible in BASIC is to write a machine language subroutine. Usually, the machine language (ML) routine is loaded from disk by the BASIC program or is encoded in BASIC DATA statements that are POKEd into memory. The latter method has the advantage of making the BASIC program a stand-alone unit, not dependent on other files that must be on the same disk. Its major disadvantage, though, is that if the ML routine is more than a few bytes long, the job of converting the object code to DATA is extremely tedious and error-prone. One minor mistake could mess up the whole routine and possibly crash the system.
    The solution is Program 1 below, "BIN2DAT." It takes an ML (binary) file on disk and converts it to DATA statements, ready to be merged into your BASIC program. It is impeccably reliable and takes only seconds to do its work.

Using BIN2DAT
After typing in Program 1 and saving it on disk, make sure that the ML object file you wish to convert into DATA statements is stored on disk in the .COM format. This is necessary because EXE files have relocation information used by DOS when they are loaded into memory. Since DOS isn't used when a BASIC program POKES an ML routine into memory, an EXE program would not be relocated and therefore would not execute. If you've already written an .EXE file that you wish to convert to DATA statements, convert it to .COM format by using the EXE2BIN program included on the PC-DOS disk.

    Now follow these steps:

    1. Run BIN2DAT. It asks you for the filename of the .COM file you wish to convert. Enter the file name and press Enter. As an extra safeguard, BIN2DAT makes sure that the file has a .COM extension.
    2. BIN2DAT prompts you for the output filename (the file that will contain the DATA statements). If you simply press Enter here, the filename defaults to the one displayed within brackets.
    3. Next, you're asked for the starting line number of the DATA statements. Again, a default, which is line 100, is printed within brackets. Either press Enter or type your own starting line number.
    4. BIN2DAT now asks for the line number increment (the default is 10) and the numeric base of the data-decimal or hexadecimal. The base makes little difference, but the default is hexadecimal because sometimes it's useful to compare the .LST file generated by the assembler with the DATA statements.

Merging The DATA
Once you've entered all the required information, BIN2DAT creates the BASIC data file to your specifications. To merge it with your BASIC program, load the BASIC program and type:

MERGE "filename.ext"

    You'll notice that the first line in the file has only one data value. This isn't part of the ML. This value is the size of the ML routine in bytes, minus one. Therefore, it corresponds to the upper limit of a FOR-NEXT loop that is required to POKE the ML routine into memory.
    Next are the lines containing the data for the ML program. An example of an ML routine is seen in Program 2, "EXAMPLE.ML." Program 3, "Demo DATA," shows the file produced by BIN2DAT after converting the .COM file produced by an assembler and EXAMPLE.ML. Extra lines have been added to POKE the ML routine into memory and CALL it. Examining these listings should clear up any questions about how to use BIN2DAT.

How It Works
BIN2DAT is fairly straightforward. Once all the information has been entered by the user, the SHELL command is used to create a file with the directory entry of the ML file. SHELL allows the use of DOS commands from BASIC, but in the DOS 2.0/2.1 generation, it has the flaw of altering memory locations 30H and 31H, which happen to point to the beginning of the BASIC program in memory. To overcome this, the values for these locations are PEEKed before the SHELL command is executed and then POKEd back later.
    The next part of the program reads the size of the ML file out of the directory random file which was made by SHELL. Then it begins constructing the DATA statements, which are sent to the output file. The first DATA line has only the count value (described above). Subsequent lines have ten data numbers each. The MOD 10 function checks for the end of a line. When a line ends, it is sent to the output file and a new line is started.
    After the ML program has been completely read and the new file is finished, the CLOSE command closes the input and output files, and the program terminates.
    Several changes can make BIN2DAT serve your particular needs better. If you usually start your data on some line other than 100, this default value can be changed. Also, the default values for the line increment and numeric base can be changed to make running the program easier. If you want to have more than or fewer than ten items per data line, you can change the number 10 in each MOD function to some other number.


Program 1: BIN2DAT
For instructions on entering this listing, please
refer to "COMPUTE!'s Guide to Typing In
Programs" In this issue of COMPUTE!.


LJ 10 DEF SEG
BH 20 KEY OFF
CC 30 ON ERROR GOTO 570
OM 40
CF 50 REM Print title and get in
      fo
CD 60
KN 70 PRINT "Binary to Data Stat
      ement Converter"
NM 80 PRINT "(c) Copyright 1986,
       Compute! Publications"
FL 90 PRINT
OD 100 INPUT "File to convert: "
       ,FSOURCE$
BF 110 IF INSTR(FSOURCE$,".COM")
       =0 AND INSTR(FSOURCE$,".c
       om")=0 THEN PRINT:BEEP:P
       RINT "File must have .COM
        extension.":END
DO 120 FILEN=INSTR(FSOURCE$,".")
       -1:FILEN$=LEFT$(FSOURCE$,
       FILEN)+".BAS"
PL 130 PRINT "Data file [";FILEN
       $;"]";:INPUT ": ",FDEST$
NF 140 IF FDEST$="" THEN FDEST$=
       FILEN$
HF 150 INPUT "Starting line numb
       er [100]: ",SLN
ED 160 INPUT "Line increment [10
       ]: ",LINC
JF 170 INPUT "Hex/decimal [h]: "
       ,H$
FD 180 IF SLN=0 THEN SLN=100
HC 190 IF LINC=0 THEN LINC=10
GM 200 IF H$="" OR H$="h" OR H$=
       "H" THEN H=1
MG 210 :
NG 220 REM Capture directory in
       random file
NK 230 :
JA 240 Pl=PEEK(&H30):P2=PEEK(&H3
       1)
NC 250 SHELL "dir "+FSOURCE$+" >
       $$zztemp"
MG 260 POKE &H30,P1:POKE &H31,P2
DB 270 OPEN "$$zztemp" FOR INPUT
        AS 2
QJ 280 FOR I=1 TO 4:INPUT#2,DMY$
       :NEXT
JH 290 INPUT#2,ENTRY$
PL 300 REM Get size of com file
       from dir
KO 310 SIZE=VAL(MID$(ENTRY$,16,6
       ))
ND 320 CLOSE #2 :KILL "$$zztemp"
NL 330 :
EL 340 REM Open com file and new
        dat file
NP 350 :
NF 360 OPEN FSOURCE$ AS 1 LEN=1
MD 370 FIELD 1,1 AS BYTE$
IF 380 OPEN FDEST$ FOR OUTPUT AS
        2
LO 390 LINNUM=SLN+LINC
HF 400 LINE=STR$(SLN)+" DATA"
IE 410 IF H=1 THEN LIN$=LIN$+" &
       h"+HEX$(SIZE-1) ELSE LINE
       =LIN$+STR$(SIZE-1)
MH 420 PRINT#2,LIN$
PJ 430 LIN$=STR$(LINNUM)+" DATA
       "
CM 440 LINNUM=LINNUM+LINC
EH 450 FOR COUNT=1 TO SIZE
BH 460     GET #1,C0UNT
OM 470   WBYTE$=BYTE$
CC 480   IF H=1 THEN NUM$="&h"+H
       EX$(ASC(WBYTE$)) ELSE NUM
       $=STR$(ASC(WBYTE$)):
              NUM$=RIGHT$(NUM$,L
       EN(NUM$)-1)
LI 490   IF COUNT MOD 10;0 THEN
       520
OB 500   PRINT#2,LIN$+NUM$:LIN$=
       STR$(LINNUM)+" DATA "
PC 510   LINNUM=LINNUM+LINC:GOTO
        530
MF 520   LIN$=LIN$+NUM$+","
NH 530 NEXT
HP 540 IF COUNT MOD 10<>1 THEN L
       IN$=LEFT$(LIN$,LEN(LIN$)-
       1):PRINT#2,LIN$
PE 550 CLOSE
FD 560 PRINT:PRINT "File written
       ":END
PO 570 BEEP:PRINT "DOS error - a
       borting.":CLOSE:END


Program 2: EXAMPLEML
Note: This source code listing is for illustrative purposes only. It
requires an assembler to enter.

; This is a sample assembly
; language program that will
; be poked in and run from
; BASIC.

prog    segment

        assume  cs:prog,ds:prog
main    proc    far
        push    ax
        push    bx
        push    dx

; Print characters

        mov     bx,offset mess1
print:  mov     dl,cs:[bx] ;get char
        cmp     dl,0    ;are we through?
        je      exit    ;yes, return
        inc     bx      ;no, get nxt char
        mov     ah,2    ;dos print routine
        int     21h
        jmp     print   ;get more
exit:   pop     dx      ;restore stack
        pop     bx
        pop     ax
        ret

mess1   db 0dh,0ah,'This is output of a'
        db ' sample assembly language'
        db ' program.',0dh,0ah,0dh,0ah
        db Odh,0

main    endp

prog    ends
        end     main


Program 3: Demo DATA
For instructions on entering this listing, please
refer to "COMPUTE!'s Guide to Typing In
Programs" in this issue of COMPUTE!.

BC 10 REM This program will pok
      e in an
MF 20 REM assembly language pro
      gram and
LL 30 REM then CALL it.
OM 40 :
ND 50 DEF SEG=&H1700
QN 60 READ COUNT
EB 70 FOR MEM=0 TO COUNT
JP 80 READ BYTE
LB 90 POKE MEM,BYTE
NN 100 NEXT
MF 110 :
KC 120 SAMPLE=0
QI 130 CALL SAMPLE
LC 140 END
NN 150 :
CM 160 DATA &h55
OP 170 DATA &h50,&h53,&h52,&hBB,
       &hl9,&h0,&h2E,&hBA,&h17,&
       h80
LM 180 DATA &hFA,&h0,&h74,&h7,&h
       43,&hB4,&h2,&hCD,&h21,&hE
       B
HG 190 DATA &hFl,&h5A,&h5B,&h58,
       &hCB,&hD,&hA,&h54,&h68,&h
       69
KE 200 DATA &h73,&h20,&h69,&h73,
       &h20,&h6F,&h75,&h74,&h70,
       &h75
FO 210 DATA &h74,&h20,&h6F,&h66,
       &h20,&h6l,&h20,&h73,&h61,
       &h6D
JM 220 DATA &h70,&h6C,&h65,&h20,
       &h61,&h73,&h73,&h65,&h6D,
       &h62
PG 230 DATA &h6C,&h79,&h20,&h6C,
       &h61,&h6E,&h67,&h75,&h61,
       &h67
DE 240 DATA &h65,&h20,&h70,&h72,
       &h6F,&h67,&h72,&h61,&h6D,
       &h2E
CO 250 DATA &hD,&hA,&hD,&hA,&hD,
       &h0