Adding TAP file contents to a Spectrum +3e DOS Partition
Hello my fellow WoS'ers,
This thread is a companion to a great little guide from a fellow AmiBayer mnjnurney you can view his thread by clicking here.
This pictorial is how to add TAP based games / software to your +3 DOS hard-disk partition. For this we use 3estrowsaw (click here to download) and DriveImage (click here to download).
To set these up, please do have a look at Mikes guide above.
Now, what I am about to show you is the simplest form of adding TAP files. there are more complex ways the more complex the game or software TAP is.
So without further ado -
To Begin

here we select the partition [ Data-003 ] on the left and then select [ Items ] from the menu, and finally [ Put TAP contents ] with left click of the mouse.

Here I navigate and select the 128K TAP version of ELITE and click OPEN, if you have been living in a cryogenic freezer the last 30 years, Elite is a great game and quite topical at the moment.

3estrowsaw decodes the TAP into its constituent files, its not very good at naming them, we will work on that later. as you can see 3eStrowsaw can determine a BASIC program and CODE, but it cannot tell the difference between what a SCREEN$ files is nor GAME CODE. This is where your intuition comes into play.
If you notice the REAL SIZE of the file "A.2" it is 6912 bytes - this is exactly the size oft he screen memory of the Spectrum - so this is most likely a SCREEN$ data file.

Now, 3estrowsaw has the ability to read BASIC programs - in this case I am having a quick look at the header file for ELITE128.

Now, comes the fun bit, I am using an emulator (eightyone) so I can make this pictorial quicker - but you can do this as easily on the target machine.
I will be putting up a guide on how to emulate your +3e DOS environment and how handy it is to have and use! (comming soon)
So in the above picture we are looking at the files 3estrowsaw has put on the Data-003 partition. I have assigned "E:" as the focus XPD for this partition and have set the current focus to that XPD.
To know how to do this I have touched on it briefly in this thread, however there will be a guide comming shortly.
The BASIC LOADER HEADER

So the first thing to do is load the header, we could just write one from scratch, however this is slightly quicker and a little safer to begin with.

As expected the Loader failed since its expecting to run from TAPE,
CLOSER INSPECTION

