Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

.SNA file format

289 views
Skip to first unread message

Fidel Viegas

unread,
Apr 30, 2003, 4:37:56 PM4/30/03
to
Hello guys, I have just recently downloaded a spectrum emulator and I
want to remember the good old days when I used to create my own games
for the Amstrad CPC 464 and the Spectrum 48k. What I want to know is how
the .SNA file format works. That is, my specific question is regarding
its header. Why do we load the registers? What information do this
registers contain when we generate a .SNA file. Let's say that I want to
write an assembler that generates .SNA files. What do I have to
include in the .SNA file so that it can be readable by an emulator? What
values for the registers do I need to add in the file? As for the 48k of
RAM from 16384 to 65525, I pretty much get it. This is the actual
Spectrum RAM, isn't it? Where the program (or game goes).

Could anyone tell me about the header? I mean about the registers. Why
do I need to load them? And what values do I need to add to them?

Thanks in advance.

Fidel.

PS: Is there an assembler that generates .SNA files or .Z80 files? or
even a compiler?

Geoff Wearmouth

unread,
Apr 30, 2003, 5:24:11 PM4/30/03
to
Hello Fidel

>Hello guys, I have just recently downloaded a spectrum emulator and I
>want to remember the good old days when I used to create my own games
>for the Amstrad CPC 464 and the Spectrum 48k. What I want to know is how
>the .SNA file format works. That is, my specific question is regarding
>its header. Why do we load the registers? What information do this
>registers contain when we generate a .SNA file.

I assume you've seen the format in the FAQ. The registers must be saved
so that the game or BASIC program continues exactly where it left off.
If the Program Counter was just zero then the emulator would just reset
itself so it is saved on the stack. It is likely that all the registers
will be important, but in particular IY which may address the system
variables should be set.

>PS: Is there an assembler that generates .SNA files or .Z80 files? or
>even a compiler?

There are, as an example DOS assemblers that generate a binary z80
machine code.
So if you edit a file game.asm as
--------------------------
.org 32768
ld hl,16384
ld de,20480
ld bc,2048
ldir
ret
#end
--------------------------
and then assemble it with TASM -80 -b game.asm
then you can load the game.obj (which will be a 12 byte file)
into, say, VBSpec using the load binary option.

The code copies the top third of the screen to the bottom third so if
you draw a circle at the top you can test it with
PRINT USR 32768

Then save it as a snapshot.

If you happened to save it half way through the copy it would know
exactly where to start again as the HL,DE,BC registers are preserved.

Using a DOS emulator means you could adapt the same code for CPC and
Spectrum emulators.

Cheers,

--
Geoff Wearmouth
The ZX ROM Files http://www.wearmouth.demon.co.uk

Jaime Tejedor Gómez, aka Metalbrain

unread,
Apr 30, 2003, 5:39:15 PM4/30/03
to
Hi

Your answer is in the Sinclair FAQ:

http://www.sinclairfaq.com/cssfaq/reference/formats.htm

>PS: Is there an assembler that generates .SNA files or .Z80 files? or
>even a compiler?

Try Nick Flemming's:
http://www.bigfatpanda.fsnet.co.uk/z80.html

Most other assemblers usually generate binary code that can be converted
to .TAP format. Here are some of them:

http://www.falstaff.demon.co.uk/cross.html
http://www.tim-mann.org/trs80/zmac13.zip
http://www.tni.nl/pc/tniasm/
ftp://ftp.worldofspectrum.org/pub/sinclair/tools/pc/tasm301.zip

And there's also a C compiler that can generate Spectrum output:
http://z88dk.sourceforge.net/

METALBRAIN
(C) 1977 Tejedor & Gómez Research Ltd.

Geoff Wearmouth

unread,
Apr 30, 2003, 5:41:36 PM4/30/03
to
In article <rjdy7LA7...@wearmouth.demon.co.uk>, Geoff Wearmouth
<ge...@wearmouth.demon.co.uk> writes

>Using a DOS emulator means you could adapt the same code for CPC and
>Spectrum emulators.
I intended to say using a DOS assembler you can adapt the same code for
CPC and Spectrum emulators.
There are a few out there and I use TASM which can be downloaded from

ftp://ftp.nvg.ntnu.no/pub/sinclair/utils/pc/tasm301.zip

Fidel Viegas

unread,
May 1, 2003, 5:04:41 PM5/1/03
to
Geoff Wearmouth wrote:
> I assume you've seen the format in the FAQ. The registers must be saved
> so that the game or BASIC program continues exactly where it left off.
> If the Program Counter was just zero then the emulator would just reset
> itself so it is saved on the stack. It is likely that all the registers
> will be important, but in particular IY which may address the system
> variables should be set.

Yes, I have seen the format in the FAQ. So basically, the file is a dump
of the system status. That is, the registers together with RAM, right?
Now, are there specific values for some of the registers when we are
generating .SNA or .Z80 files?

> There are, as an example DOS assemblers that generate a binary z80
> machine code.
> So if you edit a file game.asm as
> --------------------------
> .org 32768
> ld hl,16384
> ld de,20480
> ld bc,2048
> ldir
> ret
> #end
> --------------------------
> and then assemble it with TASM -80 -b game.asm
> then you can load the game.obj (which will be a 12 byte file)
> into, say, VBSpec using the load binary option.
>
> The code copies the top third of the screen to the bottom third so if
> you draw a circle at the top you can test it with
> PRINT USR 32768
>
> Then save it as a snapshot.
>
> If you happened to save it half way through the copy it would know
> exactly where to start again as the HL,DE,BC registers are preserved.

