(Ñ) by Vladimir Kladov, 2003
EmuZWin 2.0-2.4 can work directly with only its own format, EZX. To allow reading or writing any other snap formats, tape and disk images, and to save screenshots in different graphic formats, plugins should be created using rules described in this document. At least plugins are provided for reading TAP, TZX, HoBeta (*.$?) and reading and writing Z80, SNA, SCL, TRD, and also to save screenshot as SCR, BMP or PNG image - in a distributive.
SNAPSHOT / TAPE IMAGE / DISK IMAGE PLUGINS
A plugin is a DLL, located in the same directory, where EmuZWin is installed. A name of DLL has no matter. It must have entry point RegisterLoadSpectrum, and one or more additional entries: LoadSpectrum, SaveSpectrum and ReleaseData.
TSaveFileProc = function( FilePath: PChar; Data: PSpecData ): Boolean; stdcall; FilePath - a path to a file which should be written or rewritten. Data - state of Spectrum machine.
TLoadFileProc = function( FileData: Pointer; FileSize: Integer; FileExt: PChar; Data: PSpecData; ScreenOnly: Boolean ): Boolean; stdcall; FileData - image of a file to be load. It is loaded by the main application to a memory, pointed by FileData; FileSize - size of FileData memory to load from; FileExt - filename extension in lower case with dot preceding it (e.g. '.z80'). Can be used by the plugin to determine file type; can be ignored if the plugin looks at signature or handles files of a single extension only; Data - data structure to be filled in with loaded data. Some fields have initial values, the most for a configuration of S48 (though Lock7FFD initially false, and if S48 loaded, Lock7FFD should be set by the plugin. ScreenOnly - True, if only screen needed. The plugin can load only screen area of length $1B00 bytes to an appropriate memory to optimize Open Dialog performance. TReleaseProc = procedure( Descriptor: DWORD ); stdcall; Descriptor - ReleaseDescriptor, returned by the LoadProc, if it allocates some memory to store additional data (tape image, key map, etc.); ReleaseProc must use it to free allocated memory.
TRegProc = procedure( LoadFileTypes: PChar; SaveFileTypes: PChar; Descriptions: PChar; CanSaveS128: PByte ); stdcall; LoadFileTypes - a buffer (1024 bytes) to fill it with a list of file types supported for load (file masks like *.ext should be listed in a list separated with semicolon, e.g.: '**.sna;*.zxs;.z80'); SaveFileTypes - a buffer (1024 bytes) to fill it with a list of file types supported for save (like LoadFileTypes above); Descriptions - a buffer (1024 bytes) to fill it with a list of file types descriptions separated with semicolon (';'). The first is a description of all the loaded file types if the plugin supports file loading (in such case, entry point LoadSpectrum must be present in the plugin DLL). The others are descriptions for each saved file type separately. Even if a description is the same for all cases, it is necessary to provide always a decription of file types supported for load and for each file type supported to save. Only in case when there are no file types supported for load, its description have to be omitted; CanSaveS128 - a buffer (1024 bytes, initially ones) to fill it with flags (1 byte for each saved file type); plugin should set correspondent byte to 0, if such file type is for save only S48 state. When save dialog is opened to save Spectrum128 state, such file types will not be listed. Also, this array is used as an array of flags indicating if correspondent file type can save/load TR-DOS disk image. If yes, a flag should be set to 2.
TSpecData structure used to pass Spectrum state between EmuZ and plugins, is the same as defined in EZX file format specification:
TSpecData = packed Record Signature : array[ 0..3 ] of Char; // 'EmuZ' ROM0, ROM1: TMemoryBank; // ROM0=S128 rom; ROM1=S48 rom * RAMs : array[ 0..7 ] of TMemoryBank; Lock7FFD : Boolean; // true, if S48K only State : TSpecState; // registers, etc. Sound : TSoundChipState; // sound chip AY state ReleaseDescriptor: DWORD; // returned by LoadProc, to be passed to // ReleaseProc to free memory Tape : TTapeData; // current tape reader, TR-DOS disks and other states end;
* Note: before calling LoadSpectrum entry, main program prefills entire structure with the current state of the emulated Spectrum machine. (At least, ROM1 and (may be) ROM0 is filled with current default ROM image.) Plugins, which just load tape or disk image, can leave memory untouched.
Substructures used are defined as follows:
TMemoryBank = array[ 0..16383 ] of Byte; TSpecState = packed Record AF, BC, DE, HL, IX, IY, AFalt, BCalt, DEalt, HLalt, PC, SP : Word; I, R, HiddenReg : Byte; IFF1, IFF2, IntSignal: Boolean; ImMode : Byte; TactsFromLastInt : DWord; BankROM_0000, // 0=rom S128, 1=rom S48 BankRAM_C000, // 0..7 BankVideo : Byte; // 0=RAM Bank 5, 1=RAM Bank 7 BorderColor : Byte; // 0..7 MIC : Boolean; JustAfterEI : Boolean; // Interrupt disable for a single instruction if TRUE Flash : Boolean; // TRUE if flushed FramesFromLastFlashSwitch: Byte; // 0..15 end; TSoundChipState = packed record LastFFFD : Byte; Regs : array[ 0..15 ] of Byte; end; TTapeData = packed record TapeImgData : Pointer; // a pointer to tape/disk and other chunks sequence, // see EZX format specification. TapeImgLen : Integer; // Length of tape image (can be set to 0). TapePosition: Integer; PulseLength: Integer; // current pulse length TactsLeast: Integer; // how many tacts current pulse should continue BitIndex: Integer; CurByte: Byte; Pilot : Integer; // Pilot length (not in "tones") Active: Boolean; end;
If tape image is loaded, LoadSpectrum procedure allocates a memory for it (storing a pointer to a start of tape in TapeImgData), and to release this memory, it also returns ReleaseDescriptor <> 0. Later main program calls entry point ReleaseDate with this ReleaseDescriptor as a parameter, and ReleaseData should use this parameter to determine which resources to free. Suggested way is passing a pointer to memory allocated, since it is a single memory block.
Tape image must be in internal EmuZ format, described in EZX format specification. It consists of chunks with first 4 bytes storing chunk name ('TAPE' for tape image, 'POKS' for pokes chunk, 'DISK' for TR-DOS disk image, etc.), and following 4 bytes store chunk data length (without these 8 bytes of chunk header). For 'TAPE' chunk, length of the chunk can be set to 0 by a plugin, in such case least of memory block passed through TapeImgData pointer assumed to be a tape image, and its size is calculated on base of TapeImgLen field value: if memory block contains only 'TAPE' chunk, this size is equal to (TapeImgLen - 8).
SCREENSHOT PLUGINS
Another kind of plugins used are specific to save screenshot operation (it is called in the emulator using F5). The difference is that another register procedure (RegisterSpectrumScreenshots) and another entry (SaveSpectrumScreen) to perform save screenshot operation should be used .
TRegScreenShotProc = procedure( FileTypes: PChar; Description: PChar ); stdcall;
FileTypes - a buffer (1024 bytes) to
fill it with a list of file types
supported. E.g.: *.bmp;*.gif;*.png;*.scr
This type of procedure is used to register another kind of plugins (usually separate
set from ones
purposed to load/save snapshots or/and disk/tape images). These plugins
are used to save screenshots only in different graphic formats. To do
a registration, all such plugins must export a procedure named
RegisterSaveScreenshots of the type TRegScreenShotProc.
All such plugins should also have exported entry SaveSpectrumScreen,
see its definition below.
TSaveScreenShotProc = procedure(
Filename: PChar; SpecScreen: Pointer;
Bmp: HBitmap; BmpScreenRect: PRect ); stdcall;
Filename - name of file to save
screenshot to.
SpecScreen - a buffer to Spectrum screen area, 6912 bytes (just a copy of
spectrum RAM at addresses 4000h-5AFFh, for video page 0).
Bmp - a handle of a bitmap image of the current screen,
including border. Can be strecthed or of 1:1 scale depending
on option selected in save dialog.
IMPORTANT: Plugin must release Bmp bitmap handle when it is
saved. The emulator no more cares of it at all.
BmpScreenRect - a pointer to a rectangle on the Bmp bitmap image which
defines screen only without a border on the Bmp.
My recommendation for writing screenshot plugins.
Please provide a separate
pluging for each graphic file type supported whenever possible. This
simplifies file type selection for the user while making a screenshot.
SAVE SOUND PLUGINS
One more kind of plugins become available in version 2.4: Save Sound Plugin. It is registered using specific entry RegisterSpectrumSaveSound (and may be RegisterSpectrumPlaySound), and returns in that call types of files or audio output destination unit name supported, allowing to the user to select a plugin to save audio or video in the specified format or to the specified device. Also a special modification for SaveSound plugin - PlaySound. Such one can be used to play sound generated in the emulated Spectrum. To do so, a user selects the plugin in the Configuration dialog, and all the sound is redirected to the plugin.
TRegSpectrumSaveSound = procedure( FileTypes: PChar; Description: PChar ); stdcall;
TRegSpectrumPlaySound = procedure( Description: PChar ); stdcall;
TRegSpectrumSaveVideo = procedure( FileTypes: PChar; Description: PChar ); stdcall;
FileTypes - a buffer (1024 bytes) to
fill it with a list of file types
supported for save (like SaveFileTypes in save snapshot plugins above);
Description - a buffer (1024 bytes) to fill it with a list of file
types
descriptions separated with semicolon (';'). The first is
a description of all the file types if the plugin
supports file saving. The others are descriptions
for each saved file type separately.
Even if a description is the same for all cases, it is necessary
to provide always a decription of file types supported.
TSpectrumStartSaveSound = function( Filepath: PChar ): Boolean; stdcall;
The function SpectrumStartSaveSound is called to start recording sound in a file specified. It should return true, if the operation can be started succefully. If false is returned, the emulator cancels recording.
TSpectrumSaveSound = procedure( const SaveSndRec: TSndRec ); stdcall;
The procedure SpectrumSaveSound is called to continue saving sound, when any sound-related event is occur: on write to AY register, on the end of frame, on the end of sound recording.
TSpectrumPlaySound = procedure( const PlaySndRec: TSndRec ); stdcall;
TSndRec = packed record Event: Byte; // type of the event: // bit 0 = 1 if write to AY register // bit 1 = 1 if end of frame occurs // bits 2 and 3: // 00 - 10 - // 01 - ACB 11 - mono // bit 7 = 1 if end of recording // bit 6 = 1 if cancel recording AYreg: Byte; // register to write AYval: Byte; // value written Mic: Byte; // 0 or not 0 - MIC out value (not used in PlaySound, only in SaveSound) PtrToSoundChipState: PSoundChipState; // Sound chip state after writing the value. FrameIndex: Integer; TactsFromStartOfFrame: Integer; TactsInFrame: Integer; // Following fields are added in version 2.5 Build 1.7 and these are provided // only on the end of the frame (when Event bit 1 is set). SoundFormat: TWaveFormatEx; // Describes data format in frame sound buffer below. FrameBufferLength: Integer; // Size of following frame buffer. PtrToFrameBuffer: Pointer; // Maximum buffer size can be calculated as 44100 * 4 / 50 = 3528 end;
PSoundChipState = ^TSoundChipState; TSoundChipState = packed record LastFFFD: Byte; Regs: array[ 0..15 ] of Byte; end;
(C) by Vladimir Kladov, 2003