900 REM MGT GDOS/UniDOS/G+DOS/MasterDOS/SAMDOS reader 910 REM Adapted for Next from the Sinclair SuperBASIC 920 REM GDOS_READER_BAS by SNG, 10..20th October 2018 930 REM VERSION 3.4N Copyright 1991,2018,2020 S N Goodwin. 940 : 950 REM Memory Map 1000 CLEAR 44370 1005 LET plus3header=44371: REM 128 bytes (headLen) 1010 LET sector=44500: REM 512 bytes 1015 LET dirAddr=45012: REM 20480 bytes + 43 spare 1017 LET headLen=128 1020 LET entryLen=256 1025 LET sectorBytes = 512 1030 LET sectorDataSize = 510 1035 LET nameLen=10 1040 LET maxDirTracks=4: REM Extended root not supported 1045 LET trackBytes=sectorBytes*10 1050 LET cylinderBytes=trackBytes*2: REM 2 sides 1055 LET maxFiles=maxDirTracks*20 1060 DIM f$(maxFiles,nameLen) 1070 DIM x$(nameLen) 1075 REM DIM d$(sectorDataSize) 1080 DIM t(maxFiles): REM Start Cylinder 0..79 1090 DIM s(maxFiles): REM Start Sector 1..20 1100 DIM z(maxFiles): REM SiZe in bytes 1102 DIM x(maxFiles): REM Type from MGT header 1104 DIM a(maxFiles): REM Line/name/address 1105 DIM b(maxFiles): REM Type 0 program offset 1109 LAYER CLEAR : LAYER 1,2: CLS : REM Screen-breathing-space 1110 : 1111 REM PROC ReadDir("MiscDsk/SamROM30Source.DSK") 1112 REM PROC ReadDir("DATON/TRIFLIP.DSK") 1113 REM PROC ReadDir("ZIP3.DSK") 1114 REM PROC ReadDir("SUPPLEMENT.DSK") 1115 REM PROC ReadDir("MagDisk/SUPPLEMENT10.DSK") 1116 .browse -m -r -t DSK -p "Please select an MGT .DSK file" a$ 1118 PROC ReadDir(a$) 1120 STOP 1125 : 1130 REM CopyBlock copies left bytes starting at t,s onwards 1132 REM omitting strip bytes of BASIC header from the start 1135 DEFPROC CopyBlock() 1137 LOCAL last: REM clobbers %s 1140 REPEAT 1145 REM PRINT left;" bytes left, stripping ";strip 1150 WHILE t+s>0 1160 REM PRINT "TRACK/SECTOR ";t;"/";s 1170 PROC GetSector() 1171 REM LET %s=s: INK %s & 7: REM Timex border flags frags 1172 LET last=sectorDataSize 1173 IF left0 THEN LET strip=0: REM Just the start 1179 IF NOT view THEN PRINT AT 0,22; INVERSE 1; INT (100*(1-(left/z(f))));"%"; 1180 IF t=0 AND s=0 THEN PRINT AT 21,0,"**END** Press any key" 1190 REPEAT UNTIL NOT left 1195 IF file THEN CLOSE # 5 1200 ENDPROC 1210 : 1215 REM Seek, read and update track and sector t, s to g$ 1220 DEFPROC GetSector() 1225 LOCAL b$,t$ 1230 LET b$= STR$ (t*cylinderBytes+sectorBytes*(s-1)) 1235 LET b$=b$+" "+ STR$ sectorBytes 1240 LET t$=" "+p$+" +"+b$+" -m "+ STR$ sector 1245 REM PRINT "extract ";t$ 1250 .$ extract t$ 1260 LET g$= PEEK$ (sector,sectorDataSize) 1310 REM Follow the linked list to the next sector 1315 LET s= PEEK (sector+sectorDataSize+1) 1320 LET t= PEEK (sector+sectorDataSize) 1325 IF t>127 THEN LET t=t-128: LET s=s+10 1330 IF t>79 THEN PRINT "BAD TRACK #";t: STOP 1335 IF s>20 THEN PRINT "BAD SECTOR #";s: STOP 1340 ENDPROC 1345 : 1347 REM Sort f$(1 TO limit) into ascending order and 1348 REM a(), b(), s(), t(), x() and z() correspondingly 1350 DEFPROC SortDir() 1352 LOCAL a,b,c,d,i,j,k,s,t,x,z 1355 LET d=1 1356 REPEAT 1358 LET d=d+d 1360 REPEAT UNTIL d>=limit 1362 REPEAT 1364 LET d= INT ((d-1)*.5) 1367 WHILE d 1368 LET c=limit-d 1370 FOR i=1 TO c 1371 LET j=i 1372 REPEAT 1373 LET k=j+d 1375 WHILE f$(k)0 THEN PRINT "Extended dir": STOP 1440 FOR f=0 TO maxFiles-1 1450 PROC UnPack(f*entryLen+dirAddr) 1455 IF finished THEN GO TO 1470: REM EXIT 1460 NEXT f 1470 PRINT limit;" files found." 1472 PRINT "Sorting..." 1474 PROC SortDir() 1476 CLS 1478 PROC ShowDir() 1480 PROC SelectFile() 1490 ENDPROC 1500: 1510 DEFPROC SelectFile() 1515 LOCAL found,n$,f,type 1517 REPEAT 1520 INPUT '"Filename? ";n$ 1522 WHILE LEN n$>0 1523 PROC UPPER(n$) TO n$ 1524 LET x$=n$: REM Pad into dimensioned global 1526 LET found=0 1530 FOR f=1 TO limit 1540 IF x$=f$(f) THEN LET found=1: GO TO 1548 1545 NEXT f 1546 PRINT " Sorry, ";n$;" not found." 1547 GO TO 1520 1548 LET type=x(f) 1549 IF type=5 OR type=9 THEN LET type=3: REM MDV/OPEN-Type 1550 IF type=7 THEN LET type=3: REM MGT SCREEN$->ZX CODE 1551 IF type>14 AND type<21 THEN LET type=3: REM SAM->ZX 1552 IF type<4 THEN GO TO 1560 1554 PRINT "File type ";t(found);", length ";z(found), 1556 PRINT "Header magic ";a(found);" ";b(found);" ...", 1558 GO TO 1610 1560 REPEAT 1562 INPUT '"View ";(n$);" Extract or Both, V/E/B?";t$ 1563 PROC UPPER(t$) TO t$ 1565 LET view=t$="V" OR t$="B" 1566 LET file=t$="E" OR t$="B" 1567 REPEAT UNTIL view OR file 1568 IF file THEN CLOSE # 5: OPEN # 5,"O>"+n$ 1569 REM Prepare global in/out args for CopyBlock 1570 LET t=t(f) 1572 LET s=s(f) 1573 LET left=z(f) 1577 LET strip=(type<3)*9: REM ZX BASIC header bytes 1580 PROC SetHead(type,z(f),a(f),b(f)): REM PLUS3DOS 1585 REPEAT 1590 PROC CopyBlock() 1600 REPEAT UNTIL left+t+s=0 1610 PAUSE 0 1620 CLS 1625 PROC ShowDir() 1630 REPEAT UNTIL 0 1640 ENDPROC 1660 : 1680 DEFPROC UnPack(p) 1690 LOCAL s,t,n$,i,d,k,length: REM SAM file calcs clobber %k 1695 IF PEEK (p+1)=0 THEN LET finished=1: ENDPROC 1700 LET t= PEEK p 1703 IF t>127 THEN LET t=t-128: REM Hidden flag 1706 IF t>64 THEN LET t=t-64: REM protected flag 1708 IF t=0 OR t>31 THEN ENDPROC 1720 LET n$= PEEK$ (p+1,nameLen) 1725 PROC UPPER(n$) TO n$ 1780 IF t=0 OR t>31 THEN ENDPROC 1790 GO SUB 1800+t: REM Real BASIC 1800 GO TO 1920 1801 PRINT "ZX BASIC ";: RETURN 1802 PRINT "ZX DATA ";: RETURN 1803 PRINT "ZX DATA$ ";: RETURN 1804 PRINT "ZX CODE ";: RETURN 1805 PRINT "SNAP 48K ";: RETURN 1806 PRINT "MDV FILE ";: RETURN 1807 PRINT "SPECIAL ";: RETURN 1808 PRINT "SCREEN$ ";: RETURN 1809 PRINT "SNAP 128K ";: RETURN 1810 PRINT "OPEN-TYPE ";: RETURN 1811 PRINT "EXECUTE ";: RETURN 1812 PRINT "Directory ";: RETURN 1813 PRINT "CREATE ";: RETURN 1814 PRINT "UniDOS 14 ";: RETURN 1815 PRINT "UniDOS 15 ";: RETURN 1816 PRINT "SAM BASIC ";: RETURN 1817 PRINT "SAM DATA ";: RETURN 1818 PRINT "SAM DATA$ ";: RETURN 1819 PRINT "SAM CODE ";: RETURN 1820 PRINT "SAM SCRN$ ";: RETURN 1821 PRINT "SAM type21";: RETURN 1822 PRINT "Directory ";: RETURN 1823 PRINT "Filetype23";: RETURN 1824 PRINT "Filetype24";: RETURN 1825 PRINT "Filetype25";: RETURN 1826 PRINT "Filetype26";: RETURN 1827 PRINT "Filetype27";: RETURN 1828 PRINT "Filetype28";: RETURN 1829 PRINT "Filetype29";: RETURN 1830 PRINT "Filetype30";: RETURN 1831 PRINT "Filetype31";: RETURN 1920 PRINT n$;" "; 1930 LET limit=limit+1 1940 IF limit>maxFiles THEN PRINT "MAX ";maxFiles: STOP 1960 LET f$(limit)=n$ 1970 IF t>15 THEN GO TO 2060 1980 LET length= FN d(p+212) 1990 LET length=length+ PEEK (p+210) 2000 IF t=5 THEN LET length=49152: REM 48K MGT Snapshot 2010 IF t=9 THEN LET length=131072: REM 128K MGT Snapshot 2020 REM TODO: Integrate directory snapshot register dump 2050 GO TO 2100 2060 REM SAM file in 16K pages, to be checked... 2070 LET length=16384* PEEK (p+239) 2080 LET d= PEEK (p+240) 2085 LET %k= PEEK (p+241) 2090 LET %k=%k & 63 2092 LET k=%256*k 2095 LET length=length+d+k 2100 LET z(limit)=length 2110 PRINT length, 2120 LET x(limit)=t-1 2130 REM Ad-hocery converts MGT ZX type info to Amstrad format 2135 REM Supports ZX PROGRAM, ARRAY($), CODE, SCREEN$ 2140 LET a(limit)= FN d(p+214): REM CODE/SCREEN$ load address 2150 IF t=1 THEN LET a(limit)= FN d(p+218): REM Start line 2155 IF t=1 AND a(limit)>32767 THEN LET a(limit)=32768 2160 IF t=2 OR t=3 THEN LET a(limit)= FN d(216): REM array name 2170 LET b(limit)= (t=1)* FN d(p+216): REM vars 2175 IF t=7 THEN LET t(limit)=3: REM MGT SCREEN$ = Amstrad CODE 2180 REM PRINT a(limit);" ";b(limit): PAUSE 0 2190 REM TODO: Custom snapshot, MDV and opentype file headers 2200 LET s= PEEK (p+14) 2205 IF s=0 OR s>10 THEN PRINT "Bad sector number ";s: STOP 2207 REM From now on, t is the disk track number not file type 2210 LET t= PEEK (p+13): REM 0..79 side 1, 128+ side 2; 2220 IF t>127 THEN LET t=t-128: LET s=s+10 2221 LET s(limit)=s: REM Sector in cylinder, 1..20 2222 IF t>79 THEN PRINT "Bad track number ";t: STOP 2225 LET t(limit)=t: REM Cylinder number, 0..79 2230 ENDPROC 2240 : 2245 REM PEEKSTR for RAM only, uses #15 and end+1 temporarily 2250 DEFPROC PEEKSTR(p,n) 2260 LOCAL t,end,t$,z$,c$ 2265 LET end=p+n 2270 IF end>65535 THEN PRINT "PAST 64K": STOP 2275 LET t= PEEK end 2280 POKE end,13 2285 IF PEEK (end)<>13 THEN PRINT "Something's ROM": STOP 2290 LET t$="" 2293 LET c$="m>"+ STR$ p+","+ STR$ (n+1) 2296 CLOSE # 15 2300 OPEN # 15,c$ 2305 REPEAT 2310 INPUT #15; LINE z$ 2320 LET t$=t$+z$ 2330 IF LEN t$13 THEN PRINT "CRASH": STOP 2490 POKE end,t 2500 ENDPROC =t$ 2510 : 2520 DEFPROC ShowDir() 2525 LOCAL i 2530 FOR i=1 TO limit 2540 PRINT f$(i);" ";z(i), 2550 NEXT i 2580 ENDPROC 2590 : 2600 DEFPROC POKESTR(p,t$) 2605 IF 0= LEN t$ THEN ENDPROC 2610 IF 65536"+ STR$ p+","+ STR$ LEN t$ 2630 PRINT #4;t$; 2640 CLOSE # 4 2645 ENDPROC 2650 : 2655 DEFPROC UPPER(t$) 2660 FOR i=1 TO LEN t$ 2665 IF t$(i)>"z" OR t$(i)<"a" THEN GO TO 2670 2667 LET t$(i)= CHR$ ( CODE t$(i)-32) 2670 NEXT i 2675 ENDPROC = t$ 2680 : 2700 DEFPROC SetHead(t,l,a,b) 2701 LOCAL p,q,t$: REM clobbers %s, %c 2702 POKE plus3header,99: REM Set up MUNGWALL for BANK ERASE 2705 POKE plus3header+headLen,199 2707 LET q= PEEK (plus3header-1) 2708 IF plus3header<2^15 OR plus3header>49192-headLen THEN STOP 2710 BANK 2 ERASE plus3header-32768,headLen: REM Hairy memzero 2712 REM MUNGWALL sanity checks, still faster than 120 pokes 2713 IF PEEK (plus3header-1)<>q THEN PRINT "ARGH": STOP 2715 IF PEEK (plus3header+headLen)<>199 THEN PRINT "UH": STOP 2717 IF 0<> PEEK plus3header THEN PRINT "AAARGH": STOP 2720 POKE plus3header,"PLUS3DOS"+ CHR$ 26+ CHR$ 1 2730 REM All PLUS3DOS files seem to be issue 1 version 0 2735 DPOKE plus3header+11,headLen+l 2737 REM TODO DPOKE high word to plus3header+13 for files >= 64K 2740 POKE plus3header+15,t 2750 DPOKE plus3header+16,l,a,b 2790 LET %s=%0: REM Checksum the non-zero header bytes 2800 FOR p=plus3header TO plus3header+22 2810 LET %c= PEEK p 2820 LET %s=%s+c 2830 NEXT p 2840 POKE plus3header+headLen-1,%s & 255 2850 IF file THEN PROC PEEKSTR(plus3header,headLen) TO t$ 2860 IF file THEN PRINT #5;t$; 2880 ENDPROC 2890 : 2960 REM 16-bit unsigned PEEK 2970 DEF FN d(p)= PEEK p+ 256* PEEK (p+1) 2975 : 2980 REM Diagnostic and unit-test routines 2990 : 3000 DEFPROC StrTest() 3005 LOCAL q$,i,c,z$: REM Clobbers dirAddr et seq. 3010 LET q$="Brave New"+ CHR$ 13+"World" 3015 POKE dirAddr+ LEN q$,127: REM Sentinel 3020 POKE dirAddr,q$ 3030 FOR i=dirAddr TO dirAddr+ LEN q$ 3040 LET c= PEEK i 3050 IF c>127 OR c<32 THEN PRINT c;: ELSE PRINT CHR$ c; 3060 NEXT i 3070 IF 127<> PEEK (dirAddr+ LEN q$) THEN PRINT "BANG": STOP 3080 LET z$= PEEK$ (dirAddr, LEN q$) 3085 PRINT q$''z$ 3090 IF q$<>z$ THEN PRINT "MISMATCH": STOP 3095 PRINT "POKESTR/PEEKSTR unit-test passed." 3100 ENDPROC 3110 : 3115 DEFPROC MdevTest() 3120 LOCAL x$, y$ 3125 CLOSE # 4 3130 OPEN # 4,"m>60000,1" 3135 POKE 60000,0,13 3145 PRINT #4; CHR$ 127; 3150 CLOSE # 4 3160 PRINT PEEK 60000, PEEK 60001: REM expect 127,13 3165 LET x$= PEEK$ (60000,1) 3170 PRINT LEN x$, x$: REM 1, CHR$ 127 3180 CLOSE # 4 3190 OPEN # 4,"m>60000,2" 3195 PRINT "opened" 3200 INPUT #4; LINE y$ 3210 IF y$<>x$ THEN PRINT "MISMATCH": STOP 3230 CLOSE # 4 3240 PRINT "closed" 3250 PRINT "M> byte read/write unit-test passed." 3270 ENDPROC 3280 : 3950 DEFPROC WordTest() 3960 DPOKE dirAddr,12345 3970 IF FN d(dirAddr)<>12345 THEN PRINT "BROKE": STOP 3990 DPOKE dirAddr,54321 4000 IF FN d(dirAddr)<>54321 THEN PRINT "BROKER": STOP 4010 PRINT "16-bit PEEK/POKE unit test passed" 4020 ENDPROC 5000 : 5010 DEFPROC GetHead(n$) 5015 LOCAL i,k,t$,type,n 5020 CLOSE # 4 5025 PRINT '"Checking header of ";n$ 5030 OPEN # 4,"i>"+n$ 5040 FOR i=0 TO 127+68*(n$="Cat Loader") 5050 LET k= CODE INKEY$ #4 5060 IF k>31 AND k<128 THEN PRINT CHR$ k;: GO TO 5080 5070 PRINT " "; INVERSE 1;k; 5080 POKE plus3header+i,k 5100 NEXT i 5110 CLOSE # 4 5120 LET t$= PEEK$ (plus3header,8) 5130 IF t$<>"PLUS3DOS" THEN PRINT "NO HEADER!": STOP 5140 LET length= FN d(plus3header+11) 5150 LET length=length+ FN d(plus3header+13)*2^16 5160 PRINT "File length = ";length;" bytes" 5165 LET type= PEEK (plus3header+15) 5170 PRINT "ZX/MGT type = "; type;" "; 5172 IF type>3 THEN GO TO 5180 5175 PRINT "BASICARRAYARRAYCODE "(1+type*5 TO 5+type*5) 5180 PRINT "Data length = "; FN d(plus3header+16);" bytes" 5190 IF type>0 THEN GO TO 5230 5200 LET l= FN d(plus3Header+18) 5210 IF l<32768 THEN PRINT "Start line = ";l 5220 GO TO 5290 5230 IF type=3 THEN GO TO 5280 5235 LET %n= PEEK (plus3header+19) 5240 LET n=%n & 31+64 5245 PRINT "Array name = "; CHR$ n; 5250 PRINT "$" AND type=2 5270 GO TO 5290 5280 PRINT "Default load address = "; FN d(plus3header+18) 5290 ENDPROC 5300 : 5555 PROC GetHead("Strings") 5560 PROC GetHead("Values") 5570 PROC GetHead("Bytes") 5580 PROC GetHead("Basic") 5590 PROC GetHead("LISTING") 5600 PROC getHead("Cat Loader") 5660 STOP 6110 : 6115 REM TestFile is 256 numbered sectors of 512 bytes 6120 DEFPROC MakeTestFile(n$) 6130 LOCAL t 6140 CLOSE # 4 6150 OPEN # 4,"o>"+n$ 6155 IF sector<32768 OR sector+sectorBytes>49192 THEN STOP 6160 FOR t=0 TO 255 6180 BANK 2 ERASE sector-32768,sectorBytes,t 6190 LET q$= PEEK$ (sector,sectorBytes) 6200 PRINT #4;q$; 6204 PRINT AT 0,0;t; 6210 NEXT t 6220 CLOSE # 4 6240 ENDPROC 7990 : 8000 DEFPROC FLIST() 8005 CLOSE # 4 8010 OPEN # 4,"o>c:/LISTING" 8020 LIST #4 8030 CLOSE # 4 8040 ENDPROC 8050 : 9980 STOP 9990 PROC FLIST() 9999 SAVE "c:/NextMGTreader.bas"