As I mentioned above, it is a dump of the system. Well, but everytime we
want to save the game or program, we have to save a new snapshot, isn't
it? Wouldn't it be better to have another file with the same name as the
.SNA or .Z80 file for that purpose? The emulator would read that file
cause it has the same name as the snapshot. In that case, the files
would be smaller. Let's say you give the .SNA file to a friend and that
it is at some level you were playing. If he wants to start from the
beginning, then he has to reset the system and then save another
snapshot, isn't it? I think that is how MAME works, I am not quite sure.
Correct me if I am wrong.
AS for the tasm assembler, that is really cool, but I am using a Mac and
my laptop just crashes when I try to run any DOS application. I have got
ZASM, which is open source and could be modified to generate .SNA files.
What I really want to know is the values of the registers when I
generate the .SNA file. Can I just set them all to zero?

Thanks in advance.

Fidel.

Fidel Viegas

unread,
May 1, 2003, 5:06:56 PM5/1/03
to

Jaime Tejedor Gómez, aka Metalbrain wrote:
> Hi
>
> Your answer is in the Sinclair FAQ:
>
> http://www.sinclairfaq.com/cssfaq/reference/formats.htm
>
>
>>PS: Is there an assembler that generates .SNA files or .Z80 files? or
>>even a compiler?
>
>
> Try Nick Flemming's:
> http://www.bigfatpanda.fsnet.co.uk/z80.html

Thanks for the link dude, but the guy that created it hasn't released
the source code, hence we cannot port it to another platform like Mac OS X.

> Most other assemblers usually generate binary code that can be converted
> to .TAP format. Here are some of them:
>
> http://www.falstaff.demon.co.uk/cross.html
> http://www.tim-mann.org/trs80/zmac13.zip
> http://www.tni.nl/pc/tniasm/
> ftp://ftp.worldofspectrum.org/pub/sinclair/tools/pc/tasm301.zip
>
> And there's also a C compiler that can generate Spectrum output:
> http://z88dk.sourceforge.net/

Thanks for all those links.

All the best

Fidel.

Dunny

unread,
May 1, 2003, 7:28:38 PM5/1/03
to
Fidel Viegas wrote:

> Yes, I have seen the format in the FAQ. So basically, the file is a
> dump of the system status. That is, the registers together with RAM,
> right? Now, are there specific values for some of the registers when
> we are generating .SNA or .Z80 files?

No, there's no specific values you want to set - if you understand z80 assembly
at all, you'll know that the z80 registers are changing constantly as the chip
executes instructions in memory. About the only things you can do are get a
broad idea of what was going on when the snapshot was taken - start by
disassembling from PC, following program flow according to the register contents
etc.

> As I mentioned above, it is a dump of the system. Well, but everytime
> we want to save the game or program, we have to save a new snapshot,
> isn't it? Wouldn't it be better to have another file with the same
> name as the .SNA or .Z80 file for that purpose?

If you mean use a "base" file containing your initial registers, and then
further on using that file in conjunction with other smaller files which
together make up a complete snapshot, then ... yes ... I suppose you *could* but
you would have to be *extremely* careful that both snapshots had the exact same
values in the registers when they were taken - you can't simply set new ones,
you'll likely crash the emulated spectrum as soon as it was loaded.

> What I really want to know is the values of the registers
> when I generate the .SNA file. Can I just set them all to zero?

No! Quite a few programs whilst running have the stack (SP) set pointing
somewhere - if you set *that* to zero, for example, then the very next RET or
POP (or even PUSH/CALL) would have disastrous effects, at best corruption of the
memory, at worst a total USR 0 reset. Other registers would have similar
effects, ranging again from an all out crash to memory corruption.

You ideally want to be creating a tzx file (tape image) which can be loaded into
an emulator. If you have no cross platform dev tools, then you should be working
from within the emulation itself. I'd dread the thought of creating custom
snapshots.

D.

It's 00:30 am, and I'm knackered - hope all that made sense :)

Fidel Viegas

unread,
May 1, 2003, 10:17:07 PM5/1/03
to

Dunny wrote:
> Fidel Viegas wrote:
>
>
>>Yes, I have seen the format in the FAQ. So basically, the file is a
>>dump of the system status. That is, the registers together with RAM,
>>right? Now, are there specific values for some of the registers when
>>we are generating .SNA or .Z80 files?
>
>
> No, there's no specific values you want to set - if you understand z80 assembly
> at all, you'll know that the z80 registers are changing constantly as the chip
> executes instructions in memory. About the only things you can do are get a
> broad idea of what was going on when the snapshot was taken - start by
> disassembling from PC, following program flow according to the register contents
> etc.

I guess there has been a misunderstanding here. What I want to know is
not the generation of .sna files from the emulator, but from an
assembler. What you have just described is what happens when the program
is in execution. What I want to know is if there are any specific values
that the emulator needs when you generate the .sna file from an
assembler or a compiler. I pretty much got the generation of the RAM
section, which is the actual program that gets loaded into memory. But I
am just wondering about the values for the registers. What is the actual
purpose of the .sna file? Is it just to take snapshots of the running
program, which will be used later on at the point we left it on?

> If you mean use a "base" file containing your initial registers, and then
> further on using that file in conjunction with other smaller files which
> together make up a complete snapshot, then ... yes ... I suppose you *could* but
> you would have to be *extremely* careful that both snapshots had the exact same
> values in the registers when they were taken - you can't simply set new ones,
> you'll likely crash the emulated spectrum as soon as it was loaded.