So lets look at this code, as you can see its quite simple, but I will break this down.
LOAD "" SCREEN$
This is loading the next file in the sequence as a SCREEN$ file type (that means directly to the Video Memory. Now as we are using a hard disk we have random access, so we need to change this to a "location:filename" for it to work.
PRINT AT 20,0
This is superflous statment and can be ignored.
LOAD "" CODE
This is loading the next file in the sequence as a CODE file type that loaded at RAM TOP. Now like the previous SCREEN$ loading statement - as we are using a hard disk we have random access, so we need to change this to a "location:filename" for it to work.
So thats the basic loader and what it does... we now need to edit this to reflect the fact it is loading from the hard disk partition.
MODIFYING THE LOADER

Here I have modified a couple of statements as well as added some, so lets look at these in detail.
So thats it, the main modification done, all thats left is to save it.
SAVING THE LOADER

I use a couple of lines with compound statements to save my basic programs, usually these are the first lines I write when writing any BASIC program.
line 9999 clears the screen, informs the user that it is saving the program. It them saves the program with an auto run (LINE command) of line 10, finally the program informs the user that it has saved and is read for input.
to activate the process all the user has to do is type RUN 9999 and that small collection of statements will execute.

As you can see - its tres' handy ;)
SORTING OUT FILE NAMES

So now when we CAT the partition we can see we have a new file "ELITE.LDR"
So to get everything working we now need to rename files, I find this easy to do on the target machine so here is how.

the MOVE command can be used to change the name of a file as you can see above, we are changing the file named "A.2" to "ELITE.SCR"

I always check with a CAT of the partition, it also helps me remember to know the name of the other file I have forgotten in the last two minutes.

We do the same again, however we are renaming the code block called "elite.1" to "ELITE.CDE"

A final check with CAT, and we can see a superfluous file we don't need - "elite." which was the original TAP loader file.

No worries we can delete that with an ERASE statement - like the above, you can use wild-cards in this by using the "*" operand, like MSDOS

A Final Check reveals perfect organization, of course you dont need to use the extentions I have .LDR, .SCR or .CDE you can use your own, as long as they tally to the LOAD statements of the LOADER file they will be okay.
CONCLUSION

Okay lets get it on!.... LOAD "ELITE.LDR"

Mmmm YEAH!!! pressing that KEY!

WHOOHOOOO.....
and there you have it...... a tasty 128k Elite Treat on how to get a simple loader onto your Spectrum +3e Partition
This thread is a companion to a great little guide from a fellow AmiBayer mnjnurney you can view his thread by clicking here.
This pictorial is how to add TAP based games / software to your +3 DOS hard-disk partition. For this we use 3estrowsaw (click here to download) and DriveImage (click here to download).
To set these up, please do have a look at Mikes guide above.
Now, what I am about to show you is the simplest form of adding TAP files. there are more complex ways the more complex the game or software TAP is.
So without further ado -
To Begin

here we select the partition [ Data-003 ] on the left and then select [ Items ] from the menu, and finally [ Put TAP contents ] with left click of the mouse.

Here I navigate and select the 128K TAP version of ELITE and click OPEN, if you have been living in a cryogenic freezer the last 30 years, Elite is a great game and quite topical at the moment.

3estrowsaw decodes the TAP into its constituent files, its not very good at naming them, we will work on that later. as you can see 3eStrowsaw can determine a BASIC program and CODE, but it cannot tell the difference between what a SCREEN$ files is nor GAME CODE. This is where your intuition comes into play.
If you notice the REAL SIZE of the file "A.2" it is 6912 bytes - this is exactly the size oft he screen memory of the Spectrum - so this is most likely a SCREEN$ data file.

Now, 3estrowsaw has the ability to read BASIC programs - in this case I am having a quick look at the header file for ELITE128.

Now, comes the fun bit, I am using an emulator (eightyone) so I can make this pictorial quicker - but you can do this as easily on the target machine.
I will be putting up a guide on how to emulate your +3e DOS environment and how handy it is to have and use! (comming soon)
So in the above picture we are looking at the files 3estrowsaw has put on the Data-003 partition. I have assigned "E:" as the focus XPD for this partition and have set the current focus to that XPD.
To know how to do this I have touched on it briefly in this thread, however there will be a guide comming shortly.
The BASIC LOADER HEADER

So the first thing to do is load the header, we could just write one from scratch, however this is slightly quicker and a little safer to begin with.

As expected the Loader failed since its expecting to run from TAPE,
CLOSER INSPECTION

So lets look at this code, as you can see its quite simple, but I will break this down.
5 SAVE "ELITE" LINE 10When this line is executed it saves the program (to tape) with an auto run ( LINE command ) of line 10. because we are on a hard disk we do not need this, so delete this line, simply by typing its number and pressing return / enter.
10 CLEAR 65535This command clears the memory and sets RAM TOP, this is the area the game CODE will load into as a start address. Interestingly code is loaded from the top and then down as I will explain further in a minute.
15 BORDER 0 : PAPER 0 : INK 0 : CLSThis is a simple statement line that changes the screen to black
20 LOAD "" SCREEN$ : PRINT AT 20,0 : LOAD "" CODEthis line has three commands, so lets look at these
LOAD "" SCREEN$
This is loading the next file in the sequence as a SCREEN$ file type (that means directly to the Video Memory. Now as we are using a hard disk we have random access, so we need to change this to a "location:filename" for it to work.
PRINT AT 20,0
This is superflous statment and can be ignored.
LOAD "" CODE
This is loading the next file in the sequence as a CODE file type that loaded at RAM TOP. Now like the previous SCREEN$ loading statement - as we are using a hard disk we have random access, so we need to change this to a "location:filename" for it to work.
30 RANDOMIZE USR 28672Now this statement tells the spectrum to jump to the memory location of 28672 and execute the next instruction code from there.
So thats the basic loader and what it does... we now need to edit this to reflect the fact it is loading from the hard disk partition.
MODIFYING THE LOADER

Here I have modified a couple of statements as well as added some, so lets look at these in detail.
20 LOAD "ELITE.SCR" SCREEN$As you can see I have specified the file to load as screen data. If you notice I have not declared a location i.e. "C:filename" as I already have set focus to the partition using the VERIFY command (more on this in another tutorial)
30 PRINT #1 ; INK 7 ; BRIGHT 1 ; " ";FLASH 1;"PRESS ANY KEY"This line prints (at the information line - line 22) in bright white ink, flashing "press any key"
40 PAUSE 0This statement will wait until the user presses a key
50 LOAD "ELITE.CDE" CODEsimply, like the SCREEN$ loading statement this loads the code using the "ELITE.CDE" filename at the position of RAM TOP set by CLEAR 65535 on line 10
So thats it, the main modification done, all thats left is to save it.
SAVING THE LOADER

I use a couple of lines with compound statements to save my basic programs, usually these are the first lines I write when writing any BASIC program.
9998 STOP
9999 CLS : PRINT "SAVING LOADER" : SAVE "ELITE.LDR" LINE 10 : CLS :
PRINT "LOADER SAVED"
should the program run to 9998 BASIC is stopped, this protects line 9999 from accidentally runningline 9999 clears the screen, informs the user that it is saving the program. It them saves the program with an auto run (LINE command) of line 10, finally the program informs the user that it has saved and is read for input.
to activate the process all the user has to do is type RUN 9999 and that small collection of statements will execute.

As you can see - its tres' handy ;)
SORTING OUT FILE NAMES

So now when we CAT the partition we can see we have a new file "ELITE.LDR"
So to get everything working we now need to rename files, I find this easy to do on the target machine so here is how.

the MOVE command can be used to change the name of a file as you can see above, we are changing the file named "A.2" to "ELITE.SCR"

I always check with a CAT of the partition, it also helps me remember to know the name of the other file I have forgotten in the last two minutes.

We do the same again, however we are renaming the code block called "elite.1" to "ELITE.CDE"

A final check with CAT, and we can see a superfluous file we don't need - "elite." which was the original TAP loader file.

No worries we can delete that with an ERASE statement - like the above, you can use wild-cards in this by using the "*" operand, like MSDOS

A Final Check reveals perfect organization, of course you dont need to use the extentions I have .LDR, .SCR or .CDE you can use your own, as long as they tally to the LOAD statements of the LOADER file they will be okay.
CONCLUSION

Okay lets get it on!.... LOAD "ELITE.LDR"

Mmmm YEAH!!! pressing that KEY!

WHOOHOOOO.....
and there you have it...... a tasty 128k Elite Treat on how to get a simple loader onto your Spectrum +3e Partition
Post edited by Zetr0 on
Comments
Why is that? If it can deconstruct the TAP file it can read the individual file headers contained therein, each of which contain the correct name, file type and loading parameters. Isn't there a "copy names from TAP file" option or somesuch?
There's no need to distinguish them. For a file saved as a SCREEN$:
LOAD "" SCREEN$ <=> LOAD "" CODE <=> LOAD "" CODE 16384,6912
SAVE "name" SCREEN$ is shorthand for saving the display file; the file header is the same as if SAVE "name" CODE 16384,6912 was used, in which case LOAD "" SCREEN$ and LOAD "" CODE are functionally identical.
John Elliot's TAPROM simulates tapes using TAP files on disk, although it has to install a custom ROM to do so. One of the extra utilities with it allows the individual TAP file contents to be extracted. I would have thought it easy enough, once a TAP file's on the +3e disk, to write a little program to deconstruct the file and save its contents as LOAD-able Spectrum files without any need for a custom ROM, then the procedures described above could be automated to run on the +3e alone. (I feel another mini-project coming on.)
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.
This method only works for standard loaders, and simply crumbles on custom loaders, byte shifted headers and fast loaders.
I have a plan to write a TAP loader of sorts into the +3e ROM, alas time is limited. A program that could be used to convert TAP files on the Target machine would indeed be awesome!
*if you ever find a spare few minutes ;)*
Here's the listing. It needs to be merged into my DOS_Defn program, which provides all the DOS functionality referenced by the "GO SUB FN a(...)" calls. 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.
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" 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 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" REM read TAP block length & flag bytes 6030 IF pointer>=EOF THEN GO TO 6499 6040 LET fl=k3: GO SUB 6500: LET fv=FN p(base): LET flag=PEEK (base+k2): GO TO 7000+kFF-flag REM end of program 6499 GO SUB FN a("C",t$,k3,k0,k0,k0): STOP REM read fl bytes from file 3 to base 6500 LET l$="="+STR$ fl: GO SUB FN a("R",l$,k3,page,bhi,blo): LET pointer=pointer+fl: RETURN REM skip tape file data block (flag=255) 7000 LET pointer=pointer+fv-k1: GO SUB FN a("s",t$,k3,k0,FN i(pointer),FN o(pointer)): GO TO 6030 REM invalid 1<= flag byte <=254 7254 PRINT "Invalid Flag Byte: ";flag: GO TO 6499 REM read tape file header block (flag=0) & checksum 7255 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 6499 7270 GO TO 6030- Any program that loads data in the system vars area (consider 23296 to 23755 reserved). Turbo Sprit does that.
- Programs that have "standard" blocks, but loads with c/m. ATV Simulator have two blocks, with headers included, but it has a loader inside BASIC that perform calls to the ROM.
- Some programs load data into main RAM and then move it to paged memory, or directly does some POKEs to load into paged memory. Those can probably fail if use the first 4Kb of page 1 (Arkanoid II fails, Spellbound does not), and will surely fail if load anything into page 7 (Operation Wolf).
I've been converting some games to load from disk/hard disk, and there is not any general solution, even for BASIC loaders. I've got a page in El trastero del Spectrum with some games converted (those games were already converted or easy to convert) that will work in +3e, you may want to take a look at them. That reminds me that I must start a thread about some loaders...
An' you know what they said?
Well, some of it was true!
You are as prolific as you are awesome
I plan to write up your utils as a
Battle Bunny's Hyper +3e Utils!
As it will now be known across the land!
or "BB Hyper Utils" sounds like a decathlon game already =)
y'know.... I thought I had speky BASIC licked years ago.... how wrong was I !?!?!?!?
Our fellow forumite nuggetreggae has done some amazing work with cracking, hacking loaders for the spectrum +3e DOS.... including a lot of games that shouldn't work - i.e. turbo esprit which I have running here thanks to his efforts.
Also a lot of awesome Russian demo's as well... also FMV too...
Our Mr nuggetreggae is a very quite Legend in my book, and I have been most fortunate to strike up a friendship with the fellow, I have learned a lot about +3e DOS environment.
And lets not forget our transient tractor trainer Guesser! he does put up with some silly questions from me... that will teach him to add me as a google+ friend =)
Every case I've noted have easy solutions (i.e.: preserving some bytes somewhere else, invoking DOSSET1346, rewriting the BASIC loader), except those that involves page 7. That games needs some planning before doing anything (and even when finished I usually compare page 7 from "good" game with page 7 from my converted game).
An' you know what they said?
Well, some of it was true!
I converted Op.Wolf from the +3/+3e bootstrap loader to use a standard file loader just recently; see my posts in "RAM used / corrupted by +3DOS" at the end of December; the final problem being solved on the 28th. Having no explicit loader as such the conversion couldn't be automated, although the new disk loader did simply resolve to:
10 CLEAR 32767: LOAD "e" SCREEN$: LOAD "h" CODE 32768: LOAD "ow" CODE 38672: RANDOMIZE USR 38672All the tricky bits being dealt with in the 400 bytes at USR 38672.When converting Space Gun last week I determined that with no buffers other than the permanent one in page 7 the only RAMpage segment of significance 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.
For automated TAP conversions I was just going to start with standard 48k loaders without USR calls, so there'd be no problems with paging or machine code. When I'd got all the straightforward stuff working I could then progressively add on the handling of variations. A comprehensive list of loader variants would be handy; not the protection schemes like Alkatraz, Bleepload & Speedlock; but variants which use LD_BYTES in some way or t'other - the sort of things which you mentioned above.
When I ran into that with TAPROM, the solution I ended up with was to put a +3DOS header on the .TAP file. Hence .ZXT. Which is also, by the way, why the tape archiving format is named .TZX rather than .ZXT -- I got there first :-)
The alternative would be to rewrite +3DOS to support exact file sizes for files without +3DOS headers. There's a spare byte in the directory entry that later Digital Research operating systems use for the purpose.
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 multiple levels of function calls (well, four, anyway, as that's the number of rows in the array which holds the pointers). It wasn't essential - I could have just had a bunch of LETs before each GO SUB - but I just wanted to see if it would still work as much as anything, which it did - eventually. 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 (although trying to change them isn't always advisable - not with string parameters, anyway, they tend to go horribly wrong).
Well... a couple of things:
It's not strowsaw which decodes the TAP file. Strowsaw works most of the time as a nice front-end GUI to a commandline utility named "3e". I wrote "3e" that way so portability would be easier to accomplish. Of course, this means "3e" works under Linux, Win32 and OSX. Working with command line allows you some neat things, such as making a complete backup of your CF/SD card, with one directory per partition. Quoting from the "readme" file at 3e.zip package...
The TAP extraction feature of "3e" works the way Zetr0 described just because I wanted to make sure that every decoded file would be unique within the partition (EDIT: I see it doesn't work the way I've just said. It's been 3 years from I wrote this... "3e" uses the tape name when a name is available). You see... I don't expect you put a single game into one partition, but share a partition among many games. Some of them could use the same filenames for different parts (such as "screen" for the screen, "code" for the machine code block, or "loader" for the BASIC loader... think about the way Ultimate names the loading blocks for his games, for instance). In order to avoid filename clashes, I decided to rename every block that was read from TAP using a consistent naming scheme that would allow for easy identification of such blocks among dozens of files, and woud avoid clashes, although further tweaking with the loader is necessary.
That said, one could then wonder why "3e" doesn't recode the loader, as strowsaw is able to show it to us in plain text. This is because the decoding of a BASIC loader is not a feature of "3e" itself, but a value-added feature that strowsaw provides.
Personally I think its GREAT!
Now, all I have to do (with your nicely volunteered help ;) ) is to get some FMV working on the 8bit IDE and the z80 SDMMC
The 48k ROM is paged in when the game is running on the +3, so the game Save/Load options will work, but they're still calling the tape routines, so you'd need a cassette deck connected to the TAPE socket on the +3 to be able to make use of them.
(as well as bards tale.... )
DSK version of Elite. save/load on disk http://zx.pk.ru/attachment.php?attachmentid=12206&d=1243776645 (19.05.2009)
Thank you Thank you Thank you Thank you
I shall try that again.........
Shame there was no Spectrum Disk version of Bards Tale
*waits.....
I found only a tap and tzx version.. sorry
Many thanks for your help =)
I have found the the +3 disk version of "The Bards Tale" on WoS, however when I copy the files to an active partition and even assign an XDPB mount point and focus - the game doesn't load =(
trying to load "boot." or "bard." in basic returns "reached end of file" so I expect that there is a hidden loader on the disk?
[update]
Sadly after trying to just run the game from "A:" it fails as well, same symptoms =(
I got the files from here
It's in my big +3e whojammawhatsit where i edited the DOS INIT as BB mentioned above
now why the hell does
LD IX,16384
LD DE,17
XOR A
SCF
CALL 1366
RET
Crash my DivIDE while virtually every game works?!?!?!?
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).
REM TAP2DSK.BAS 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" 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") REM open TAP file 6010 PRINT "Opening ";t$: GO SUB FN a("O",t$,k3,k1,k0,k2) 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 6499 REM read TAP block length & flag bytes 6030 IF pt>=EOF-k7F THEN GO TO 6400+(99 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 6300 FOR h=k1 TO LEN n$: IF n$(h)<>" " THEN PRINT n$(h);: POKE db,CODE n$(h): LET db=db+k1 6301 NEXT h: RETURN REM reconstruct the loader 6400 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) 6402!LET base=db: GO SUB FN s("R",k4,k4): LET sl=FN p(base+k2): REM read a BASIC line header !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 "": REM read the remainder of the BASIC line PRINT TAB FN t(tl,k3);tl;" ";: LET tl=k0 REM edit LOAD statements; ensure ASCII/$0e.FP numbers match 6403 FOR o=base TO base+sl-k1: LET g$=CHR$ PEEK o: REM check if in LOAD IF g$=CHR$ 239 THEN LET il=NOT il: GO TO edl REM check for SCREEN$,CODE,DATA,:,ENTER 6405 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 6300: LET p=p+kE: LET il=NOT il: GO TO edl 6406 IF il THEN GO TO edn REM check if in quotes 6407 IF g$="""" THEN LET iq=NOT iq: GO TO edl 6408 IF iq THEN GO TO edl REM check if in number 6409 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 6410 IF NOT ia OR g$<>CHR$ kE THEN GO TO edl REM convert FP number to decimal 6411 FOR h=k1 TO k5: POKE pp,CODE "AEDCB"(h) 6413 LET A=PEEK (o+h): NEXT h: REM cf. line 6002 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 6300: LET ia=NOT ia: REM POKE ASCII number FOR h=k0 TO k5: POKE db+h,PEEK (o+h): NEXT h: REM POKE $0e.FP number 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: REM edl 6460!NEXT o: POKE dp-k2,FN o(db-dp): POKE dp-k1,FN i(db-dp): REM update line length IF pt-kFG<pn THEN GO TO 6402 6461 IF dn-pn THEN LET base=db: GOSUB FN s("R",k4,dn-pn): REM read in any variables data 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) REM end of program 6499 GO SUB FN a("C","",k3,k0,k0,k0): POKE 23658,k0: STOP 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 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 REM select routine for tape file data block (flag=255) & checksum REM skip tape file data block (flag=255) & checksum 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 REM invalid 1<= flag byte <=254 REM lines 7001-7254 reserved for error jumps 7254 PRINT "Bad Flag Byte: ";flag: GO TO 6499 REM read tape file header block (flag=0) & checksum 7255 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) 7256 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")) 7257 NEXT h 7258 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 6499 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 REM copy tape file data block (flag=255), skip checksum 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): REM returns IX=header address POKE IX,type: FOR h=k1 TO k6: POKE IX+h,PEEK (hb+kB+h-k1): NEXT h REM copy data block in 16kb segments plus a remainder segment 8255 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: RETURNFor Phase IV (that's when the hyper-intelligent ants take over the world, if you recall) I want to test it with a selection of different TAP files and add some extra conversion features (whenever I think of them).
If you look carefully you might spot a slight difference between the two versions. 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 - whichever can be done with the least amount of code, I suppose; the latter, I would expect.
If you are using ZXSpin, put a breakpoint at 28792, load the game and then save the contents of page 1. Do that with both the "good" version and your won version and compare it to see if it's been corrupted.
In this thread, nuggetregggae put some +3 utilities; look for the routine called "nocache.bin" load it and execute it BEFORE loading anything from disk... that will solve your problems.
That problem will reproduce in every game that uses page 1; also keep in mind that most times use of page 7 also is forbidden (but you may store data in another place and put it into page 7 when disk is not needed anymore).
An' you know what they said?
Well, some of it was true!
I have - thanks!
@Battle Bunny
Now, that is shaping up to be a most awesome tool!!!
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.
Phase V involves more testing ...
Here's the screens for Amaurote:
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 here. 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.
Could you please write a tutorial how to use your programs?
Instructions for DOS_Demo/DOS_Defn are already included in the listings.
Send any questions or bug reports to ... me at ... here.
TAP2DSK
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.
DSK2TAP
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.