Resource centre for ZX Spectrum games
      using Manic Miner and Jet Set Willy game engines

 

Archive of the

Manic Miner & Jet Set Willy Yahoo! Group

messages

 

 

 

Message: 5042

Author: andrewbroad

Date: 14/11/2005

Subject: Comparison of TAP files

 

Daniel wrote:

>
> Right now I just want to share my experiences with modifying
> Maria's colour attributes.
>
> The discussion about this came at a very appropriate time for me,
> because I had the same problem in "Mind Control".

>
> I have applied the following method:
>
> I loaded the "MC" file (.tap format, a standard product of JSWED)
> into Vaggelis Kapartzianis's ZX32 emulator. I applied the POKEs
> mentioned by Andrew – I established the correct value by trial-and-
> error method (in my case the value is 78, for bright navy-blue
> paper and yellow ink). So I applied the following POKEs:
>
> POKE 38250,78
> POKE 38251,78
> POKE 38256,78
> POKE 38257,78
>
> Then I saved the file as a .z80 file.
>
> Then I loaded it into JSWED again, changed to .tap and saved it as
> a .tap file.
>
> It seems to have worked fine, but I have a question to John and
> Andrew and those among you who have technical knowledge: is my
> current .tap file identical to the original .tap file with the
> exception of the four values modified by POKEs? Can there be any
> problems after the operation I have done?

It depends when you saved the snapshot. The ideal time is after
loading the game into memory, but before evaluating the USR 32039
function which transfers control from the BASIC interpreter to the
game-engine.

If you saved the snapshot after starting to execute the game-engine,
then the two TAP files will differ in the values of runtime
variables - which doesn't usually stop the game working (it did once
when I converted a snapshot to TAP - Utility Cubicles if I remember
rightly), but is inelegant as it causes comparison-tools to report
unnecessary differences.


So what's the best method for comparing TAP files?

-------
1. comp
-------

The MS-DOS command "comp" compares two binary files byte by byte,
but only reports individual byte-differences if the files are of
equal length, and relies on corresponding bytes being at the same
offsets in the two files. It has no knowledge of Spectrum memory-
addresses, or of the structure of TAP files.

e.g.> comp PART1_BETA2.TAP PART1.TAP
>>>
Comparing PART1_BETA2.TAP and PART1.TAP...
Compare error at OFFSET 47D9
file1 = 2A
file2 = 16
Compare error at OFFSET 47DC
file1 = 2A
file2 = 16
Compare error at OFFSET 675C
file1 = B5
file2 = 9E
<<<

It's annoying that comp writes its output to stderr rather than
stdout, so you can't redirect the output to a text-file!

---------------------
2. SPECSAISIE Compare
---------------------

This compares two instances of 48K Spectrum memory, reporting each
byte-difference in terms of a Spectrum memory-address and the new
value (and old value if the -b flag is used).

Not only does it recognise the structure of TAP files, it even has a
rudimentary facility to describe how each address is used in MM (-m
flag) or JSW (-j flag)!

e.g.> java Compare -bj PART1_BETA2.TAP PART1.TAP
>>>
[50538]: 42 -> 22 Room 5 Offset 106
[50541]: 42 -> 22 Room 5 Offset 109
[58605]: 181 -> 158 Room 36 Offset 237
<<<

In its current avatar, Compare is designed for comparing two 48K SNA
files. It can take TAP files as input instead, but it just loads
them into the 48K Spectrum memories, then compares the memories.