What I really meant was to have the program (or game) in one file and
then the status of the game or program in another file that stores only
the register values. That is, the header of the .sna file would be a
separate file rather than having it on the same file. But, I guess I am
the one that didn't quite get the use of the .sna file.

> No! Quite a few programs whilst running have the stack (SP) set pointing
> somewhere - if you set *that* to zero, for example, then the very next RET or
> POP (or even PUSH/CALL) would have disastrous effects, at best corruption of the
> memory, at worst a total USR 0 reset. Other registers would have similar
> effects, ranging again from an all out crash to memory corruption.

Again, this only applies to programs in execution.

> You ideally want to be creating a tzx file (tape image) which can be loaded into
> an emulator. If you have no cross platform dev tools, then you should be working
> from within the emulation itself. I'd dread the thought of creating custom
> snapshots.

Ok, now seems to clear my doubts. Is the tzx file contains only the code
for the program (or game). unfortunately, my emulator only accepts .sna
and .z80 files and I can't seem to find conversion utilities for mac os
x, or open source ones that I can port to mac os x.
Anyway, I will keep reading the stuff. If someone could clarify my
doubts about the snapshots and the tape images.

I have just revised the FAQ specs again and I think I got it.
What I would want to create would be a .TAP or .TZX file, right? The
snapshot is just to save the status of the program (or game) for later
usage, isn't it? It is sort of game status backup that we can use to
continue where we left before.

My last question is: I have checked Nick Flemming's assembler that
generates .sna files. What values does he add to the registers to
prevent a crash? Where does he point to SP to? The PC, etc...? This was
what lead me to ask the question about the .sna file format.

Anyway, hope I have made myself clear this time.

All the best

Fidel.

JamesW

unread,
May 2, 2003, 5:31:16 AM5/2/03
to
In article <3EB1D52...@nodomain.com>, fidel...@nodomain.com
says...

> I guess there has been a misunderstanding here. What I want to know is
> not the generation of .sna files from the emulator, but from an
> assembler. What you have just described is what happens when the program
> is in execution. What I want to know is if there are any specific values
> that the emulator needs when you generate the .sna file from an
> assembler or a compiler. I pretty much got the generation of the RAM
> section, which is the actual program that gets loaded into memory. But I
> am just wondering about the values for the registers. What is the actual
> purpose of the .sna file? Is it just to take snapshots of the running
> program, which will be used later on at the point we left it on?
>
> What I really meant was to have the program (or game) in one file and
> then the status of the game or program in another file that stores only
> the register values. That is, the header of the .sna file would be a
> separate file rather than having it on the same file. But, I guess I am
> the one that didn't quite get the use of the .sna file.
>
> Again, this only applies to programs in execution.
I think you want to create a Spectrum executable - so a tap or tzx is
what you want. Snapshots are a save state of the whole machine rather
than just the executable so they're good for saving games in progress but
not really the way to go as the output target of an assembler.

>
> Ok, now seems to clear my doubts. Is the tzx file contains only the code
> for the program (or game). unfortunately, my emulator only accepts .sna
> and .z80 files and I can't seem to find conversion utilities for mac os
> x, or open source ones that I can port to mac os x.
> Anyway, I will keep reading the stuff. If someone could clarify my
> doubts about the snapshots and the tape images.
What emulator are you using - I would recommend MacFUSE:
http://www.srcf.ucam.org/~pak21/spectrum/fuse.html
as that understands tzx and tap files.

>
> I have just revised the FAQ specs again and I think I got it.
> What I would want to create would be a .TAP or .TZX file, right? The
> snapshot is just to save the status of the program (or game) for later
> usage, isn't it? It is sort of game status backup that we can use to
> continue where we left before.

Yes!

Dunny

unread,
May 2, 2003, 6:43:24 AM5/2/03
to

"Fidel Viegas" <fidel...@nodomain.com> wrote in message
news:3EB1D52...@nodomain.com...

> I guess there has been a misunderstanding here. What I want to know is
> not the generation of .sna files from the emulator, but from an
> assembler. What you have just described is what happens when the program
> is in execution. What I want to know is if there are any specific values
> that the emulator needs when you generate the .sna file from an
> assembler or a compiler. I pretty much got the generation of the RAM
> section, which is the actual program that gets loaded into memory. But I
> am just wondering about the values for the registers. What is the actual
> purpose of the .sna file? Is it just to take snapshots of the running
> program, which will be used later on at the point we left it on?

I suppose that if you had assembled your program into memory, then PC would
point to the address you wanted to execute to, AF, BC, DE, HL can be whatever
you desire, so long as your program *knows* what they are going to be. Be
careful with F though, that's the z80's flag register. SP would have to point to
somewhere empty in RAM, as it is used for the stack, and will quite quickly
"grow" through your program if you're not careful. The rest of the registers can
be set up in a similar manner - have a good read of some z80 hardware
documentation to discover what they do.

> Ok, now seems to clear my doubts. Is the tzx file contains only the code
> for the program (or game). unfortunately, my emulator only accepts .sna
> and .z80 files and I can't seem to find conversion utilities for mac os
> x, or open source ones that I can port to mac os x.
> Anyway, I will keep reading the stuff. If someone could clarify my
> doubts about the snapshots and the tape images.

Then you need a decent emulator, and (I might add) a decent platform to run it
on. I'd recommend Windows/PC as being the best OS/Hardware combination, but this
lot will bite my head off[1].

> I have just revised the FAQ specs again and I think I got it.
> What I would want to create would be a .TAP or .TZX file, right? The
> snapshot is just to save the status of the program (or game) for later
> usage, isn't it? It is sort of game status backup that we can use to
> continue where we left before.

