Jump to content
  • entries
    15
  • comments
    7
  • views
    14,452

Convert Binary File to BASIC XL Data


kenjennings

1,183 views

Sifting the heap of aged floppy disks has turned up an interesting pile of ... shtuff.

 

Machine language routines can be a big help to Atari BASIC/BASIC XL programs boosting performance and providing nifty features not included by BASIC. That is, provided the machine language routine can be brought into the BASIC program. In this case the Atari BASIC language can be like an island fortress repelling all invading data, because the language makes it inconvenient to get a machine language routine into a safe place usable by the BASIC progam.

 

Convenience for BASIC means representing the machine language routine as either DATA statements to be READ and POKE'd into memory, or as string assignments. However, assembler output -- the machine language bytes -- does not come in a convenient format for BASIC. A machine language program is usually stored in Atari's segmented load file format. This is binary information including structure which embeds starting and ending addresses for blocks of memory data.

 

While the segmented file is clever and is the standard format for Atari machine language programs loaded and run from DOS, neither Atari BASIC nor BASIC XL have a facility to easily load the segmented file formats into memory. A user could choose the DOS load option to load machine language utilities into memory, but this requires extra work on the part of users (who may be famous for forgetting to tie their shoe laces), and requires more thought and planning to design the machine language program to load into a location that will not be overwritten by the BASIC program when it loads and runs.

 

BASIC XL does have the BGET command which can load an arbitrary amount of binary data from a file into any specified location of memory. The problem here is that this does exactly that -- it just loads data directly into memory. It does not understand the segmented file format. The BGET function is useful only if a machine language file is stripped of its segment information which is not smart to do if the file does not describe one contiguous block of memory.

 

So, how to easily get a file of machine language data turned into something useful for BASIC namely, DATA statements or string assignments? Here is a BASIC XL program, BIN2DATA.BXL that processes binary/machine language file into more convenient data for OSS BASIC XL.

 

 

BIN2DATA.zip

 

 

What it does:

  • Accepts the input file name containing machine language or other binary data

  • Accepts the output file which will contain BASIC code that can be ENTER'd into BASIC XL and lightly modified for use.

  • Allows the user to choose to load the segmented file into the memory specified by the file. Note that while this can load multiple segments it is intended for simple routines. Multiple segments will be consolidated into one range from the minimum/starting address to the maximum/ending address.

  • Allows the user to choose to load the file directly into memory without reading any segment addresses. In this case the user will be prompted to specify a starting address for loading the data.

  • Accepts the user's choice of output as DATA statements or string assignments.

  • In the case of DATA statements the user can choose if the data will be in Hex or Decimal.

  • The user may choose (within reason) the minimum and maximum number of values that will be output in a single line.

  • The user may choose the starting line number and line increment.

  • The program prints the BASIC statements to the screen and the output file at the same time providing the user visual feedback of the file output.

 

Note that for the string representation there are two binary values that cannot be directly expressed in the string. The first is $02 which is the internal code for the the double quote character (ATASCII 34). The other is value $DB which is the internal code for the end of line character (ATASCII 155). The program substitutes a blank space for both of these values and remembers the position of the data in the string. After writing the string assignments the program will then output DATA statements listing the position of the problem characters within the string. This is expressed using BASIC's string position assignment values where the first character is position (1), not (0). It is up to the programmer to utilize the list to correctly update the data in the string.

 

 

Here are a few, lovely pictures of the program in operation:

 

 

Input entry of a program to output as a string:

 

blogentry-32235-0-75618200-1428896115_thumb.png

 

Here is the actual string output. This program has an embedded double quote character (binary $02) at position 8 in the string.

 

blogentry-32235-0-23099900-1428896235_thumb.png

 

 

Input entry of a program to output as Data:

 

blogentry-32235-0-95256400-1428896286_thumb.png

 

Here is the resulting output:

 

blogentry-32235-0-83921700-1428896301_thumb.png

 

 

The Good The Bad And the Ugly...

 

The input handling is merely fairly decent, and not completely bullet resistant. When it asks for input from the user it pretty much needs actual input. Hitting RETURN by itself is a reliable way to break the program.

 

The output is designed to fit the wider range of possibilities that BASIC XL permits. Using the program's output for regular Atari BASIC requires some tweaks: the command statements need to be changed to all upper case (i.e. "DATA" not "Data"), and the Hex format for Data statements cannot be used.

 

The quotes and end of line position lists are always generated in hexadecimal. (Oops. Well, BASIC XL, you know.)

 

The program has a lot of comments which could free up a lot of space when deleted.

 