So Compare is ideal for comparing TAP and/or SNA files of JSW48
games where there is no overlap in Spectrum memory between the
Spectrum-files in each TAP file (intra-TAP overlap, as opposed to
inter-TAP overlap across the two TAP files - remember this
distinction, it's important in what I say later).

But if there *is* overlap, the later Spectrum-files in the TAP file
overwrite the earlier ones, which may cause some differences not to
be reported! This is certainly the case for JSW128 and JSW64 games.

e.g.> java PrintHeaders JSW-MC-Club.tap

Name: "JetSet.128"
Type: BASIC
Line: 0
VarA: 959
Leng: 965

Name: "hiload.js7"
Type: CODE
Star: 0 (Stop: 7596)
Leng: 7597

Name: "rooms.js6 "
Type: CODE
Star: 49152 (Stop: 65535)
Leng: 16384

Name: "rooms.js4 "
Type: CODE
Star: 49152 (Stop: 65535)
Leng: 16384

Name: "rooms.js3 "
Type: CODE
Star: 49152 (Stop: 65535)
Leng: 16384

Name: "rooms.js1 "
Type: CODE
Star: 49152 (Stop: 65535)
Leng: 16384

Name: "title.js7 "
Type: CODE
Star: 49152 (Stop: 56063)
Leng: 6912

Name: "rtime.js2 "
Type: CODE
Star: 32768 (Stop: 65535)
Leng: 32768

Name: "main.tun "
Type: CODE
Star: 63232 (Stop: 63487)
Leng: 256

Name: "cheat.tun "
Type: CODE
Star: 63746 (Stop: 63999)
Leng: 254

Name: "tune.bin "
Type: CODE
Star: 64000 (Stop: 65535)
Leng: 1536

Name: "title.tun "
Type: CODE
Star: 29994 (Stop: 31703)
Leng: 1710

So each rooms.js* Spectrum-file overwrites (in Spectrum memory) the
one before it; rooms.js1 is overwritten partially by title.js7, then
completely by rtime.js2, and the *.tun files overwrite parts of
memory too. Only the final contents of the two memories are compared.

What I plan to do (though not before the gamma-release of SPECSAISIE
1.3) is to implement a representation of 128K Spectrum memory, and
to write functions to:
* load an arbitrary 128K SNA file into a 128K Spectrum memory;
* load a JSW128 or JSW64 TAP file into 128K memory, writing the
various Spectrum-files to the correct memory-banks (as identified by
the js-numbers in their Spectrum-filenames).

(The obvious problem is how to denote 128K addresses to (and from)
the user - should I call them 65536 and greater, or adopt a
bank:offset notation?)

Then it would be possible to write a 128K version of Compare to
report all differences in two JSW128 or JSW64 games (not to mention
128K versions of many other SPECSAISIE functions).

Using SPECSAISIE 1.3, the only way around these intra-TAP overlaps
is to Split the TAP files and compare their components individually.

e.g.
> java Split JSW-MC-A.tap
> java Split JSW-MC-B.tap

Then to compare rooms.js6:
> java Compare JSW-MC-A_3.tap JSW-MC-B_3.tap

rooms.js4:
> java Compare JSW-MC-A_4.tap JSW-MC-B_4.tap

rooms.js3:
> java Compare JSW-MC-A_5.tap JSW-MC-B_5.tap

rooms.js1:
> java Compare JSW-MC-A_6.tap JSW-MC-B_6.tap

rtime.js2:
> java Compare JSW-MC-A_8.tap JSW-MC-B_8.tap

and so on.


Loading a JSW128 or JSW64 TAP file into 128K memory is an ad hoc
solution which does not solve the overlap-problem for arbitrary TAP
files.

For this, it would be desirable to have a CompareTAP function which
could automate the task of comparing pairs of corresponding Spectrum-
files in the two TAP files being compared.

The problem is: for each Spectrum-file in the first TAP file, how
does it know which Spectrum-file it corresponds to in the second TAP
file?

It must decide this using a correspondence-heuristic [A.P. Broad,
Comparative Code Understanding of Information Models, PhD thesis,
University of Manchester, 2003,
http://www.geocities.com/andrewbroad/cs/phd/thesis/ ].

A correspondence-heuristic is a rule of thumb for inferring
correspondences between pairs of objects - objects which are part of
two larger objects being compared - in this case, between the
Spectrum-files in the two TAP files being compared.

The most obvious correspondence-heuristic for TAP files is pair-by-
order. This means compare the first Spectrum-file in TAP-file A with
the first Spectrum-file in TAP-file B, the second in A with the
second in B, and so on.

In a lot of cases, pair-by-order is just what we need.

But what if, for TAP-file B, we inserted a SCREEN$ as the second
Spectrum-file, between the BASIC loader and the game itself? Pair-by-
order would mean comparing the game itself in TAP-file A with the
SCREEN$ in TAP-file B, and presumably ignoring the game itself in
TAP-file B?

To handle cases like this correctly, we would need more advanced
correspondence-heuristics, such as:

* Name-equivalence: compare two Spectrum-files that have the same
filename. But what if one of them gets renamed? For example, editing
We Pretty in JSWED would break both pair-by-order (by losing the
SCREEN$) and name-equivalence (by renaming the game-file
to "jsw.js2").

* Address-equivalence: compare two Spectrum-files that have the same
start-address and/or length, or the two Spectrum-files whose address-
spaces have the most inter-TAP overlap. Easily defeated by JSW128 or
JSW64 TAP files (because some intra-TAP Spectrum-files have the same
addresses), and by relocation (e.g. LOAD "rooms.js6" 32768).

* Analyse the BASIC loaders to decide what to do. This would tell it
about relocated start-addresses, bonus-files on the TAP which aren't
normally loaded, but it would be hard-pressed to understand JSW128
and JSW64 loaders unless we gave it some ad hoc knowledge of the
machine-code routine at 32001!

* One Spectrum-file in TAP-file A may correspond to two or more
Spectrum-files in TAP-file B (e.g. SAVE "gameEngine" CODE
32768,16384: SAVE "rooms" CODE 49152,16384) - it would have to
analyse their address-spaces and do some merging of Spectrum-files.

* Cluster together the Spectrum-files with no intra-TAP overlap, and
compare corresponding clusters (two clusters correspond if they have
inter-TAP overlap?).

* Statistical analysis: pair the Spectrum-files such that the ones
with the fewest differences correspond (this requires a heavy
comparison-effort, whose results are unseen by the user, before even
deciding which Spectrum-files correspond).

* Interact with the user to determine correspondences.

* Meta-correspondence heuristics: how the program decides which
correspondence-heuristic(s) to use for a given pair of TAP files -
under what conditions, and in which order, does it try the above
correspondence-heuristics?

--
Dr. Andrew Broad
http://www.geocities.com/andrewbroad/
http://www.geocities.com/andrewbroad/spectrum/
http://www.geocities.com/andrewbroad/spectrum/willy/

 

 

arrowleft
arrowright