Yes. That's exactly right. Forget snapshots - you need a rather good
understanding of the z80 processor to really use them. Thing is, you need a
rather good understanding of how the spectrum loads from tape signals and how
the TZX blocks are built before you can create your own TZX files - you need to
understand the ROM too. Find a program that will create them for you.

> My last question is: I have checked Nick Flemming's assembler that
> generates .sna files. What values does he add to the registers to
> prevent a crash? Where does he point to SP to? The PC, etc...? This was
> what lead me to ask the question about the .sna file format.

The thing here is that he likely sets the registers up according to what *you*
have told it to assemble - there are no "magic" values to set them to. If you
examine a few .sna files generated by that application, there may be a couple of
common registers that are always the same, which you could use as a starting
point, but the rest will all be preloaded with values pertinant to the execution
of that program.

Here you're building up a save state of a program in execution, without having
actually emulated it into an executing state, IYSWIM. In this way, you're
fooling an emulation into thinking it *Was* executing.

Am I making any Sense?

D.

[1] And now, we're likely to leave the subject entirely in an OT debate about
OS/Hardware. Such is CSS, and I'm surprised it didn't happen earlier :)


JamesW

unread,
May 2, 2003, 7:39:56 AM5/2/03
to
In article <b8ti4c$d96hd$1...@ID-106816.news.dfncis.de>,
paul....@ntlworld.com says...

>
> Then you need a decent emulator, and (I might add) a decent platform to run it
> on. I'd recommend Windows/PC as being the best OS/Hardware combination, but this
> lot will bite my head off[1].
>
Cannot ... resist .. temptation ... to ... reply ...
IMHO MacFUSE running on OS X is just fine and dandy. 'Windows' and 'best
OS' in the same sentence just does not compute for me.

> > I have just revised the FAQ specs again and I think I got it.
> > What I would want to create would be a .TAP or .TZX file, right? The
> > snapshot is just to save the status of the program (or game) for later
> > usage, isn't it? It is sort of game status backup that we can use to
> > continue where we left before.
>
> Yes. That's exactly right. Forget snapshots - you need a rather good
> understanding of the z80 processor to really use them. Thing is, you need a
> rather good understanding of how the spectrum loads from tape signals and how
> the TZX blocks are built before you can create your own TZX files - you need to
> understand the ROM too. Find a program that will create them for you.

TAP files are pretty easy to understand - that's why I used them in
Mac2Spec. If you just want to get some code loaded into an emulator I
would recommend them.

Fidel Viegas

unread,
May 2, 2003, 10:39:27 AM5/2/03
to

JamesW wrote:
> In article <b8ti4c$d96hd$1...@ID-106816.news.dfncis.de>,
> paul....@ntlworld.com says...
>
>>Then you need a decent emulator, and (I might add) a decent platform to run it
>>on. I'd recommend Windows/PC as being the best OS/Hardware combination, but this
>>lot will bite my head off[1].
>>
>
> Cannot ... resist .. temptation ... to ... reply ...
> IMHO MacFUSE running on OS X is just fine and dandy. 'Windows' and 'best
> OS' in the same sentence just does not compute for me.

I just agree with this one. How can windows crash-prone be the best OS?
I rather use Linux and my OS X. Unfortunately, at work, I have no choice
and have to use Microsoft Crash OS (also know as Windows). Well, they
have improved a bit with XP, but still Microsoft will never produce
something they we can say: "wow!!! they finally did it".

As for the the rest of the discussion, now I get it. I have just
downloaded Fuse for Mac OS X and it is now loading my .TAP files. Now I
just need to study the file in-depth.

Does anyone know a good pointer for Spectrum assembly language
programming? I still have the original +2 manual and I was reading the
RAM+ROM configuration, but the manual is not very complete. I understand
the memory banks and so on, but where can I get very good tutorials that
talk about programming in assembly language for the Spectrum?

Thank you all for your kind patience in explaining in details the
difference between .SNA and .TAP files.

It has been quite a while since a programmed for the Spectrum and it
wasn't really in assembly. I used to program in BASIC, and my main
platform was the Amstrad CPC 464.

All the best

Fidel.

Jeff Braine

unread,
May 3, 2003, 9:22:29 PM5/3/03
to

On Fri, 2 May 2003, Fidel Viegas wrote:

SNA and Z80 files are machine-state snapshots.

If you're writing an assembler to write snapshots, you'll need to
set the registers, interrupt mode, system variables and so on.

At the very least, you'll want to set SP to somewhere far from your
code, PC to the execution point of your code, and select the appropriate
interrupt mode.

As others have pointed out, you'd be better off just doing a binary
load into an emulator.

The source for Fuse is around. Whether you can compile that up for your
particular one-mousebuttoned wonder, I've no idea.


--
Jeff
There is no snooze button on a cat who wants breakfast.


Fidel Viegas

unread,
May 4, 2003, 6:04:13 AM5/4/03
to

Jeff Braine wrote:
> On Fri, 2 May 2003, Fidel Viegas wrote:
>
> SNA and Z80 files are machine-state snapshots.
>
> If you're writing an assembler to write snapshots, you'll need to
> set the registers, interrupt mode, system variables and so on.
>
> At the very least, you'll want to set SP to somewhere far from your
> code, PC to the execution point of your code, and select the appropriate
> interrupt mode.

Thanks for the advice.

> As others have pointed out, you'd be better off just doing a binary
> load into an emulator.
>
> The source for Fuse is around. Whether you can compile that up for your
> particular one-mousebuttoned wonder, I've no idea.

