szmanda@aluxp.UUCP (szmanda) (01/06/85)
The following is an MS-Basic nibble copier. It is reputed to be able to circumvent the copy protection on most disks. As written, the program requires two disk drives, but I was able to hack my own version so that only one disk drive is required. I should warn you that a one disk version requires 80 swaps to copy a single disk - so be sure to pack a lunch. This program, written by Dennis Brothers, is being posted because of the recent interest in backing up protected software. It is intended **only** for use by persons who wish to exercise their legal right to produce archival copies of software that they have purchased. It is **not** intended that this program be used for unlawful purposes. Chuck Szmanda aluxp!szmanda AT&T Bell Laboratories Allentown PA -------------------------------------------------------------------------------- 1000 CALL TEXTSIZE(9): CALL TEXTFONT(4) ' Use 9-point Monaco 1010 CLS: PRINT 1020 PRINT " MacBackup - Macintosh Disk Backup Program" 1030 PRINT 1040 PRINT " Version 1.10 - 30 October, 1984" 1050 PRINT " Dennis F. Brothers - Compuserve 70065,172 - MCI Mail DBROTHERS" 1060 PRINT 1070 PRINT " Copyright (c) 1984 - Brothers Associates, Wayland MA" 1080 PRINT " All rights reserved." 1090 PRINT: PRINT 1100 PRINT " Initializing - Please wait....." 1110 PRINT 1120 ' 1130 CLEAR ,16000: DEFINT A-Z 1140 GOSUB 4230 ' Initialize for later M.L. setup 1150 ' 1160 ' Define constants 1170 FALSE=0: TRUE=-1: NIL#=0 1180 SONY=-5 ' RefNum for disk driver 1190 BLKMUL=20 ' Number of blocks at a time 1200 TAGSIZE=12: BLOCKSIZE=512: PARAMSIZE=80 1210 DMSIZE=12: DMREADSIZE=50 1220 MEMSIZE=BLKMUL*BLOCKSIZE+BLKMUL*TAGSIZE+PARAMSIZE+2*DMSIZE+DMREADSIZE+500 1230 GOSUB 4550 ' go allocate fixed memory 1240 BLOCK#=MEM# 1250 TAG#=BLOCK#+BLKMUL*BLOCKSIZE 1260 PARAM#=TAG#+BLKMUL*TAGSIZE 1270 BADDM#=PARAM#+PARAMSIZE 1280 MODDM#=BADDM#+DMSIZE 1290 DMREAD#=MODDM#+DMSIZE 1300 MLBASE#=DMREAD#+DMREADSIZE 1310 ' 1320 RESTORE 1330 1330 ' hex data for Peek/Poke ML subroutines 1340 DATA "1C0000000008001C002C00404E560000206E00083010206E000C30804E5E4E7521" 1350 DATA "1C001C004E560000206E000A30AE00084E5E4E754E560000206E00082010206E3F" 1360 DATA "18003800000C20804E5E4E754E560000206E000C20AE00084E5E4E7512" 1370 DATA "000000000000" 1380 MLNAME$="Peek/Poke": ML#=MLBASE#: GOSUB 4310 1390 WPEEK#=ML#+FNW#(ML#+0) 1400 WPOKE#=ML#+FNW#(ML#+2) 1410 LPEEK#=ML#+FNW#(ML#+4) 1420 LPOKE#=ML#+FNW#(ML#+6) 1430 RESTORE 1440 1440 ' hex data for Input/Output ML subroutines 1450 DATA "1C0000000012002C00460060007A009400A200B000BE4E56FFF848EE0101FFF818" 1460 DATA "1C001C00206E0008A0044CEE0101FFF84E5E4E754E56FFF848EE0101FFF8206E94" 1470 DATA "1C0038000008A0054CEE0101FFF84E5E4E754E56FFF848EE0101FFF8206E0008FD" 1480 DATA "1C005400A0004CEE0101FFF84E5E4E754E56FFF848EE0101FFF8206E0008A00A44" 1490 DATA "1C0070004CEE0101FFF84E5E4E754E56FFF848EE0101FFF8206E0008A0014CEE97" 1500 DATA "1C008C000101FFF84E5E4E754E560000206E0008A0024E5E4E754E560000206E73" 1510 DATA "1C00A8000008A4024E5E4E754E560000206E0008A0034E5E4E754E560000206EA1" 1520 DATA "0800C4000008A4034E5E4E7516" 1530 DATA "000000000000" 1540 MLNAME$="Input/Output": ML#=MLNEXT#: GOSUB 4310 1550 CONTROL#=ML#+FNW#(ML#+0) 1560 STATUS#=ML#+FNW#(ML#+2) 1570 OPENDF#=ML#+FNW#(ML#+4) 1580 OPENRF#=ML#+FNW#(ML#+6) 1590 CLOSEF#=ML#+FNW#(ML#+8) 1600 READS#=ML#+FNW#(ML#+10) 1610 READA#=ML#+FNW#(ML#+12) 1620 WRITES#=ML#+FNW#(ML#+14) 1630 WRITEA#=ML#+FNW#(ML#+16) 1640 RESTORE 1650 1650 ' hex data for Data Mark ML routines 1660 DATA "1C00000000120014001A004C004E00540058005A0060227C00000000343C000096" 1670 DATA "1C001C0021DF0124760070C0283C01FE000A7A007C007E00244916146AFC4A15C0" 1680 DATA "1C0038006B021F1614C351CAFFF270C72F3801244A404E75227C000000004EF932" 1690 DATA "1000540000000000247C000000004EF900000000B5" 1700 DATA "000000000000" 1710 MLNAME$="Data Mark": ML#=MLNEXT#: GOSUB 4310 1720 RDMPRE#=ML#+FNW#(ML#+0) 1730 RDMPREDMPNT#=ML#+FNW#(ML#+2) 1740 RDMPRECNT#=ML#+FNW#(ML#+4) 1750 RDATAPRE#=ML#+FNW#(ML#+6) 1760 RDATAPREDMPNT#=ML#+FNW#(ML#+8) 1770 RDATAPREJMP#=ML#+FNW#(ML#+10) 1780 WDATAPRE#=ML#+FNW#(ML#+12) 1790 WDATAPREDMPNT#=ML#+FNW#(ML#+14) 1800 WDATAPREJMP#=ML#+FNW#(ML#+16) 1810 ' 1820 ' Define fields within I/O parameter block 1830 IOCOMPLETION#=PARAM#+12 1840 IORESULT#=PARAM#+16 1850 IOFILENAME#=PARAM#+18 1860 IODRVNUM#=PARAM#+22 1870 IOREFNUM#=PARAM#+24 1880 CSCODE#=PARAM#+26 1890 CSPARAM#=PARAM#+28 1900 IOBUFFER#=PARAM#+32 1910 IOBYTECOUNT#=PARAM#+36 1920 IONUMDONE#=PARAM#+40 1930 IOPOSMODE#=PARAM#+44 1940 IOPOSOFFSET#=PARAM#+46 1950 ' 1960 ' Use our own tag buffer 1970 FOR I=0 TO PARAMSIZE-1:POKE PARAM#+I,0: NEXT I 1980 CALL WPOKE#(IOREFNUM#,SONY) 1990 CALL WPOKE#(CSCODE#,8) ' Set Tag Buffer 2000 CALL LPOKE#(CSPARAM#,TAG#) 2010 CALL CONTROL#(PARAM#) 2020 FOR I=0 TO BLKMUL*TAGSIZE-1: POKE TAG#+I,0: NEXT I 2030 ' 2040 GOSUB 4130 ' Eject both disks 2050 ' 2060 ' do the copy 2070 PRINT "About to copy - insert disks, enter source drive # -" 2080 LINE INPUT " (1 = Internal, 2 = External, Return = Quit ):",L$ 2090 IF L$<>"" THEN GOTO 2200 2100 ' 2110 ' Terminate - restore system tag buffer 2120 FOR I=0 TO PARAMSIZE-1:POKE PARAM#+I,0: NEXT I 2130 CALL WPOKE#(IOREFNUM#,SONY) 2140 CALL WPOKE#(CSCODE#,8) ' Set Tag Buffer 2150 CALL LPOKE#(CSPARAM#,NIL#) 2160 CALL CONTROL#(PARAM#) 2170 GOSUB 4130 2180 SYSTEM 2190 ' 2200 IF L$<>"1" AND L$<>"2" THEN GOTO 2070 2210 FROMDRIVE=VAL(L$): TODRIVE=3-FROMDRIVE 2220 LINE INPUT "First block to copy (Return = 0): ",L$ 2230 IF L$="" THEN FIRSTBLOCK=0 ELSE FIRSTBLOCK=VAL(L$) 2240 LINE INPUT "Last block to copy (Return = 799): ",L$ 2250 IF L$="" THEN LASTBLOCK=799 ELSE LASTBLOCK=VAL(L$) 2260 LINE INPUT "Log exceptions to printer (Y/N, Return=N)? ";PL$ 2270 PL=(LEFT$(PL$,1)="Y") OR (LEFT$(PL$,1)="y") 2280 ' 2290 BLKNUM=FIRSTBLOCK: SEARCHBAD=FALSE: FOUNDBAD=FALSE 2300 ' 2310 ' Main copy loop 2320 IF SEARCHBAD THEN NBLOCKS=1: GOTO 2340 2330 NBLOCKS=BLKMUL: IF (BLKNUM+NBLOCKS-1)>LASTBLOCK THEN NBLOCKS=(LASTBLOCK-BLKNUM)+1 2340 PRINT "Reading ";BLKNUM;:IF NBLOCKS>1 THEN PRINT " - ";BLKNUM+NBLOCKS-1; 2350 ' 2360 ' read block(s) 2370 FOR I=0 TO PARAMSIZE-1:POKE PARAM#+I,0: NEXT I 2380 CALL WPOKE#(IODRVNUM#,FROMDRIVE) 2390 CALL WPOKE#(IOREFNUM#,SONY) 2400 CALL LPOKE#(IOBUFFER#,BLOCK#) 2410 CALL LPOKE#(IOBYTECOUNT#,512#*NBLOCKS) 2420 CALL WPOKE#(IOPOSMODE#,1) 2430 CALL LPOKE#(IOPOSOFFSET#,BLKNUM*512#) 2440 CALL READS#(PARAM#) 2450 ERRCOD#=FNW#(IORESULT#): IF ERRCOD#>=32768! THEN ERRCOD#=ERRCOD#-65536! 2460 IF ERRCOD#=0 THEN GOTO 2500 2470 IF SEARCHBAD THEN GOTO 2680 2480 SEARCHBAD=TRUE: FOUNDBAD=FALSE: PRINT TAB(25);"Bad block in group": GOTO 2310 2490 ' 2500 ' write normal block(s) 2510 IF FOUNDBAD THEN SEARCHBAD=FALSE: FOUNDBAD=FALSE 2520 PRINT TAB(25);"Writing ";BLKNUM;:IF NBLOCKS>1 THEN PRINT " - ";BLKNUM+NBLOCKS-1; 2530 PRINT 2540 FOR I=0 TO PARAMSIZE-1:POKE PARAM#+I,0: NEXT I 2550 CALL WPOKE#(IODRVNUM#,TODRIVE) 2560 CALL WPOKE#(IOREFNUM#,SONY) 2570 CALL LPOKE#(IOBUFFER#,BLOCK#) 2580 CALL LPOKE#(IOBYTECOUNT#,512#*NBLOCKS) 2590 CALL WPOKE#(IOPOSMODE#,1) 2600 CALL LPOKE#(IOPOSOFFSET#,BLKNUM*512#) 2610 CALL WRITES#(PARAM#) 2620 ERRCOD#=FNW#(IORESULT#): IF ERRCOD#>=32768! THEN ERRCOD#=ERRCOD#-65536! 2630 IF ERRCOD#=0 THEN BLKNUM=BLKNUM+NBLOCKS: GOTO 4040 2640 PRINT "Write Error = ";ERRCOD# 2650 IF PL THEN LPRINT "Write Error = ";ERRCOD# 2660 PRINT "Write failure - Copy aborted - press Return to continue": GOTO 4080 2670 ' 2680 FOUNDBAD=TRUE 2690 PRINT TAB(25);"Block ";BLKNUM;" - Read Error = ";ERRCOD# 2700 IF PL THEN LPRINT "Block ";BLKNUM;" - Read Error = ";ERRCOD# 2710 IF ERRCOD#<>-71 THEN GOTO 3800 2720 ' 2730 ' write block with modified data mark 2740 RDROMADDR#=FNL#(558#) 2750 FOR I=0 TO 49: POKE DMREAD#+I,0: NEXT I 2760 CALL LPOKE#(RDMPREDMPNT#,DMREAD#) 2770 CALL WPOKE#(RDMPRECNT#,DMREADSIZE-2) 2780 CALL LPOKE#(558#,RDMPRE#) 2790 ' 2800 FOR I=0 TO PARAMSIZE-1:POKE PARAM#+I,0: NEXT I 2810 CALL WPOKE#(IODRVNUM#,FROMDRIVE) 2820 CALL WPOKE#(IOREFNUM#,SONY) 2830 CALL LPOKE#(IOBUFFER#,BLOCK#) 2840 CALL LPOKE#(IOBYTECOUNT#,512#) 2850 CALL WPOKE#(IOPOSMODE#,1) 2860 CALL LPOKE#(IOPOSOFFSET#,BLKNUM*512#) 2870 CALL READS#(PARAM#) 2880 CALL LPOKE#(558#,RDROMADDR#) 2890 ' 2900 RESTORE 2910 2910 DATA &HFF, &H3F, &HCF, &HF3, &HFC, &HFF, &HD5, &HAA, &HAD, &HDE, &HAA, &HFF 2920 FOR I=0 TO 11: READ B: POKE MODDM#+I, B: NEXT I 2930 FOR I=2 TO DMREADSIZE-3 2940 IF (PEEK(DMREAD#+I-2)<>&HFF) OR (PEEK(DMREAD#+I-1)<>&HFF) THEN GOTO 2970 2950 IF (PEEK(DMREAD#+I)=&HFF) OR (PEEK(DMREAD#+I+1)=&HFF) THEN GOTO 2970 2960 IF (PEEK(DMREAD#+I+2)=&HAD) THEN GOTO 3010 2970 NEXT I 2980 PRINT "Could not locate data mark" 2990 IF PL THEN LPRINT "Could not locate data mark" 3000 GOTO 3800 3010 PRINT "Using modified data mark: "; 3020 IF PL THEN LPRINT "Using modified data mark: "; 3030 FOR J=0 TO 2: K=PEEK(DMREAD#+I+J) 3040 POKE MODDM#+6+J, K 3050 PRINT HEX$(K); " "; 3060 IF PL THEN LPRINT HEX$(K); " "; 3070 NEXT J 3080 PRINT 3090 IF PL THEN LPRINT 3100 ' 3110 RDROMADDR#=FNL#(558#) 3120 CALL LPOKE#(RDATAPREDMPNT#,MODDM#+6) 3130 CALL LPOKE#(RDATAPREJMP#,RDROMADDR#) 3140 CALL LPOKE#(558#,RDATAPRE#) 3150 ' 3160 FOR I=0 TO PARAMSIZE-1:POKE PARAM#+I,0: NEXT I 3170 CALL WPOKE#(IODRVNUM#,FROMDRIVE) 3180 CALL WPOKE#(IOREFNUM#,SONY) 3190 CALL LPOKE#(IOBUFFER#,BLOCK#) 3200 CALL LPOKE#(IOBYTECOUNT#,512#) 3210 CALL WPOKE#(IOPOSMODE#,1) 3220 CALL LPOKE#(IOPOSOFFSET#,BLKNUM*512#) 3230 CALL READS#(PARAM#) 3240 CALL LPOKE#(558#,RDROMADDR#) 3250 ERRCOD#=FNW#(IORESULT#): IF ERRCOD#>=32768! THEN ERRCOD#=ERRCOD#-65536! 3260 IF ERRCOD#=0 THEN GOTO 3310 3270 PRINT "Could not read src. modified data mark - Error = ";ERRCOD# 3280 IF PL THEN LPRINT "Could not read src. modified data mark - Error = ";ERRCOD# 3290 GOTO 3800 3300 ' 3310 FAIL=0 3320 WDROMADDR#=FNL#(562#) 3330 CALL LPOKE#(WDATAPREDMPNT#,MODDM#) 3340 CALL LPOKE#(WDATAPREJMP#,WDROMADDR#) 3350 CALL LPOKE#(562#,WDATAPRE#) 3360 ' 3370 FOR I=0 TO PARAMSIZE-1:POKE PARAM#+I,0: NEXT I 3380 CALL WPOKE#(IODRVNUM#,TODRIVE) 3390 CALL WPOKE#(IOREFNUM#,SONY) 3400 CALL LPOKE#(IOBUFFER#,BLOCK#) 3410 CALL LPOKE#(IOBYTECOUNT#,512#) 3420 CALL WPOKE#(IOPOSMODE#,1) 3430 CALL LPOKE#(IOPOSOFFSET#,BLKNUM*512#) 3440 CALL WRITES#(PARAM#) 3450 CALL LPOKE#(562#,WDROMADDR#) 3460 ERRCOD#=FNW#(IORESULT#): IF ERRCOD#>=32768! THEN ERRCOD#=ERRCOD#-65536! 3470 IF ERRCOD#=0 THEN GOTO 3520 3480 PRINT "Modified Block ";BLKNUM;" - Write Error = ";ERRCOD# 3490 IF PL THEN LPRINT "Modified Block ";BLKNUM;" - Write Error = ";ERRCOD# 3500 PRINT "Write failure - Copy aborted - press Return to continue": GOTO 4080 3510 ' 3520 ' Verify that modified block is readable 3530 FOR RETRY=1 TO 5 3540 RDROMADDR#=FNL#(558#) 3550 CALL LPOKE#(RDATAPREDMPNT#,MODDM#+6) 3560 CALL LPOKE#(RDATAPREJMP#,RDROMADDR#) 3570 CALL LPOKE#(558#,RDATAPRE#) 3580 ' 3590 FOR I=0 TO PARAMSIZE-1:POKE PARAM#+I,0: NEXT I 3600 CALL WPOKE#(IODRVNUM#,TODRIVE) 3610 CALL WPOKE#(IOREFNUM#,SONY) 3620 CALL LPOKE#(IOBUFFER#,BLOCK#) 3630 CALL LPOKE#(IOBYTECOUNT#,512#) 3640 CALL WPOKE#(IOPOSMODE#,1) 3650 CALL LPOKE#(IOPOSOFFSET#,BLKNUM*512#) 3660 CALL READS#(PARAM#) 3670 CALL LPOKE#(558#,RDROMADDR#) 3680 ERRCOD#=FNW#(IORESULT#): IF ERRCOD#>=32768! THEN ERRCOD#=ERRCOD#-65536! 3690 IF ERRCOD#=0 THEN GOTO 3760 3700 PRINT "Could not read dest. modified data mark - Error = ";ERRCOD#; 3710 IF PL THEN LPRINT "Could not read dest. modified data mark - Error = ";ERRCOD#; 3720 FAIL=FAIL+1: IF FAIL>=5 THEN PRINT: GOTO 3800 3730 PRINT " - retrying." 3740 IF PL THEN LPRINT " - retrying." 3750 GOTO 3320 3760 ' 3770 NEXT RETRY 3780 BLKNUM=BLKNUM+1: GOTO 4040 3790 ' 3800 ' write block with bad data mark 3810 PRINT "Writing bad data mark" 3820 IF PL THEN LPRINT "Writing bad data mark" 3830 WDROMADDR#=FNL#(562#) 3840 FOR I=0 TO 7: POKE BADDM#+I,0: NEXT I 3850 CALL LPOKE#(WDATAPREDMPNT#,BADDM#) 3860 CALL LPOKE#(WDATAPREJMP#,WDROMADDR#) 3870 CALL LPOKE#(562#,WDATAPRE#) 3880 ' 3890 FOR I=0 TO PARAMSIZE-1:POKE PARAM#+I,0: NEXT I 3900 CALL WPOKE#(IODRVNUM#,TODRIVE) 3910 CALL WPOKE#(IOREFNUM#,SONY) 3920 CALL LPOKE#(IOBUFFER#,BLOCK#) 3930 CALL LPOKE#(IOBYTECOUNT#,512#) 3940 CALL WPOKE#(IOPOSMODE#,1) 3950 CALL LPOKE#(IOPOSOFFSET#,BLKNUM*512#) 3960 CALL WRITES#(PARAM#) 3970 CALL LPOKE#(562#,WDROMADDR#) 3980 ERRCOD#=FNW#(IORESULT#): IF ERRCOD#>=32768! THEN ERRCOD#=ERRCOD#-65536! 3990 IF ERRCOD#=0 THEN BLKNUM=BLKNUM+1: GOTO 4040 4000 PRINT "Bad Block ";BLKNUM;" - Write Error = ";ERRCOD# 4010 IF PL THEN LPRINT "Bad Block ";BLKNUM;" - Write Error = ";ERRCOD# 4020 PRINT "Write failure - Copy aborted - press Return to continue": GOTO 4080 4030 ' 4040 ' End of loop - test for completion 4050 IF BLKNUM<=LASTBLOCK THEN GOTO 2310 4060 ' 4070 PRINT "Copy completed - press Return to continue" 4080 LINE INPUT " (Type E Return to eject disks):",L$ 4090 IF L$="" THEN GOTO 2060 4100 IF LEFT$(L$,1)="E" OR LEFT$(L$,1)="e" THEN GOSUB 4130 4110 GOTO 2060 4120 ' 4130 ' Subroutine to eject both disks 4140 FOR I=0 TO PARAMSIZE-1:POKE PARAM#+I,0: NEXT I 4150 CALL WPOKE#(IODRVNUM#,1) ' Internal 4160 CALL WPOKE#(IOREFNUM#,SONY) 4170 CALL WPOKE#(CSCODE#,7) ' Eject 4180 CALL CONTROL#(PARAM#) 4190 CALL WPOKE#(IODRVNUM#,2) ' External 4200 CALL CONTROL#(PARAM#) 4210 RETURN 4220 ' 4230 ' Initialize for later machine-language setup 4240 DEF FNP#(HI,LO)=HI*65536#+LO-65536#*(LO<0) 4250 DEF FNW#(A#)=PEEK(A#)*256#+PEEK(A#+1) 4260 DEF FNL#(A#)=PEEK(A#+1)*65536#+PEEK(A#+2)*256#+PEEK(A#+3) 4270 MLNAME$="": ML#=0: MLNEXT#=0 4280 ML$="": MLLINE=0: MLCHK=0: I=0: MLL=0: MLS=0: MLP#=0 4290 RETURN 4300 ' 4310 ' Subroutine to set up machine language. 4320 MLLINE=1 4330 MLNEXT#=ML# 4340 READ ML$ ' Read next line of HEX data 4350 MLCHK=0 ' Initialize checksum 4360 FOR I=1 TO LEN(ML$)-1 STEP 2 ' Scan by bytes (pairs of hex digits) 4370 MLCHK=(MLCHK+VAL("&H"+MID$(ML$,I,2))) MOD &H100' Compute checksum 4380 NEXT I 4390 IF MLCHK=0 THEN GOTO 4410 4400 CLS: PRINT CHR$(7);"Error in relative line ";MLLINE; " of ";MLNAME$;" ML data": STOP 4410 MLL=VAL("&H"+MID$(ML$,3,2)+MID$(ML$,1,2)) ' Get byte count of ML string 4420 MLS=VAL("&H"+MID$(ML$,7,2)+MID$(ML$,5,2)) ' Get start addr of ML string 4430 IF MLL=0 THEN GOTO 4510 ' Zero byte count ends ML data 4440 FOR I=0 TO MLL-1: MLP#=ML#+MLS+I 4450 POKE MLP#,VAL("&H"+MID$(ML$,9+I*2,2)) ' Put ML data in array 4460 NEXT I 4470 IF MLP#>MLNEXT# THEN MLNEXT#=MLP# 4480 MLLINE=MLLINE+1 ' Keep track of DATA line number, in case of error 4490 GOTO 4340 4500 ' 4510 MLNEXT#=MLNEXT#+1: MLP#=INT(MLNEXT#/2) 4520 IF MLNEXT#<>(MLP#*2) THEN MLNEXT#=MLNEXT#+1 4530 RETURN 4540 ' 4550 ' Allocate fixed memory in the application heap 4560 GETMEM#=0: DIM GETMEMARRAY(50) ' Array to hold "get memory" subroutine 4570 MEMARG=0: MEMARG#=0: DIM MEMHANDLE(1): MEMHANDLE#=0 4580 MEMARG#=VARPTR(MEMARG): MEMHANDLE#=VARPTR(MEMHANDLE(0)) 4590 GETMEM#=VARPTR(GETMEMARRAY(0)) 4600 RESTORE 4610 4610 ' Hex data for Get Memory ML subroutine 4620 DATA "1C0000004E56FFF448EE0301FFF4226E000842803011A122226E000C2288226EEC" 4630 DATA "14001C00000832806604A02932804CEE0301FFF44E5E4E7591" 4640 DATA "000000000000" 4650 MLNAME$="Get Memory": ML#=GETMEM#: GOSUB 4310 4660 MEMARG=MEMSIZE: CALL GETMEM#(MEMHANDLE#, MEMARG#) 4670 IF MEMARG<>0 THEN PRINT "Couldn't allocate enough memory": STOP 4680 MEMHANDLE#=FNP#(MEMHANDLE(0), MEMHANDLE(1)) 4690 MEM#=FNL#(MEMHANDLE#) 4700 RETURN 4710 END