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?
>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
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.
ftp://ftp.nvg.ntnu.no/pub/sinclair/utils/pc/tasm301.zip
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.
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.
> 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 :)
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.
>
> 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!
> 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 :)
> > 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.
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.
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.
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.
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/
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.
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;
}
<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.
How many expected errors do you tend to get? :)
Statistcally.. about 2 per 100 lines of code .. or there abouts :-)
..usually typo's :-)