Someone has managed to do so. I have it running on my machine now. And
as for the one-mousebuttoned wonder it isn't that bad. I must admit that
being used to two-button mouse, it felt kind of weird when I started
using Macs. But you can still use it as if you had two buttons. Just
press the control key and click the mouse and you have your right button.

All the best

Fidel.


Philip Kendall

unread,
May 4, 2003, 6:21:10 AM5/4/03
to
In article <Pine.GSO.4.33.0305041118360.12885-100000@hades>,

Jeff Braine <je...@griffith.edu.au> wrote:
>
>The source for Fuse is around. Whether you can compile that up for your
>particular one-mousebuttoned wonder, I've no idea.

http://www.srcf.ucam.org/~pak21/spectrum/FuseForMacOSXSource-0.6.0.tar.gz

is a fairly large clue :-)

Phil

--
Philip Kendall <pa...@srcf.ucam.org>
http://www.srcf.ucam.org/~pak21/

JamesW

unread,
May 6, 2003, 9:01:57 AM5/6/03
to
In article <3EB4E59D...@nodomain.com>, fidel...@nodomain.com
says...

> > The source for Fuse is around. Whether you can compile that up for your
> > particular one-mousebuttoned wonder, I've no idea.
>
> Someone has managed to do so. I have it running on my machine now. And
> as for the one-mousebuttoned wonder it isn't that bad. I must admit that
> being used to two-button mouse, it felt kind of weird when I started
> using Macs. But you can still use it as if you had two buttons. Just
> press the control key and click the mouse and you have your right button.
>
> All the best
>
> Fidel.
>

I would recommend ditching Apple's mouse and plugging in a multibutton
mouse. OS X understands two button mice and scroll wheels without any
additional software. I use an MS-Intellimouse with five buttons and a
scrolly wheel on my Mac.

Nick Fleming

unread,
May 8, 2003, 1:45:05 PM5/8/03
to

> > Try Nick Flemming's:
> > http://www.bigfatpanda.fsnet.co.uk/z80.html
>
> Thanks for the link dude, but the guy that created it hasn't released
> the source code, hence we cannot port it to another platform like Mac OS
X.
>

No I haven't .. you're not wrong.
But I'm happy to give away what I can:
Hope you can understand 'C' code.

The values I used for the SNA file were simple.
I set IX and IY to the 'default' values, as you'd
find with normal interrupts disabled and the rest to
some values I got while saving a blank sna file from
an emulator. The stack pointer SP just points to
the top of ram where the start of the program
is stored.

It won't compile without some modification as
it uses my code library.for debugging and file handle
tracking.. but *might* be of use.

I've no plans to port it to anything else at
the moment as I've got enough to be
getting on with.

Note in this version I've got some rudimentary
code to generate .z80 files (much of it
from the FAQ.. haven't time to work it
all out myself.)

Hope it helps..

Cheers.

Nick.


save_img.c

/* ----------------------------------------------------------------

Title : Create SNA Image File

Info : Version 2.0 3rd February 2002

Author : Nick Fleming.

Updated : 25th September 2002

Notes:
--------
Takes a block of code and a program origin
and creates an 'SNA' file suitable for running with
a sinclair spectrum emulator.

24th April 2002
-----------------
.z80 file support added as a precursor to 128K files.

Started to add initial code for handling ram banks for
128K files. - using the old 2.01 .z80 format as it seems
to be the most widely supported 128K format.

..requires generating a horrible compression routine. :-/

25th September 2002
---------------------
Updated #includes to use clibs.h
-------------------------------------------------------------------- */

#include <stdio.h>

#include "clibs.h"

//#include "debug.h"
//#include "files.h"
//#include "globals.h"
#include "save_img.h"

#define SNA_HEADER_SIZE 27 // sizeof(struct sna_s)=27

#define Z80_OLD_HEADER_SIZE 30

//#define Z80_HEADER_SIZE 30+23
#define Z80_HEADER_SIZE 55

