
**********************************************************************
files: TAP2DSK.TXT, TAP2DSK.TAP
       TAP2List.BAS, TAP2DSK.BAS
       http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk.zip
**********************************************************************


** Converting programs from TAP to DSK
   ===================================

One point to note regarding conversion of BASIC loaders is that literal number values should be ignored and the 5-byte FP form following the $0e marker should always be used, as some loaders have bogus data in the readable text to confuse hackers.

   ===================================

I've written a BASIC programmette which reads a TAP copied to a +3 disk and lists the contents, thusly ...
[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk.gif[/img][/center]
The "Invalid Flag Byte" message is because of the end of file padding; the TAP copied to the +3 is slightly longer than the original. I'll think of some way to resolve that.

The listing needs to be merged into my DOS_Defn program, which provides all the DOS functionality referenced by the "GO SUB FN a(...)" calls (see the file Plus3.TXT). This shows "proof of concept". Now I just need to add a sub-routine for each file type to write it out as a LOAD-able file to disk.

TAP2List.BAS [code]
6001 GO SUB 900 : LET pointer=k0: LET base=k32k-k32: POKE 23658,k8:
     LET bhi=FN i(base): LET blo=FN o(base):
     LET page=k5-(k3 AND base>=k32k):
     DEF FN i(v)=INT (v/kFG): DEF FN o(v)=FN l(v,kFG)

6005 INPUT "Enter TAP file name ";LINE t$:
     IF t$(LEN t$-k3 TO)<>".TAP" THEN LET t$=t$+".TAP"
6009 REM open TAP file
6010 PRINT "Opening ";t$: GO SUB FN a("O",t$,k3,k1,k0,k2):
     IF Aerr THEN PRINT "ERROR ";Aerr-k1: STOP
6019 REM get End Of File pointer
6020 GO SUB FN a("e",t$,k3,k0,k0,k0): LET EOF=cde:
     CAT t$: PRINT EOF+k1;" bytes"
6029 REM read TAP block length & flag bytes
6030 IF pointer>=EOF THEN GO TO 6498
6040 LET fl=k3: GO SUB 6500: LET fv=FN p(base): LET flag=PEEK (base+k2):
     GO TO 7000+kFF-flag
6498 REM end of program
6499 GO SUB FN a("C",t$,k3,k0,k0,k0): STOP

6500 REM read fl bytes from file 3 to base
6501 LET l$="="+STR$ fl: GO SUB FN a("R",l$,k3,page,bhi,blo):
     LET pointer=pointer+fl: RETURN

7000 REM skip tape file data block (flag=255)
7001 LET pointer=pointer+fv-k1:
     GO SUB FN a("s",t$,k3,k0,FN i(pointer),FN o(pointer)):
     GO TO 6030

7253 REM invalid 1<= flag byte <=254
7254 PRINT "Invalid Flag Byte: ";flag: GO TO 6498

7255 REM read tape file header block (flag=0) & checksum
7256 LET fl=fv-k1: GO SUB 6500: LET type=PEEK base: LET o=type*k4+k1:
     PRINT type;" ";"ProgNumbCharCode"(o TO o+k3);TAB k8;:
     FOR h=base+k1 TO base+10:
       LET i=PEEK h: PRINT CHR$ i AND i>=k32;:
     NEXT h:
     PRINT TAB 20;"Bytes: ";FN p(base+kB);TAB k2;
7260 IF type=k0 THEN
       PRINT "Auto:  ";FN p(base+13) AND FN p(base+13)<=9999;
             TAB 20;"Prog:  ";FN p(base+kF)
7261 IF type=k1 OR type=k2 THEN
       PRINT "Array: ";CHR$ PEEK (base+14);"$" AND type=k2;"()"
7263 IF type=k3 THEN
       PRINT "Start: ";FN p(base+13)
7264 IF type>k3 THEN
       PRINT "Invalid File Type: ";type: GO TO 6498
7270 GO TO 6030
[/code]

   ===================================

I converted Op.Wolf from the +3/+3e bootstrap loader to use a standard file loader. Having no explicit loader as such the conversion couldn't be automated, although the new disk loader did simply resolve to:

[code]
  10 CLEAR 32767: LOAD "e" SCREEN$: LOAD "h" CODE 32768:
     LOAD "ow" CODE 38672: RANDOMIZE USR 38672
[/code]

All the tricky bits being dealt with in the 400 bytes at USR 38672.

When converting Space Gun I determined that with no buffers other than the permanent one in page 7 the only significant RAMpage segment is the 3,328 bytes at 7:$db00->$e7ff, and even then it's only important to preserve any code/data that may have been put there before using any DOS functions, so the display file should usually be a satisfactory location for temporary storage. None of the DOS workspace needs preserving, as long as any file is opened+read+closed before running any code which may contend with that space, as it can be re-initialised with a few lines of code.

I did some timing tests with no buffers and, as long as the disk is only being read from and not written to, compared to having the maximum buffers, it only makes a difference when loading into pages 0,1,3,4,6 and even then it's only 10% slower.

   ===================================

I've got the TAP-to-DSK extract working now, and it looks like this:
[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dskc.gif[/img][/center]
... which is much the same as it looked before, except for the "=>" lines.

At present it just extracts the files from the TAP and puts them separately onto the disk, with a header matching the one from the TAP where there was one, or just a headerless file otherwise. The loader just gets copied across unchanged, so it wouldn't actually work as yet; that's the next thing to sort out.

There's options to just list the TAP contents, or list & copy to A or B. It will work with both headed or headerless files, as long as they were saved using the standard ROM tape routine. It will actually extract anything that's in a TAP file, but if it's not standard tape data it won't know what to do with it afterwards.

The version of DOS_Defn it runs with has been modified to handle four levels of function calls, being the number of rows in the array which holds the pointers. So it can do; eg.:
GO SUB FN s(s$,b,d) => GO SUB FN a(A$,F$,b,c,d,e) => LET bc=FN u(A$,H$,b,d)
with each called function being able to refer to the parameters from the calling function.


   ===================================

The 48k ROM is paged in when Elite is running on the +3, so the game Save/Load options will work, but they're still calling the tape routines, so a cassette deck would need to be connected to the TAPE socket on the +3 to be able to make use of them.

   ===================================

The Bard's Tale disk version has a boot sector loader. Even without that it wouldn't work on the +3e, however, as the loader copies part of DOS_INITIALISE, checks that it matches the +3 ROM, and then makes some mods to it. The +3e DOS_INITIALISE routine is different and so fails the check and sends it into a permanent error loop with a flashing border.

   ===================================

I've got TAP2DSK creating the executable loader now. I've tidied up the display a bit (aligning the numbers) and added a couple of little progress indicators, plus now it displays the new loader lines as it builds them.

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dskc2.gif[/img] : [img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dskt.gif[/img][/center]

As well as putting in the new file names, it also sets any visible numbers to match their hidden FP forms, so for each number it decodes the FP value into a decimal string and then replaces the preceding ASCII form with the recalculated number.

Here's the listing of the transfer segment, excluding the DOS functions segment (which has been modified some more in that I've removed code for those DOS functions which are not used by TAP2DSK).

TAP2DSK.BAS [code]
6000 DEF FN s(s$,b,d)=6500: DEF FN t(n,t)=t+k1-LEN STR$ n
6002 CLEAR 31300-12: GO SUB 900 : LET pt=k0: POKE 23658,k8: LET q$="":
     LET iq=k0: LET db=k32k-k32: LET hb=db-17: LET type=kFF:
     REM header type $ff="no header"
6003 RESTORE 6413: LET pp=FN p(23639)+k6: LET c$="0123456789+-.eE?":
     LET edl=6450: LET edn=6460:
     LET ia=k0: LET il=k0: DEF FN i(v)=INT (v/kFG): DEF FN o(v)=FN l(v,kFG)
6005 INPUT "TAP name ";LINE t$:
     IF t$(LEN t$-k3 TO)<>".TAP" THEN LET t$=t$+".TAP"
6006 INPUT "Output to [@AB] ";d$: GO TO FN c()+(d$>="@" AND d$<="B")
6009 REM open TAP file
6010 PRINT "Opening ";t$: GO SUB FN a("O",t$,k3,k1,k0,k2)
6019 REM get End Of File pointer
6020 GO SUB FN a("e","",k3,k0,k0,k0): LET EOF=cde:
     CAT t$: PRINT EOF;" bytes"
6025 IF d$<>"@" THEN
       GO SUB FN a("D",d$,k0,k0,k0,k0):
       GO SUB FN a("F",d$,k0,k0,k0,k0):
       IF HL<INT(EOF/1024) THEN PRINT "NO ROOM ON ";d$: GO TO 6489
6029 REM read TAP block length & flag bytes
6030 IF pt>=EOF-k7F THEN GO TO 6300+(199 AND d$="@")
6040 LET base=hb-k3: GO SUB FN s("R",k3,k3):
     LET dn=FN p(base)-k2: LET flag=PEEK (base+k2):
     GO SUB 7000+kFF-flag+(kM+type AND flag=kFF AND d$<>"@"): GO TO 6030

6200 FOR h=k1 TO LEN n$:
       IF n$(h)<>" " THEN PRINT n$(h);: POKE db,CODE n$(h): LET db=db+k1
6201 NEXT h: RETURN

6299 REM reconstruct the loader
6300 LET n$=q$(TO kC): LET p=kF:
     GO SUB FN a("O",n$,k4,k3,k0,k1): LET pt=kFG:
     GO SUB FN a("H","",k4,k0,k0,k0): LET dn=FN p(IX+k1): LET pn=FN p(IX+k5)
6310 LET base=db: GO SUB FN s("R",k4,k4): LET sl=FN p(base+k2):
6311 REM read a BASIC line header
6312 LET db=db+k4: LET dp=db: LET tl=kFG*PEEK base+PEEK (base+k1):
     POKE 23692,kFF: LET base=hb-sl: GO SUB FN s("R",k4,sl): INPUT ""
6315 REM read the remainder of the BASIC line
6316 PRINT TAB FN t(tl,k3);tl;" ";: LET tl=k0
6319 REM edit LOAD statements; ensure ASCII/$0e.FP numbers match
6320 FOR o=base TO base+sl-k1:
       LET g$=CHR$ PEEK o:
6322   REM check if in LOAD
6323   IF g$=CHR$ 239 THEN LET il=NOT il: GO TO edl
6329   REM check for SCREEN$,CODE,DATA,:,ENTER
6330   IF il THEN LET g=CODE g$:
         IF g=170 OR g=175 OR g=228 OR g$=":" OR g=kD THEN
         LET n$=""""+q$(p TO p+kB)+"""": GO SUB 6200: LET p=p+kE:
         LET il=NOT il: GO TO edl
6335   IF il THEN GO TO edn
6406   REM check if in quotes
6407   IF g$="""" THEN LET iq=NOT iq: GO TO edl
6408   IF iq THEN GO TO edl
6409   REM check if in number
6410   IF NOT ia THEN
         FOR h=k1 TO LEN c$: LET tl=(tl<>k0 OR g$=c$(h)): NEXT h:
         IF tl THEN LET ia=NOT ia: LET tl=NOT tl: GO TO edn
6411   IF NOT ia OR g$<>CHR$ kE THEN GO TO edl
6412   REM convert FP number to decimal
6413   FOR h=k1 TO k5: POKE pp,CODE "AEDCB"(h): REM POKE 6413 LET ...
6414     LET A=PEEK (o+h): NEXT h: REM cf. line 6003
6415   IF NOT a THEN
         LET DE=kFG*c+d: LET DE=DE-(k64k AND e=kFF):
         GO TO 6421
6417   LET IX=a-k7G: LET HL=-k1:
       IF e<=k7F THEN LET HL=+k1: LET e=e+k7G
6420   LET BC=e/kFG+d/k64k+c/16777216+b/4294967296:
       LET DE=HL*k2^IX*BC
6421   LET n$=STR$ DE: GO SUB 6200: LET ia=NOT ia:    REM POKE ASCII number
6422   FOR h=k0 TO k5: POKE db+h,PEEK (o+h): NEXT h:  REM POKE $0e.FP number
6423   LET db=db+k6: LET o=o+k5: GO TO edn
6450   IF NOT ia THEN PRINT g$;: POKE db,CODE g$: LET db=db+k1
6460 NEXT o: POKE dp-k2,FN o(db-dp): POKE dp-k1,FN i(db-dp):
     REM update line length
6461 IF pt-kFG<pn THEN GO TO 6310
6462 IF dn-pn THEN LET base=db: GO SUB FN s("R",k4,dn-pn):
     REM read in any variables data
6479 REM write new loader & finish
6480 LET n$=q$(TO kC): GO SUB FN a("s","",k4,k0,k0,k7G):
     LET base=k32k-k32: REM reset file & data pointers
     LET dn=db-base+(dn-pn): LET pn=db-base
6481 GO SUB FN s("W",k4,dn):
     GO SUB FN a("H","",k4,k0,k0,k0):
     POKE IX+k1,FN o(dn): POKE IX+k2,FN i(dn):
     POKE IX+k5,FN o(pn): POKE IX+k6,FN i(pn):
     GO SUB FN a("C","",k4,k0,k0,k0)

6488 REM end of program
6489 GO SUB FN a("C","",k3,k0,k0,k0): POKE 23658,k0: STOP

6499 REM read 'd' bytes from file 'b' to 'base'
6500 RESTORE 6000: GO SUB ret-k1: LET l$="="+STR$ d:
     LET pg=k5-(k3 AND base>=k32k): LET pt=pt+(d AND s$="R"):
     GO SUB FN a(s$,l$,b,pg,FN i(base),FN o(base)): GO TO ret+k1

6554 REM missing header
6555 IF type=kFF THEN
       PRINT "* Code";TAB k8;"NO HEADER ";
             TAB 20;[color=green]"Bytes:";TAB FN t(dn,31);dn[/color]
6556 RETURN

6997 REM select routine for tape file data block (flag=255) & checksum
6998 REM skip tape file data block (flag=255) & checksum
6999 REM lines 7001-7254 reserved for error jumps
7000 GO SUB 6555: LET base=db: LET pt=pt+dn+k1:
     GO SUB FN a("s","",k3,k0,FN i(pt),FN o(pt)): [color=green]LET type=kFF:[/color] RETURN

7254 PRINT "Bad Flag Byte: ";flag: GO TO 6489:
     REM invalid 1<= flag byte <=254

7255 REM read tape file header block (flag=0) & checksum
7256 LET base=hb: GO SUB FN s("R",k3,dn+k1):
     LET type=PEEK base: LET o=type*k4+k1:
     PRINT type;" ";"ProgNumbCharCode"(o TO o+k3);TAB k8;: DIM n$(kC+k2)
7257 LET o=k0: FOR h=base+k1 TO base+10:
       LET g$=CHR$ PEEK h: PRINT g$;:
       IF g$>=" " AND g$<>"." THEN
         LET o=o+k1: LET n$(o)=CHR$ (CODE g$-(k32 AND g$>="a" AND g$<="z"))
7258 NEXT h
7259 LET BC=FN p(base+kB): PRINT TAB 20;"Bytes:";TAB FN t(BC,31);BC;TAB k2;:
     LET DE=FN p(base+kD): LET DE=DE AND (FN p(base+kD)<=9999 OR type=k3):
     LET HL=FN p(base+kF)
7260 IF type=k0 THEN
       PRINT "Auto:";TAB FN t(DE,kD);DE;TAB 20;"Prog:";TAB FN t(HL,31);HL
7261 IF type=k1 OR type=k2 THEN
       PRINT "Array: ";[color=green]CHR$ (PEEK (base+kE)-k32-(k32*k2 AND type=k2))[/color];"$" AND type=k2;"()"
7263 IF type=k3 THEN PRINT "Start:";TAB FN t(DE,kD);DE
7264 IF type>k3 THEN PRINT "Bad File Type: ";type: GO TO 6489
7265 LET n$=n$(TO k8)+"."+"BASNUMCHRCOD"(type*k3+k1 TO type*k3+k3):
     PRINT #k0;AT k0,k0;n$: LET q$=q$+n$
7270 RETURN

7998 REM copy tape file data block (flag=255), skip checksum
7999 REM lines 8000-8254 reserved for entry jumps
8254 GO SUB FN a("O",n$,k4,k2,k1+(type=kFF),k4):
     IF type<>kFF THEN
       GO SUB FN a("H","",k4,k0,k0,k0):
       POKE IX,type: FOR h=k1 TO k6: POKE IX+h,PEEK (hb+kB+h-k1): NEXT h:
       REM returns IX=header address

8255 REM copy data block in 16kb segments plus a remainder segment
8256 GO SUB 6555: LET tl=k0
8260 LET dn=dn-k16k: LET sf=(dn<=k0): LET sl=k16k-(k16k-(dn+k16k) AND sf):
     LET base=db: PRINT TAB FN t(sl,kD);sl,"=> ";:
     GO SUB FN s("R",k3,sl+sf): GO SUB FN s("W",k4,sl):
     LET tl=tl+sl: PRINT TAB FN t(tl,23);tl: GO TO FN c()+sf
8261 GO SUB FN a("C","",k4,k0,k0,k0): LET type=kFF: RETURN
[/code]

   ===================================

TAP2DSK is working quite well now. Extracting the files from a TAP and converting the loader works OK, but there's a problem when loading from disk if files are to be loaded into the extended memory. Elite works OK as it's all in "normal" memory, whereas LodeRunner 128 isn't quite right yet.

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk-lodetap.gif[/img] : [img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk-runloke.gif[/img][/center]

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk-loderun.gif[/img] : [img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk-runlode.gif[/img][/center]

The problem is that the level data is being loaded into the wrong pages. Looking at the LOAD code around $0834 in ROM1, when loading CODE from tape it just leaves the top page as it is, so the page changes made in the loader remain in effect; but when loading from disk it always loads into page 0. So when files are being loaded into extended memory TAP2DSK needs to alter the loader to either use DOS_OPEN / READ / CLOSE instead, or else copy each file to its correct page after loading.

   ===================================

The loaders are a bit different now - and LodeRunner works.

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk-iv-test2.gif[/img] : [img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk-iv-elite.gif[/img][/center]

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk-iv-test3.gif[/img] : [img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk-iv-lode.gif[/img][/center]

The extract screen hasn't changed, so I haven't shown that again. The colour on the loader screens indicates alterations; the colour changes if a different RAMpage has to be loaded.

There's 146 bytes of m/code in the REM "" statement in the loader, which does three things: at the start, sets DOS & RAM caches to zero; while loading, loads CODE for pages other than page 0 (the DOS LOAD command always has page 0 switched in); at the end, turns off the disk motor and  the motor timeout check. I tried various locations for the m/code; putting it in the loader was the least troublesome.

The first two extensions are attached to the auto-run line; the third is inserted penultimately into the last line. I've assumed for now that the last line in the loader is the last line to be executed (which might not necessarily be the case).

When the construction of the new loader is being displayed on the screen printing is suppressed for any $0e.FP number forms and also for the m/code string. The m/code is LOADed directly into the string during construction rather than being POKEd. USR/CALL addresses in the loader and m/code are adjusted by reference to an offset table when that line is constructed, so they'll always be correct regardless of the length of the loader or the position of the m/code string within it.

   ===================================

Elite, LodeRunner and Amaurote converted OK with TAP2DSK to run from +3 disk. Here's the screens for Amaurote:

[center][img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk-v-test4a.gif[/img] : [img]http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk-v-test4b.gif[/img][/center]

Dragons of Flame, Forest of Doom, Times of Lore, Wrath of Magra - they all have "issues", in that unpacking the TAP contents to disk and generating a new BASIC loader is insufficient to get them to work on the +3; they would all need changes to the game code (ie. the load routines) in order to get them to work, as they don't load everything at the start, and when they do come to load other bits later on they will either use a tape load routine from within the game or jump out to a secondary loader.

Times of Lore has a number of headerless blocks, and the preceding code would have to be disassembled in order to work out what to do with them. The Dragons of Flame TAP on WoS is a cracked version, so it would be necessary to work out what's original code and what's alterations. They could all be got to work, but I can't see any practical way of doing it without manual intervention.

The TAP2DSK program files are [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/tap2dsk.zip]here[/url]. The programs use the DOS_Demo routines which I posted a little while ago. Also included is a little program DSK2TAP which I used for transferring TAP files to DSK ... and if you're wondering why that's not called TAP2DSK as well, that's because it takes a TAP file split into 16kb (max) blocks from (hard) disk to a reconstructed copy in a DSK image file. Well, the name seemed to make sense at the time; it's what I used set up my test files, anyway. I used Mael Horz's excellent HxD hex editor to split the files; as well as having everything needed in a hex editor it also does file split, join and compare.

   ===================================

I've added the following notes to the TAP2DSK/DSK2TAP program listings. Instructions for DOS_Demo/DOS_Defn are already included in the listings.

[b]TAP2DSK[/b]
REM This program includes a compact version of DOS_Defn.BAS, with all
REM the routines which are not required here having been removed;
REM RUN 6000 to start.
REM If the DOS_Demo m/code block is not found then DOS_Defn will attempt
REM to load it from the default LOAD medium as "DOS_DEMO" CODE 31989,410.

REM TAP2DSK takes a TAP file copied to disk and disassembles it into its
REM constituent files then builds a modified loader based on the initial
REM auto-run file (if there is one) to run the game from disk.
REM Follow the prompts; for "Output", enter @ just to list the contents
REM of the TAP file; otherwise, A or B to actually do the extract.
REM It should be clear from the list of contents as to whether it will
REM be possible to build a working loader automatically or not.
REM If the TAP includes headerless files, or the original loader doesn't
REM load all the files at the start, or the game attempts to re-load files
REM later on, then a new loader to run from disk will have to be prepared
REM manually (which will entail changing the game code itself as well).

REM The m/c routine "LC" is TAP2DSK.ASM with the offsets table removed.
REM It needs to be on the default LOAD drive, as it gets merged into the
REM new loader. Its ORG doesn't matter, as any absolute addresses are
REM adjusted after merging to take account of its new location.

REM * NOTE * The final line 9009 in the listing, which holds the machine
REM code is there but not visible as the high byte of the line number has
REM been POKEd with $80.


[b]DSK2TAP[/b]
REM This routine is merged into DOS_Defn.BAS; RUN 6000 to start.
REM If the DOS_Demo m/code block is not found then DOS_Defn will attempt
REM to load it from the default LOAD medium as "DOS_DEMO" CODE 31989,410.

REM DSK2TAP joins together a TAP file which has been split into a series
REM of blocks (max. 16kb each) and saves the re-assembled file to disk.
REM I use it to copy a TAP file into a DSK file for testing TAP2DSK.
REM I use Mael Horz's excellent HxD hex editor to split the TAP files.
REM Follow the prompts; enter a blocksize > 16k to finish.


**********************************************************************


** Converting bootstrap loaders to standard loaders
   ================================================

Commercial protected disks will usually use a bootstrap loader via DOS_BOOT in ROM2:$1ace. This uses one of the special RAM configurations RAM 4,7,6,3 with the game's boot routine loaded at $fe10 in page 3. This will usually switch back to a normal configuration fairly quickly to read the CODE blocks for the game directly from the disk (ie. bypassing +3DOS). Then it's a matter of finding where the read routine is in the code; there will be some precursor point where registers will have been set up indicating the location & size of the block to read, which will be called a number of times. So I put a break in there and when it returns I save that block from RAM to a BIN file. When I've got them all I save them as normal CODE files and then disassemble the loader blocks to check if anything needs to be done in between. Then I construct a BASIC loader based on that. Here's my notes from [url=http://www.users.globalnet.co.uk/~jg27paw4/pourri/ownotes.txt]Operation Wolf[/url] to give a (probably confusing) idea of what's involved. The lines starting "SAVE..." are where I'm saving the BIN files. The assembler fragments at the end are my notes when I was working out where to break in the code.

Sometimes there may be more than one read subroutine. With one conversion I did (I think it was Doom) I found that I was missing some chunks of code, which was because some blocks were being read via a second subroutine which I'd missed, as I'd presumed that the first routine I found was the only one.

This might be of interest as well, also from Op.Wolf. As you can see, although there were numerous code blocks on the original disk, the new BASIC loader resolved into something quite simple.
[code]
1000 CLEAR 25999:
     LET j=NOT PI: LET k=SGN PI: LET l=k+k: LET m=INT PI: LET u=26000:
     LET a=m*m+k: LET b=a+k: LET c=b+k: LET d=c+k: LET e=d+k: LET f=e+k: LET g=f+k
1100 FOR x=k TO f: READ d$: LET l$=CHR$ (VAL d$(k)*g+VAL d$(l)): PRINT l$;":p.";d$(m+k);" a=";:
       FOR y=l TO m:
         READ d$: LET v=j: FOR z=j TO m: LET v=v+VAL d$(m+k-z)*g^z: NEXT z:
         PRINT v;"> 26000" AND y=m,"s=" AND y=l;: NEXT y
1200   INPUT d$: IF x<f THEN SAVE l$ CODE u,v
1201 NEXT x
1300 FOR x=k TO m: READ l$,v: SAVE l$ CODE u,v: LET u=u+v: NEXT x: STOP
     REM The last 256 bytes (all zeroes) of file "e" are not saved so as not to overwrite the 128k system variables
     REM after loading the SCREEN$; they are copied in later.
2000 DATA "6103","c000","4000","6202","8000","0200","6302","8500","0200",
"6403","fed3","012d","6505","4000","1b00","6802","8000","4000","6903",
"c000","3000","6a00","c000","4000","6b01","c000","4000","6c04","c000",
"4000","6e06","c000","4000","6f05","4000","1000","6605","5cd8","0800",
"6705","632f","1cd1","6d07","c000","4000"
2001 DATA "x",6912,"y",3328,"z",6144

****

REM      7:$c000<-->$c05f XXXX (  96 bytes) used by +3 BASIC }shadow
REM      7:$c060<-->$daff free (6816 bytes)                  }screen
REM      7:$db00<-->$e7ff XXXX (3328 bytes) used by +3 DOS
REM      7:$fe00<-->$ffff used to load bootsector by +3 DOS ROM; otherwise:
REM      7:$e800<-->$ffff free unused by +3 DOS after boot (6144 bytes, 6kb)

****

   1 REM Operation Wolf loader
  10 CLEAR 32767: LOAD "e" SCREEN$: LOAD "h" CODE 32768:
     LOAD "ow" CODE 38672: RANDOMIZE USR 38672
[/code]


**********************************************************************

