[net.sources] Backing up protected software on the Macintosh

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