There are certainly redundant things going on in the code which could be optimized, and probably stupid, ugly misuse of TRAP. (So, you have fun with that, OK? Let me know how it goes.)

 

 

The BIN2DATA.BXL Program:

1000 Rem SAVE "H1:BIN2DATA.BXL"1005 Rem1010 Rem BY KEN JENNINGS1015 Rem1020 Rem READ A BINARY DOS OR MAC/651025 Rem OBJECT FILE INTO MEMORY THEN1030 Rem OUTPUT THE BYTES AS BASIC1035 Rem DATA STATEMENTS OR STRING1040 Rem ASSIGNMENT.1045 Rem1050 Rem THE LOADER CAN HANDLE MULTI-1055 Rem SEGMENT FILES, BUT THIS IS1060 Rem ONLY TO COMPENSATE FOR1065 Rem MAC/65s PROPENSITY TO MAKE1070 Rem SEGMENTS WHEN NOT NEEDED.1075 Rem1080 Rem MULTIPLE SEGMENTS WILL BE1085 Rem CONSOLIDATED TO ONE, ALL-1090 Rem ENCOMPASSING START AND END1095 Rem ADDRESS. THIS MEANS A  1100 Rem PROGRAM WITH SEPARATED1105 Rem SEGMENTS MAY HAVE TONS OF1110 Rem USELESS MEMORY INCLUDED.1115 Rem1120 Rem THIS IS REALLY MEANT FOR1125 Rem SHORT MACHINE LANGUAGE1130 Rem ROUTINES THAT BASIC WILL CALL1135 Rem VIA USR().1140 Rem1145 Rem FOR SAFETY THE PROGRAM WILL1150 Rem LIMIT ADDRESSES TO PAGE 6,1155 Rem OR ADDRESSES BEWEEN THE END1160 Rem OF BASIC PROGRAM MEMORY AND1165 Rem THE START OF THE CURRENT1170 Rem DISPLAY LIST.1175 Rem1180 Fast1185 Graphics 0:Poke 82,0:Poke 710,01190 ?  "Convert DOS or MAC/65 binary/object"1195 ? "file into BASIC Data or string."1200 Dim I$(20),Infile$(12),Outfile$(12),File$(12)1205 Dim H$(5),Hx$(32),W$(6)1210 Maxqe=256:Ql=0:El=01215 Dim Qlist$(Maxqe,2),Elist$(Maxqe,2)1220 W$="What?"1225 Rem1230 Rem FILENAMES1235 Rem1240 ? :Input "Enter file name to read: ",Infile$1245 ? :Input "Enter file name to write: ",Outfile$1250 Rem1255 Rem PROCESS BIN FILE ADDRESS INFORMATION?1260 Rem1265 ? :Input "Ignore address structures (Y/N): ",I$1270 Ignadd=Find("NnYy",I$(1,1),0)1275 If Ignadd=0 Then ? W$:Goto 12651280 Ignadd=Int((Ignadd+1)/2)1285   If Ignadd=2:Rem GET START ADDR1290   Rem1295   Rem STARTING ADDRESS IF LOADING1300   Rem DIRECTLY. LIMIT THIS TO1305   Rem SENSIBLY VALID ADDRESSES:1310   Rem PAGE 6 AND RAM AFTER THE1315   Rem PROGRAM IN MEMORY BEFORE1320   Rem THE DISPLAY LIST1325   Rem1330   ? :Input "Enter starting address (hex): ",H$1335   Gosub 2695:If Xaddr=0 Then ? W$:Goto 13301340   Ad=Xaddr:Gosub 2775:If Erradd>0 Then ? W$:Goto 13301345   Endif1350 Rem1355 Rem DUMP AS DATA STATEMENTS OR ATASCII STRING?1360 Rem1365 ? :Input "Output Data or String (D/S): ",I$1370 Ds=Find("DdSs",I$(1,1),0)1375 If Ds=0 Then ? W$:Goto 13651380 Ds=Int((Ds+1)/2)1385 Hd=1:Rem A DEFAULT FOR QUOTE/EOL LISTS1390   If Ds=1:Rem OUTPUT HEX OR DEC?1395   Rem1400   Rem DATA CAN BE HEX OR DECIMAL.1405   Rem1410   ? :Input "Output Hex or Decimal (H/D): ",I$1415   Hd=Find("HhDd",I$(1,1),0)1420   If Hd=0 Then ? W$:Goto 14101425   Hd=Int((Hd+1)/2)1430   Endif1435 Rem1440 Rem STARTING LINE NUMBER. BASIC1445 Rem ALLOWS LINES UP TO 32767, BUT1450 Rem TO BE REALISTIC THE STARTING1455 Rem LINE NUMBER SHOULD BE A LOT1460 Rem LESS THAN 32K. SO, THIS IS1465 Rem LIMITED TO 30000.1470 Rem1475 ? :Input "Starting line number: ",I$1480 If Len(I$)<1 Then ? W$:Goto 14751485 If I$(1,1)<"0" Or I$(1,1)>"9" Then ? W$:Goto 14751490 Sline=Val(I$)1495 If Sline>30000 Then ? W$:Goto 14751500 Rem1505 Rem LINE INCREMENT1510 Rem1515 ? :Input "Line increment (1-50): ",I$1520 If Len(I$)<1 Then ? W$:Goto 15151525 If I$(1,1)<"0" Or I$(1,1)>"9" Then ? W$:Goto 15151530 Iline=Val(I$)1535 If Iline>50 Or Iline<1 Then ? W$:Goto 15151540 Rem1545 Rem DATA ITEMS PER LINE. ALLOW1550 Rem MORE DATA FOR STRINGS.1555 Rem1560 ?  "Bytes per line (";1565   If Ds=1:? "4";:Else  "16";:Endif1570 ? " to ";1575   If Ds=1:? "24";:Else  "96";:Endif1580 Input "): ",I$1585 If Len(I$)<1 Then ? W$:Goto 15601590 If I$(1,1)<"0" Or I$(1,1)>"9" Then ? W$:Goto 15601595 Bline=Val(I$)1600 If (Ds=1 And (Bline<4 Or Bline>24)) Or (Ds=2 And (Bline<16 Or Bline>96)) Then ? W$:Goto 15601605 ?1610 Rem1615 Rem INIT THE READING VARS1620 Rem1625 Zstart=$ffff:Rem THE ABSOLUTE STARTING ADDRESS1630 Zend=$00:Rem THE ABSOLUTE END ADDRESS1635 Tstart=$ffff:Rem TEMPORARY START1640 Tend=$00:Rem TEMPORARY END.1645 Rem1650 Rem OPEN THE FILE TO READ1655 Rem1660 File$=Infile$1665 Trap 2970:Open #2,4,0,Infile$1670 Rem1675 Rem READING DATA1680 Rem1685 If Ignadd=2 Then Goto 1825:Rem STRAIGHT FILE READ1690 Rem1695 Rem READING SEGMENTED FILE  1700 Rem1705 Trap 30401710 Lb=-1:Hb=-1:Tstart=$ffff:Tend=$001715 Get #2,Lb:Get #2,Hb1720 If Lb=$ff And Hb=$ff Then Goto 17101725 If Tstart=$ffff Then Tstart=Hb*256+Lb:Goto 17151730 Tend=Hb*256+Lb1735 Ad=Tstart:Gosub 2770:Eads=Erradd1740 Ad=Tend:Gosub 2770:Eade=Erradd1745 Rem IF BAD ADDRESS THEN EXIT...1750 If Eads>0 Or Eade>0 Then Goto 31201755 Rem REDETERMINE MIN/START, MAX/END ADDRESS1760 If Tstart<Zstart Then Zstart=Tstart1765 If Tend>Zend Then Zend=Tend1770 Rem SEGMENT READ, POKE AT START1775 Trap 30051780 Get #2,Ch1785 Poke Tstart,Ch1790 If Tstart=Tend Then Goto 1695:Rem BLOCK DONE1795 Tstart=Tstart+11800 Goto 17751805 Rem1810 Rem STRAIGHT FILE READ1815 Rem UNKNOWN SIZE, POKE AT END1820 Rem1825 Tstart=Xaddr:Tend=Xaddr-11830 Trap 3085:Rem EXPECT EOF1835 Get #2,Ch1840 Tend=Tend+11845 Ad=Tend:Gosub 27751850 If Erradd>0 Then Goto 3120:Rem ABORT1855 Poke Tend,Ch1860 Goto 18351865 Zstart=Tstart:Zend=Tend1870 Rem1875 Rem IF DATA WAS READ, OUTPUT BYTES...1880 Rem1885 Close #21890   If Zstart>Zend:Rem NO ADDRESS RANGE1895   ?  "No addresses set from file"1900   End1905   Endif1910 Rem1915 Rem OUTPUT FILE...1920 Rem1925 File$=Outfile$1930 Trap 2970:Open #2,8,0,Outfile$1935 Trap 32151940 Rem1945 Rem OUTPUT BYTES.1950 Rem1955 Sep=1:Byte=01960 Rem1965 Rem DESCRIPTIVE COMMENT FOR THE FORGETFUL1970 Rem1975 ? Sline;" Rem ";Infile$1980 ? #2;Sline;" Rem ";Infile$1985 Sline=Sline+Iline1990 Ch=Zend-Zstart+11995 ? Sline;" Rem Size  = ";2000 ? #2;Sline;" Rem Size  = ";:Gosub 31502005 Sline=Sline+Iline2010 Ch=Zstart2015 ?  Sline;" Rem Start = ";2020 ? #2:? #2;Sline;" Rem Start = ";:Gosub 31502025 Sline=Sline+Iline2030 Ch=Zend2035 ?  Sline;" Rem End   = ";2040 ? #2:? #2;Sline;" Rem End   = ";:Gosub 31502045 Sline=Sline+Iline2050 ?  #22055 Rem2060 Rem REAL DATA OUT...2065 Rem2070   While Zstart<=Zend2075   Rem NEW LINE SEPARATION2080     If Sep=1:Rem NEW LINE2085     ? Sline;" ";2090     ? #2;Sline;" ";2095     Sline=Sline+Iline2100       If Ds=1:Rem BASIC DATA2105       ? "Data ";2110       ? #2;"Data ";2115       Else :Rem OUTPUT STRING2120       ? "A$";2125       ? #2;"A$";2130         If Byte>0:Rem STRING CONTINUATION2135         ? "(";Byte+1;")";2140         ? #2;"(";Byte+1;")";2145         Endif2150       ? "=";Chr$(34);2155       ? #2;"=";Chr$(34);2160       Endif2165     Endif2170   Rem BETWEEN DATA2175     If Sep=0 And Ds=1:Rem NOT END OF LINE2180     ? ",";2185     ? #2;",";2190     Endif2195   Rem NOW GET DATA TO OUTPUT2200   Ch=Peek(Zstart)2205   Rem DATA OR STRING?2210     If Ds=1:Rem DATA2215     Gosub 3150:Rem DEX OR DEC OUTPUT?2220     Else :Rem STRING2225     Gosub 2915:Rem CONVERT BYTE TO CHR$2230     Gosub 2810:Rem CHECK FOR SUBSTITUTION2235       If Subqe=1:Rem QUOTE OR EOL2240       ? " ";2245       ? #2;" ";2250       Else :Rem NOT SUBSTITUTED2255       ? Chr$(27);Chr$(Chs);:Rem CONVERTED FROM CH2260       ? #2;Chr$(Chs);2265       Endif2270     Endif2275   Rem COUNT THE BYTE OUTPUT2280   Rem AND DETERMINE END OF LINE2285   Byte=Byte+1:Sep=02290     If Byte/Bline=Int(Byte/Bline):Rem BREAK LINE2295     Sep=12300     Endif2305   Zstart=Zstart+12310     If Sep=1 Or Zstart>Zend:Rem END OF LINE OR END OF DATA2315       If Ds=2:Rem END OF DATA FOR STRING2320       ? Chr$($22)2325       ? #2;Chr$($22)2330       Else :Rem DATA2335       ?  #22340       Endif2345     Endif2350   Endwhile2355 Rem2360 Rem OUTPUT QUOTE AND EOL LISTS2365 Rem2370   If Ql>0:Rem OUTPUT QUOTE LIST2375   ?  Sline;" Rem Embedded quote list"2380   ? #2:? #2;Sline;" Rem Embedded quote list"2385   Sline=Sline+Iline2390   Sep=1:Byte=02395   I=12400     While I<=Ql2405       If Sep=1:Rem NEW LINE2410       ? Sline;" Data ";2415       ? #2;Sline;" Data ";2420       Sline=Sline+Iline2425       Endif2430       If Sep=0:Rem BETWEEN DATA2435       ? ",";2440       ? #2;",";2445       Endif2450     Ch=Dpeek(Adr(Qlist$(I;)))2455     Gosub 3150:Rem OUTPUT CH2460     Sep=02465     If I&$fff8=I Then Sep=12470     I=I+12475     Endwhile2480   Endif2485 Rem2490   If El>0:Rem OUTPUT EOL LIST2495   ?  Sline;" Rem Embedded EOL list"2500   ? #2:? #2;Sline;" Rem Embedded EOL list"2505   Sline=Sline+Iline2510   Sep=1:Byte=02515   I=12520     While I<=El2525       If Sep=1:Rem NEW LINE2530       ? Sline;" Data ";2535       ? #2;Sline;" Data ";2540       Sline=Sline+Iline2545       Endif2550       If Sep=0:Rem BETWEEN DATA2555       ? ",";2560       ? #2;",";2565       Endif2570     Ch=Dpeek(Adr(Elist$(I;)))2575     Gosub 3150:Rem OUTPUT CH2580     Sep=02585     If I&$fff8=I Then Sep=12590     I=I+12595     Endwhile2600   Endif2605 ?  #22610 Close #2:Trap 400002615 End2620 Rem2625 Rem REPORT LAST ERROR2630 Rem2635 ?  "Error ";Err(0);" at line ";Err(1)2640 Return2645 Rem2650 Rem REPORT LAST ADDRESSES2655 Rem2660 ? "Start: $";Hex$(Tstart);"/";Tstart2665 ? "  End: $";Hex$(Tend);"/";Tend:?2670 Return2675 Rem2680 Rem SUBROUTINE CONVERT HEX2685 Rem STRING TO NUMERIC VALUE.2690 Rem2695 Hx$="0123456789ABCDEFabcdef"2700 Xaddr=02705 If Len(H$)<1 Then Return2710 If H$(1,1)="$" And Len(H$)>1 Then H$(1)=H$(2):Goto 27052715 If H$(1,1)="$" Then Return2720   For X=1 To Len(H$)2725   B=Find(Hx$,H$(X,X),0)2730   If B=0 Then Xaddr=0:Return2735   If B>16 Then B=B-62740   Xaddr=Xaddr*16+(B-1)2745   Next X2750 Return2755 Rem2760 Rem ADDRESS RANGE CHECK2765 Rem2770 Erradd=02775 If (Ad<Dpeek($0e) And (Ad<$0600 Or Ad>$06ff)) Or Ad>=Dpeek($0230) Then Erradd=12780 Return2785 Rem2790 Rem CANNOT EMBED QUOTE OR EOL2795 Rem IN STRING, SO TRACK FOR2800 Rem DATA OUTPUT LATER.2805 Rem2810 Subqe=02815   If Ch=2:Rem BYTE 2 = CHR$($22) = QUOTE2820     If Ql>=Maxqe:Rem TOO MANY2825     ?  "Too many embedded quotes ($02=$22)."2830     End2835     Endif2840   Ql=Ql+1:Subqe=12845   Dpoke Adr(Qlist$(Ql;)),Byte+12850   Endif2855   If Ch=$db:Rem BYTE $DB = CHR$($9B) = EOL2860     If El>=Maxqe:Rem TOO MANY2865     ?  "Too many embedded End Of Lines ($DB=$9B)."2870     End2875     Endif2880   El=El+1:Subqe=12885   Dpoke Adr(Elist$(El;)),Byte+12890   Endif2895 Return2900 Rem2905 Rem CONVERT CH BYTE TO CHR$2910 Rem2915 Chs=Ch:Chi=Ch&$7f2920   If Chi>=$00 And Chi<=$3f:Rem ++ $202925   Chs=Chs+$202930   Endif2935   If Chi>=$40 And Chi<=$5f:Rem -- $402940   Chs=Chs-$402945   Endif2950 Return2955 Rem2960 Rem FILE OPEN ERROR2965 Rem2970 ? "Failed to open file: ";File$2975 Gosub 26352980 End2985 Rem2990 Rem ERROR DURING SEGMENT DATA2995 Rem IS NOT ALLOWED3000 Rem3005 ?  "Error Reading Segment Data"3010 Gosub 2635:Gosub 26603015 End3020 Rem3025 Rem ERROR DURING SEGMENT ADDRESSES3030 Rem3035 Rem EOF MAY MEAN END OF SEGMENT3040 If Err(0)=136 Then Goto 18853045 Rem NOT EOF IS BAD(DER)3050 ?  "Error Reading Segment Header"3055 Gosub 2635:Gosub 26603060 End3065 Rem3070 Rem ERROR DURING REGULAR FILE3075 Rem3080 Rem EOF MEANS END OF FILE3085 If Err(0)=136 Then Goto 18653090 ?  "Error reading data"3095 Gosub 2635:Gosub 26603100 End3105 Rem3110 Rem ADDRESS RANGE ERROR3115 Rem3120 ?  "Address Range Error During I/O":?3125 Gosub 26603130 End3135 Rem3140 Rem OUTPUT CH AS HEX OR DEC3145 Rem3150   If Hd=1:Rem HEX OR DEC3155   B$=Hex$(Ch)3160   If Ch<=$ff Then B$=B$(3)3165   ? "$";B$;3170   ? #2;"$";B$;3175   Else :Rem DEC3180   ? Ch;3185   ? #2;Ch;3190   Endif3195 Return3200 Rem3205 Rem FILE WRITE ERROR3210 Rem3215 ? "Failed to write file: ";File$3220 Gosub 26353225 End

0 Comments


Recommended Comments

There are no comments to display.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...