int SNA_SaveImage (char *filename, unsigned char* z80_buffer, int
program_org)
{
// z80 buffer should point to 'address' 16384 in
// buffer - the spectrum ROM is not stored in
// the image.

FILE *fp;
int sp;
int addr;

// int lp;

unsigned char sna_header [SNA_HEADER_SIZE];

/* store return (start) address on the stack. */

sp = 65536-4; /* set the stack to be very high ... */
addr = sp - 0x4000;
z80_buffer[addr] = program_org & 255;
z80_buffer[addr+1] = program_org / 256;

sna_header[0] = 0x3f; /* I */
sna_header[1] = 0x58; /* lax */
sna_header[2] = 0x27; /* hax */
sna_header[3] = 0x9b; /* eax */
sna_header[4] = 0x36; /* dax */
sna_header[5] = 0x21; /* cax */
sna_header[6] = 0x05; /* bax */
sna_header[7] = 0x6d; /* fax */
sna_header[8] = 0x7e; /* aax */
sna_header[9] = 0xcd; /* l */
sna_header[10] = 0x12; /* h */
sna_header[11] = 0x15; /* e */
sna_header[12] = 0x50; /* d */
sna_header[13] = 0x50; /* c */
sna_header[14] = 0x2a; /* b */
sna_header[15] = 0x3a; /* iyl */
sna_header[16] = 0x5c; /* iyh */
sna_header[17] = 0x80; /* ixl */
sna_header[18] = 0xdc; /* ixh */
sna_header[19] = 0xff; /* iff2 */
sna_header[20] = 0x29; /* r */
sna_header[21] = 0x38; /* f */
sna_header[22] = 0xff; /* a */
sna_header[23] = sp & 255; /* spl */
sna_header[24] = sp / 256; /* sph */
sna_header[25] = 1; /* im */
sna_header[26] = 5; /* border */

fp = FileOpen (filename, "wb");
if (fp == NULL)
{
Debug (DEBUG_TRACE, "save_img.c : SNA_SaveImage(): ** unable to create
file");
return RETCODE_ERROR;
}

fwrite (&sna_header[0], SNA_HEADER_SIZE, 1, fp);
fwrite (z80_buffer, 49152, 1, fp);
CloseFile (fp);
return RETCODE_OK;
}

int SNA_Save128KImage (char *filename, RAMBANK* ram_bank_array, int
program_org)
{
/* experimental 128K snap file output. */

// ** UNDER CONSTRUCTION - NOT YET WORKING **

FILE *fp;
int sp;
int addr;

// int lp;

unsigned char sna_header [SNA_HEADER_SIZE];

/* store return (start) address on the stack. */

sp = 65536-4; /* set the stack to be very high ... */
addr = sp - 0x4000;

addr -= 0xC000;
ram_bank_array[0].data[addr] = program_org & 255;
ram_bank_array[0].data[addr+1] = program_org / 256;

sna_header[0] = 0x3f; /* I */
sna_header[1] = 0x58; /* lax */
sna_header[2] = 0x27; /* hax */
sna_header[3] = 0x9b; /* eax */
sna_header[4] = 0x36; /* dax */
sna_header[5] = 0x21; /* cax */
sna_header[6] = 0x05; /* bax */
sna_header[7] = 0x6d; /* fax */
sna_header[8] = 0x7e; /* aax */
sna_header[9] = 0xcd; /* l */
sna_header[10] = 0x12; /* h */
sna_header[11] = 0x15; /* e */
sna_header[12] = 0x50; /* d */
sna_header[13] = 0x50; /* c */
sna_header[14] = 0x2a; /* b */
sna_header[15] = 0x3a; /* iyl */
sna_header[16] = 0x5c; /* iyh */
sna_header[17] = 0x80; /* ixl */
sna_header[18] = 0xdc; /* ixh */
sna_header[19] = 0xff; /* iff2 */
sna_header[20] = 0x29; /* r */
sna_header[21] = 0x38; /* f */
sna_header[22] = 0xff; /* a */
sna_header[23] = sp & 255; /* spl */
sna_header[24] = sp / 256; /* sph */
sna_header[25] = 1; /* im */
sna_header[26] = 5; /* border */

fp = FileOpen (filename, "wb");
if (fp == NULL)
{
Debug (DEBUG_TRACE, "save_img.c : SNA_SaveImage(): ** unable to create
file");
return RETCODE_ERROR;
}

// output the standard sna header as per usual.

fwrite (&sna_header[0], SNA_HEADER_SIZE, 1, fp);

// output ram banks 5,2 and 0. 5 & 2 are fixed,
// 0 is assumed to be currently paged in.

fwrite (&ram_bank_array[5].data[0], RAM_BANK_SIZE, 1, fp);
fwrite (&ram_bank_array[2].data[0], RAM_BANK_SIZE, 1, fp);
fwrite (&ram_bank_array[0].data[0], RAM_BANK_SIZE, 1, fp);

// output the 128k sna header
sna_header[0] = program_org & 255; /* low byte of PC */
sna_header[1] = program_org / 256; /* high byte of PC */
sna_header[2] = 0x00; /* port 7FFD setting */
sna_header[3] = 0; /* tr-dos ? ..set to 0 for now */

fwrite (&sna_header[0], 4, 1, fp);

// output remaining pages in ascending order:

fwrite (&ram_bank_array[1].data[0], RAM_BANK_SIZE, 1, fp);
fwrite (&ram_bank_array[3].data[0], RAM_BANK_SIZE, 1, fp);
fwrite (&ram_bank_array[4].data[0], RAM_BANK_SIZE, 1, fp);
fwrite (&ram_bank_array[6].data[0], RAM_BANK_SIZE, 1, fp);
fwrite (&ram_bank_array[7].data[0], RAM_BANK_SIZE, 1, fp);

CloseFile (fp);
return RETCODE_OK;
}

/* --------------------------------------------------------
Experimental .z80 snapshot format
-------------------------------------------------------- */

// bog-standard 48K .z80 image, with no data compression.

int Z80_SaveImage (char *filename, RAMBANK* ram_bank_array, int program_org)
{
// uses z80 file format 1.45 from
// http://www.breezer.demon.co.uk/spec/tech/snapformats.html

FILE *fp;
int sp;
int addr;

int lp;

unsigned char z80_header [Z80_OLD_HEADER_SIZE];

sp = 65536-4; /* set the stack to be very high ... */

z80_header[0] = 0xff; /* a */
z80_header[1] = 0x38; /* f */
z80_header[2] = 0x50; /* c */
z80_header[3] = 0x2a; /* b */
z80_header[4] = 0xcd; /* l */
z80_header[5] = 0x12; /* h */

z80_header[6] = program_org & 255; /* program counter low byte*/
z80_header[7] = program_org / 256; /* program counter high byte */

// z80_header[6] = 0; /* old program counter (must be 0) */
// z80_header[7] = 0; /* old program counter (must be 0) */

z80_header[8] = sp & 255; /* stack pointer low byte*/
z80_header[9] = sp / 256; /* stack pointer high byte */

z80_header[10] = 0x3F; /* I - interrupt register */
z80_header[11] = 0x29; /* R - refresh register */
z80_header[12] = 0; /* 0 = data not compressed*/
z80_header[13] = 0x15; /* e */
z80_header[14] = 0x50; /* d */
z80_header[15] = 0x21; /* c' */
z80_header[16] = 0x05; /* b' */
z80_header[17] = 0x9b; /* e' */
z80_header[18] = 0x36; /* d' */
z80_header[19] = 0x58; /* l' */
z80_header[20] = 0x27; /* h' */
z80_header[21] = 0x7e; /* a' */
z80_header[22] = 0x6d; /* f' */

z80_header[23] = 0x3a; /* iy - low byte */
z80_header[24] = 0x5c; /* iy - high byte */

z80_header[25] = 0x80; /* ix - low byte */
z80_header[26] = 0xdc; /* ix - high byte */

z80_header[27] = 1; /* interrupt on/off 0 = di, 1 = ei */
z80_header[28] = 0xff; /* iff2 */

z80_header[29] = 1; /* interrupt mode 1, all other
stuff 0 (for now)*/

fp = FileOpen (filename, "wb");
if (fp == NULL)
{
Debug (DEBUG_TRACE, "save_img.c : Z80_SaveImage(): ** unable to create
file");
return RETCODE_ERROR;
}

// output the standard sna header as per usual.

fwrite (&z80_header[0], Z80_OLD_HEADER_SIZE, 1, fp);

// output ram banks 5,2 and 0. 5 & 2 are fixed,
// 0 is assumed to be currently paged in.

fwrite (&ram_bank_array[5].data[0], RAM_BANK_SIZE, 1, fp);
fwrite (&ram_bank_array[2].data[0], RAM_BANK_SIZE, 1, fp);
fwrite (&ram_bank_array[0].data[0], RAM_BANK_SIZE, 1, fp);

CloseFile (fp);
return RETCODE_OK;
}

/* --------------------------------------------------------
Experimental .z80 snapshot format for 128K support
-------------------------------------------------------- */

int _z80_compress_block (char* block, int block_size, FILE* fp)
{
// if fp = NULL then the size of the compressed block
// is returned. if fp is anything else, the data is
// output to the file.

// direct file output is used rather than a buffer.
// yes I know its slow...but it works (hopefully).

// ..and this is a horrible function !!

int compressed_block_length;

int lp; // loop counter
unsigned char* block_ptr; // pointer to a byte in the block
int ch; // ch = first character in a sequence
int run_length; // length of a run of bytes.

block_ptr = block;
compressed_block_length = 0;
lp = block_size;
while (lp > 1) // last byte = special case :-)
{
ch = *block_ptr++; // get a byte to process.

// look ahead at next byte for 0xED,byte sequence.
// where (byte != ED)

if ((ch == 0xED) && (*block_ptr != 0xED))
{
// single ED followed by a byte. So output
// ED followed by a byte, then skip over
// the byte after ED as its been processed.

if (fp != NULL)
{
// **TO DO** output ED followed by a single byte
fputc (0xED, fp);
fputc (*block_ptr, fp);
}

compressed_block_length += 2;
block_ptr++;
lp -= 2;
}
else
{
// looking at one of the following:
// a run of ED's.
// a run of 5 or more bytes
// a run of less than 5 bytes

// note : its not clear in the format
// specification, so i've assumed that
// the maximum run length is 255.

run_length = 1;
while ((*block_ptr == ch) && (run_length < 255) && (lp > 0))
{
block_ptr++;
run_length++;
lp--;
}

if ((run_length >= 5) || (ch == 0xED))
{
// output the run.
if (fp != NULL)
{
fputc (0xED, fp);
fputc (0xED, fp);
fputc (run_length, fp);
fputc (ch, fp);
}

compressed_block_length += 4;
}
else
{
// not a compressed run..so just output the bytes

compressed_block_length += run_length;
while (run_length > 0)
{
if (fp != NULL)
{
fputc (ch, fp);
}
run_length--;
}
}
lp--; // extra -1 needed to account for ch
}
}

// last byte is treated as a special case as no matter
// what its value is..it just gets output.

if (lp == 1)
{
if (fp != NULL)
{
fputc (*block_ptr, fp);
}
compressed_block_length++;
}

return compressed_block_length;
}

int Z80_Save128KImage (char *filename, RAMBANK* ram_bank_array, int
program_org)
{
FILE *fp;
int sp;
int addr;

int lp;

int compressed_block_size;

unsigned char z80_header [Z80_HEADER_SIZE];

// sp = 65536-4; /* set the stack to be very high ... */

sp = 0xC000-4; /* set the stack to be very high (ish)... */

// sp = 0x5BF9; /* set the stack to be very high (ish)... */

z80_header[0] = 0xff; /* a */
z80_header[1] = 0x38; /* f */
z80_header[2] = 0x50; /* c */
z80_header[3] = 0x2a; /* b */
z80_header[4] = 0xcd; /* l */
z80_header[5] = 0x12; /* h */

z80_header[6] = 0; /* old program counter (must be 0) */
z80_header[7] = 0; /* old program counter (must be 0) */

z80_header[8] = sp & 255; /* stack pointer low byte*/
z80_header[9] = sp / 256; /* stack pointer high byte */

z80_header[10] = 0x00; /* I - interrupt register */
z80_header[11] = 0x19; /* R - refresh register */
z80_header[12] = 0; /* border colour * 2 (effectively)*/
z80_header[13] = 0x5c; /* e */
z80_header[14] = 0xec; /* d */
z80_header[15] = 0x01; /* c' */
z80_header[16] = 0x16; /* b' */
z80_header[17] = 0x68; /* e' */
z80_header[18] = 0x00; /* d' */
z80_header[19] = 0x38; /* l' */
z80_header[20] = 0x00; /* h' */
z80_header[21] = 0x00; /* a' */
z80_header[22] = 0x44; /* f' */

z80_header[23] = 0x3a; /* iy - low byte */
z80_header[24] = 0x5c; /* iy - high byte */

z80_header[25] = 0x6c; /* ix - low byte */
z80_header[26] = 0xfd; /* ix - high byte */

z80_header[27] = 1; /* interrupt on/off 0 = di, 1 = ei */
// z80_header[28] = 0xff; /* iff2 */
z80_header[28] = 0x00; /* iff2 */

z80_header[29] = 1; /* interrupt mode 1, all other
stuff 0 (for now)*/

// now add additional information specific to
// the 128k .z80 file (version format 2.01)

z80_header[30] = 23; /* length of additional header block */
z80_header[31] = 0; /* high byte of block length */

z80_header[32] = program_org & 255; /* program counter low byte*/
z80_header[33] = program_org / 256; /* program counter high byte */

z80_header[34] = 3; /* hardware mode 3 = 128K, no interface 1 */

// z80_header[35] = 0x07; /* 128K mode.. contains last out to 7FFD (ram bank
info) */
z80_header[35] = 0x00; /* 128K mode.. contains last out to 7FFD (ram bank
info) */

z80_header[36] = 0x00; /* interface 1 - not supported here */
z80_header[37] = 3; /* enable R and LDIR emulation */
z80_header[38] = 0x0e; /* last out to FFFD (soundchip register number) */

z80_header[39] = 0; /* contents of the sound chip registers */
z80_header[40] = 0; /* all set to zero for now */
z80_header[41] = 0;
z80_header[42] = 0;
z80_header[43] = 0;
z80_header[44] = 0;
z80_header[45] = 0;
z80_header[46] = 0;
z80_header[47] = 0;
z80_header[48] = 0;
z80_header[49] = 0;
z80_header[50] = 0;
z80_header[51] = 0;
z80_header[52] = 0;
z80_header[53] = 0;
z80_header[54] = 0;

fp = FileOpen (filename, "wb");
if (fp == NULL)
{
Debug (DEBUG_TRACE, "save_img.c : Z80_SaveImage(): ** unable to create
file");
return RETCODE_ERROR;
}

// output the standard sna header as per usual.

fwrite (&z80_header[0], Z80_HEADER_SIZE, 1, fp);

// output compresssed blocks in order

for (lp = 0; lp < 8; lp++)
{
DebugNumber (DEBUG_TRACE, "lp=", lp);

compressed_block_size = _z80_compress_block
(&ram_bank_array[lp].data[0],
RAM_BANK_SIZE, NULL);

DebugNumber (DEBUG_TRACE, "compressed block size", compressed_block_size);

z80_header[0] = compressed_block_size & 255;
z80_header[1] = compressed_block_size / 256;
z80_header[2] = lp + 3; // ram page number + 3

fwrite (&z80_header[0], 3, 1, fp);

compressed_block_size = _z80_compress_block
(&ram_bank_array[lp].data[0],
RAM_BANK_SIZE, fp);
}


// output ram banks 5,2 and 0. 5 & 2 are fixed,
// 0 is assumed to be currently paged in.

// fwrite (&ram_bank_array[5].data[0], RAM_BANK_SIZE, 1, fp);
// fwrite (&ram_bank_array[2].data[0], RAM_BANK_SIZE, 1, fp);
// fwrite (&ram_bank_array[0].data[0], RAM_BANK_SIZE, 1, fp);

CloseFile (fp);
return RETCODE_OK;
}

Philip Kendall

unread,
May 8, 2003, 1:55:02 PM5/8/03
to
In article <b9e582$gas$1...@newsg1.svr.pol.co.uk>,

Nick Fleming <Ni...@nospammbigfatpanda.fsnet.co.uk> wrote:
>
>Note in this version I've got some rudimentary
>code to generate .z80 files (much of it
>from the FAQ.. haven't time to work it
>all out myself.)

<plug type="don't reinvent the wheel">
http://www.srcf.ucam.org/~pak21/spectrum/libspectrum.html
</plug>

Nick Fleming

unread,
May 10, 2003, 1:24:50 PM5/10/03
to
> <plug type="don't reinvent the wheel">
> http://www.srcf.ucam.org/~pak21/spectrum/libspectrum.html
> </plug>


If I re-invent the wheel its only because
I like to use my own wrappers to the
standard 'C' library calls in places.. for my own
automatic housekeeping & debugging code.

.. I get less unexpected errors that way :-)

cheers.

Nick.

Robert J Baker

unread,
May 12, 2003, 5:38:56 PM5/12/03
to
"Nick Fleming" <Ni...@nospammbigfatpanda.fsnet.co.uk> wrote in message news:<b9jcq1$8nj$1...@newsg4.svr.pol.co.uk>...

How many expected errors do you tend to get? :)

Nick Fleming

unread,
May 14, 2003, 12:30:44 AM5/14/03
to
> > I like to use my own wrappers to the
> > standard 'C' library calls in places.. for my own
> > automatic housekeeping & debugging code.
> >
> > .. I get less unexpected errors that way :-)
>
> How many expected errors do you tend to get? :)
>

Statistcally.. about 2 per 100 lines of code .. or there abouts :-)

..usually typo's :-)


0 new messages