diff options
author | Miodrag Milanovic <mmicko@gmail.com> | 2011-05-06 19:42:26 +0000 |
---|---|---|
committer | Miodrag Milanovic <mmicko@gmail.com> | 2011-05-06 19:42:26 +0000 |
commit | 62d841c2703286b34983eb8dae174ecbf2c82c5f (patch) | |
tree | d97a50172e47372637a155978f3096618da8c275 | |
parent | 987909ff31107893c4b748d86d97619db48c4b47 (diff) |
Moved format handling and all formats to separate library [Miodrag Milanovic]
146 files changed, 18541 insertions, 99 deletions
diff --git a/.gitattributes b/.gitattributes index 7b72d847c92..0eea66f7e81 100644 --- a/.gitattributes +++ b/.gitattributes @@ -675,14 +675,10 @@ src/emu/imagedev/cartslot.c svneol=native#text/plain src/emu/imagedev/cartslot.h svneol=native#text/plain src/emu/imagedev/cassette.c svneol=native#text/plain src/emu/imagedev/cassette.h svneol=native#text/plain -src/emu/imagedev/cassimg.c svneol=native#text/plain -src/emu/imagedev/cassimg.h svneol=native#text/plain src/emu/imagedev/chd_cd.c svneol=native#text/plain src/emu/imagedev/chd_cd.h svneol=native#text/plain src/emu/imagedev/flopdrv.c svneol=native#text/plain src/emu/imagedev/flopdrv.h svneol=native#text/plain -src/emu/imagedev/flopimg.c svneol=native#text/plain -src/emu/imagedev/flopimg.h svneol=native#text/plain src/emu/imagedev/harddriv.c svneol=native#text/plain src/emu/imagedev/harddriv.h svneol=native#text/plain src/emu/imagedev/multcart.c svneol=native#text/plain @@ -691,8 +687,6 @@ src/emu/imagedev/printer.c svneol=native#text/plain src/emu/imagedev/printer.h svneol=native#text/plain src/emu/imagedev/snapquik.c svneol=native#text/plain src/emu/imagedev/snapquik.h svneol=native#text/plain -src/emu/imagedev/wavfile.c svneol=native#text/plain -src/emu/imagedev/wavfile.h svneol=native#text/plain src/emu/info.c svneol=native#text/plain src/emu/info.h svneol=native#text/plain src/emu/inptport.c svneol=native#text/plain @@ -702,8 +696,6 @@ src/emu/input.c svneol=native#text/plain src/emu/input.h svneol=native#text/plain src/emu/inputseq.c svneol=native#text/plain src/emu/inputseq.h svneol=native#text/plain -src/emu/ioprocs.c svneol=native#text/plain -src/emu/ioprocs.h svneol=native#text/plain src/emu/layout/dualhovu.lay svneol=native#text/plain src/emu/layout/dualhsxs.lay svneol=native#text/plain src/emu/layout/dualhuov.lay svneol=native#text/plain @@ -1339,6 +1331,138 @@ src/lib/expat/xmltok.h svneol=native#text/plain src/lib/expat/xmltok_impl.c svneol=native#text/plain src/lib/expat/xmltok_impl.h svneol=native#text/plain src/lib/expat/xmltok_ns.c svneol=native#text/plain +src/lib/formats/a26_cas.c svneol=native#text/plain +src/lib/formats/a26_cas.h svneol=native#text/plain +src/lib/formats/ace_tap.c svneol=native#text/plain +src/lib/formats/ace_tap.h svneol=native#text/plain +src/lib/formats/ami_dsk.c svneol=native#text/plain +src/lib/formats/ami_dsk.h svneol=native#text/plain +src/lib/formats/ap2_dsk.c svneol=native#text/plain +src/lib/formats/ap2_dsk.h svneol=native#text/plain +src/lib/formats/ap_dsk35.c svneol=native#text/plain +src/lib/formats/ap_dsk35.h svneol=native#text/plain +src/lib/formats/apf_apt.c svneol=native#text/plain +src/lib/formats/apf_apt.h svneol=native#text/plain +src/lib/formats/apridisk.c svneol=native#text/plain +src/lib/formats/apridisk.h svneol=native#text/plain +src/lib/formats/atarist_dsk.c svneol=native#text/plain +src/lib/formats/atarist_dsk.h svneol=native#text/plain +src/lib/formats/atom_tap.c svneol=native#text/plain +src/lib/formats/atom_tap.h svneol=native#text/plain +src/lib/formats/basicdsk.c svneol=native#text/plain +src/lib/formats/basicdsk.h svneol=native#text/plain +src/lib/formats/cassimg.c svneol=native#text/plain +src/lib/formats/cassimg.h svneol=native#text/plain +src/lib/formats/cbm_tap.c svneol=native#text/plain +src/lib/formats/cbm_tap.h svneol=native#text/plain +src/lib/formats/cgen_cas.c svneol=native#text/plain +src/lib/formats/cgen_cas.h svneol=native#text/plain +src/lib/formats/coco_cas.c svneol=native#text/plain +src/lib/formats/coco_cas.h svneol=native#text/plain +src/lib/formats/coco_dsk.c svneol=native#text/plain +src/lib/formats/coco_dsk.h svneol=native#text/plain +src/lib/formats/comx35_dsk.c svneol=native#text/plain +src/lib/formats/comx35_dsk.h svneol=native#text/plain +src/lib/formats/coupedsk.c svneol=native#text/plain +src/lib/formats/coupedsk.h svneol=native#text/plain +src/lib/formats/cpis_dsk.c svneol=native#text/plain +src/lib/formats/cpis_dsk.h svneol=native#text/plain +src/lib/formats/cqm_dsk.c svneol=native#text/plain +src/lib/formats/csw_cas.c svneol=native#text/plain +src/lib/formats/csw_cas.h svneol=native#text/plain +src/lib/formats/d64_dsk.c svneol=native#text/plain +src/lib/formats/d64_dsk.h svneol=native#text/plain +src/lib/formats/d81_dsk.c svneol=native#text/plain +src/lib/formats/d81_dsk.h svneol=native#text/plain +src/lib/formats/d88_dsk.c svneol=native#text/plain +src/lib/formats/dim_dsk.c svneol=native#text/plain +src/lib/formats/dim_dsk.h svneol=native#text/plain +src/lib/formats/dsk_dsk.c svneol=native#text/plain +src/lib/formats/fdi_dsk.c svneol=native#text/plain +src/lib/formats/flopimg.c svneol=native#text/plain +src/lib/formats/flopimg.h svneol=native#text/plain +src/lib/formats/fm7_cas.c svneol=native#text/plain +src/lib/formats/fm7_cas.h svneol=native#text/plain +src/lib/formats/fmsx_cas.c svneol=native#text/plain +src/lib/formats/fmsx_cas.h svneol=native#text/plain +src/lib/formats/g64_dsk.c svneol=native#text/plain +src/lib/formats/g64_dsk.h svneol=native#text/plain +src/lib/formats/gtp_cas.c svneol=native#text/plain +src/lib/formats/gtp_cas.h svneol=native#text/plain +src/lib/formats/hect_tap.c svneol=native#text/plain +src/lib/formats/hect_tap.h svneol=native#text/plain +src/lib/formats/imageutl.c svneol=native#text/plain +src/lib/formats/imageutl.h svneol=native#text/plain +src/lib/formats/imd_dsk.c svneol=native#text/plain +src/lib/formats/ioprocs.c svneol=native#text/plain +src/lib/formats/ioprocs.h svneol=native#text/plain +src/lib/formats/kim1_cas.c svneol=native#text/plain +src/lib/formats/kim1_cas.h svneol=native#text/plain +src/lib/formats/lviv_lvt.c svneol=native#text/plain +src/lib/formats/lviv_lvt.h svneol=native#text/plain +src/lib/formats/msx_dsk.c svneol=native#text/plain +src/lib/formats/msx_dsk.h svneol=native#text/plain +src/lib/formats/mz_cas.c svneol=native#text/plain +src/lib/formats/mz_cas.h svneol=native#text/plain +src/lib/formats/nes_dsk.c svneol=native#text/plain +src/lib/formats/nes_dsk.h svneol=native#text/plain +src/lib/formats/orao_cas.c svneol=native#text/plain +src/lib/formats/orao_cas.h svneol=native#text/plain +src/lib/formats/oric_dsk.c svneol=native#text/plain +src/lib/formats/oric_dsk.h svneol=native#text/plain +src/lib/formats/oric_tap.c svneol=native#text/plain +src/lib/formats/oric_tap.h svneol=native#text/plain +src/lib/formats/p6001_cas.c svneol=native#text/plain +src/lib/formats/p6001_cas.h svneol=native#text/plain +src/lib/formats/pc_dsk.c svneol=native#text/plain +src/lib/formats/pc_dsk.h svneol=native#text/plain +src/lib/formats/pmd_pmd.c svneol=native#text/plain +src/lib/formats/pmd_pmd.h svneol=native#text/plain +src/lib/formats/primoptp.c svneol=native#text/plain +src/lib/formats/primoptp.h svneol=native#text/plain +src/lib/formats/rk_cas.c svneol=native#text/plain +src/lib/formats/rk_cas.h svneol=native#text/plain +src/lib/formats/smx_dsk.c svneol=native#text/plain +src/lib/formats/smx_dsk.h svneol=native#text/plain +src/lib/formats/sorc_dsk.c svneol=native#text/plain +src/lib/formats/sorc_dsk.h svneol=native#text/plain +src/lib/formats/sord_cas.c svneol=native#text/plain +src/lib/formats/sord_cas.h svneol=native#text/plain +src/lib/formats/svi_cas.c svneol=native#text/plain +src/lib/formats/svi_cas.h svneol=native#text/plain +src/lib/formats/svi_dsk.c svneol=native#text/plain +src/lib/formats/svi_dsk.h svneol=native#text/plain +src/lib/formats/td0_dsk.c svneol=native#text/plain +src/lib/formats/thom_cas.c svneol=native#text/plain +src/lib/formats/thom_cas.h svneol=native#text/plain +src/lib/formats/thom_dsk.c svneol=native#text/plain +src/lib/formats/thom_dsk.h svneol=native#text/plain +src/lib/formats/ti99_dsk.c svneol=native#text/plain +src/lib/formats/ti99_dsk.h svneol=native#text/plain +src/lib/formats/trd_dsk.c svneol=native#text/plain +src/lib/formats/trd_dsk.h svneol=native#text/plain +src/lib/formats/trs_cas.c svneol=native#text/plain +src/lib/formats/trs_cas.h svneol=native#text/plain +src/lib/formats/trs_dsk.c svneol=native#text/plain +src/lib/formats/trs_dsk.h svneol=native#text/plain +src/lib/formats/tzx_cas.c svneol=native#text/plain +src/lib/formats/tzx_cas.h svneol=native#text/plain +src/lib/formats/uef_cas.c svneol=native#text/plain +src/lib/formats/uef_cas.h svneol=native#text/plain +src/lib/formats/vg5k_cas.c svneol=native#text/plain +src/lib/formats/vg5k_cas.h svneol=native#text/plain +src/lib/formats/vt_cas.c svneol=native#text/plain +src/lib/formats/vt_cas.h svneol=native#text/plain +src/lib/formats/vt_dsk.c svneol=native#text/plain +src/lib/formats/vt_dsk.h svneol=native#text/plain +src/lib/formats/wavfile.c svneol=native#text/plain +src/lib/formats/wavfile.h svneol=native#text/plain +src/lib/formats/x1_tap.c svneol=native#text/plain +src/lib/formats/x1_tap.h svneol=native#text/plain +src/lib/formats/z80ne_dsk.c svneol=native#text/plain +src/lib/formats/z80ne_dsk.h svneol=native#text/plain +src/lib/formats/zx81_p.c svneol=native#text/plain +src/lib/formats/zx81_p.h svneol=native#text/plain src/lib/lib.mak svneol=native#text/plain src/lib/softfloat/README.txt svneol=native#text/plain src/lib/softfloat/mamesf.h svneol=native#text/plain @@ -1369,8 +1493,6 @@ src/lib/util/harddisk.c svneol=native#text/plain src/lib/util/harddisk.h svneol=native#text/plain src/lib/util/huffman.c svneol=native#text/plain src/lib/util/huffman.h svneol=native#text/plain -src/lib/util/imageutl.c svneol=native#text/plain -src/lib/util/imageutl.h svneol=native#text/plain src/lib/util/jedparse.c svneol=native#text/plain src/lib/util/jedparse.h svneol=native#text/plain src/lib/util/md5.c svneol=native#text/plain @@ -2738,22 +2860,6 @@ src/mame/drivers/zodiack.c svneol=native#text/plain src/mame/drivers/zr107.c svneol=native#text/plain src/mame/etc/fd1094dp.c svneol=native#text/plain src/mame/etc/jrcrypt.c svneol=native#text/plain -src/mame/formats/ami_dsk.c svneol=native#text/plain -src/mame/formats/ami_dsk.h svneol=native#text/plain -src/mame/formats/basicdsk.c svneol=native#text/plain -src/mame/formats/basicdsk.h svneol=native#text/plain -src/mame/formats/cqm_dsk.c svneol=native#text/plain -src/mame/formats/d88_dsk.c svneol=native#text/plain -src/mame/formats/dsk_dsk.c svneol=native#text/plain -src/mame/formats/fdi_dsk.c svneol=native#text/plain -src/mame/formats/fmsx_cas.c svneol=native#text/plain -src/mame/formats/fmsx_cas.h svneol=native#text/plain -src/mame/formats/imd_dsk.c svneol=native#text/plain -src/mame/formats/msx_dsk.c svneol=native#text/plain -src/mame/formats/msx_dsk.h svneol=native#text/plain -src/mame/formats/nes_dsk.c svneol=native#text/plain -src/mame/formats/nes_dsk.h svneol=native#text/plain -src/mame/formats/td0_dsk.c svneol=native#text/plain src/mame/imagedev/aescart.c svneol=native#text/plain src/mame/imagedev/aescart.h svneol=native#text/plain src/mame/includes/1942.h svneol=native#text/plain @@ -500,6 +500,7 @@ INCPATH += \ -I$(OBJ)/emu \ -I$(OBJ)/emu/layout \ -I$(SRC)/lib/util \ + -I$(SRC)/lib \ -I$(SRC)/osd \ -I$(SRC)/osd/$(OSD) \ @@ -611,6 +612,8 @@ endif # add SoftFloat floating point emulation library SOFTFLOAT = $(OBJ)/libsoftfloat.a +# add formats emulation library +FORMATS_LIB = $(OBJ)/libformats.a #------------------------------------------------- @@ -722,9 +725,9 @@ $(sort $(OBJDIRS)): ifndef EXECUTABLE_DEFINED # always recompile the version string -$(VERSIONOBJ): $(DRVLIBS) $(LIBOSD) $(LIBCPU) $(LIBEMU) $(LIBSOUND) $(LIBUTIL) $(EXPAT) $(ZLIB) $(SOFTFLOAT) $(LIBOCORE) $(RESFILE) +$(VERSIONOBJ): $(DRVLIBS) $(LIBOSD) $(LIBCPU) $(LIBEMU) $(LIBSOUND) $(LIBUTIL) $(EXPAT) $(ZLIB) $(SOFTFLOAT) $(FORMATS_LIB) $(LIBOCORE) $(RESFILE) -$(EMULATOR): $(VERSIONOBJ) $(DRIVLISTOBJ) $(DRVLIBS) $(LIBOSD) $(LIBCPU) $(LIBEMU) $(LIBDASM) $(LIBSOUND) $(LIBUTIL) $(EXPAT) $(SOFTFLOAT) $(ZLIB) $(LIBOCORE) $(RESFILE) +$(EMULATOR): $(VERSIONOBJ) $(DRIVLISTOBJ) $(DRVLIBS) $(LIBOSD) $(LIBCPU) $(LIBEMU) $(LIBDASM) $(LIBSOUND) $(LIBUTIL) $(EXPAT) $(SOFTFLOAT) $(FORMATS_LIB) $(ZLIB) $(LIBOCORE) $(RESFILE) @echo Linking $@... $(LD) $(LDFLAGS) $(LDFLAGSEMULATOR) $^ $(LIBS) -o $@ ifeq ($(TARGETOS),win32) diff --git a/src/emu/emu.mak b/src/emu/emu.mak index bb74919add9..2e794605003 100644 --- a/src/emu/emu.mak +++ b/src/emu/emu.mak @@ -81,7 +81,6 @@ EMUOBJS = \ $(EMUOBJ)/input.o \ $(EMUOBJ)/inputseq.o \ $(EMUOBJ)/inptport.o \ - $(EMUOBJ)/ioprocs.o \ $(EMUOBJ)/mame.o \ $(EMUOBJ)/machine.o \ $(EMUOBJ)/mconfig.o \ @@ -287,15 +286,12 @@ EMUIMAGEDEVOBJS = \ $(EMUIMAGEDEV)/bitbngr.o \ $(EMUIMAGEDEV)/cartslot.o \ $(EMUIMAGEDEV)/cassette.o \ - $(EMUIMAGEDEV)/cassimg.o \ $(EMUIMAGEDEV)/chd_cd.o \ $(EMUIMAGEDEV)/flopdrv.o \ - $(EMUIMAGEDEV)/flopimg.o \ $(EMUIMAGEDEV)/harddriv.o \ $(EMUIMAGEDEV)/multcart.o \ $(EMUIMAGEDEV)/printer.o \ $(EMUIMAGEDEV)/snapquik.o \ - $(EMUIMAGEDEV)/wavfile.o \ LIBEMUOBJS = $(EMUOBJS) $(EMUSOUNDOBJS) $(EMUAUDIOOBJS) $(EMUDRIVEROBJS) $(EMUMACHINEOBJS) $(EMUIMAGEDEVOBJS) $(EMUVIDEOOBJS) diff --git a/src/emu/image.c b/src/emu/image.c index d4fef313e2c..6dd8e70a926 100644 --- a/src/emu/image.c +++ b/src/emu/image.c @@ -15,6 +15,7 @@ #include "image.h" #include "config.h" #include "xmlfile.h" +#include "formats/ioprocs.h" /* ----------------------------------------------------------------------- */ diff --git a/src/emu/image.h b/src/emu/image.h index 659a43dacf6..f1c963ffd3c 100644 --- a/src/emu/image.h +++ b/src/emu/image.h @@ -18,8 +18,6 @@ #ifndef __IMAGE_H__ #define __IMAGE_H__ -#include "ioprocs.h" - void image_init(running_machine &machine); void image_postdevice_init(running_machine &machine); diff --git a/src/emu/imagedev/cassette.c b/src/emu/imagedev/cassette.c index 792d3463ff0..313a3b2c4a8 100644 --- a/src/emu/imagedev/cassette.c +++ b/src/emu/imagedev/cassette.c @@ -7,9 +7,8 @@ *********************************************************************/ #include "emu.h" -#include "imageutl.h" +#include "formats/imageutl.h" #include "cassette.h" -#include "cassimg.h" #include "ui.h" diff --git a/src/emu/imagedev/cassette.h b/src/emu/imagedev/cassette.h index ad07955ba15..f742a00c13b 100644 --- a/src/emu/imagedev/cassette.h +++ b/src/emu/imagedev/cassette.h @@ -10,7 +10,7 @@ #define CASSETTE_H #include "image.h" -#include "cassimg.h" +#include "formats/cassimg.h" enum _cassette_state diff --git a/src/emu/imagedev/flopdrv.c b/src/emu/imagedev/flopdrv.c index d0715a377c5..e920c7bdd7c 100644 --- a/src/emu/imagedev/flopdrv.c +++ b/src/emu/imagedev/flopdrv.c @@ -12,7 +12,7 @@ */ #include "emu.h" -#include "imageutl.h" +#include "formats/imageutl.h" #include "flopdrv.h" #define VERBOSE 0 diff --git a/src/emu/imagedev/flopdrv.h b/src/emu/imagedev/flopdrv.h index 4a8303e57c6..a4c940fd230 100644 --- a/src/emu/imagedev/flopdrv.h +++ b/src/emu/imagedev/flopdrv.h @@ -6,7 +6,7 @@ #include "devcb.h" #include "image.h" -#include "flopimg.h" +#include "formats/flopimg.h" #define FLOPPY_TYPE_REGULAR 0 #define FLOPPY_TYPE_APPLE 1 diff --git a/src/emu/machine/wd17xx.c b/src/emu/machine/wd17xx.c index 2b1c32b8c75..789daf25cfc 100644 --- a/src/emu/machine/wd17xx.c +++ b/src/emu/machine/wd17xx.c @@ -145,10 +145,9 @@ #include "emu.h" -#include "imageutl.h" +#include "formats/imageutl.h" #include "machine/wd17xx.h" #include "imagedev/flopdrv.h" -#include "imagedev/flopimg.h" /*************************************************************************** CONSTANTS diff --git a/src/lib/formats/a26_cas.c b/src/lib/formats/a26_cas.c new file mode 100644 index 00000000000..feceba102d9 --- /dev/null +++ b/src/lib/formats/a26_cas.c @@ -0,0 +1,166 @@ +/* + +Atari 2600 SuperCharger support + +*/ + + +#include "formats/a26_cas.h" + + +#define A26_CAS_SIZE 8448 +#define A26_WAV_FREQUENCY 44100 +#define BIT_ZERO_LENGTH 10 +#define BIT_ONE_LENGTH 15 +#define ZEROS_ONES 2755 + +static const UINT16 one_wave[BIT_ONE_LENGTH] = { + 0x2AE5, 0x4E60, 0x644E, 0x68E4, 0x5B56, 0x3DFE, 0x15ED, 0xEA13, 0xC202, 0xA4AA, + 0x971C, 0x9BB2, 0xB1A0, 0xD51B, 0x0000 +}; + +static const UINT16 zero_wave[BIT_ZERO_LENGTH] = { + 0x3DFE, 0x644E, 0x644E, 0x3DFE, 0x0000, 0xC202, 0x9BB2, 0x9BB2, 0xC202, 0x0000 +}; + +static int a26_cas_output_wave( INT16 **buffer, INT16 wave_data, int length ) { + int i; + + if ( buffer ) { + for ( i = 0 ; i < length; i++ ) { + **buffer = wave_data; + *buffer = *buffer + 1; + } + } + return length; +} + +static int a26_cas_output_bit( INT16 **buffer, int bit ) { + int size = 0; + int bit_size = bit ? BIT_ONE_LENGTH : BIT_ZERO_LENGTH; + const INT16 *p = bit ? (const INT16 *)one_wave : (const INT16 *)zero_wave; + int i; + + for ( i = 0; i < bit_size; i++ ) { + size += a26_cas_output_wave( buffer, p[i], 1 ); + } + + return size; +} + +static int a26_cas_output_byte( INT16 **buffer, UINT8 byte ) { + int size = 0; + int i; + + for( i = 0; i < 8; i++, byte <<= 1 ) { + size += a26_cas_output_bit( buffer, ( ( byte & 0x80 ) ? 1 : 0 ) ); + } + return size; +} + +static int a26_cas_clearing_tone( INT16 **buffer ) { + int size = 0; + + size += a26_cas_output_wave( buffer, 0, 44100 ); + return size; +} + +static int a26_cas_zeros_ones( INT16 **buffer ) { + int size = 0; + int i; + + for ( i = 0; i < ZEROS_ONES; i++ ) { + size += a26_cas_output_bit( buffer, 0 ); + size += a26_cas_output_bit( buffer, 1 ); + } + size += a26_cas_output_bit( buffer, 0 ); + size += a26_cas_output_bit( buffer, 0 ); + return size; +} + +static int a26_cas_output_contents( INT16 **buffer, const UINT8 *bytes ) { + int size = 0; + int i, pages, j; + + /* There are 8 header bytes */ + for( i = 0; i < 8; i++ ) { + size += a26_cas_output_byte( buffer, bytes[ 0x2000 + i ] ); + } + + pages = bytes[0x2003]; + + /* Output each page prefixed with a small page header */ + for ( i = 0; i < pages; i++ ) { + size += a26_cas_output_byte( buffer, bytes[ 0x2010 + i ] ); + size += a26_cas_output_byte( buffer, bytes[ 0x2040 + i ] ); + for ( j = 0; j < 256; j++ ) { + size += a26_cas_output_byte( buffer, bytes[ i * 256 + j ] ); + } + } + + return size; +} + +static int a26_cas_do_work( INT16 **buffer, const UINT8 *bytes ) { + int size = 0; + + /* Output clearing tone */ + size += a26_cas_clearing_tone( buffer ); + + /* Output header tone, alternating 1s and 0s for about a second ending with two 0s */ + size += a26_cas_zeros_ones( buffer ); + + /* Output the actual contents of the tape */ + size += a26_cas_output_contents( buffer, bytes ); + + /* Output footer tone, alternating 1s and 0s for about a second ending with two 0s */ + size += a26_cas_zeros_ones( buffer ); + + return size; +} + +static int a26_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) { + INT16 *p = buffer; + + return a26_cas_do_work( &p, (const UINT8 *)bytes ); +} + +static int a26_cas_to_wav_size( const UINT8 *casdata, int caslen ) { + return a26_cas_do_work( NULL, casdata ); +} + +static const struct CassetteLegacyWaveFiller a26_legacy_fill_wave = { + a26_cas_fill_wave, + -1, + 0, + a26_cas_to_wav_size, + A26_WAV_FREQUENCY, + 0, + 0 +}; + +static casserr_t a26_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) { + UINT64 size; + + size = cassette_image_size( cassette ); + if ( size == A26_CAS_SIZE ) { + return cassette_legacy_identify( cassette, opts, &a26_legacy_fill_wave ); + } + return CASSETTE_ERROR_INVALIDIMAGE; +} + +static casserr_t a26_cassette_load( cassette_image *cassette ) { + return cassette_legacy_construct( cassette, &a26_legacy_fill_wave ); +} + +static const struct CassetteFormat a26_cassette_format = { + "a26", + a26_cassette_identify, + a26_cassette_load, + NULL +}; + +CASSETTE_FORMATLIST_START(a26_cassette_formats) + CASSETTE_FORMAT(a26_cassette_format) +CASSETTE_FORMATLIST_END + diff --git a/src/lib/formats/a26_cas.h b/src/lib/formats/a26_cas.h new file mode 100644 index 00000000000..3d5e74bfd6d --- /dev/null +++ b/src/lib/formats/a26_cas.h @@ -0,0 +1,14 @@ +/* + a26_cas.h + + Format code for a2600 SuperCharger cassette files +*/ + +#ifndef A26_CAS_H +#define A26_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(a26_cassette_formats); + +#endif /* A26_CAS_H */ diff --git a/src/lib/formats/ace_tap.c b/src/lib/formats/ace_tap.c new file mode 100644 index 00000000000..3cabe2e4ec4 --- /dev/null +++ b/src/lib/formats/ace_tap.c @@ -0,0 +1,188 @@ +/******************************************************************** + +Support for Jupiter Ace .tap cassette images + +For more information see: +- http://www.jupiter-ace.co.uk/faq_ace_tap_format.html +- http://www.jupiter-ace.co.uk/doc_AceTapeFormat.html + +********************************************************************/ +#include "ace_tap.h" + + +#define SMPLO -32768 +#define SILENCE 0 +#define SMPHI 32767 + + +static int cas_size; + + +/******************************************************************* + Generate one high-low cycle of sample data +********************************************************************/ +INLINE int ace_tap_cycle(INT16 *buffer, int sample_pos, int high, int low) +{ + int i = 0; + + if ( buffer ) + { + while( i < high) + { + buffer[ sample_pos + i ] = SMPHI; + i++; + } + + while( i < high + low ) + { + buffer[ sample_pos + i ] = SMPLO; + i++; + } + } + return high + low; +} + + +INLINE int ace_tap_silence(INT16 *buffer, int sample_pos, int samples) +{ + int i = 0; + + if ( buffer ) + { + while( i < samples ) + { + buffer[ sample_pos + i ] = SILENCE; + i++; + } + } + return samples; +} + + +INLINE int ace_tap_byte(INT16 *buffer, int sample_pos, UINT8 data) +{ + int i, samples; + + samples = 0; + for ( i = 0; i < 8; i++ ) + { + if ( data & 0x80 ) + samples += ace_tap_cycle( buffer, sample_pos + samples, 21, 22 ); + else + samples += ace_tap_cycle( buffer, sample_pos + samples, 10, 11 ); + + data <<= 1; + } + return samples; +} + + +static int ace_handle_tap(INT16 *buffer, const UINT8 *casdata) +{ + int data_pos, sample_count; + + /* Make sure the file starts with a valid header */ + if ( cas_size < 0x1C ) + return -1; + if ( casdata[0] != 0x1A || casdata[1] != 0x00 ) + return -1; + + data_pos = 0; + sample_count = 0; + + while( data_pos < cas_size ) + { + UINT16 block_size; + int i; + + /* Handle a block of tape data */ + block_size = casdata[data_pos] + ( casdata[data_pos + 1] << 8 ); + data_pos += 2; + + /* Make sure there are enough bytes left */ + if ( data_pos > cas_size ) + return -1; + + /* 2 seconds silence */ + sample_count += ace_tap_silence( buffer, sample_count, 2 * 44100 ); + + /* Add pilot tone samples: 4096 for header, 512 for data */ + for( i = ( block_size == 0x001A ) ? 4096 : 512; i; i-- ) + sample_count += ace_tap_cycle( buffer, sample_count, 27, 27 ); + + /* Sync samples */ + sample_count += ace_tap_cycle( buffer, sample_count, 8, 11 ); + + /* Output block type identification byte */ + sample_count += ace_tap_byte( buffer, sample_count, ( block_size != 0x001A ) ? 0xFF : 0x00 ); + + /* Data samples */ + for ( ; block_size ; data_pos++, block_size-- ) + sample_count += ace_tap_byte( buffer, sample_count, casdata[data_pos] ); + + /* End mark samples */ + sample_count += ace_tap_cycle( buffer, sample_count, 12, 57 ); + + /* 3 seconds silence */ + sample_count += ace_tap_silence( buffer, sample_count, 3 * 44100 ); + } + return sample_count; +} + + +/******************************************************************* + Generate samples for the tape image +********************************************************************/ +static int ace_tap_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + return ace_handle_tap( buffer, bytes ); +} + + +/******************************************************************* + Calculate the number of samples needed for this tape image +********************************************************************/ +static int ace_tap_to_wav_size(const UINT8 *casdata, int caslen) +{ + cas_size = caslen; + + return ace_handle_tap( NULL, casdata ); +} + + +static const struct CassetteLegacyWaveFiller ace_legacy_fill_wave = +{ + ace_tap_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + ace_tap_to_wav_size, /* chunk_sample_calc */ + 44100, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + +static casserr_t ace_tap_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &ace_legacy_fill_wave); +} + + +static casserr_t ace_tap_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &ace_legacy_fill_wave); +} + + +static const struct CassetteFormat ace_tap_format = +{ + "tap", + ace_tap_identify, + ace_tap_load, + NULL +}; + + +CASSETTE_FORMATLIST_START(ace_cassette_formats) + CASSETTE_FORMAT(ace_tap_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/ace_tap.h b/src/lib/formats/ace_tap.h new file mode 100644 index 00000000000..8d180a0255c --- /dev/null +++ b/src/lib/formats/ace_tap.h @@ -0,0 +1,29 @@ +/********************************************************************* + + ace_tap.h + + Format code for Jupiter Ace casette files + +*********************************************************************/ + +#ifndef ACE_TAP_H +#define ACE_TAP_H + +#include "cassimg.h" + +typedef struct +{ + UINT8 hdr_type; + UINT8 hdr_name[10]; + UINT16 hdr_len; + UINT16 hdr_addr; + UINT8 hdr_vars[8]; + UINT8 hdr_3c4c; + UINT8 hdr_3c4d; + UINT16 dat_len; +} ace_tape_t; + +CASSETTE_FORMATLIST_EXTERN(ace_cassette_formats); + +#endif /* ACE_TAP_H */ + diff --git a/src/mame/formats/ami_dsk.c b/src/lib/formats/ami_dsk.c index a9dc98af7b4..a9dc98af7b4 100644 --- a/src/mame/formats/ami_dsk.c +++ b/src/lib/formats/ami_dsk.c diff --git a/src/mame/formats/ami_dsk.h b/src/lib/formats/ami_dsk.h index a2ed187744b..ef88560cd1f 100644 --- a/src/mame/formats/ami_dsk.h +++ b/src/lib/formats/ami_dsk.h @@ -9,7 +9,7 @@ #ifndef AMI_DSK_H_ #define AMI_DSK_H_ -#include "imagedev/flopimg.h" +#include "flopimg.h" FLOPPY_OPTIONS_EXTERN(amiga); FLOPPY_OPTIONS_EXTERN(amiga_only); diff --git a/src/lib/formats/ap2_dsk.c b/src/lib/formats/ap2_dsk.c new file mode 100644 index 00000000000..3964a040806 --- /dev/null +++ b/src/lib/formats/ap2_dsk.c @@ -0,0 +1,511 @@ +/********************************************************************* + + ap2_dsk.c + + Apple II disk images + +*********************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include "ap2_dsk.h" +#include "basicdsk.h" + + +#define APPLE2_IMAGE_DO 0 +#define APPLE2_IMAGE_PO 1 +#define APPLE2_IMAGE_NIB 2 + + +/* used in for all Apple II images */ +static UINT32 apple2_get_track_size(floppy_image *floppy, int head, int track); +static int disk_decode_nib(UINT8 *data, const UINT8 *nibble, int *volume, int *track, int *sector); +static void disk_encode_nib(UINT8 *nibble, const UINT8 *data, int volume, int track, int sector); + +/* used in DOS/ProDOS order images */ +static int apple2_do_translate_sector(floppy_image *floppy, int sector); +static int apple2_po_translate_sector(floppy_image *floppy, int sector); +static floperr_t apple2_dsk_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen); +static floperr_t apple2_dsk_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen); + +/* used in nibble order images */ +static floperr_t apple2_nib_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen); +static floperr_t apple2_nib_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen); +static floperr_t apple2_nib_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen); +static floperr_t apple2_nib_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam); +static floperr_t apple2_nib_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length); + + +static const UINT8 translate6[0x40] = +{ + 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, + 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, + 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, + 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + + + +/* ----------------------------------------------------------------------- + * Utility code + * ----------------------------------------------------------------------- */ + +static const UINT8 *get_untranslate6_map(void) +{ + static UINT8 map[256]; + static int map_inited = 0; + UINT8 i; + + if (!map_inited) + { + memset(map, 0xff, sizeof(map)); + for (i = 0; i < ARRAY_LENGTH(translate6); i++) + map[translate6[i]] = i; + map_inited = 1; + } + return map; +} + + + +/* ----------------------------------------------------------------------- + * Core constructor + * ----------------------------------------------------------------------- */ + + +static floperr_t apple2_general_construct(floppy_image *floppy, int floppy_type) +{ + floperr_t err; + struct basicdsk_geometry geometry; + struct FloppyCallbacks *format; + + format = floppy_callbacks(floppy); + + switch(floppy_type) { + case APPLE2_IMAGE_DO: + case APPLE2_IMAGE_PO: + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = 1; + geometry.tracks = APPLE2_TRACK_COUNT; + geometry.sectors = APPLE2_SECTOR_COUNT; + geometry.sector_length = APPLE2_SECTOR_SIZE; + geometry.translate_sector = (floppy_type == APPLE2_IMAGE_DO) ? apple2_do_translate_sector : apple2_po_translate_sector; + + err = basicdsk_construct(floppy, &geometry); + if (err) + return err; + + format->read_track = apple2_dsk_read_track; + format->write_track = apple2_dsk_write_track; + break; + + case APPLE2_IMAGE_NIB: + format->read_track = apple2_nib_read_track; + format->write_track = apple2_nib_write_track; + format->read_sector = apple2_nib_read_sector; + format->write_sector = apple2_nib_write_sector; + format->get_sector_length = apple2_nib_get_sector_length; + break; + + default: + assert(0); + return FLOPPY_ERROR_INTERNAL; + } + + format->get_track_size = apple2_get_track_size; + + return FLOPPY_ERROR_SUCCESS; +} + + + +/* ----------------------------------------------------------------------- + * DOS order and ProDOS order code + * ----------------------------------------------------------------------- */ + +static FLOPPY_IDENTIFY(apple2_dsk_identify) +{ + UINT64 size; + UINT64 expected_size; + + size = floppy_image_size(floppy); + expected_size = APPLE2_TRACK_COUNT * APPLE2_SECTOR_COUNT * APPLE2_SECTOR_SIZE; + + if (size == expected_size) + *vote = 100; + else if (size - expected_size < 8 && size - expected_size > -8) + *vote = 90; /* tolerate images with up to eight fewer/extra bytes (bug #638) */ + else + *vote = 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +static int apple2_do_translate_sector(floppy_image *floppy, int sector) +{ + static const UINT8 skewing[] = + { + /* DOS order (*.do) */ + 0x00, 0x07, 0x0E, 0x06, 0x0D, 0x05, 0x0C, 0x04, + 0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x0F + }; + return skewing[sector]; +} + + + +static int apple2_po_translate_sector(floppy_image *floppy, int sector) +{ + static const UINT8 skewing[] = + { + /* ProDOS order (*.po) */ + 0x00, 0x08, 0x01, 0x09, 0x02, 0x0A, 0x03, 0x0B, + 0x04, 0x0C, 0x05, 0x0D, 0x06, 0x0E, 0x07, 0x0F + }; + return skewing[sector]; +} + + + +static FLOPPY_CONSTRUCT(apple2_do_construct) +{ + return apple2_general_construct(floppy, APPLE2_IMAGE_DO); +} + + + +static FLOPPY_CONSTRUCT(apple2_po_construct) +{ + return apple2_general_construct(floppy, APPLE2_IMAGE_PO); +} + + + +static floperr_t apple2_dsk_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) +{ + UINT8 sector_buffer[APPLE2_SECTOR_SIZE]; + int sector; + UINT8 *nibble; + + if (buflen < APPLE2_NIBBLE_SIZE*APPLE2_SECTOR_COUNT) + return FLOPPY_ERROR_INTERNAL; + if (offset != 0) + return FLOPPY_ERROR_UNSUPPORTED; + + memset(buffer, 0, buflen); + + for (sector = 0; sector < APPLE2_SECTOR_COUNT; sector++) + { + nibble = (UINT8 *)buffer; + nibble += sector * APPLE2_SMALL_NIBBLE_SIZE; + + floppy_read_sector(floppy, head, track, sector, 0, sector_buffer, sizeof(sector_buffer)); + disk_encode_nib(nibble, sector_buffer, 254, track, sector); + } + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t apple2_dsk_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen) +{ + int sector; + UINT8 sector_buffer[APPLE2_SECTOR_SIZE]; + const UINT8 *nibble; + + if (offset != 0) + return FLOPPY_ERROR_UNSUPPORTED; + + for (sector = 0; sector < APPLE2_SECTOR_COUNT; sector++) + { + nibble = (UINT8 *)buffer; + nibble += sector * APPLE2_SMALL_NIBBLE_SIZE; + + disk_decode_nib(sector_buffer, nibble, NULL, NULL, NULL); + floppy_write_sector(floppy, head, track, sector, 0, sector_buffer, sizeof(sector_buffer), 0); + } + + return FLOPPY_ERROR_SUCCESS; +} + + + +/* ----------------------------------------------------------------------- + * Nibble order code + * ----------------------------------------------------------------------- */ + +static FLOPPY_IDENTIFY(apple2_nib_identify) +{ + UINT64 size; + size = floppy_image_size(floppy); + *vote = (size == APPLE2_TRACK_COUNT * APPLE2_SECTOR_COUNT * APPLE2_NIBBLE_SIZE) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_CONSTRUCT(apple2_nib_construct) +{ + return apple2_general_construct(floppy, APPLE2_IMAGE_NIB); +} + + + +static floperr_t apple2_nib_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) +{ + if ((head != 0) || (track < 0) || (track >= APPLE2_TRACK_COUNT)) + return FLOPPY_ERROR_SEEKERROR; + if (offset != 0) + return FLOPPY_ERROR_UNSUPPORTED; + floppy_image_read(floppy, buffer, track * APPLE2_NIBBLE_SIZE * APPLE2_SECTOR_COUNT, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t apple2_nib_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen) +{ + if ((head != 0) || (track < 0) || (track >= APPLE2_TRACK_COUNT)) + return FLOPPY_ERROR_SEEKERROR; + if (offset != 0) + return FLOPPY_ERROR_UNSUPPORTED; + floppy_image_write(floppy, buffer, track * APPLE2_NIBBLE_SIZE * APPLE2_SECTOR_COUNT, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +/* ----------------------------------------------------------------------- + * Track conversion + * ----------------------------------------------------------------------- */ + +static int decode_nibbyte(UINT8 *nibint, const UINT8 *nibdata) +{ + if ((nibdata[0] & 0xAA) != 0xAA) + return 1; + if ((nibdata[1] & 0xAA) != 0xAA) + return 1; + + *nibint = (nibdata[0] & ~0xAA) << 1; + *nibint |= (nibdata[1] & ~0xAA) << 0; + return 0; +} + + + +static int disk_decode_nib(UINT8 *data, const UINT8 *nibble, int *volume, int *track, int *sector) +{ + UINT8 read_volume; + UINT8 read_track; + UINT8 read_sector; + UINT8 read_checksum; + int i; + UINT8 b, xorvalue, newvalue; + + const UINT8 *untranslate6 = get_untranslate6_map(); + + /* pick apart the volume/track/sector info and checksum */ + if (decode_nibbyte(&read_volume, &nibble[10])) + return 1; + if (decode_nibbyte(&read_track, &nibble[12])) + return 1; + if (decode_nibbyte(&read_sector, &nibble[14])) + return 1; + if (decode_nibbyte(&read_checksum, &nibble[16])) + return 1; + if (read_checksum != (read_volume ^ read_track ^ read_sector)) + return 1; + + /* decode the nibble core */ + xorvalue = 0; + for (i = 0; i < 342; i++) + { + b = untranslate6[nibble[i+28]]; + if (b == 0xff) + return 1; + newvalue = b ^ xorvalue; + + if (i >= 0x56) + { + /* 6 bit */ + data[i - 0x56] |= (newvalue << 2); + } + else + { + /* 3 * 2 bit */ + data[i + 0x00] = ((newvalue >> 1) & 0x01) | ((newvalue << 1) & 0x02); + data[i + 0x56] = ((newvalue >> 3) & 0x01) | ((newvalue >> 1) & 0x02); + if (i + 0xAC < APPLE2_SECTOR_SIZE) + data[i + 0xAC] = ((newvalue >> 5) & 0x01) | ((newvalue >> 3) & 0x02); + } + xorvalue = newvalue; + } + + /* success; write out values if pointers not null */ + if (volume) + *volume = read_volume; + if (track) + *track = read_track; + if (sector) + *sector = read_sector; + return 0; +} + + + +static floperr_t apple2_nib_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + floperr_t err; + const UINT8 *nibble; + UINT8 *track_data; + void *track_data_v; + + if ((sector < 0) || (sector >= APPLE2_SECTOR_COUNT)) + return FLOPPY_ERROR_SEEKERROR; + if (buflen != APPLE2_SECTOR_SIZE) + return FLOPPY_ERROR_INTERNAL; + + err = floppy_load_track(floppy, head, track, FALSE, &track_data_v, NULL); + if (err) + return err; + track_data = (UINT8 *) track_data_v; + + nibble = track_data + (sector * APPLE2_NIBBLE_SIZE); + + if (disk_decode_nib((UINT8 *)buffer, nibble, NULL, NULL, NULL)) + return FLOPPY_ERROR_INVALIDIMAGE; + + return FLOPPY_ERROR_SUCCESS; +} + + + +static void disk_encode_nib(UINT8 *nibble, const UINT8 *data, int volume, int track, int sector) +{ + int checksum, oldvalue, xorvalue, i; + + /* setup header values */ + checksum = volume ^ track ^ sector; + + memset(nibble, 0xFF, APPLE2_NIBBLE_SIZE); + nibble[ 7] = 0xD5; + nibble[ 8] = 0xAA; + nibble[ 9] = 0x96; + nibble[10] = (volume >> 1) | 0xAA; + nibble[11] = volume | 0xAA; + nibble[12] = (track >> 1) | 0xAA; + nibble[13] = track | 0xAA; + nibble[14] = (sector >> 1) | 0xAA; + nibble[15] = sector | 0xAA; + nibble[16] = (checksum >> 1) | 0xAA; + nibble[17] = (checksum) | 0xAA; + nibble[18] = 0xDE; + nibble[19] = 0xAA; + nibble[20] = 0xEB; + nibble[25] = 0xD5; + nibble[26] = 0xAA; + nibble[27] = 0xAD; + nibble[27+344] = 0xDE; + nibble[27+345] = 0xAA; + nibble[27+346] = 0xEB; + xorvalue = 0; + + for (i = 0; i < 342; i++) + { + if (i >= 0x56) + { + /* 6 bit */ + oldvalue = data[i - 0x56]; + oldvalue = oldvalue >> 2; + } + else + { + /* 3 * 2 bit */ + oldvalue = 0; + oldvalue |= (data[i + 0x00] & 0x01) << 1; + oldvalue |= (data[i + 0x00] & 0x02) >> 1; + oldvalue |= (data[i + 0x56] & 0x01) << 3; + oldvalue |= (data[i + 0x56] & 0x02) << 1; + if (i + 0xAC < APPLE2_SECTOR_SIZE) + { + oldvalue |= (data[i + 0xAC] & 0x01) << 5; + oldvalue |= (data[i + 0xAC] & 0x02) << 3; + } + + } + xorvalue ^= oldvalue; + nibble[28+i] = translate6[xorvalue & 0x3F]; + xorvalue = oldvalue; + } + + nibble[27+343] = translate6[xorvalue & 0x3F]; +} + + + +static floperr_t apple2_nib_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + floperr_t err; + UINT8 *track_data; + void *track_data_v; + + if ((sector < 0) || (sector >= APPLE2_SECTOR_COUNT)) + return FLOPPY_ERROR_SEEKERROR; + if (buflen != APPLE2_SECTOR_SIZE) + return FLOPPY_ERROR_INTERNAL; + + err = floppy_load_track(floppy, head, track, TRUE, &track_data_v, NULL); + if (err) + return err; + track_data = (UINT8 *) track_data_v; + + disk_encode_nib(track_data + sector * APPLE2_NIBBLE_SIZE, (const UINT8 *)buffer, 254, track, sector); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t apple2_nib_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) +{ + *sector_length = APPLE2_SECTOR_SIZE; + return FLOPPY_ERROR_SUCCESS; +} + + + +static UINT32 apple2_get_track_size(floppy_image *floppy, int head, int track) +{ + return APPLE2_NIBBLE_SIZE * APPLE2_SECTOR_COUNT; +} + + + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( apple2 ) + FLOPPY_OPTION( apple2_do, "do,dsk,bin", "Apple ][ DOS order disk image", apple2_dsk_identify, apple2_do_construct, NULL, + HEADS([1]) + TRACKS([35]) + SECTORS([16]) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID([0])) + FLOPPY_OPTION( apple2_po, "po,dsk,bin", "Apple ][ ProDOS order disk image", apple2_dsk_identify, apple2_po_construct, NULL, + HEADS([1]) + TRACKS([35]) + SECTORS([16]) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID([0])) + FLOPPY_OPTION( apple2_nib, "dsk,nib", "Apple ][ Nibble order disk image", apple2_nib_identify, apple2_nib_construct, NULL, + HEADS([1]) + TRACKS([35]) + SECTORS([16]) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID([0])) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/ap2_dsk.h b/src/lib/formats/ap2_dsk.h new file mode 100644 index 00000000000..bee72f9dfac --- /dev/null +++ b/src/lib/formats/ap2_dsk.h @@ -0,0 +1,33 @@ +/********************************************************************* + + ap2_dsk.h + + Apple II disk images + +*********************************************************************/ + +#ifndef AP2_DISK_H +#define AP2_DISK_H + +#include "flopimg.h" + + +/*************************************************************************** + + Constants + +***************************************************************************/ + +#define APPLE2_NIBBLE_SIZE 416 +#define APPLE2_SMALL_NIBBLE_SIZE 374 +#define APPLE2_TRACK_COUNT 35 +#define APPLE2_SECTOR_COUNT 16 +#define APPLE2_SECTOR_SIZE 256 + + + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(apple2); + +#endif /* AP2_DISK_H */ diff --git a/src/lib/formats/ap_dsk35.c b/src/lib/formats/ap_dsk35.c new file mode 100644 index 00000000000..acb95a8a11d --- /dev/null +++ b/src/lib/formats/ap_dsk35.c @@ -0,0 +1,1187 @@ +/********************************************************************* + + ap_dsk35.c + + Apple 3.5" disk images + + This code supports 3.5" 400k/800k disks used in early Macintoshes + and the Apple IIgs, and 3.5" 1440k MFM disks used on most Macintoshes. + + These disks have the following properties: + + 400k: 80 tracks, 1 head + 800k: 80 tracks, 2 heads + + Tracks 0-15 have 12 sectors each + Tracks 16-31 have 11 sectors each + Tracks 32-47 have 10 sectors each + Tracks 48-63 have 9 sectors each + Tracks 64-79 have 8 sectors each + + 1440k disks are simply 80 tracks, 2 heads and 18 sectors per track. + + Each sector on 400/800k disks has 524 bytes, 512 of which are really used by the Macintosh + + (80 tracks) * (avg of 10 sectors) * (512 bytes) * (2 sides) = 800 kB + + Data is nibblized : 3 data bytes -> 4 bytes on disk. + + In addition to 512 logical bytes, each sector contains 800 physical + bytes. Here is the layout of the physical sector: + + Pos + 0 0xFF (pad byte where head is turned on ???) + 1-35 self synch 0xFFs (7*5) (42 bytes actually written to media) + 36 0xD5 + 37 0xAA + 38 0x96 + 39 diskbytes[(track number) & 0x3F] + 40 diskbytes[(sector number)] + 41 diskbytes[("side")] + 42 diskbytes[("format byte")] + 43 diskbytes[("sum")] + 44 0xDE + 45 0xAA + 46 pad byte where head is turned off/on (0xFF here) + 47-51 self synch 0xFFs (6 bytes actually written to media) + 52 0xD5 + 53 0xAA + 54 0xAD + 55 spare byte, generally diskbytes[(sector number)] + 56-754 "nibblized" sector data ... + 755-758 checksum + 759 0xDE + 760 0xAA + 761 pad byte where head is turned off (0xFF here) + + MFM Track layout for 1440K disks: + Pos + --------- track ID + 0 0x4E (x80) + 80 00 (x12) + 92 C2 (x3) Mark byte + 93 FC Index mark + 94 4E (x50) + --------- sector ID + 144 00 (x12) + 156 A1 (x3) Mark byte + 159 FE Address mark + 160 xx Track number + 161 xx Side number + 162 xx Sector number + 163 xx Sector length + 164/165 xx CRC + 166 4E (x22) + --------- sector data + 188 00 (x12) + 200 A1 (x3) Mark byte + 203 FB Data address mark + 204 xx (x256) data + 460 4E (x54) + (repeat from sector ID to fill track; end of track is padded with 4E) + + Note : "Self synch refers to a technique whereby two zeroes are inserted + between each synch byte written to the disk.", i.e. "0xFF, 0xFF, 0xFF, + 0xFF, 0xFF" is actually "0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF" on disk. + Since the IWM assumes the data transfer is complete when the MSBit of its + shift register is 1, we do read 4 0xFF, even though they are not + contiguous on the disk media. Some reflexion shows that 4 synch bytes + allow the IWM to synchronize with the trailing data. + + Format byte codes: + 0x00 Apple II + 0x01 Lisa + 0x02 Mac MFS (single sided)? + 0x22 Mac MFS (double sided)? + +*********************************************************************/ + +#include <stdio.h> +#include <assert.h> + +#include "formats/ap_dsk35.h" + +struct apple35_tag +{ + UINT32 data_offset; + UINT32 data_size; + UINT8 format_byte; + UINT8 sides; + unsigned int is_1440k : 1; + + /* stuff used in DiskCopy images */ + UINT32 tag_offset; + UINT32 tag_size; +}; + + + +/* normal number of sector for each track */ +static const UINT8 apple35_tracklen_800kb[80] = +{ + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 +}; + +/* blocks of data used to nibblize tracks */ +static const UINT8 diskbytes[] = +{ + 0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6, /* 0x00 */ + 0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3, + 0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC, /* 0x10 */ + 0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3, + 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, /* 0x20 */ + 0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC, + 0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, /* 0x30 */ + 0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; + +/* reverse lookup of diskbytes */ +static const INT16 rev_diskbytes[] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x20 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x30 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x40 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x50 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x60 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x70 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 0x00, 0x01, /* 0x90 */ + -1, -1, 0x02, 0x03, -1, 0x04, 0x05, 0x06, + -1, -1, -1, -1, -1, -1, 0x07, 0x08, /* 0xA0 */ + -1, -1, -1, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + -1, -1, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, /* 0xB0 */ + -1, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, + -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */ + -1, -1, -1, 0x1B, -1, 0x1C, 0x1D, 0x1E, + -1, -1, -1, 0x1F, -1, -1, 0x20, 0x21, /* 0xD0 */ + -1, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + -1, -1, -1, -1, -1, 0x29, 0x2A, 0x2B, /* 0xE0 */ + -1, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, + -1, -1, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, /* 0xF0 */ + -1, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F +}; + +static const UINT8 blk1[] = +{ + /*0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xD5, 0xAA, 0x96*/ + 0xFF, + 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, + 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, + 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, + 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, + 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, + 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, + 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, + 0xD5, 0xAA, 0x96 +}; +static const UINT8 blk2[] = +{ + 0xDE, 0xAA, 0xFF, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0xD5, 0xAA, 0xAD +}; +static const UINT8 blk3[] = +{ + 0xDE, 0xAA, 0xFF +}; + + + +static struct apple35_tag *get_apple35_tag(floppy_image *floppy) +{ + struct apple35_tag *tag; + tag = (struct apple35_tag *) floppy_tag(floppy); + return tag; +} + + + +int apple35_sectors_per_track(floppy_image *image, int track) +{ + int sectors; + + assert(track >= 0); + assert(track < ARRAY_LENGTH(apple35_tracklen_800kb)); + + if (get_apple35_tag(image)->is_1440k) + sectors = 18; + else + sectors = apple35_tracklen_800kb[track]; + return sectors; +} + + + +/* + converts data to its nibblized representation, and generate checksum + + now handles 524-byte-long sectors + + tag data IS important, since it allows data recovery when the catalog is trashed +*/ +static void sony_nibblize35(const UINT8 *in, UINT8 *nib_ptr, UINT8 *csum) +{ + int i, j; + UINT32 c1, c2, c3, c4; + UINT8 val; + UINT8 w1, w2, w3, w4; + UINT8 b1[175], b2[175], b3[175]; + + /* Copy from the user's buffer to our buffer, while computing + * the three-byte data checksum + */ + + i = 0; + j = 0; + c1 = 0; + c2 = 0; + c3 = 0; + while (1) + { + c1 = (c1 & 0xFF) << 1; + if (c1 & 0x0100) + c1++; + + val = in[i++]; + c3 += val; + if (c1 & 0x0100) + { + c3++; + c1 &= 0xFF; + } + b1[j] = (val ^ c1) & 0xFF; + + val = in[i++]; + c2 += val; + if (c3 > 0xFF) + { + c2++; + c3 &= 0xFF; + } + b2[j] = (val ^ c3) & 0xFF; + + if (i == 524) break; + + val = in[i++]; + c1 += val; + if (c2 > 0xFF) + { + c1++; + c2 &= 0xFF; + } + b3[j] = (val ^ c2) & 0xFF; + j++; + } + c4 = ((c1 & 0xC0) >> 6) | ((c2 & 0xC0) >> 4) | ((c3 & 0xC0) >> 2); + b3[174] = 0; + + j = 0; + for (i = 0; i <= 174; i++) + { + w1 = b1[i] & 0x3F; + w2 = b2[i] & 0x3F; + w3 = b3[i] & 0x3F; + w4 = ((b1[i] & 0xC0) >> 2); + w4 |= ((b2[i] & 0xC0) >> 4); + w4 |= ((b3[i] & 0xC0) >> 6); + + nib_ptr[j++] = w4; + nib_ptr[j++] = w1; + nib_ptr[j++] = w2; + + if (i != 174) nib_ptr[j++] = w3; + } + + csum[0] = c1 & 0x3F; + csum[1] = c2 & 0x3F; + csum[2] = c3 & 0x3F; + csum[3] = c4 & 0x3F; +} + + + +/* + does the reverse process of sony_nibblize35 +*/ +static void sony_denibblize35(UINT8 *out, const UINT8 *nib_ptr, UINT8 *checksum) +{ + int i, j; + UINT32 c1,c2,c3,c4; + UINT8 val; + UINT8 w1,w2,w3=0,w4; + UINT8 b1[175],b2[175],b3[175]; + + j = 0; + for (i=0; i<=174; i++) + { + w4 = nib_ptr[j++]; + w1 = nib_ptr[j++]; + w2 = nib_ptr[j++]; + + if (i != 174) w3 = nib_ptr[j++]; + + b1[i] = (w1 & 0x3F) | ((w4 << 2) & 0xC0); + b2[i] = (w2 & 0x3F) | ((w4 << 4) & 0xC0); + b3[i] = (w3 & 0x3F) | ((w4 << 6) & 0xC0); + } + + /* Copy from the user's buffer to our buffer, while computing + * the three-byte data checksum + */ + + i = 0; + j = 0; + c1 = 0; + c2 = 0; + c3 = 0; + while (1) + { + c1 = (c1 & 0xFF) << 1; + if (c1 & 0x0100) + c1++; + + val = (b1[j] ^ c1) & 0xFF; + c3 += val; + if (c1 & 0x0100) + { + c3++; + c1 &= 0xFF; + } + out[i++] = val; + + val = (b2[j] ^ c3) & 0xFF; + c2 += val; + if (c3 > 0xFF) + { + c2++; + c3 &= 0xFF; + } + out[i++] = val; + + if (i == 524) break; + + val = (b3[j] ^ c2) & 0xFF; + c1 += val; + if (c2 > 0xFF) + { + c1++; + c2 &= 0xFF; + } + out[i++] = val; + j++; + } + c4 = ((c1 & 0xC0) >> 6) | ((c2 & 0xC0) >> 4) | ((c3 & 0xC0) >> 2); + b3[174] = 0; + + checksum[0] = c1 & 0x3F; + checksum[1] = c2 & 0x3F; + checksum[2] = c3 & 0x3F; + checksum[3] = c4 & 0x3F; +} + + + +void sony_filltrack(UINT8 *buffer, size_t buffer_len, size_t *pos, UINT8 data) +{ + buffer[*pos / 8] &= 0xFF << (8 - (*pos % 8)); + buffer[*pos / 8] |= data >> (*pos % 8); + + *pos += 8; + *pos %= buffer_len * 8; + + buffer[*pos / 8] &= 0xFF >> (*pos % 8); + buffer[*pos / 8] |= data << (8 - (*pos % 8)); +} + + + +UINT8 sony_fetchtrack(const UINT8 *buffer, size_t buffer_len, size_t *pos) +{ + UINT8 data; + + data = buffer[*pos / 8] << (*pos % 8); + *pos += 8; + *pos %= (buffer_len * 8); + data |= buffer[*pos / 8] >> (8 - (*pos % 8)); + + while ((data & 0x80) == 0) + { + /* this code looks weird because it isn't simply rotating the new bit + * in, but for some reason it won't work if I rotate the bit in; I + * have to match the algorithm used by the old code */ + data <<= 1; + data |= (buffer[*pos / 8] >> (8 - ((*pos % 8) + 1))); + (*pos)++; + *pos %= (buffer_len * 8); + } + return data; +} + + + +static UINT32 apple35_get_offset(floppy_image *floppy, int head, int track, int sector, UINT32 *tag_offset) +{ + int i; + UINT32 sector_index = 0; + struct apple35_tag *tag; + + tag = get_apple35_tag(floppy); + + if (track >= ARRAY_LENGTH(apple35_tracklen_800kb)) + return ~0; + if (head >= tag->sides) + return ~0; + if (sector >= apple35_sectors_per_track(floppy, track)) + return ~0; + + for (i = 0; i < track; i++) + sector_index += apple35_sectors_per_track(floppy, i); + sector_index *= tag->sides; + if (head) + sector_index += apple35_sectors_per_track(floppy, i); + sector_index += sector; + + if (tag_offset) + { + *tag_offset = sector_index * 12 + tag->tag_offset; + if (*tag_offset >= tag->tag_size) + *tag_offset = ~0; + } + return sector_index * 0x200 + tag->data_offset; +} + + + +static floperr_t apple35_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + UINT32 data_offset; +// printf("Read sector: T %d S %d H %d\n", track, sector, head); + data_offset = apple35_get_offset(floppy, head, track, sector, NULL); + if (data_offset == ~0) + return FLOPPY_ERROR_SEEKERROR; + floppy_image_read(floppy, buffer, data_offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t apple35_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + UINT32 data_offset; + + data_offset = apple35_get_offset(floppy, head, track, sector, NULL); + if (data_offset == ~0) + return FLOPPY_ERROR_SEEKERROR; + floppy_image_write(floppy, buffer, data_offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t apple35_read_sector_td(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + floperr_t err; + UINT32 tag_offset = 0; + + assert(buflen == 524); + +// printf("Read sector TD: T %d S %d H %d\n", track, sector, head); + + /* first read the sector */ + err = apple35_read_sector(floppy, head, track, sector, ((UINT8 *) buffer) + 12, 512); + if (err) + return err; + + /* read the tag data, if possible */ + memset(buffer, '\0', 12); + apple35_get_offset(floppy, head, track, sector, &tag_offset); + if (tag_offset != ~0) + floppy_image_read(floppy, buffer, tag_offset, 12); + + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t apple35_write_sector_td(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + floperr_t err; + UINT32 tag_offset = 0; + + assert(buflen == 524); + + /* first write the sector */ + err = apple35_write_sector(floppy, head, track, sector, ((const UINT8 *) buffer) + 12, 512, 0); + if (err) + return err; + + /* write the tag data, if possible */ + apple35_get_offset(floppy, head, track, sector, &tag_offset); + if (tag_offset != ~0) + floppy_image_write(floppy, buffer, tag_offset, 12); + + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t apple35_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) +{ + *sector_length = 512; + return FLOPPY_ERROR_SUCCESS; +} + + + +static int apple35_get_heads_per_disk(floppy_image *floppy) +{ + return get_apple35_tag(floppy)->sides; +} + + + +static int apple35_get_tracks_per_disk(floppy_image *floppy) +{ + return 80; +} + + + +static UINT32 apple35_get_track_size(floppy_image *floppy, int head, int track) +{ + if ((track < 0) || (track >= 80)) + return 0; + return apple35_sectors_per_track(floppy, track) * 800; +} + + + +static UINT8 calculate_side(int head, int track) +{ + UINT8 side; + side = head ? 0x20 : 0x00; + if (track & 0x40) + side |= 0x01; + return side; +} + + + +static floperr_t apple35_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) +{ + floperr_t err; + size_t pos = 0; + int sector_count, sector, i; + UINT8 sum, side; + struct apple35_tag *tag; + UINT8 sector_data[524]; + UINT8 nibble_data[699]; + UINT8 checksum[4]; + + tag = get_apple35_tag(floppy); + + if (track >= ARRAY_LENGTH(apple35_tracklen_800kb)) + return FLOPPY_ERROR_SEEKERROR; + if (offset != 0) + return FLOPPY_ERROR_UNSUPPORTED; + + memset(buffer, 0xFF, buflen); + sector_count = apple35_sectors_per_track(floppy, track); + side = calculate_side(head, track); + + for (sector = 0; sector < sector_count; sector++) + { + /* read the sector */ + err = apple35_read_sector_td(floppy, head, track, sector, sector_data, ARRAY_LENGTH(sector_data)); + if (err) + return err; + sony_nibblize35(sector_data, nibble_data, checksum); + + for (i = 0; i < ARRAY_LENGTH(blk1); i++) + sony_filltrack((UINT8*)buffer, buflen, &pos, blk1[i]); + + sum = (track ^ sector ^ side ^ tag->format_byte) & 0x3F; + + sony_filltrack((UINT8*)buffer, buflen, &pos, diskbytes[track & 0x3f]); + sony_filltrack((UINT8*)buffer, buflen, &pos, diskbytes[sector]); + sony_filltrack((UINT8*)buffer, buflen, &pos, diskbytes[side]); + sony_filltrack((UINT8*)buffer, buflen, &pos, diskbytes[tag->format_byte]); + sony_filltrack((UINT8*)buffer, buflen, &pos, diskbytes[sum]); + + for (i = 0; i < ARRAY_LENGTH(blk2); i++) + sony_filltrack((UINT8*)buffer, buflen, &pos, blk2[i]); + + sony_filltrack((UINT8*)buffer, buflen, &pos, diskbytes[sector]); + + for (i = 0; i < ARRAY_LENGTH(nibble_data); i++) + sony_filltrack((UINT8*)buffer, buflen, &pos, diskbytes[nibble_data[i]]); + + for (i = 3; i >= 0; i--) + sony_filltrack((UINT8*)buffer, buflen, &pos, diskbytes[checksum[i]]); + + for (i = 0; i < ARRAY_LENGTH(blk3); i++) + sony_filltrack((UINT8*)buffer, buflen, &pos, blk3[i]); + } + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t apple35_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen) +{ + floperr_t err; + size_t pos = 0; + int sector_count, sector, i, j; + struct apple35_tag *tag; + UINT8 sum, format_byte, val, side; + UINT32 found_sectors = 0; + UINT8 sector_data[524]; + UINT8 nibble_data[699]; + UINT8 checksum[4]; + + tag = get_apple35_tag(floppy); + + if (track >= ARRAY_LENGTH(apple35_tracklen_800kb)) + return FLOPPY_ERROR_SEEKERROR; + if (offset != 0) + return FLOPPY_ERROR_UNSUPPORTED; + + sector_count = apple35_sectors_per_track(floppy, track); + side = calculate_side(head, track); + + /* do 2 rotations, in case the bit slip stuff prevent us to read the first sector */ + for (j = 0; j < (buflen * 2); j++) + { + if (sony_fetchtrack((UINT8*)buffer, buflen, &pos) != 0xD5) + continue; + j++; + + if (sony_fetchtrack((UINT8*)buffer, buflen, &pos) != 0xAA) + continue; + j++; + + if (sony_fetchtrack((UINT8*)buffer, buflen, &pos) != 0x96) + continue; + j++; + + if (rev_diskbytes[sony_fetchtrack((UINT8*)buffer, buflen, &pos)] != (track & 0x3F)) + continue; + j++; + + sector = rev_diskbytes[sony_fetchtrack((UINT8*)buffer, buflen, &pos)]; + if ((sector < 0) || (sector >= sector_count)) + continue; + j++; + + if (rev_diskbytes[sony_fetchtrack((UINT8*)buffer, buflen, &pos)] != side) + continue; + j++; + + format_byte = rev_diskbytes[sony_fetchtrack((UINT8*)buffer, buflen, &pos)]; + if (format_byte != tag->format_byte) + { + /* this is an error, but not THAT critical, I guess */ + } + j++; + + sum = track ^ sector ^ side ^ format_byte; + if (rev_diskbytes[sony_fetchtrack((UINT8*)buffer, buflen, &pos)] != sum) + continue; + j++; + + if (sony_fetchtrack((UINT8*)buffer, buflen, &pos) != 0xDE) + continue; + j++; + + if (sony_fetchtrack((UINT8*)buffer, buflen, &pos) != 0xAA) + continue; + j++; + + while((val = sony_fetchtrack((UINT8*)buffer, buflen, &pos)) == 0xFF) + j++; + if (val != 0xD5) + continue; /* lost bit slip mark! */ + j++; + + if (sony_fetchtrack((UINT8*)buffer, buflen, &pos) != 0xAA) + continue; + j++; + + if (sony_fetchtrack((UINT8*)buffer, buflen, &pos) != 0xAD) + continue; + j++; + + /* should this be regarded as a critical error ??? */ + if (rev_diskbytes[sony_fetchtrack((UINT8*)buffer, buflen, &pos)] != sector) + continue; + j++; + + for (i = 0; i < ARRAY_LENGTH(nibble_data); i++) + { + nibble_data[i] = rev_diskbytes[sony_fetchtrack((UINT8*)buffer, buflen, &pos)]; + j++; + } + + for (i = 3; i >= 0; i--) + { + /* should be checking checksum */ + sony_fetchtrack((UINT8*)buffer, buflen, &pos); + } + + sony_fetchtrack((UINT8*)buffer, buflen, &pos); /* should get 0xDE */ + sony_fetchtrack((UINT8*)buffer, buflen, &pos); /* should get 0xAA */ + sony_fetchtrack((UINT8*)buffer, buflen, &pos); /* should get 0xFF */ + + /* did we already write this sector? */ + if ((found_sectors & (1 << sector)) == 0) + { + sony_denibblize35(sector_data, nibble_data, checksum); + + /* write the sector */ + err = apple35_write_sector_td(floppy, head, track, sector, sector_data, ARRAY_LENGTH(sector_data), 0); + if (err) + return err; + + found_sectors |= 1 << sector; + } + } + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t apple35_construct(floppy_image *floppy, UINT32 data_offset, UINT32 data_size, + UINT32 tag_offset, UINT32 tag_size, INT16 format_byte, UINT8 sides, int is_1440k) +{ + struct apple35_tag *tag; + struct FloppyCallbacks *format; + + /* figure out format byte if not specified */ + if (format_byte < 0) + { + switch(sides) + { + case 1: + format_byte = 0x02; + break; + case 2: + format_byte = 0x22; + break; + default: + return FLOPPY_ERROR_INVALIDIMAGE; + } + } + + /* create tag */ + tag = (struct apple35_tag *) floppy_create_tag(floppy, sizeof(struct apple35_tag)); + if (!tag) + return FLOPPY_ERROR_OUTOFMEMORY; + tag->data_offset = data_offset; + tag->data_size = data_size; + tag->tag_offset = tag_offset; + tag->tag_size = tag_size; + tag->format_byte = (UINT8) format_byte; + tag->sides = sides; + tag->is_1440k = is_1440k ? 1 : 0; + + /* set up format callbacks */ + format = floppy_callbacks(floppy); + format->read_sector = apple35_read_sector; + format->write_sector = apple35_write_sector; + format->read_track = apple35_read_track; + format->write_track = apple35_write_track; + format->get_sector_length = apple35_get_sector_length; + format->get_heads_per_disk = apple35_get_heads_per_disk; + format->get_tracks_per_disk = apple35_get_tracks_per_disk; + format->get_track_size = apple35_get_track_size; + return FLOPPY_ERROR_SUCCESS; +} + + + +/* ----------------------------------------------------------------------- */ + +static FLOPPY_IDENTIFY(apple35_raw_identify) +{ + UINT64 size; + size = floppy_image_size(floppy); + *vote = ((size == 80*1*10*512) || (size == 80*2*10*512) || (size == (80*2*18*512)+84) + || (size == 80*2*18*512)) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + + + + +static FLOPPY_CONSTRUCT(apple35_raw_construct) +{ + UINT64 size; + UINT8 sides; + int is_1440k; + + if (params) + { + /* create */ + sides = option_resolution_lookup_int(params, PARAM_HEADS); + size = 80*sides*10*512; + is_1440k = FALSE; + } + else + { + /* load */ + size = floppy_image_size(floppy); + if (size == 80*1*10*512) + { + sides = 1; + is_1440k = FALSE; + } + else if ((size == 80*2*10*512) || (size == 80*2*18*512) || (size == (80*2*18*512)+84)) + { + sides = 2; + is_1440k = (size == 80*2*18*512) || (size == (80*2*18*512)+84); + } + else + return FLOPPY_ERROR_INVALIDIMAGE; + } + + return apple35_construct(floppy, 0, (UINT32) size, 0, 0, -1, sides, is_1440k); +} + + + +/* ----------------------------------------------------------------------- */ + +struct header_diskcopy +{ + UINT8 disk_name[64]; /* name of the disk */ + UINT32 data_size; /* total size of data for all sectors (512*number_of_sectors) */ + UINT32 tag_size; /* total size of tag data for all sectors (12*number_of_sectors for GCR 3.5" floppies, 20*number_of_sectors for HD20, 0 otherwise) */ + UINT32 data_checksum; /* CRC32 checksum of all sector data */ + UINT32 tag_checksum; /* CRC32 checksum of all tag data */ + UINT8 disk_format; /* 0 = 400K, 1 = 800K, 2 = 720K, 3 = 1440K (other values reserved) */ + UINT8 format_byte; /* should be $00 Apple II, $01 Lisa, $02 Mac MFS ??? */ + /* $12 = 400K, $22 = >400K Macintosh (DiskCopy uses this value for + all Apple II disks not 800K in size, and even for some of those), + $24 = 800K Apple II disk */ + UINT16 magic; /* always $0100 (otherwise, the file may be in a different format. */ +}; + + + +static floperr_t apple35_diskcopy_headerdecode(floppy_image *floppy, UINT32 *data_offset, + UINT32 *data_size, UINT32 *tag_offset, UINT32 *tag_size, UINT8 *format_byte, UINT8 *sides) +{ + UINT64 size; + struct header_diskcopy header; + + if (data_offset) + *data_offset = 0; + if (data_size) + *data_size = 0; + if (tag_offset) + *tag_offset = 0; + if (tag_size) + *tag_size = 0; + if (format_byte) + *format_byte = 0; + if (sides) + *sides = 0; + + size = floppy_image_size(floppy); + if (size < sizeof(struct header_diskcopy)) + return FLOPPY_ERROR_INVALIDIMAGE; + + floppy_image_read(floppy, &header, 0, sizeof(header)); + + header.data_size = BIG_ENDIANIZE_INT32(header.data_size); + header.tag_size = BIG_ENDIANIZE_INT32(header.tag_size); + header.data_checksum = BIG_ENDIANIZE_INT32(header.data_checksum); + header.tag_checksum = BIG_ENDIANIZE_INT32(header.tag_checksum); + header.magic = BIG_ENDIANIZE_INT16(header.magic); + + if (header.disk_name[0] >= sizeof(header.disk_name)) + return FLOPPY_ERROR_INVALIDIMAGE; + if (header.magic != 0x0100) + return FLOPPY_ERROR_INVALIDIMAGE; + if (size != (header.data_size + header.tag_size + sizeof(header))) + return FLOPPY_ERROR_INVALIDIMAGE; + + if (header.data_size == 80*1*10*512) + { + if (sides) + *sides = 1; + } + else if (header.data_size == 80*2*10*512) + { + if (sides) + *sides = 2; + } + else + return FLOPPY_ERROR_INVALIDIMAGE; + + if (data_offset) + *data_offset = sizeof(header); + if (data_size) + *data_size = header.data_size; + if (tag_offset) + *tag_offset = sizeof(header) + header.data_size; + if (tag_size) + *tag_size = header.tag_size; + if (format_byte) + *format_byte = header.format_byte; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_IDENTIFY(apple35_diskcopy_identify) +{ + *vote = apple35_diskcopy_headerdecode(floppy, NULL, NULL, NULL, NULL, NULL, NULL) ? 0 : 100; + return FLOPPY_ERROR_SUCCESS; +} + + + + +static FLOPPY_CONSTRUCT(apple35_diskcopy_construct) +{ + floperr_t err; + UINT8 format_byte, sides; + UINT32 data_offset, data_size; + UINT32 tag_offset, tag_size; + INT16 format_byte_param = -1; + struct header_diskcopy header; + + if (params) + { + /* create */ + sides = option_resolution_lookup_int(params, PARAM_HEADS); + + data_size = 80*sides*10*512; + tag_size = 80*sides*10*12; + + memset(&header, 0, sizeof(header)); + header.data_size = BIG_ENDIANIZE_INT32(data_size); + header.tag_size = BIG_ENDIANIZE_INT32(tag_size); + header.disk_format = (sides > 1) ? 1 : 0; + header.magic = BIG_ENDIANIZE_INT16(0x100); + + floppy_image_write(floppy, &header, 0, sizeof(header)); + floppy_image_write_filler(floppy, 0, sizeof(header), data_size + tag_size); + } + + /* load */ + err = apple35_diskcopy_headerdecode(floppy, &data_offset, &data_size, + &tag_offset, &tag_size, &format_byte, &sides); + if (err) + return err; + + format_byte_param = format_byte; + + return apple35_construct(floppy, data_offset, data_size, + tag_offset, tag_size, format_byte_param, sides, FALSE); +} + + + +/* ----------------------------------------------------------------------- */ + +struct header_2img +{ + char magic[4]; /* '2IMG' */ + char creator[4]; /* signature; 'MESS' for MESS */ + UINT16 header_length; + UINT16 version; + UINT32 image_format; + UINT32 flags; + UINT32 block_count; + UINT32 data_offset; + UINT32 data_length; + UINT32 comment_offset; + UINT32 comment_length; + UINT32 creator_offset; + UINT32 creator_length; + UINT32 padding[4]; +}; + +#define IMAGE_FORMAT_DO 0 +#define IMAGE_FORMAT_PO 1 +#define IMAGE_FORMAT_NIB 2 +#define IMAGE_FLAGS_LOCKED 0x80000000 + + + +static floperr_t apple35_2img_decode(floppy_image *floppy, UINT32 *image_format, + UINT32 *data_offset, UINT32 *data_length) +{ + struct header_2img header; + UINT64 size; + + if (image_format) + *image_format = 0; + if (data_offset) + *data_offset = 0; + if (data_length) + *data_length = 0; + + size = floppy_image_size(floppy); + if (size < sizeof(header)) + { + return FLOPPY_ERROR_INVALIDIMAGE; + } + + floppy_image_read(floppy, &header, 0, sizeof(header)); + + if (memcmp(header.magic, "2IMG", 4)) + { + return FLOPPY_ERROR_INVALIDIMAGE; + } + + header.header_length = LITTLE_ENDIANIZE_INT16(header.header_length); + header.version = LITTLE_ENDIANIZE_INT16(header.version); + header.image_format = LITTLE_ENDIANIZE_INT32(header.image_format); + header.flags = LITTLE_ENDIANIZE_INT32(header.flags); + header.block_count = LITTLE_ENDIANIZE_INT32(header.block_count); + header.data_offset = LITTLE_ENDIANIZE_INT32(header.data_offset); + header.data_length = LITTLE_ENDIANIZE_INT32(header.data_length); + header.comment_offset = LITTLE_ENDIANIZE_INT32(header.comment_offset); + header.comment_length = LITTLE_ENDIANIZE_INT32(header.comment_length); + header.creator_offset = LITTLE_ENDIANIZE_INT32(header.creator_offset); + header.creator_length = LITTLE_ENDIANIZE_INT32(header.creator_length); + + // at least some images "in the wild" (e.g. TOSEC Minor Set 1) have big-endian data sizes + // even though that's against the .2mg spec + if (header.data_length == 0x800c00) + { + logerror("ap_dsk35: corrected bad-endian data length\n"); + header.data_length = 0x0c8000; + } + + if ((((UINT64) header.data_offset) + header.data_length) > size) + return FLOPPY_ERROR_INVALIDIMAGE; + if ((((UINT64) header.comment_offset) + header.comment_length) > size) + return FLOPPY_ERROR_INVALIDIMAGE; + if ((((UINT64) header.creator_offset) + header.creator_length) > size) + return FLOPPY_ERROR_INVALIDIMAGE; + if ((header.image_format != IMAGE_FORMAT_DO) && + (header.image_format != IMAGE_FORMAT_PO) && + (header.image_format != IMAGE_FORMAT_NIB)) + return FLOPPY_ERROR_INVALIDIMAGE; + + if (image_format) + *image_format = header.image_format; + if (data_offset) + *data_offset = header.data_offset; + if (data_length) + *data_length = header.data_length; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_IDENTIFY(apple35_2img_identify) +{ + *vote = apple35_2img_decode(floppy, NULL, NULL, NULL) ? 0 : 100; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_CONSTRUCT(apple35_2img_construct) +{ + floperr_t err; + UINT32 image_format; + UINT32 data_offset; + UINT32 data_size; + UINT8 sides = 2; + struct header_2img header; + + if (params) + { + /* create */ + sides = option_resolution_lookup_int(params, PARAM_HEADS); + + data_offset = sizeof(header); + data_size = 80*sides*10*512; + + memset(&header, 0, sizeof(header)); + header.header_length = LITTLE_ENDIANIZE_INT16(sizeof(header)); + header.block_count = LITTLE_ENDIANIZE_INT32(80*sides*10); + header.data_offset = LITTLE_ENDIANIZE_INT32(data_offset); + header.data_length = LITTLE_ENDIANIZE_INT32(data_size); + + floppy_image_write(floppy, &header, 0, sizeof(header)); + floppy_image_write_filler(floppy, 0, sizeof(header), data_size); + } + else + { + /* load */ + err = apple35_2img_decode(floppy, &image_format, &data_offset, &data_size); + if (err) + return err; + + if (data_size == 80*1*10*512) + sides = 1; /* single sided */ + else if (data_size == 80*2*10*512) + sides = 2; /* double sided */ + else + sides = 2; /* unknown... what to do... */ + } + + return apple35_construct(floppy, data_offset, data_size, + 0, 0, -1, sides, FALSE); +} + + + +FLOPPY_OPTIONS_START( apple35_mac ) + FLOPPY_OPTION( apple35_raw, "dsk,img,image", "Apple raw 3.5\" disk image", apple35_raw_identify, apple35_raw_construct, NULL, + HEADS([1]-2) + TRACKS([80]) + SECTOR_LENGTH([512]) + FIRST_SECTOR_ID([0])) + FLOPPY_OPTION( apple35_dc, "dc,dsk,img,image", "Apple DiskCopy disk image", apple35_diskcopy_identify, apple35_diskcopy_construct, NULL, + HEADS([1]-2) + TRACKS([80]) + SECTOR_LENGTH([512]) + FIRST_SECTOR_ID([0])) +FLOPPY_OPTIONS_END + +FLOPPY_OPTIONS_START( apple35_iigs ) + FLOPPY_OPTION( apple35_raw, "dsk,img,image", "Apple raw 3.5\" disk image", apple35_raw_identify, apple35_raw_construct, NULL, + HEADS([1]-2) + TRACKS([80]) + SECTOR_LENGTH([512]) + FIRST_SECTOR_ID([0])) + FLOPPY_OPTION( apple35_dc, "dc,dsk,img,image", "Apple DiskCopy disk image", apple35_diskcopy_identify, apple35_diskcopy_construct, NULL, + HEADS([1]-2) + TRACKS([80]) + SECTOR_LENGTH([512]) + FIRST_SECTOR_ID([0])) + FLOPPY_OPTION( apple35_2img, "2img,2mg", "Apple ][gs 2IMG disk image", apple35_2img_identify, apple35_2img_construct, NULL, + HEADS([1]-2) + TRACKS([80]) + SECTOR_LENGTH([512]) + FIRST_SECTOR_ID([0])) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/ap_dsk35.h b/src/lib/formats/ap_dsk35.h new file mode 100644 index 00000000000..c02fa0a19f3 --- /dev/null +++ b/src/lib/formats/ap_dsk35.h @@ -0,0 +1,25 @@ +/********************************************************************* + + ap_dsk35.h + + Apple 3.5" disk images + +*********************************************************************/ + +#ifndef AP_DSK35_H +#define AP_DSK35_H + +#include "flopimg.h" + +void sony_filltrack(UINT8 *buffer, size_t buffer_len, size_t *pos, UINT8 data); +UINT8 sony_fetchtrack(const UINT8 *buffer, size_t buffer_len, size_t *pos); + +int apple35_sectors_per_track(floppy_image *image, int track); + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(apple35_mac); +FLOPPY_OPTIONS_EXTERN(apple35_iigs); + + +#endif /* AP_DSK35_H */ diff --git a/src/lib/formats/apf_apt.c b/src/lib/formats/apf_apt.c new file mode 100644 index 00000000000..1b079919a52 --- /dev/null +++ b/src/lib/formats/apf_apt.c @@ -0,0 +1,157 @@ +/* .APT tape images */ + + +#include "formats/apf_apt.h" + + +#define WAVEENTRY_LOW -32768 +#define WAVEENTRY_HIGH 32767 +#define WAVEENTRY_NULL 0 + +/* frequency of wave */ +#define APF_WAV_FREQUENCY 11050 + +#define APF_APT_BIT_0_LENGTH ((int)(APF_WAV_FREQUENCY*0.0005)) +#define APF_APT_BIT_1_LENGTH ((int)(APF_WAV_FREQUENCY*0.001)) + +struct apf_t +{ + int cassette_length; +}; +static apf_t apf; + + +/* 500 microsecond of bit 0 and 1000 microsecond of bit 1 */ + +static INT16 *apf_emit_level(INT16 *p, int count, INT16 wave_state) +{ + int i; + + for (i=0; i<count; i++) + { + *(p++) = wave_state; + } + return p; +} + +/* 4 periods at 1200Hz */ +static INT16* apf_output_bit(INT16 *p, UINT8 b) +{ + if (b) + { + p = apf_emit_level(p, (APF_APT_BIT_1_LENGTH>>1), WAVEENTRY_HIGH); + p = apf_emit_level(p, (APF_APT_BIT_1_LENGTH>>1), WAVEENTRY_LOW); + } + else + { + p = apf_emit_level(p, (APF_APT_BIT_0_LENGTH>>1), WAVEENTRY_HIGH); + p = apf_emit_level(p, (APF_APT_BIT_0_LENGTH>>1), WAVEENTRY_LOW); + } + + return p; +} + +static int apf_get_bit_size_in_samples(UINT8 b) +{ + if (b) + { + return APF_APT_BIT_1_LENGTH; + } + + return APF_APT_BIT_0_LENGTH; +} + + +/*************************************************************************************/ + +/* each bit in the data represents a "1" or "0" waveform */ +static int apf_cassette_calculate_size_in_samples(const UINT8 *bytes, int length) +{ + unsigned i; + int size; + int b; + UINT8 data; + + size = 0; + apf.cassette_length = length; + + + for (i=0; i<length; i++) + { + data = bytes[i]; + + for (b=0; b<8; b++) + { + size += apf_get_bit_size_in_samples((data>>7) & 0x01); + data = data<<1; + } + } + + return size; +} + +/*************************************************************************************/ + +static int apf_cassette_fill_wave(INT16 *buffer, int length, UINT8 *bytes) +{ + int i; + INT16 *p; + UINT8 data; + int b; + + p = buffer; + + for (i=0; i<apf.cassette_length; i++) + { + data = bytes[i]; + for (b=0; b<8; b++) + { + p = apf_output_bit(p,(data>>7) & 0x01); + data = data<<1; + } + } + return p - buffer; +} + + + +static const struct CassetteLegacyWaveFiller apf_legacy_fill_wave = +{ + apf_cassette_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + apf_cassette_calculate_size_in_samples, /* chunk_sample_calc */ + APF_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + + +static casserr_t apf_apt_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &apf_legacy_fill_wave); +} + + + +static casserr_t apf_apt_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &apf_legacy_fill_wave); +} + + + +static const struct CassetteFormat apf_apt_format = +{ + "apt", + apf_apt_identify, + apf_apt_load, + NULL +}; + + + +CASSETTE_FORMATLIST_START(apf_cassette_formats) + CASSETTE_FORMAT(apf_apt_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/apf_apt.h b/src/lib/formats/apf_apt.h new file mode 100644 index 00000000000..faea27669c2 --- /dev/null +++ b/src/lib/formats/apf_apt.h @@ -0,0 +1,9 @@ +#ifndef APFCAS_H +#define APFCAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(apf_cassette_formats); + + +#endif /* APFCAS_H */ diff --git a/src/lib/formats/apridisk.c b/src/lib/formats/apridisk.c new file mode 100644 index 00000000000..d93503f54ad --- /dev/null +++ b/src/lib/formats/apridisk.c @@ -0,0 +1,226 @@ +/*************************************************************************** + + APRIDISK disk image format + +***************************************************************************/ + +#include "apridisk.h" +#include "imageutl.h" + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define APR_HEADER_SIZE 128 + +/* sector types */ +#define APR_DELETED 0xe31d0000 +#define APR_MAGIC 0xe31d0001 +#define APR_COMMENT 0xe31d0002 +#define APR_CREATOR 0xe31d0003 + +/* compression type */ +#define APR_UNCOMPRESSED 0x9e90 +#define APR_COMPRESSED 0x3e5a + +static const char *apr_magic = "ACT Apricot disk image\x1a\x04"; + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct apr_sector +{ + int head; + int track; + int sector; + UINT8 data[512]; +}; + +struct apr_tag +{ + int heads; + int tracks; + int sectors_per_track; + struct apr_sector sectors[2880]; + char *comment; +}; + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/* sector format: + * + * 00-03: sector type + * 04-05: compression type + * 06-07: header size + * 08-11: data size + * 12-13: track + * 14: head + * 15: sector + */ + +static floperr_t apr_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + struct apr_tag *tag = (apr_tag *)floppy_tag(floppy); +// printf("apr_read_sector %d %d %d\n", head, track, sector); + memcpy(buffer, tag->sectors[head * track * sector].data, buflen); + return FLOPPY_ERROR_SUCCESS; +} + +static floperr_t apr_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + struct apr_tag *tag = (apr_tag *)floppy_tag(floppy); +// printf("apr_read_indexed_sector %d %d %d\n", head, track, sector); + memcpy(buffer, tag->sectors[head * track * sector].data, buflen); + return FLOPPY_ERROR_SUCCESS; +} + +/* sector length is always 512 byte */ +static floperr_t apr_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) +{ + *sector_length = 512; + return FLOPPY_ERROR_SUCCESS; +} + +static int apr_get_heads_per_disk(floppy_image *floppy) +{ + struct apr_tag *tag = (apr_tag *)floppy_tag(floppy); + return tag->heads; +} + +static int apr_get_tracks_per_disk(floppy_image *floppy) +{ + struct apr_tag *tag = (apr_tag *)floppy_tag(floppy); + return tag->tracks; +} + +static floperr_t apr_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) +{ + struct apr_tag *tag = (apr_tag *)floppy_tag(floppy); + +// printf("apr_get_indexed_sector_info %d %d %d\n", head, track, sector_index); + + /* sanity checks */ + if (head < 0 || head > (tag->heads - 1)) return FLOPPY_ERROR_SEEKERROR; + if (track < 0 || track > (tag->tracks - 1)) return FLOPPY_ERROR_SEEKERROR; + if (sector_index < 0 || sector_index > (tag->sectors_per_track - 1)) return FLOPPY_ERROR_SEEKERROR; + + if (cylinder) *cylinder = tag->sectors[head * track * sector_index].track; + if (side) *side = tag->sectors[head * track * sector_index].head; + if (sector) *sector = tag->sectors[head * track * sector_index].sector; + if (sector_length) *sector_length = 512; + + return FLOPPY_ERROR_SUCCESS; +} + +FLOPPY_IDENTIFY( apridisk_identify ) +{ + UINT8 header[APR_HEADER_SIZE]; + + /* get header */ + floppy_image_read(floppy, &header, 0, sizeof(header)); + + /* look for the magic string */ + if (memcmp(header, apr_magic, sizeof(apr_magic)) == 0) + *vote = 100; + else + *vote = 0; + + return FLOPPY_ERROR_SUCCESS; +} + +FLOPPY_CONSTRUCT( apridisk_construct ) +{ + struct FloppyCallbacks *callbacks; + struct apr_tag *tag; + + int cur_sector = 0; + UINT8 sector_header[16]; + UINT64 pos = 128; + UINT32 type; + + tag = (apr_tag *)floppy_create_tag(floppy, sizeof(struct apr_tag)); + + if (!tag) + return FLOPPY_ERROR_OUTOFMEMORY; + + tag->heads = 0; + tag->tracks = 0; + tag->sectors_per_track = 0; + + floppy_image_read(floppy, §or_header, pos, 16); + type = pick_integer_le(§or_header, 0, 4); + + while (type == APR_DELETED || type == APR_MAGIC || type == APR_COMMENT || type == APR_CREATOR) + { + UINT16 compression = pick_integer_le(§or_header, 4, 2); + UINT16 header_size = pick_integer_le(§or_header, 6, 2); + UINT32 data_size = pick_integer_le(§or_header, 8, 4); + + tag->sectors[cur_sector].head = pick_integer_le(§or_header, 12, 1); + tag->sectors[cur_sector].sector = pick_integer_le(§or_header, 13, 1); + tag->sectors[cur_sector].track = pick_integer_le(§or_header, 14, 1); + + pos += header_size; + + if (type == APR_MAGIC) + { + if (compression == APR_UNCOMPRESSED) + { + floppy_image_read(floppy, &tag->sectors[cur_sector].data, pos, data_size); + + } + else if (compression == APR_COMPRESSED) + { + UINT8 *buffer = (UINT8 *)osd_malloc(data_size * sizeof(UINT8)); + UINT16 length; + UINT8 value; + + floppy_image_read(floppy, buffer, pos, data_size); + + length = pick_integer_le(buffer, 0, 2); + value = pick_integer_le(buffer, 2, 1); + + osd_free(buffer); + + /* not sure if this is possible */ + if (length != 512) { + printf("Compression unsupported"); + exit(-1); + } + + memset(&tag->sectors[cur_sector].data, value, length); + } + else + return FLOPPY_ERROR_INVALIDIMAGE; + + tag->heads = MAX(tag->sectors[cur_sector].head, tag->heads); + tag->tracks = MAX(tag->sectors[cur_sector].track, tag->tracks); + tag->sectors_per_track = MAX(tag->sectors[cur_sector].sector, tag->sectors_per_track); + } + + /* seek to next sector */ + pos += data_size; + cur_sector++; + + floppy_image_read(floppy, §or_header, pos, 16); + type = pick_integer_le(§or_header, 0, 4); + } + + tag->heads++; + tag->tracks++; + + callbacks = floppy_callbacks(floppy); + callbacks->read_sector = apr_read_sector; + callbacks->read_indexed_sector = apr_read_indexed_sector; + callbacks->get_sector_length = apr_get_sector_length; + callbacks->get_heads_per_disk = apr_get_heads_per_disk; + callbacks->get_tracks_per_disk = apr_get_tracks_per_disk; + callbacks->get_indexed_sector_info = apr_get_indexed_sector_info; + + return FLOPPY_ERROR_SUCCESS; +} diff --git a/src/lib/formats/apridisk.h b/src/lib/formats/apridisk.h new file mode 100644 index 00000000000..e7c39993016 --- /dev/null +++ b/src/lib/formats/apridisk.h @@ -0,0 +1,15 @@ +/*************************************************************************** + + APRIDISK disk image format + +***************************************************************************/ + +#ifndef __APRIDISK_H__ +#define __APRIDISK_H__ + +#include "flopimg.h" + +FLOPPY_IDENTIFY( apridisk_identify ); +FLOPPY_CONSTRUCT( apridisk_construct ); + +#endif /* __APRIDISK_H__ */ diff --git a/src/lib/formats/atarist_dsk.c b/src/lib/formats/atarist_dsk.c new file mode 100644 index 00000000000..b97a1cb8edd --- /dev/null +++ b/src/lib/formats/atarist_dsk.c @@ -0,0 +1,82 @@ +/********************************************************************* + + formats/atarist_dsk.c + + Atari ST disk images + +*********************************************************************/ + +#include "formats/atarist_dsk.h" +#include "formats/basicdsk.h" + +/* + + TODO: + + - MSA format + - STT format + - DIM format + +*/ + +/*************************************************************************** + CONSTANTS / MACROS +***************************************************************************/ + +#define LOG 0 + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + FLOPPY_IDENTIFY( atarist_st_identify ) +-------------------------------------------------*/ + +static FLOPPY_IDENTIFY( atarist_st_identify ) +{ + *vote = 100; + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + FLOPPY_CONSTRUCT( atarist_st_construct ) +-------------------------------------------------*/ + +static FLOPPY_CONSTRUCT( atarist_st_construct ) +{ + int heads = 0; + int tracks = 0; + int sectors = 0; + UINT8 bootsector[512]; + + floppy_image_read(floppy, bootsector, 0, 512); + sectors = bootsector[0x18]; + heads = bootsector[0x1a]; + tracks = (bootsector[0x13] | (bootsector[0x14] << 8)) / sectors / heads; + + struct basicdsk_geometry geometry; + memset(&geometry, 0, sizeof(geometry)); + + geometry.heads = heads; + geometry.first_sector_id = 1; + geometry.sector_length = 512; + geometry.tracks = tracks; + geometry.sectors = sectors; + + if (LOG) logerror("ST Heads %u Tracks %u Sectors %u\n", heads, tracks, sectors); + + return basicdsk_construct(floppy, &geometry); +} + +/*------------------------------------------------- + FLOPPY_CONSTRUCT(atarist_dsk_construct) +-------------------------------------------------*/ + +FLOPPY_OPTIONS_START( atarist ) + FLOPPY_OPTION( atarist, "st", "Atari ST floppy disk image", atarist_st_identify, atarist_st_construct, NULL, NULL ) +/* FLOPPY_OPTION( atarist, "stt", "Atari ST floppy disk image", atarist_stt_identify, atarist_stt_construct, NULL, NULL ) + FLOPPY_OPTION( atarist, "msa", "Atari ST floppy disk image", atarist_msa_identify, atarist_msa_construct, NULL, NULL ) + FLOPPY_OPTION( atarist, "dim", "Atari ST floppy disk image", atarist_dim_identify, atarist_dim_construct, NULL, NULL )*/ +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/atarist_dsk.h b/src/lib/formats/atarist_dsk.h new file mode 100644 index 00000000000..6ed71f7b58d --- /dev/null +++ b/src/lib/formats/atarist_dsk.h @@ -0,0 +1,18 @@ +/********************************************************************* + + formats/atarist_dsk.h + + atarist disk images + +*********************************************************************/ + +#ifndef ATARIST_DSK_H +#define ATARIST_DSK_H + +#include "flopimg.h" + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(atarist); + +#endif /* ATARIST_DSK_H */ diff --git a/src/lib/formats/atom_tap.c b/src/lib/formats/atom_tap.c new file mode 100644 index 00000000000..37c9a0d6d49 --- /dev/null +++ b/src/lib/formats/atom_tap.c @@ -0,0 +1,138 @@ +/********************************************************************* + + formats/atom_tap.c + + Cassette code for Acorn Atom tap files (Kansas City Standard) + +*********************************************************************/ + +/* + + http://beebwiki.jonripley.com/Acorn_cassette_format#Atom + + The Atom supports the System series format (as a 'nameless file') and its own format. The data consists of files separated + by carrier breaks and the appropriate leaders and trailers, and further subdivided into blocks separated by inter-block gaps. + An Atom block consists of the following sequence of bytes: + + Four synchronisation bytes (&2A). + File name (one to thirteen characters). + One end of file name marker byte (&0D). + Block flag, one byte. + Block number, two bytes, high byte first. + Data block length ? 1, one byte. + Execution address, two bytes, high byte first. + Load address, two bytes, high byte first. + Data, 0 to 256 bytes. + Checksum, one byte. + + The block flag is set as follows: + + Bit 7 is set if this block is not the last block of a file. + Bit 6 is set if the block contains data. If clear then the 'data block length' field is invalid. + Bit 5 is set if this block is not the first block of a file. + Bits 4 to 0 are undefined. Normally their contents are: + in the first block, bits 15 to 11 of the end address (=1 + the last address saved in the file); + in subsequent blocks, bits 6 to 2 of the previous block flag. + + The checksum is a simple sum (modulo 256) of all bytes from the start of the filename to the end of the data. + +*/ + +#include "atom_tap.h" + +/*************************************************************************** + PARAMETERS +***************************************************************************/ + +#define LOG 1 + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + CassetteModulation atom_tap_modulation +-------------------------------------------------*/ + +static const struct CassetteModulation atom_tap_modulation = +{ + CASSETTE_MODULATION_SINEWAVE, + 1200.0 - 300, 1200.0, 1200.0 + 300, + 2400.0 - 600, 2400.0, 2400.0 + 600 +}; + +/*------------------------------------------------- + cassette_image_read_uint8 - read tape data +-------------------------------------------------*/ + +static UINT8 cassette_image_read_uint8(cassette_image *cassette, UINT64 offset) +{ + UINT8 data; + cassette_image_read(cassette, &data, offset, 1); + return data; +} + +/*------------------------------------------------- + atom_tap_identify - identify cassette +-------------------------------------------------*/ + +static casserr_t atom_tap_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_modulation_identify( cassette, &atom_tap_modulation, opts); +} + +/*------------------------------------------------- + atom_tap_load - load cassette +-------------------------------------------------*/ + +#define MODULATE(_value) \ + for (int i = 0; i < (_value ? 8 : 4); i++) { \ + err = cassette_put_modulated_data_bit(cassette, 0, time_index, _value, &atom_tap_modulation, &time_displacement);\ + if (err) return err;\ + time_index += time_displacement;\ + } + +#define BIT(x,n) (((x)>>(n))&1) + +static casserr_t atom_tap_load(cassette_image *cassette) +{ + casserr_t err; + UINT64 image_size = cassette_image_size(cassette); + UINT64 image_pos = 0; + double time_index = 0.0; + double time_displacement; + + while (image_pos < image_size) + { + UINT8 data = cassette_image_read_uint8(cassette, image_pos); + + /* start bit */ + MODULATE(0); + + /* data bits */ + for (int bit = 0; bit < 8; bit++) + { + MODULATE(BIT(data, bit)); + } + + /* stop bits */ + MODULATE(1); + MODULATE(1); + + image_pos++; + } + + return CASSETTE_ERROR_SUCCESS; +} + +/*------------------------------------------------- + CassetteFormat atom_tap_cassette_format +-------------------------------------------------*/ + +const struct CassetteFormat atom_tap_format = +{ + "tap", + atom_tap_identify, + atom_tap_load, + NULL +}; diff --git a/src/lib/formats/atom_tap.h b/src/lib/formats/atom_tap.h new file mode 100644 index 00000000000..ab0722adb75 --- /dev/null +++ b/src/lib/formats/atom_tap.h @@ -0,0 +1,18 @@ +/********************************************************************* + + formats/atom_tap.h + + Cassette code for Acorn Atom tap files + +*********************************************************************/ + +#pragma once + +#ifndef __ATOM_TAP__ +#define __ATOM_TAP__ + +#include "cassimg.h" + +extern const struct CassetteFormat atom_tap_format; + +#endif diff --git a/src/mame/formats/basicdsk.c b/src/lib/formats/basicdsk.c index 10e07a6ccd9..10e07a6ccd9 100644 --- a/src/mame/formats/basicdsk.c +++ b/src/lib/formats/basicdsk.c diff --git a/src/mame/formats/basicdsk.h b/src/lib/formats/basicdsk.h index 22af0499014..03c1a147b20 100644 --- a/src/mame/formats/basicdsk.h +++ b/src/lib/formats/basicdsk.h @@ -9,7 +9,7 @@ #ifndef BASICDSK_H #define BASICDSK_H -#include "imagedev/flopimg.h" +#include "flopimg.h" struct basicdsk_geometry { diff --git a/src/emu/imagedev/cassimg.c b/src/lib/formats/cassimg.c index f7237420c6c..151940bb169 100644 --- a/src/emu/imagedev/cassimg.c +++ b/src/lib/formats/cassimg.c @@ -9,7 +9,6 @@ #include <string.h> -#include "emu.h" #include "imageutl.h" #include "pool.h" #include "cassimg.h" @@ -453,7 +452,7 @@ casserr_t cassette_get_samples(cassette_image *cassette, int channel, INT32 dword; INT64 sum; - assert(cassette); +// assert(cassette); err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges); if (err) @@ -900,9 +899,9 @@ casserr_t cassette_legacy_construct(cassette_image *cassette, struct CassetteLegacyWaveFiller args; /* sanity check the args */ - assert(legacy_args->header_samples >= -1); - assert(legacy_args->trailer_samples >= 0); - assert(legacy_args->fill_wave); + //assert(legacy_args->header_samples >= -1); + //assert(legacy_args->trailer_samples >= 0); + //assert(legacy_args->fill_wave); size = cassette_image_size(cassette); diff --git a/src/emu/imagedev/cassimg.h b/src/lib/formats/cassimg.h index d251ab81806..84655eadc8c 100644 --- a/src/emu/imagedev/cassimg.h +++ b/src/lib/formats/cassimg.h @@ -9,8 +9,13 @@ #ifndef CASSIMG_H #define CASSIMG_H +#include "osdcore.h" #include "pool.h" -#include "emu.h" +#include "ioprocs.h" + +#ifndef logerror +#define logerror printf +#endif /*************************************************************************** diff --git a/src/lib/formats/cbm_tap.c b/src/lib/formats/cbm_tap.c new file mode 100644 index 00000000000..eb95cba8e86 --- /dev/null +++ b/src/lib/formats/cbm_tap.c @@ -0,0 +1,369 @@ +/* + + Tape support for C16 / C64 / VIC20 TAP format + + + Credits to: + + - Peter Schepers for the informations on C64 formats + - Vice Team for the source of their very complete emulator + - Attila G. for tap2wav (both the source and the actual tool) + + + TODO: + * to verify and fix (if needed) support for .TAP v2 + * to implement reading (and logging) the remaining part of the header + * to verify if it is more accurate to use a different HIGH_WAVE value + when the pulse corresponds to a 0x00 in the .TAP file + (...) + * (far away in the future) can this code be merged with TZX code? + +*/ + +/* Info based on http://ist.uwaterloo.ca/~schepers/formats/TAP.TXT */ +/* Please refer to the webpage for the latest version and for a very + complete listing of various cart types and their bankswitch tricks */ +/* + + Designed by Per Hakan Sundell (author of the CCS64 C64 emulator) in 1997, +this format attempts to duplicate the data stored on a C64 cassette tape, +bit for bit. Since it is simply a representation of the raw serial data +from a tape, it should handle *any* custom tape loaders that exist. + + The TAP images are generally very large, being a minimum of eight times, +and up to sixteen times as large as what a raw PRG file would be. This is +due to the way the data is stored, with each bit of the original file now +being one byte large in the TAP file. The layout is fairly simple, with a +small 14-byte header followed by file data. + + Bytes: $0000-000B: File signature "C64-TAPE-RAW" + 000C: TAP version (see below for description) + $00 - Original layout + 01 - Updated + 000D-000F: Future expansion + 0010-0013: File data size (not including this header, in + LOW/HIGH format). + 0014-xxxx: File data + + In TAP version $00 files, each data byte in the file data area represents +the length of a pulse, when the C64's hardware will trigger again. This +pulse length is determined by the following formula: + + pulse length (in seconds) = (8 * data byte) / (clock cycles) + + Therefore, a data value of $2F (47 in decimal) would be: + + (47 * 8) / 985248 = .00038975 seconds. + + A data value of $00 represents an "overflow" condition, any pulse length +which is more that 255 * 8 in length. + + The value of "clock cylces" from above (985248) is based on the PAL +value. Since this file format was developed in Europe, which is +predominantly PAL video, this is only logical. The NTSC value would be +1022730, which is very close to the PAL, and therefore won't cause a +compatability problem converting European and NTSC tapes. I would stick to +using the PAL value just in case. + + + In TAP version $01 files, the data value of $00 has been re-coded to +represent values greater than 255 * 8. When a $00 is encountered, three +bytes will follow which are the actual time (in cycles) of a pulse, and the +above formula does not apply. The three bytes are stored in LOW/HIGH +format. + + + The actual interpretation of the serial data takes a little more work to +explain. The typical ROM tape loader (and the turbo loaders) will +initialize a timer with a specified value and start it counting down. If +either the tape data changes or the timer runs out, an IRQ will occur. The +loader will determine which condition caused the IRQ. If the tape data +changed before the timer ran out, we have a short pulse, or a "0" bit. If +the timer ran out first, we have a long pulse, or a "1" bit. Doing this +continuously and we decode the entire file. + + +[ Additional notes on v.2: + + I found no documents about this around, but it seems an expansion of the +format specifically thought for C16 tapes. In a .TAP version 2, each byte +only stores informations on half of the wave... +Unfortunately, I have no such a .tap file to test, so my implementation +below could be not working. FP ] +*/ + +#include "cbm_tap.h" + + +#define CBM_WAV_FREQUENCY 44100 + +/* Systems */ +#define C64 0 +#define VIC20 1 +#define C16 2 + +/* Video standards */ +#define PAL 0 +#define NTSC 1 + +/* Frequencies in [Hz] to determine the length of each pulse */ +#define C64_PAL 123156 /* 985248 / 8 */ +#define C64_NTSC 127841 /* 1022727 / 8 */ +#define VIC20_PAL 138551 /* 1108405 / 8 */ +#define VIC20_NTSC 127841 /* 1022727 / 8 */ +#define C16_PAL 110840 /* 886724 / 8 */ +#define C16_NTSC 111860 /* 894886 / 8 */ + +#define PAUSE (CBM_WAV_FREQUENCY / 50) /* tap2wav uses this value for 0x00 in .TAP v0, instead of 0x100 */ + +/* These values do not really matter, as long as the produced pulses + go above & below 0. However, for documentation purpose it would be + nice to find out which values were used by Commodore tapes. I was + not able to find any reference on the subject. */ +#define WAVE_HIGH (0x5a9e >> 1) +#define WAVE_LOW -(0x5a9e >> 1) +#define WAVE_PAUSE 0x80 + +#define CBM_HEADER_SIZE 20 + +static INT16 wave_data = 0; +static int len; + + +/* This in fact gives the number of samples for half of the pulse */ +INLINE int tap_data_to_samplecount(int data, int frequency) +{ +// return (int) (0.5 * (0.5 + (((double)CBM_WAV_FREQUENCY / frequency) * (double)data))); // MESS TZX formula + return (int) (0.5 * (((double)CBM_WAV_FREQUENCY / frequency) * (double)((data) + 0.5))); // tap2wav formula +} + +/* The version with parameters could be handy if we decide to implement a +different values for byte == 0x00 below (the WAVE_PAUSE) as tap2wav does. */ +#if 0 +static void toggle_wave_data(int low, int high) +{ + wave_data = (wave_data == low) ? high : low; +} +#endif + +static void toggle_wave_data(void ) +{ + wave_data = (wave_data == WAVE_HIGH) ? WAVE_LOW : WAVE_HIGH; +} + +static void cbm_output_wave( INT16 **buffer, int length ) +{ + if (buffer == NULL) + return; + + for( ; length > 0; length-- ) + { + **buffer = wave_data; + *buffer = *buffer + 1; + } +} + + +static int cbm_tap_do_work( INT16 **buffer, int length, const UINT8 *data ) +{ + int i, j = 0; + int size = 0; + + int version = data[0x0c]; + int system = data[0x0d]; + int video_standard = data[0x0e]; + int tap_frequency = 0; + + int byte_samples = 0; + UINT8 over_pulse_bytes[3] = {0 , 0, 0 }; + int over_pulse_length = 0; + /* These waveamp_* values are currently stored but not used. + Further investigations are needed to find real pulse amplitude + in Commodore tapes. Implementation here would follow */ + /* int waveamp_high, waveamp_low; */ + + /* Log .TAP info but only once */ + if (!(buffer == NULL)) + { + logerror("TAP version : %d\n", version); + logerror("Machine type : %d\n", system); + logerror("Video standard : %d\n", video_standard); + logerror("Tape frequency : %d\n", (tap_frequency) << 3); + } + + + /* is this a supported version? */ + if ((version < 0) || (version > 2)) + { + logerror("Unsupported .tap version: %d \n", version); + return -1; + } + + /* is the .tap file corrupted? */ + if ((data == NULL) || (length <= CBM_HEADER_SIZE)) + return -1; + + + /* read the frequency from the .tap header */ + switch (system) + { + case VIC20: + tap_frequency = (video_standard == NTSC) ? VIC20_NTSC : VIC20_PAL; + break; + + case C16: + tap_frequency = (video_standard == NTSC) ? C16_NTSC : C16_PAL; + break; + + case C64: + default: + tap_frequency = (video_standard == NTSC) ? C64_NTSC : C64_PAL; + break; + } + + + for (i = CBM_HEADER_SIZE; i < length; i++) + { + UINT8 byte = data[i]; + + /* .TAP v0 */ + /* Here is simple: + if byte is != 0 -> length = byte + otherwise -> length = 0x100 (i.e. 0xff + 1) */ + if (!version) + { + if (byte != 0x00) + { + byte_samples = tap_data_to_samplecount(byte, tap_frequency); + /* waveamp_high = WAVE_HIGH; */ + } + else + { + byte_samples = tap_data_to_samplecount(PAUSE, tap_frequency); // tap2wav value +// byte_samples = tap_data_to_samplecount(0x100, tap_frequency); // vice value + /* waveamp_high = WAVE_PAUSE; */ + } + /* waveamp_low = WAVE_LOW; */ + } + + /* .TAP v1 & v2 */ + /* Here is a bit more complicate: + if byte is != 0 -> length = byte + otherwise -> the length of the pulse is stored as a 24bit value in the 3 bytes after the 0. + See below for comments on the implementation of this mechanism */ + if (version) + { + if ((byte != 0x00) && !j) + { + byte_samples = tap_data_to_samplecount(byte, tap_frequency); + /* waveamp_high = WAVE_HIGH; */ + } + else + { + /* If we have a long pulse close to the end of the .TAP, check that bytes still + to be read are enough to complete it. */ + if (length - i + j >= 4) + { + /* Here we read the 3 following bytes, using an index j + j = 0 -> The 0x00 byte: we simply skip everything and go on + j = 1,2 -> The 1st and 2nd bytes after 0x00: we store them and go on + j = 3 -> The final byte of the pulse length: we store it, + and then we pass to finally output the wave */ + if (j > 0) + { + over_pulse_bytes[j-1] = byte; + j += 1; + + if (j >= 4) + { + over_pulse_length = ((over_pulse_bytes[2] << 16) | (over_pulse_bytes[1] << 8) | over_pulse_bytes[0]) >> 3; + byte_samples = tap_data_to_samplecount(over_pulse_length, tap_frequency); + /* waveamp_high = WAVE_PAUSE; */ + j = 0; + } + } + else + { + j += 1; + logerror("Found a 00 byte close to the end of the .tap file.\n"); + logerror("This is not allowed by the format specs. \n"); + logerror("Check if your .tap file got corrupted when you created it!\n"); + } + } + else j = 1; + } + /* waveamp_low = WAVE_LOW; */ + } + + if (j == 0) + { + cbm_output_wave( buffer, byte_samples ); + size += byte_samples; +// toggle_wave_data(waveamp_low, waveamp_high); + toggle_wave_data(); + if (version < 2) + { + cbm_output_wave( buffer, byte_samples ); + size += byte_samples; +// toggle_wave_data(waveamp_low, waveamp_high); + toggle_wave_data(); + } + } + } + + return size; +} + + +static int cbm_tap_to_wav_size( const UINT8 *tapdata, int taplen ) +{ + int size = cbm_tap_do_work(NULL, taplen, tapdata); + len = taplen; + + return size; +} + +static int cbm_tap_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) +{ + INT16 *p = buffer; + + return cbm_tap_do_work(&p, len, (const UINT8 *)bytes); +} + + + + +static const struct CassetteLegacyWaveFiller cbm_legacy_fill_wave = { + cbm_tap_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + cbm_tap_to_wav_size, /* chunk_sample_calc */ + CBM_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + +static casserr_t cbm_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) +{ + return cassette_legacy_identify( cassette, opts, &cbm_legacy_fill_wave ); +} + + +static casserr_t cbm_cassette_load( cassette_image *cassette ) +{ + return cassette_legacy_construct( cassette, &cbm_legacy_fill_wave ); +} + + +static const struct CassetteFormat cbm_tap_cassette_format = { + "tap", + cbm_cassette_identify, + cbm_cassette_load, + NULL +}; + + +CASSETTE_FORMATLIST_START(cbm_cassette_formats) + CASSETTE_FORMAT(cbm_tap_cassette_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/cbm_tap.h b/src/lib/formats/cbm_tap.h new file mode 100644 index 00000000000..81e4b20bcb0 --- /dev/null +++ b/src/lib/formats/cbm_tap.h @@ -0,0 +1,16 @@ +/********************************************************************* + + cbm_tap.h + + Format code for CBM .tap files + +*********************************************************************/ + +#ifndef CBM_TAP_H +#define CBM_TAP_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(cbm_cassette_formats); + +#endif /* CBM_TAP_H */ diff --git a/src/lib/formats/cgen_cas.c b/src/lib/formats/cgen_cas.c new file mode 100644 index 00000000000..b2aba94b41a --- /dev/null +++ b/src/lib/formats/cgen_cas.c @@ -0,0 +1,149 @@ +/******************************************************************** + +Support for EACA Colour Genie .cas cassette images + +Current state: Not working. Only the sync signal and 0x66 byte get + recognized. + +********************************************************************/ + +#include "formats/cgen_cas.h" + +#define TAPE_HEADER "Colour Genie - Virtual Tape File" + +#define SMPLO -32768 +#define SMPHI 32767 + + +static int cas_size; +static int level; + + +static int cgenie_output_byte(INT16 *buffer, int sample_count, UINT8 data) +{ + int i; + int samples = 0; + + for ( i = 0; i < 8; i++ ) + { + /* Output bit boundary */ + level ^= 1; + if ( buffer ) + buffer[ sample_count + samples ] = level ? SMPHI : SMPLO; + samples++; + + /* Output bit */ + if ( data & 0x80 ) + level ^= 1; + if ( buffer ) + buffer[ sample_count + samples ] = level ? SMPHI : SMPLO; + samples++; + + data <<= 1; + } + return samples; +} + + +static int cgenie_handle_cas(INT16 *buffer, const UINT8 *casdata) +{ + int i, data_pos, sample_count; + + data_pos = 0; + sample_count = 0; + level = 0; + + /* Check for presence of optional header */ + if ( ! memcmp( casdata, TAPE_HEADER, sizeof(TAPE_HEADER) - 1 ) ) + { + /* Search for 0x00 or end of file */ + while ( data_pos < cas_size && casdata[data_pos] ) + data_pos++; + + /* If we're at the end of the file it's not a valid .cas file */ + if ( data_pos == cas_size ) + return -1; + + /* Skip the 0x00 byte */ + data_pos++; + } + + /* If we're at the end of the file it's not a valid .cas file */ + if ( data_pos == cas_size ) + return -1; + + /* Check for beginning of tape file marker */ + if ( casdata[data_pos] != 0x66 ) + return -1; + + /* Create header */ + for ( i = 0; i < 256; i++ ) + sample_count += cgenie_output_byte( buffer, sample_count, 0xaa ); + + /* Start outputting data */ + while( data_pos < cas_size ) + { + sample_count += cgenie_output_byte( buffer, sample_count, casdata[data_pos] ); + data_pos++; + } + sample_count += cgenie_output_byte( buffer, sample_count, 0x00 ); + + return sample_count; +} + +/******************************************************************* + Generate samples for the tape image +********************************************************************/ +static int cgenie_cas_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + return cgenie_handle_cas( buffer, bytes ); +} + + +/******************************************************************* + Calculate the number of samples needed for this tape image +********************************************************************/ +static int cgenie_cas_to_wav_size(const UINT8 *casdata, int caslen) +{ + cas_size = caslen; + + return cgenie_handle_cas( NULL, casdata ); +} + +static const struct CassetteLegacyWaveFiller cgenie_cas_legacy_fill_wave = +{ + cgenie_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + cgenie_cas_to_wav_size, /* chunk_sample_calc */ + 2400, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + +static casserr_t cgenie_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &cgenie_cas_legacy_fill_wave); +} + + +static casserr_t cgenie_cas_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &cgenie_cas_legacy_fill_wave); +} + + +static const struct CassetteFormat cgenie_cas_format = +{ + "cas", + cgenie_cas_identify, + cgenie_cas_load, + NULL +}; + + +CASSETTE_FORMATLIST_START(cgenie_cassette_formats) + CASSETTE_FORMAT(cgenie_cas_format) +CASSETTE_FORMATLIST_END + diff --git a/src/lib/formats/cgen_cas.h b/src/lib/formats/cgen_cas.h new file mode 100644 index 00000000000..3f45cd41d05 --- /dev/null +++ b/src/lib/formats/cgen_cas.h @@ -0,0 +1,17 @@ +/********************************************************************* + + cgen_cas.h + + Format code for EACA Colour Genie .cas casette files. + +*********************************************************************/ + +#ifndef __CGEN_CAS_H +#define __CGEN_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(cgenie_cassette_formats); + +#endif /* __CGEN_CAS_H */ + diff --git a/src/lib/formats/coco_cas.c b/src/lib/formats/coco_cas.c new file mode 100644 index 00000000000..441c8620ae1 --- /dev/null +++ b/src/lib/formats/coco_cas.c @@ -0,0 +1,313 @@ +/************************************************************************** + + coco_cas.c + + Format code for CoCo CAS (*.cas) files + + This is the actual code that processes the CAS data. CAS files are a + problem because they are a legacy of previous CoCo emulators, and most + of these emulators patch the system as a shortcut. Thus, the ones and + zeroes in the file do not truly represent modulated pulses in the + waveform, but what the BIOS reads and writes. + + In doing so, they make the CoCo more tolerant of short headers and lack + of delays between blocks. This legacy is reflected in most CAS files in + use, and thus presents a problem for a pure hardware emulation like MESS. + + One alternative is to preprocess the data on the CAS, file by file, but + this proves to be problematic because in the process, legitimate data + that is unrecognized by the preprocessor may get dropped. + + The approach taken here is a hybrid approach - it retrieves the data + block by block until an error occurs; be it the end of the CAS or a + corrupt (?!) block. When "legitimate" blocks are done processing, the + remainder of the data is added to the waveform in a traditional manner. + The result has proven to work quite well. + + 2005-May-04, P.Harvey-Smith, improved handling of some "copy protected" + Dragon games that have odd blocks in odd places, this has slightly + increased loading time but does mean games like "Rommel's Revenge" will + now load. + +**************************************************************************/ + +#include "coco_cas.h" + +#define COCO_WAVESAMPLES_HEADER (1.0) +#define COCO_WAVESAMPLES_TRAILER (1.0) +#define COCO_LONGSILENCE (5.0) + +//some games load with only 5s, but most games need 15s +#define ALICE32_WAVESAMPLES_HEADER (15.0) + +// PAIR is an endian-safe union useful for representing 32-bit CPU registers +union PAIR +{ +#ifdef LSB_FIRST + struct { UINT8 l,h,h2,h3; } b; + struct { UINT16 l,h; } w; + struct { INT8 l,h,h2,h3; } sb; + struct { INT16 l,h; } sw; +#else + struct { UINT8 h3,h2,h,l; } b; + struct { INT8 h3,h2,h,l; } sb; + struct { UINT16 h,l; } w; + struct { INT16 h,l; } sw; +#endif + UINT32 d; + INT32 sd; +}; + +static int synccount; + +const struct CassetteModulation coco_cas_modulation = +{ + CASSETTE_MODULATION_SINEWAVE, + 600.0, 1200.0, 1500.0, + 1500.0, 2400.0, 3000.0 +}; + + + +static casserr_t coco_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_modulation_identify(cassette, &coco_cas_modulation, opts); +} + + + +static int get_cas_block(cassette_image *cassette, UINT64 *offset, UINT8 *block, int *block_len) +{ + UINT8 block_length = 0; + UINT8 block_checksum = 0; + UINT64 current_offset; + UINT64 image_size; + PAIR p; + int i; + int state = 0; + int phase = 0; + + synccount = 0; + p.w.l = 0; + image_size = cassette_image_size(cassette); + current_offset = *offset; + + while(current_offset < image_size) + { + cassette_image_read(cassette, &p.b.h, current_offset, 1); + current_offset++; + + for (i = 0; i < 8; i++) + { + p.w.l >>= 1; + + if (state == 0) + { + /* searching for a block */ + if (p.b.l == 0x3C) + { + /* found one! */ + phase = i; + state++; + } + else if (p.b.l == 0x55) + { + synccount++; + } + } + else if (i == phase) + { + *(block++) = p.b.l; + switch(state) { + case 1: + /* found file type */ + block_checksum = p.b.l; + state++; + break; + case 2: + /* found file size */ + block_length = p.b.l; + *block_len = ((int) block_length) + 3; + block_checksum += p.b.l; + state++; + break; + + case 3: + /* data byte */ + if (block_length) + { + block_length--; + block_checksum += p.b.l; + } + else + { + /* end of block */ + if (p.b.l != block_checksum) + { + /* checksum failure */ + return FALSE; + } + else + { + /* checksum success */ + *offset = current_offset; + return TRUE; + } + } + } + } + } + } + + /* no more blocks */ + return FALSE; +} + + + +static casserr_t cas_load(cassette_image *cassette, UINT8 silence) +{ + casserr_t err; + UINT64 offset; + UINT64 image_size; + UINT8 block[258]; /* 255 bytes per block + 3 (type, length, checksum) */ + int block_length = 0; + UINT8 last_blocktype; + double time_index = 0.0; + double time_displacement; + static const UINT8 magic_bytes[2] = { 0x55, 0x3C }; + +#if 0 + { + static const UINT8 dummy_bytes[] = + { + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x3C, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A + }; + time_index = 10.0; + return cassette_put_modulated_data(cassette, 0, time_index, dummy_bytes, sizeof(dummy_bytes), &coco_cas_modulation, &time_displacement); + } +#endif + + err = cassette_put_sample(cassette, 0, time_index, COCO_WAVESAMPLES_HEADER, 0); + if (err) + return err; + time_index += COCO_WAVESAMPLES_HEADER; + + offset = 0; + last_blocktype = 0; + image_size = cassette_image_size(cassette); + + /* try to find a block that we can untangle */ + while(get_cas_block(cassette, &offset, block, &block_length)) + { + /* Forcing a silence before a filename block, improves the ability to load some */ + /* copy protected Dragon games, e.g. Rommel's Revenge */ + /* was the last block a filename block? */ + if ((last_blocktype == 0) || (last_blocktype == 0xFF) || (block[0] == 0)) + { + /* silence */ + err = cassette_put_sample(cassette, 0, time_index, silence, 0); + if (err) + return err; + time_index += silence; + + /* sync data */ + err = cassette_put_modulated_filler(cassette, 0, time_index, 0x55, 128, &coco_cas_modulation, &time_displacement); + if (err) + return err; + time_index += time_displacement; + } + else if (synccount != 0) /* If we have multiple sync bytes in cas file, make sure they */ + { /* are passed through */ + /* sync data */ + err = cassette_put_modulated_filler(cassette, 0, time_index, 0x55, synccount, &coco_cas_modulation, &time_displacement); + if (err) + return err; + time_index += time_displacement; + } + + /* now fill in the magic bytes */ + err = cassette_put_modulated_data(cassette, 0, time_index, magic_bytes, sizeof(magic_bytes), &coco_cas_modulation, &time_displacement); + if (err) + return err; + time_index += time_displacement; + + /* now fill in the block */ + err = cassette_put_modulated_data(cassette, 0, time_index, block, block_length, &coco_cas_modulation, &time_displacement); + if (err) + return err; + time_index += time_displacement; + + /* and the last magic byte */ + err = cassette_put_modulated_filler(cassette, 0, time_index, 0x55, 1, &coco_cas_modulation, &time_displacement); + if (err) + return err; + time_index += time_displacement; + + last_blocktype = block[0]; + } + + /* all futher data is undecipherable, so output it verbatim */ + err = cassette_read_modulated_data(cassette, 0, time_index, offset, image_size - offset, &coco_cas_modulation, &time_displacement); + if (err) + return err; + time_index += time_displacement; + + return CASSETTE_ERROR_SUCCESS; +} + +static casserr_t coco_cas_load(cassette_image *cassette) +{ + return cas_load(cassette, COCO_WAVESAMPLES_HEADER); +} + +static casserr_t alice32_cas_load(cassette_image *cassette) +{ + return cas_load(cassette, ALICE32_WAVESAMPLES_HEADER); +} + +const struct CassetteFormat coco_cas_format = +{ + "cas", + coco_cas_identify, + coco_cas_load, + NULL +}; + +const struct CassetteFormat alice32_cas_format = +{ + "cas,c10,k7", + coco_cas_identify, + alice32_cas_load, + NULL +}; + + +CASSETTE_FORMATLIST_START(coco_cassette_formats) + CASSETTE_FORMAT(coco_cas_format) +CASSETTE_FORMATLIST_END + +CASSETTE_FORMATLIST_START(alice32_cassette_formats) + CASSETTE_FORMAT(alice32_cas_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/coco_cas.h b/src/lib/formats/coco_cas.h new file mode 100644 index 00000000000..03a714ce847 --- /dev/null +++ b/src/lib/formats/coco_cas.h @@ -0,0 +1,20 @@ +/********************************************************************* + + coco_cas.h + + Format code for CoCo CAS (*.cas) files + +*********************************************************************/ + +#ifndef COCO_CAS_H +#define COCO_CAS_H + +#include "cassimg.h" + +extern const struct CassetteFormat coco_cas_format; +extern const struct CassetteModulation coco_cas_modulation; + +CASSETTE_FORMATLIST_EXTERN(coco_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(alice32_cassette_formats); + +#endif /* COCO_CAS_H */ diff --git a/src/lib/formats/coco_dsk.c b/src/lib/formats/coco_dsk.c new file mode 100644 index 00000000000..8cc34627a6c --- /dev/null +++ b/src/lib/formats/coco_dsk.c @@ -0,0 +1,1199 @@ +/********************************************************************* + + formats/coco_dsk.c + + Tandy Color Computer / Dragon disk images + +*********************************************************************/ + +#include <string.h> +#include <assert.h> +#include <time.h> + +#include "osdepend.h" +#include "formats/coco_dsk.h" +#include "formats/basicdsk.h" +#include "imageutl.h" + +/* ----------------------------------------------------------------------- + * JVC (Jeff Vavasour CoCo) format + * + * Used by Jeff Vavasour's CoCo Emulators + * + * Documentation taken from Tim Linder's web site: + * http://home.netcom.com/~tlindner/JVC.html + * + * A. Header length + * The header length is determined by the file length modulo 256: + * headerSize = fileLength % 256; + * This means that the header is variable length and the minimum size + * is zero bytes, and the maximum size of 255 bytes. + * + * B. Header + * Here is a description of the header bytes: + * Byte Offset Description Default + * ----------- ----------------- ------- + * 0 Sectors per track 18 + * 1 Side count 1 + * 2 Sector size code 1 + * 3 First sector ID 1 + * 4 Sector attribute flag 0 + * + * If the sector attribute flag is zero then the track count is determined + * by the formula: + * + * (fileLength - headerSize) / (sectorsPerTrack * (128 << + * sectorSizeCode)) / sideCount + * + * If the sector attribute flag is non zero then the track count is + * determined by the more complex formula: + * + * (fileLength - headerSize) / (sectorsPerTrack * ((128 << + * sectorSizeCode) + 1) ) / sideCount + * + * If the length of the header is to short to contain the geometry desired, + * then the default values are assumed. If the header length is zero the all + * of the geometry is assumed. When creating disk images it is desirable to + * make the header length as short as possible. The header should only be + * used to deviate from the default values. + * + * The sector data begins immediately after the header. If the header length + * is zero then the sector data is at the beginning file. + * + * C. Sectors per track + * This is the number of sectors per track (ones based). A value of 18 + * means there are 18 sectors per track + * + * D. Side Count + * This is the number of sides in the disk image. Values of 1 or 2 are + * acceptable. If there are two sides then the tracks are interleaved. + * The first track in the image file is track zero side 1, the second + * track in the image file is track zero side 2. + * + * E. Sector size + * The is the same value that is stored in the wd179x ID field to + * determine sector size: + * + * 0x00 128 bytes + * 0x01 256 bytes + * 0x02 512 bytes + * 0x03 1024 bytes + * + * Other values are undefined. Every sector in the disk image must be the + * same size. + * + * F. First sector ID + * This determines the first sector ID for each track. Each successive + * sector adds one to the previous ID. If the first sector ID is 1, then + * the second sector has an ID of 2, and the third has an ID of 3. + * + * G. Sector Attribute Flag + * If this byte is non zero, then each sector contains an additional + * byte prepended to the sector data. If the attribute flag is zero then + * there are no extra bytes in front of the sector data. + * + * H. Sector attribute byte + * This byte is put at the begining of every sector if the header flag + * is turned on. The information this byte contains is the same as the + * status register (of the wd179x) would contain when a 'Read Sector' + * command was issued. The bit fields are defined as: + * + * Bit position: + * --------------- + * 7 6 5 4 3 2 1 0 + * | | | | | | | | + * | | | | | | | +--- Not used. Set to zero. + * | | | | | | +----- Not used. Set to zero. + * | | | | | +------- Not used. Set to zero. + * | | | | +--------- Set on CRC error. + * | | | +----------- Set if sector not found. + * | | +------------- Record type: 1 - Deleted Data Mark, 0 - Data Mark. + * | +--------------- Not Used. Set to zero. + * +----------------- Not Used. Set to zero. + * + * ----------------------------------------------------------------------- */ + +static int coco_jvc_decode_header(floppy_image *floppy, UINT64 size, + struct basicdsk_geometry *geometry) +{ + UINT8 header[256]; + UINT8 sector_attribute_flag; + UINT16 physical_bytes_per_sector; + UINT32 header_size, file_size; + struct basicdsk_geometry dummy_geometry; + + if (geometry) + memset(geometry, 0, sizeof(*geometry)); + else + geometry = &dummy_geometry; + + if (size > 0xFFFFFFFF) + return -1; + file_size = (UINT32) size; + + /* read the header */ + header_size = (UINT32) file_size % 0x100; + floppy_image_read(floppy, header, 0, header_size); + geometry->offset = header_size; + + /* byte offset 0 - sectors per track */ + geometry->sectors = (header_size > 0) ? header[0] : 18; + if (geometry->sectors <= 0) + return -1; + + /* byte offset 1 - side count */ + geometry->heads = (header_size > 1) ? header[1] : 1; + if (geometry->heads <= 0) + return -1; + + /* byte offset 2 - sector size code */ + geometry->sector_length = 128 << ((header_size > 2) ? header[2] : 1); + if (geometry->sector_length <= 0) + return -1; + + /* byte offset 3 - first sector ID */ + geometry->first_sector_id = (header_size > 3) ? header[3] : 1; + + /* byte offset 4 - sector attribute flag */ + sector_attribute_flag = (header_size > 4) ? header[4] : 0; + if (sector_attribute_flag != 0) + return -1; /* we do not support sector attribute flags */ + + physical_bytes_per_sector = geometry->sector_length; + if (sector_attribute_flag) + physical_bytes_per_sector++; + + geometry->tracks = (file_size - header_size) / geometry->sectors / geometry->heads / physical_bytes_per_sector; + + /* do we have an oddball size? reject this file if not */ + if ((file_size - header_size) % physical_bytes_per_sector) + return -1; + + /* minimum of 35 tracks; support degenerate JVC files */ + if (geometry->tracks < 35) + geometry->tracks = 35; + + return 0; +} + + + +static FLOPPY_IDENTIFY(coco_jvc_identify) +{ + UINT64 size; + size = floppy_image_size(floppy); + *vote = coco_jvc_decode_header(floppy, size, NULL) ? 0 : 100; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_CONSTRUCT(coco_jvc_construct) +{ + struct basicdsk_geometry geometry; + UINT8 header[5]; + size_t header_size; + + if (params) + { + /* create */ + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = option_resolution_lookup_int(params, PARAM_HEADS); + geometry.tracks = option_resolution_lookup_int(params, PARAM_TRACKS); + geometry.sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + geometry.first_sector_id = option_resolution_lookup_int(params, PARAM_FIRST_SECTOR_ID); + geometry.sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); + + header[0] = (UINT8) geometry.sectors; + header[1] = (UINT8) geometry.heads; + header[2] = (UINT8) compute_log2(geometry.sector_length) - 7; + header[3] = (UINT8) geometry.first_sector_id; + header[4] = 0; + + /* now that we have the header computed, figure out the header size */ + header_size = 0; + if (header[0] != 18) + header_size = 1; + if (header[1] != 1) + header_size = 2; + if (header[2] != 1) + header_size = 3; + if (header[3] != 1) + header_size = 4; + if (header[4] != 0) + header_size = 5; + + geometry.offset = header_size; + + floppy_image_write(floppy, header, 0, header_size); + } + else + { + /* load */ + if (coco_jvc_decode_header(floppy, floppy_image_size(floppy), &geometry)) + return FLOPPY_ERROR_INVALIDIMAGE; + } + return basicdsk_construct(floppy, &geometry); +} + + +/* ----------------------------------------------------------------------- + * OS-9 file format + * + * This file format is largely a hack because there are a large amount of + * disk images that do not have geometry image separate from the disk image + * itself. So we support OS-9 images with are simply basic disks whose + * geometry is determined by the disk image. + * + * OS-9 images identified by an LSN; which are simply blocks of 256 bytes + * + * LSN0 + * Byte size use + * $00 3 sectors on disk + * $03 1 track size in sectors + * $04 2 bytes in allocation bit map; typically 1bit/sector so for + * 35 tracks of 18 sectors each that's $4E (single sided disk) + * 40 tracks per side, 18 sectors each = $B4 + * $06 2 sectors per bit in allocation map; normally 1 + * $08 3 LSN of root directory; normally 2 but depends on size of + * $04 value + * $0B 2 owner's user number; normally 0 + * $0D 1 disk attributes; normally $FF + * $0E 2 psuedo random number for identification + * $10 1 disk format; typical is 3 + * %00000001 0=single side 1=double side + * %00000010 0=single density (non Coco) 1=double density + * %00000100 0=48tracks/inch 1=96tracks/inch + * $11 2 sectors per track; normal is $12 skip several not needed + * for Format + * $1A 5 date of creation Y:M:D:H:M + * $1F 32 ASCII name of disk, last letter has $80 added to it, + * the full 32 bytes do not need to be used. + * + * Allocation bit map, fill with zeros and set bits from low to high as + * sectors are used. So, for a fresh disk sectors LSN0,LSN1,LSN2, and LSN3 + * will be in use so the first byte will be $FF $C0 and all others + * in the map are $00 + * + * Root directory LSN2 + * Byte size use + * $00 1 attributes will be $BF + * $01 2 owners ID will be $0000 + * $03 5 date last modified will be creation date Y:M:D:H:M + * $08 1 link count; set to $02 + * $09 4 file size in bytes, set to $40 + * $0D 3 date created Y:M:D + * $10 3 block LSN set to current sector number+1 ie $03 in this case + * $13 2 size in sectors of directory block, set to $07 + * All other bytes in sector set to $00 + * LSN3 first sector of directory with names + * Fill sector with all $00 and then set listed bytes + * $00 2 value $2EAE which is .. with last byte+$80 + * $1F 1 value $02 LSN for start of this directory as there is + * none higher in tree + * $20 1 value $AE which is . with $80 added + * $3F 1 value $02 LSN for start of this directory + * ----------------------------------------------------------------------- */ + +static floperr_t coco_os9_readheader(floppy_image *floppy, struct basicdsk_geometry *geometry) +{ + UINT8 header[0x20]; + int total_sectors; + + floppy_image_read(floppy, header, 0, sizeof(header)); + + total_sectors = (header[0x00] << 16) | (header[0x01] << 8) | header[0x02]; + + memset(geometry, 0, sizeof(*geometry)); + geometry->first_sector_id = 1; + geometry->sector_length = 256; + geometry->sectors = (header[0x11] << 8) + header[0x12]; + geometry->heads = (header[0x10] & 0x01) ? 2 : 1; + + if (!geometry->sectors) + return FLOPPY_ERROR_INVALIDIMAGE; + + geometry->tracks = total_sectors / geometry->sectors / geometry->heads; + + if (total_sectors != geometry->tracks * geometry->sectors * geometry->heads) + return FLOPPY_ERROR_INVALIDIMAGE; + + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_os9_post_format(floppy_image *floppy, option_resolution *params) +{ + UINT8 header[0x0400]; + floperr_t err; + time_t t; + struct tm *ltime; + int heads, tracks, sectors, total_sectors; + + heads = option_resolution_lookup_int(params, PARAM_HEADS); + tracks = option_resolution_lookup_int(params, PARAM_TRACKS); + sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + total_sectors = heads * tracks * sectors; + + /* write the initial header */ + time(&t); + ltime = localtime(&t); + + memset(&header, 0, sizeof(header)); + header[0x0000] = (UINT8) (total_sectors >> 16); + header[0x0001] = (UINT8) (total_sectors >> 8); + header[0x0002] = (UINT8) (total_sectors >> 0); + header[0x0003] = (UINT8) sectors; + header[0x0004] = (UINT8) (((total_sectors + 7) / 8) >> 8); + header[0x0005] = (UINT8) (((total_sectors + 7) / 8) >> 0); + header[0x0006] = 0x00; + header[0x0007] = 0x01; + header[0x0008] = 0x00; + header[0x0009] = 0x00; + header[0x000a] = 0x02; + header[0x000b] = 0x00; + header[0x000c] = 0x00; + header[0x000d] = 0xff; + header[0x000e] = floppy_random_byte(floppy); + header[0x000f] = floppy_random_byte(floppy); + header[0x0010] = (heads == 2) ? 3 : 2; + header[0x0011] = (UINT8) (sectors >> 8); + header[0x0012] = (UINT8) (sectors >> 0); + header[0x001A] = (UINT8) ltime->tm_year; + header[0x001B] = (UINT8) ltime->tm_mon + 1; + header[0x001C] = (UINT8) ltime->tm_mday; + header[0x001D] = (UINT8) ltime->tm_hour; + header[0x001E] = (UINT8) ltime->tm_min; + header[0x001F] = 0xA0; + header[0x0100] = 0xFF; + header[0x0101] = 0xC0; + header[0x0200] = 0xBF; + header[0x0201] = 0x00; + header[0x0202] = 0x00; + header[0x0203] = (UINT8) ltime->tm_year; + header[0x0204] = (UINT8) ltime->tm_mon + 1; + header[0x0205] = (UINT8) ltime->tm_mday; + header[0x0206] = (UINT8) ltime->tm_hour; + header[0x0207] = (UINT8) ltime->tm_min; + header[0x0208] = 0x02; + header[0x0209] = 0x00; + header[0x020A] = 0x00; + header[0x020B] = 0x00; + header[0x020C] = 0x40; + header[0x020D] = (UINT8) (ltime->tm_year % 100); + header[0x020E] = (UINT8) ltime->tm_mon; + header[0x020F] = (UINT8) ltime->tm_mday; + header[0x0210] = 0x00; + header[0x0211] = 0x00; + header[0x0212] = 0x03; + header[0x0213] = 0x00; + header[0x0214] = 0x07; + header[0x0300] = 0x2E; + header[0x0301] = 0xAE; + header[0x031F] = 0x02; + header[0x0320] = 0xAE; + header[0x033F] = 0x02; + + if (total_sectors % 8) + header[0x0100 + (total_sectors / 8)] = 0xFF >> (total_sectors % 8); + + err = floppy_write_sector(floppy, 0, 0, 1, 0, &header[0x0000], 256, 0); + if (err) + return err; + + err = floppy_write_sector(floppy, 0, 0, 2, 0, &header[0x0100], 256, 0); + if (err) + return err; + + err = floppy_write_sector(floppy, 0, 0, 3, 0, &header[0x0200], 256, 0); + if (err) + return err; + + err = floppy_write_sector(floppy, 0, 0, 4, 0, &header[0x0300], 256, 0); + if (err) + return err; + + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_IDENTIFY(coco_os9_identify) +{ + struct basicdsk_geometry geometry; + *vote = coco_os9_readheader(floppy, &geometry) ? 0 : 100; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_CONSTRUCT(coco_os9_construct) +{ + floperr_t err; + struct basicdsk_geometry geometry; + + if (params) + { + /* create */ + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = option_resolution_lookup_int(params, PARAM_HEADS); + geometry.tracks = option_resolution_lookup_int(params, PARAM_TRACKS); + geometry.sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + geometry.first_sector_id = option_resolution_lookup_int(params, PARAM_FIRST_SECTOR_ID); + geometry.sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); + } + else + { + /* open */ + err = coco_os9_readheader(floppy, &geometry); + if (err) + return err; + } + + /* actually construct the image */ + err = basicdsk_construct(floppy, &geometry); + floppy_callbacks(floppy)->post_format = coco_os9_post_format; + return err; +} + + + +/* ----------------------------------------------------------------------- + * VDK file format + * + * Used by Paul Burgin's PC-Dragon emulator + * + * Offset Bytes Field Description + * ------ ----- ------------ ----------- + * 0 1 magic1 Signature byte 1 ('d') + * 1 1 magic2 Signature byte 2 ('k') + * 2 2 header_len Total header length (little endian) + * 4 1 ver_actual Version of the VDK format (0x10) + * 5 1 ver_compat Backwards compatibility version (0x10) + * 6 1 source_id Identify of the file source + * 7 1 source_ver Version of the file source + * 8 1 tracks Number of tracks + * 9 1 sides Number of sides (1-2) + * 10 1 flags Various flags + * bit 0: Write protect + * bit 1: A Lock + * bit 2: F Lock + * bit 3: Disk set + * 11 1 compression Compression flags (bits 0-2) and name length + * ----------------------------------------------------------------------- */ + + + +static int coco_vdk_decode_header(floppy_image *floppy, struct basicdsk_geometry *geometry) +{ + UINT8 header[12]; + int heads, tracks, sectors, sector_length, offset; + UINT64 size; + + size = floppy_image_size(floppy); + + floppy_image_read(floppy, header, 0, sizeof(header)); + + if (header[0] != 'd') + return -1; + if (header[1] != 'k') + return -1; + if (header[5] != 0x10) + return -1; + if (header[11] & 0x07) + return -1; + + heads = header[9]; + tracks = header[8]; + sectors = 18; + sector_length = 0x100; + + offset = ((int) header[3]) * 0x100 + header[2]; + + if (size != (heads * tracks * sectors * sector_length + offset)) + return -1; + + if (geometry) + { + memset(geometry, 0, sizeof(*geometry)); + geometry->heads = heads; + geometry->tracks = tracks; + geometry->sectors = sectors; + geometry->first_sector_id = 1; + geometry->sector_length = sector_length; + geometry->offset = offset; + } + return 0; +} + + + +static FLOPPY_IDENTIFY(coco_vdk_identify) +{ + *vote = coco_vdk_decode_header(floppy, NULL) ? 0 : 100; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_CONSTRUCT(coco_vdk_construct) +{ + struct basicdsk_geometry geometry; + UINT8 header[12]; + + if (params) + { + /* create */ + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = option_resolution_lookup_int(params, PARAM_HEADS); + geometry.tracks = option_resolution_lookup_int(params, PARAM_TRACKS); + geometry.sectors = 18; + geometry.first_sector_id = 1; + geometry.sector_length = 256; + geometry.offset = sizeof(header); + + memset(&header, 0, sizeof(header)); + header[0] = 'd'; + header[1] = 'k'; + header[2] = (UINT8) (sizeof(header) >> 0); + header[3] = (UINT8) (sizeof(header) >> 8); + header[4] = 0x10; + header[5] = 0x10; + header[8] = (UINT8) geometry.tracks; + header[9] = (UINT8) geometry.heads; + + floppy_image_write(floppy, header, 0, sizeof(header)); + } + else + { + /* load */ + if (coco_vdk_decode_header(floppy, &geometry)) + return FLOPPY_ERROR_INVALIDIMAGE; + } + return basicdsk_construct(floppy, &geometry); +} + + + +/* ----------------------------------------------------------------------- + * DMK file format + * + * David M. Keil's disk image format is aptly called an 'on disk' image + * format. This means that whatever written to the disk is enocded into + * the image file. IDAMS, sector headers, traling CRCs, and intra sector + * spacing. * + * + * HEADER DESCRIPTION: + * + * Offset Bytes Field Description + * ------ ----- ------------ ----------- + * 0 1 write_prot 0xff = Writed Protected, 0x00 = R/W + * 1 1 tracks Number of tracks + * 2 2 track_length Bytes per track (little endian) + * 4 1 disk_options Miscellaneous flags + * bit 0-3: Unused + * bit 4: 1=single sided 0=dbl + * bit 5: Unused + * bit 6: Single density? + * bit 6: Ignore density flags? + * 5 7 reserved Reserved for future use + * 12 4 real_disk_code If this is 0x12345678 (little endian) + * then access a real disk drive + * (unsupported) + * + * Each track begins with a track TOC, consisting of 64 little endian 16-bit + * integers. Each integer has the following format: + * bit 0-13: Offset from begining of track to 'FE' byte of IDAM + * Note these are always sorted from first to last. All empty + * entries are 0x00 + * bit 14: Undefined (reserved) + * bit 15: Sector double density (0=SD 1=DD) + * ----------------------------------------------------------------------- */ + +struct dmk_tag +{ + int heads; + int tracks; + UINT32 track_size; +}; + +#define DMK_HEADER_LEN 16 +#define DMK_TOC_LEN 64 +#define DMK_IDAM_LENGTH 7 +#define DMK_DATA_GAP 80 +#define DMK_LEAD_IN 32 +#define DMK_EXTRA_TRACK_LENGTH 156 + +#define dmk_idam_type(x) (x)[0] +#define dmk_idam_track(x) (x)[1] +#define dmk_idam_side(x) (x)[2] +#define dmk_idam_sector(x) (x)[3] +#define dmk_idam_sectorlength(x) (x)[4] +#define dmk_idam_crc(x) (((x)[5] << 8) + (x)[6]) +#define dmk_idam_set_crc(x, crc) (x)[5] = ((crc) >> 8); (x)[6] = ((crc) >> 0); + + +static struct dmk_tag *get_dmk_tag(floppy_image *floppy) +{ + return (dmk_tag *)floppy_tag(floppy); +} + + +static floperr_t coco_dmk_get_offset(floppy_image *floppy, int head, int track, UINT64 *offset) +{ + struct dmk_tag *tag = get_dmk_tag(floppy); + + if ((head < 0) || (head >= tag->heads) || (track < 0) || (track >= tag->tracks)) + return FLOPPY_ERROR_SEEKERROR; + + *offset = track; + *offset *= tag->heads; + *offset += head; + *offset *= tag->track_size; + *offset += DMK_HEADER_LEN; + return FLOPPY_ERROR_SUCCESS; +} + + + +static UINT32 coco_dmk_min_track_size(int sectors, int sector_length) +{ + int sector_physical_length; + sector_physical_length = 8 + 3 + DMK_IDAM_LENGTH + 22 + 12 + 3 + 1 + sector_length + 2 + 24; + return DMK_TOC_LEN * 2 + DMK_LEAD_IN + (sectors * sector_physical_length); +} + + + +static floperr_t coco_dmk_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) +{ + floperr_t err; + UINT64 track_offset; + + err = coco_dmk_get_offset(floppy, head, track, &track_offset); + if (err) + return err; + + floppy_image_read(floppy, buffer, offset + track_offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_dmk_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen) +{ + floperr_t err; + UINT64 track_offset; + + err = coco_dmk_get_offset(floppy, head, track, &track_offset); + if (err) + return err; + + floppy_image_write(floppy, buffer, offset + track_offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_dmk_get_track_data_offset(floppy_image *floppy, int head, int track, UINT64 *offset) +{ + *offset = DMK_TOC_LEN + 1; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_dmk_format_track(floppy_image *floppy, int head, int track, option_resolution *params) +{ + int sectors; + int sector_length; + int interleave; + int first_sector_id; + floperr_t err; + int physical_sector; + int logical_sector; + int track_position; + UINT16 idam_offset; + UINT16 crc; + UINT8 *track_data; + void *track_data_v; + UINT32 max_track_size; + int *sector_map = NULL; + + sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); + interleave = option_resolution_lookup_int(params, PARAM_INTERLEAVE); + first_sector_id = option_resolution_lookup_int(params, PARAM_FIRST_SECTOR_ID); + + max_track_size = get_dmk_tag(floppy)->track_size; + + if (sectors > DMK_TOC_LEN) + { + err = FLOPPY_ERROR_INTERNAL; + goto done; + } + + if (max_track_size < coco_dmk_min_track_size(sectors, sector_length)) + { + err = FLOPPY_ERROR_NOSPACE; + goto done; + } + + err = floppy_load_track(floppy, head, track, TRUE, &track_data_v, NULL); + if (err) + goto done; + track_data = (UINT8 *) track_data_v; + + /* set up sector map */ + sector_map = (int*)malloc(sectors * sizeof(*sector_map)); + if (!sector_map) + { + err = FLOPPY_ERROR_OUTOFMEMORY; + goto done; + } + memset(sector_map, 0xFF, sectors * sizeof(*sector_map)); + + physical_sector = 0; + for (logical_sector = 0; logical_sector < sectors; logical_sector++) + { + while(sector_map[physical_sector] >= 0) + { + physical_sector++; + physical_sector %= sectors; + } + + sector_map[physical_sector] = logical_sector + first_sector_id; + physical_sector += interleave + 1; + physical_sector %= sectors; + } + + /* set up track table of contents */ + physical_sector = 0; + track_position = DMK_TOC_LEN * 2 + DMK_LEAD_IN; + while(physical_sector < DMK_TOC_LEN) + { + if (physical_sector >= sectors) + { + /* no more sectors */ + idam_offset = 0; + } + else + { + /* this is a sector */ + logical_sector = sector_map[physical_sector]; + + /* write the sector */ + memset(&track_data[track_position], 0x00, 8); + track_position += 8; + + memset(&track_data[track_position], 0xA1, 3); + track_position += 3; + + idam_offset = track_position | 0x8000; + dmk_idam_type( &track_data[track_position]) = 0xFE; + dmk_idam_track( &track_data[track_position]) = track; + dmk_idam_side( &track_data[track_position]) = head; + dmk_idam_sector( &track_data[track_position]) = logical_sector; + dmk_idam_sectorlength( &track_data[track_position]) = compute_log2(sector_length / 128); + crc = ccitt_crc16(0xcdb4, &track_data[track_position], DMK_IDAM_LENGTH - 2); + dmk_idam_set_crc( &track_data[track_position], crc); + track_position += DMK_IDAM_LENGTH; + + memset(&track_data[track_position], 0x4E, 22); + track_position += 22; + + memset(&track_data[track_position], 0x00, 12); + track_position += 12; + + memset(&track_data[track_position], 0xA1, 3); + track_position += 3; + + /* write sector body */ + track_data[track_position] = 0xFB; + memset(&track_data[track_position + 1], floppy_get_filler(floppy), sector_length); + crc = ccitt_crc16(0xcdb4, &track_data[track_position], sector_length + 1); + track_data[track_position + sector_length + 1] = (UINT8) (crc >> 8); + track_data[track_position + sector_length + 2] = (UINT8) (crc >> 0); + track_position += sector_length + 3; + + /* write sector footer */ + memset(&track_data[track_position], 0x4E, 24); + track_position += 24; + } + + /* write the TOC entry */ + track_data[physical_sector * 2 + 0] = (UINT8) (idam_offset >> 0); + track_data[physical_sector * 2 + 1] = (UINT8) (idam_offset >> 8); + + physical_sector++; + } + + /* write track lead in */ + memset(&track_data[physical_sector * 2], 0x4e, DMK_LEAD_IN); + + /* write track footer */ + assert(max_track_size >= (UINT32)track_position); + memset(&track_data[track_position], 0x4e, max_track_size - track_position); + +done: + if (sector_map) + free(sector_map); + return FLOPPY_ERROR_SUCCESS; +} + + + +static int coco_dmk_get_heads_per_disk(floppy_image *floppy) +{ + return get_dmk_tag(floppy)->heads; +} + + + +static int coco_dmk_get_tracks_per_disk(floppy_image *floppy) +{ + return get_dmk_tag(floppy)->tracks; +} + + + +static UINT32 coco_dmk_get_track_size(floppy_image *floppy, int head, int track) +{ + return get_dmk_tag(floppy)->track_size; +} + + + +static floperr_t coco_dmk_seek_sector_in_track(floppy_image *floppy, int head, int track, int sector, int sector_is_index, int dirtify, UINT8 **sector_data, UINT32 *sector_length) +{ + struct dmk_tag *tag = get_dmk_tag(floppy); + floperr_t err; + UINT32 idam_offset = 0; + UINT16 calculated_crc; + size_t i; + size_t offs; + int state; + UINT8 *track_data; + void *track_data_v; + size_t track_length; + size_t sec_len; + + err = floppy_load_track(floppy, head, track, dirtify, &track_data_v, &track_length); + if (err) + return err; + track_data = (UINT8 *) track_data_v; + + /* search for matching IDAM */ + for (i = 0; i < DMK_TOC_LEN; i++) + { + idam_offset = track_data[i * 2 + 1]; + idam_offset <<= 8; + idam_offset |= track_data[i * 2 + 0]; + idam_offset &= 0x3FFF; + + if (idam_offset == 0) + { + /* we've reached the end of the road */ + i = DMK_TOC_LEN; + break; + } + + if ((idam_offset + DMK_IDAM_LENGTH) >= tag->track_size) + continue; + + calculated_crc = ccitt_crc16(0xCDB4, &track_data[idam_offset], DMK_IDAM_LENGTH - 2); + + if (calculated_crc == dmk_idam_crc(&track_data[idam_offset])) + { + if (sector_is_index) + { + /* the sector is indexed; decrement the index and go */ + if (sector-- == 0) + break; + } + else + { + /* check IDAM integrity and check for matching sector */ + if ((sector == dmk_idam_sector(&track_data[idam_offset])) +/* && (track == dmk_idam_track(&track_data[idam_offset])) */ +/* && (head == dmk_idam_side(&track_data[idam_offset])) */ + ) + break; + } + } + } + + if (i >= DMK_TOC_LEN) + return FLOPPY_ERROR_SEEKERROR; + + /* we found a matching sector ID */ + state = 0; + offs = idam_offset + DMK_IDAM_LENGTH; + + /* find pattern 0xA1A1FB; this represents the start of a data sector */ + for (i = 0; i < DMK_DATA_GAP; i++) + { + /* overflowing the track? */ + if ((i + offs) >= tag->track_size) + return FLOPPY_ERROR_SEEKERROR; + + if (track_data[offs + i] == 0xA1) + state++; + else if ((track_data[offs + i] == 0xFB) && state) + break; + else + state = 0; + } + if (i >= DMK_DATA_GAP) + return FLOPPY_ERROR_SEEKERROR; + + offs += i + 1; + sec_len = 128 << dmk_idam_sectorlength(&track_data[idam_offset]); + + if ((offs + sec_len) > track_length) + return FLOPPY_ERROR_INVALIDIMAGE; + + if (sector_data) + *sector_data = track_data + offs; + if (sector_length) + *sector_length = sec_len; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_dmk_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) +{ + return coco_dmk_seek_sector_in_track(floppy, head, track, sector, FALSE, FALSE, NULL, sector_length); +} + + + +static floperr_t coco_dmk_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) +{ + floperr_t err; + UINT32 idam_offset; + const UINT8 *track_data; + void *track_data_v; + + if (sector_index*2 >= DMK_TOC_LEN) + return FLOPPY_ERROR_SEEKERROR; + + err = floppy_load_track(floppy, head, track, FALSE, &track_data_v, NULL); + if (err) + return err; + track_data = (UINT8 *) track_data_v; + + idam_offset = track_data[sector_index * 2 + 1]; + idam_offset <<= 8; + idam_offset |= track_data[sector_index * 2 + 0]; + idam_offset &= 0x3FFF; + + if (idam_offset == 0) + return FLOPPY_ERROR_SEEKERROR; + + if (cylinder) + *cylinder = dmk_idam_track(&track_data[idam_offset]); + if (side) + *side = dmk_idam_side(&track_data[idam_offset]); + if (sector) + *sector = dmk_idam_sector(&track_data[idam_offset]); + if (sector_length) + *sector_length = 128 << dmk_idam_sectorlength(&track_data[idam_offset]); + if (flags) + /* TODO: read DAM or DDAM and determine flags */ + *flags = 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_coco_dmk_read_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen) +{ + floperr_t err; + UINT32 sector_length; + UINT16 crc_on_disk; + UINT16 calculated_crc; + UINT8 *sector_data; + + err = coco_dmk_seek_sector_in_track(floppy, head, track, sector, sector_is_index, FALSE, §or_data, §or_length); + if (err) + return err; + + crc_on_disk = sector_data[sector_length + 0]; + crc_on_disk <<= 8; + crc_on_disk += sector_data[sector_length + 1]; + + calculated_crc = ccitt_crc16(0xE295, sector_data, sector_length); + if (calculated_crc != crc_on_disk) + return FLOPPY_ERROR_INVALIDIMAGE; + + memcpy(buffer, sector_data, MIN(sector_length, buflen)); + + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_coco_dmk_write_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam) +{ + floperr_t err; + UINT32 sector_length; + UINT8 *sector_data; + UINT16 crc; + + err = coco_dmk_seek_sector_in_track(floppy, head, track, sector, sector_is_index, TRUE, §or_data, §or_length); + if (err) + return err; + + if (buflen > sector_length) + return FLOPPY_ERROR_INTERNAL; + + memcpy(sector_data, buffer, buflen); + + crc = ccitt_crc16(0xE295, sector_data, sector_length); + sector_data[sector_length + 0] = crc >> 8; + sector_data[sector_length + 1] = crc >> 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_dmk_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_coco_dmk_read_sector(floppy, head, track, sector, FALSE, buffer, buflen); +} + +static floperr_t coco_dmk_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_coco_dmk_write_sector(floppy, head, track, sector, FALSE, buffer, buflen, ddam); +} + +static floperr_t coco_dmk_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_coco_dmk_read_sector(floppy, head, track, sector, TRUE, buffer, buflen); +} + +static floperr_t coco_dmk_write_indexed_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_coco_dmk_write_sector(floppy, head, track, sector, TRUE, buffer, buflen, ddam); +} + + + +static void coco_dmk_interpret_header(floppy_image *floppy, int *heads, int *tracks, int *track_size) +{ + UINT8 header[DMK_HEADER_LEN]; + + floppy_image_read(floppy, header, 0, DMK_HEADER_LEN); + + if (tracks) + *tracks = header[1]; + if (heads) + *heads = (header[4] & 0x10) ? 1 : 2; + if (track_size) + *track_size = ((int) header[3]) * 0x100 + header[2]; +} + + + +FLOPPY_CONSTRUCT(coco_dmk_construct) +{ + struct FloppyCallbacks *callbacks; + struct dmk_tag *tag; + UINT8 header[DMK_HEADER_LEN]; + int heads, tracks, track_size, sectors, sector_length; + + if (params) + { + heads = option_resolution_lookup_int(params, PARAM_HEADS); + tracks = option_resolution_lookup_int(params, PARAM_TRACKS); + sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); + + track_size = coco_dmk_min_track_size(sectors, sector_length) + DMK_EXTRA_TRACK_LENGTH; + + memset(header, 0, sizeof(header)); + header[1] = tracks; + header[2] = track_size >> 0; + header[3] = track_size >> 8; + header[4] = (heads == 2) ? 0x00 : 0x10; + + floppy_image_write(floppy, header, 0, sizeof(header)); + } + else + { + coco_dmk_interpret_header(floppy, &heads, &tracks, &track_size); + } + + tag = (dmk_tag *)floppy_create_tag(floppy, sizeof(struct dmk_tag)); + if (!tag) + return FLOPPY_ERROR_OUTOFMEMORY; + tag->heads = heads; + tag->track_size = track_size; + tag->tracks = tracks; + + callbacks = floppy_callbacks(floppy); + callbacks->read_track = coco_dmk_read_track; + callbacks->write_track = coco_dmk_write_track; + callbacks->get_track_data_offset = coco_dmk_get_track_data_offset; + callbacks->format_track = coco_dmk_format_track; + callbacks->get_heads_per_disk = coco_dmk_get_heads_per_disk; + callbacks->get_tracks_per_disk = coco_dmk_get_tracks_per_disk; + callbacks->get_track_size = coco_dmk_get_track_size; + callbacks->get_sector_length = coco_dmk_get_sector_length; + callbacks->get_indexed_sector_info = coco_dmk_get_indexed_sector_info; + callbacks->read_sector = coco_dmk_read_sector; + callbacks->write_sector = coco_dmk_write_sector; + callbacks->read_indexed_sector = coco_dmk_read_indexed_sector; + callbacks->write_indexed_sector = coco_dmk_write_indexed_sector; + + return FLOPPY_ERROR_SUCCESS; +} + + + +FLOPPY_IDENTIFY(coco_dmk_identify) +{ + int heads, tracks, track_size; + UINT64 size, expected_size; + + size = floppy_image_size(floppy); + coco_dmk_interpret_header(floppy, &heads, &tracks, &track_size); + expected_size = DMK_HEADER_LEN + (heads * tracks * track_size); + *vote = (size == expected_size) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( coco ) + FLOPPY_OPTION( coco_jvc, "dsk", "CoCo JVC disk image", coco_jvc_identify, coco_jvc_construct, NULL, + HEADS([1]-2) + TRACKS([35]-255) + SECTORS(1-[18]-255) + SECTOR_LENGTH(128/[256]/512/1024) + FIRST_SECTOR_ID(0-[1])) + FLOPPY_OPTION( coco_os9, "os9", "CoCo OS-9 disk image", coco_os9_identify, coco_os9_construct, NULL, + HEADS([1]-2) + TRACKS([35]-255) + SECTORS(1-[18]-255) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID([1])) + FLOPPY_OPTION( coco_vdk, "vdk", "CoCo VDK disk image", coco_vdk_identify, coco_vdk_construct, NULL, + HEADS([1]-2) + TRACKS([35]-255) + SECTORS([18]) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID([1])) + FLOPPY_OPTION( coco_dmk, "dsk,dmk", "CoCo DMK disk image", coco_dmk_identify, coco_dmk_construct, NULL, + HEADS([1]-2) + TRACKS([35]-255) + SECTORS(1-[18]) + SECTOR_LENGTH(128/[256]/512/1024/2048/4096/8192) + INTERLEAVE(0-[6]-17) + FIRST_SECTOR_ID(0-[1])) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/coco_dsk.h b/src/lib/formats/coco_dsk.h new file mode 100644 index 00000000000..b217650e7bb --- /dev/null +++ b/src/lib/formats/coco_dsk.h @@ -0,0 +1,22 @@ +/********************************************************************* + + formats/coco_dsk.h + + Tandy Color Computer / Dragon disk images + +*********************************************************************/ + +#ifndef COCO_DSK_H +#define COCO_DSK_H + +#include "flopimg.h" + + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(coco); + +FLOPPY_IDENTIFY(coco_dmk_identify); +FLOPPY_CONSTRUCT(coco_dmk_construct); + +#endif /* COCO_DSK_H */ diff --git a/src/lib/formats/comx35_dsk.c b/src/lib/formats/comx35_dsk.c new file mode 100644 index 00000000000..bd29a3e31cb --- /dev/null +++ b/src/lib/formats/comx35_dsk.c @@ -0,0 +1,78 @@ +/********************************************************************* + + formats/comx35_dsk.c + + COMX35 disk images + +*********************************************************************/ + +#include "formats/comx35_dsk.h" +#include "formats/basicdsk.h" + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + FLOPPY_IDENTIFY( comx35_dsk_identify ) +-------------------------------------------------*/ + +static FLOPPY_IDENTIFY( comx35_dsk_identify ) +{ + *vote = ((floppy_image_size(floppy) == (35*1*16*128)) || (floppy_image_size(floppy) == (35*2*16*128))) ? 100 : 0; + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + FLOPPY_CONSTRUCT( comx35_dsk_construct ) +-------------------------------------------------*/ + +static FLOPPY_CONSTRUCT( comx35_dsk_construct ) +{ + UINT8 header[1]; + int heads = 1; + int cylinders = 35; + + switch (floppy_image_size(floppy)) + { + case 35*1*16*128: + heads = 1; + cylinders = 35; + break; + + case 35*2*16*128: + floppy_image_read(floppy, header, 0x12, 1); + + if (header[0] == 0x01) + { + heads = 1; + cylinders = 70; + } + else + { + heads = 2; + cylinders = 35; + } + break; + } + + struct basicdsk_geometry geometry; + memset(&geometry, 0, sizeof(geometry)); + + geometry.heads = heads; + geometry.first_sector_id = 0; + geometry.sector_length = 128; + geometry.tracks = cylinders; + geometry.sectors = 16; + + return basicdsk_construct(floppy, &geometry); +} + +/*------------------------------------------------- + FLOPPY_OPTIONS( comx35 ) +-------------------------------------------------*/ + +FLOPPY_OPTIONS_START( comx35 ) + FLOPPY_OPTION( comx35, "img", "COMX35 floppy disk image", comx35_dsk_identify, comx35_dsk_construct, NULL, NULL ) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/comx35_dsk.h b/src/lib/formats/comx35_dsk.h new file mode 100644 index 00000000000..1daffb3b8d1 --- /dev/null +++ b/src/lib/formats/comx35_dsk.h @@ -0,0 +1,18 @@ +/********************************************************************* + + formats/comx35_dsk.h + + COMX35 disk images + +*********************************************************************/ + +#ifndef COMX35_DSK_H +#define COMX35_DSK_H + +#include "flopimg.h" + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(comx35); + +#endif /* COMX35_DSK_H */ diff --git a/src/lib/formats/coupedsk.c b/src/lib/formats/coupedsk.c new file mode 100644 index 00000000000..1a934f8d2ef --- /dev/null +++ b/src/lib/formats/coupedsk.c @@ -0,0 +1,488 @@ +/************************************************************************* + + formats/coupedsk.c + + SAM Coupe disk image formats + +**************************************************************************/ + +#include "coupedsk.h" +#include "formats/basicdsk.h" + + +#define SAD_HEADER_LEN 22 +#define SAD_SIGNATURE "Aley's disk backup" + +#define SDF_TRACKSIZE (512 * 12) + + +struct sdf_tag +{ + UINT8 heads; + UINT8 tracks; +}; + + + +/************************************* + * + * MGT disk image format + * + *************************************/ + +/* + Straight rip without any header + + Data: + + Side 0 Track 0 + Side 1 Track 0 + Side 0 Track 1 + Side 1 Track 1 + ... +*/ + + +FLOPPY_CONSTRUCT( coupe_mgt_construct ) +{ + struct basicdsk_geometry geometry; + + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = 2; + geometry.tracks = 80; + geometry.sector_length = 512; + geometry.first_sector_id = 1; + + if (params) + { + /* create */ + geometry.sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + } + else + { + /* load */ + UINT64 size = floppy_image_size(floppy); + + /* verify size */ + if (size != 737280 && size != 819200) + return FLOPPY_ERROR_INVALIDIMAGE; + + geometry.sectors = size / 81920; + + } + + return basicdsk_construct(floppy, &geometry); +} + + +FLOPPY_IDENTIFY( coupe_mgt_identify ) +{ + UINT64 size; + + size = floppy_image_size(floppy); + + *vote = (size == 737280 || size == 819200) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +/************************************* + * + * SAD disk image format + * + *************************************/ + +/* + Header (22 bytes): + + 0-17 "Aley's disk backup" + 18 number of sides + 19 number of tracks + 20 number of sectors per track + 21 sector size divided by 64 + + Data: + + Side 0 Track 0 + Side 0 Track 1 + Side 0 Track 2 + ... + Side 1 Track 0 + Side 1 Track 1 + ... +*/ + + +static UINT64 coupe_sad_translate_offset(floppy_image *floppy, + const struct basicdsk_geometry *geom, int track, int head, int sector) +{ + return head * geom->tracks * geom->sectors + geom->sectors * track + sector; +} + + +static void coupe_sad_interpret_header(floppy_image *floppy, + int *heads, int *tracks, int *sectors, int *sector_size) +{ + UINT8 header[SAD_HEADER_LEN]; + + floppy_image_read(floppy, header, 0, SAD_HEADER_LEN); + + *heads = header[18]; + *tracks = header[19]; + *sectors = header[20]; + *sector_size = header[21] << 6; +} + + +FLOPPY_CONSTRUCT( coupe_sad_construct ) +{ + struct basicdsk_geometry geometry; + int heads, tracks, sectors, sector_length; + + memset(&geometry, 0, sizeof(geometry)); + + if (params) + { + /* create */ + UINT8 header[SAD_HEADER_LEN]; + + /* get format options */ + heads = option_resolution_lookup_int(params, PARAM_HEADS); + tracks = option_resolution_lookup_int(params, PARAM_TRACKS); + sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); + + /* build the header */ + memset(header, 0, SAD_HEADER_LEN); + memcpy(header, SAD_SIGNATURE, 18); + header[18] = heads; + header[19] = tracks; + header[20] = sectors; + header[21] = sector_length >> 6; + + /* and write it to disk */ + floppy_image_write(floppy, header, 0, sizeof(header)); + } + else + { + /* load */ + coupe_sad_interpret_header(floppy, &heads, &tracks, §ors, §or_length); + } + + /* fill in the data */ + geometry.offset = SAD_HEADER_LEN; + geometry.heads = heads; + geometry.tracks = tracks; + geometry.sectors = sectors; + geometry.first_sector_id = 1; + geometry.sector_length = sector_length; + geometry.translate_offset = coupe_sad_translate_offset; + + return basicdsk_construct(floppy, &geometry); +} + + +FLOPPY_IDENTIFY( coupe_sad_identify ) +{ + int heads, tracks, sectors, sector_size; + UINT64 size, calculated_size; + + size = floppy_image_size(floppy); + + /* read values from SAD header */ + coupe_sad_interpret_header(floppy, &heads, &tracks, §ors, §or_size); + + /* calculate expected disk image size */ + calculated_size = SAD_HEADER_LEN + heads * tracks * sectors * sector_size; + + *vote = (size == calculated_size) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +/************************************* + * + * SDF disk image format + * + * TODO: wd17xx status codes are + * currently ignored + * + *************************************/ + +static int coupe_sdf_get_heads_per_disk(floppy_image *floppy) +{ + struct sdf_tag *tag = (sdf_tag *)floppy_tag(floppy); + return tag->heads; +} + + +static int coupe_sdf_get_tracks_per_disk(floppy_image *floppy) +{ + struct sdf_tag *tag = (sdf_tag *)floppy_tag(floppy); + return tag->tracks; +} + + +static UINT32 coupe_sdf_get_track_size(floppy_image *floppy, int head, int track) +{ + return SDF_TRACKSIZE; +} + + +static floperr_t coupe_sdf_get_offset(floppy_image *floppy, + int head, int track, UINT64 *offset) +{ + struct sdf_tag *tag = (sdf_tag *)floppy_tag(floppy); + + if (head > tag->heads || track > tag->tracks) + return FLOPPY_ERROR_SEEKERROR; + + *offset = (head * tag->tracks + track) * SDF_TRACKSIZE; + + return FLOPPY_ERROR_SUCCESS; +} + + +static floperr_t coupe_sdf_read_track(floppy_image *floppy, + int head, int track, UINT64 offset, void *buffer, size_t buflen) +{ + floperr_t err; + UINT64 track_offset; + + /* get the offset to this track */ + err = coupe_sdf_get_offset(floppy, head, track, &track_offset); + if (err) return err; + + /* read track data into buffer */ + floppy_image_read(floppy, buffer, offset + track_offset, buflen); + + return FLOPPY_ERROR_SUCCESS; +} + + +static floperr_t coupe_sdf_write_track(floppy_image *floppy, + int head, int track, UINT64 offset, const void *buffer, size_t buflen) +{ + floperr_t err; + UINT64 track_offset; + + /* get the offset to this track */ + err = coupe_sdf_get_offset(floppy, head, track, &track_offset); + if (err) return err; + + /* write buffer to image */ + floppy_image_write(floppy, buffer, offset + track_offset, buflen); + + return FLOPPY_ERROR_SUCCESS; +} + + +static floperr_t coupe_sdf_get_sector_offset(floppy_image *floppy, + int head, int track, int sector, UINT64 *offset) +{ + floperr_t err; + UINT8 buffer[SDF_TRACKSIZE]; + int i, buffer_pos = 1; + + /* get track data */ + err = coupe_sdf_read_track(floppy, head, track, 0, &buffer, SDF_TRACKSIZE); + if (err) return err; + + /* check if the sector is available in this track */ + if (sector >= buffer[0]) + return FLOPPY_ERROR_SEEKERROR; + + /* find the right sector in this track */ + for (i = 0; i < buffer[0]; i++) + { + int sector_number = buffer[buffer_pos + 4]; + + if (sector_number - 1 == sector) + { + *offset = buffer_pos; + return FLOPPY_ERROR_SUCCESS; + } + + buffer_pos += (128 << buffer[buffer_pos + 5]) + 8; + } + + return FLOPPY_ERROR_INVALIDIMAGE; +} + + +static floperr_t coupe_sdf_get_total_sector_offset(floppy_image *floppy, + int head, int track, int sector, UINT64 *offset) +{ + floperr_t err; + UINT64 track_offset, sector_offset; + + /* get offset to the track start */ + err = coupe_sdf_get_offset(floppy, head, track, &track_offset); + if (err) return err; + + /* get offset to the start of the sector */ + err = coupe_sdf_get_sector_offset(floppy, head, track, sector, §or_offset); + if (err) return err; + + *offset = track_offset + sector_offset; + + return FLOPPY_ERROR_SUCCESS; +} + + +static floperr_t coupe_sdf_get_sector_length(floppy_image *floppy, + int head, int track, int sector, UINT32 *sector_length) +{ + floperr_t err; + UINT8 buffer[SDF_TRACKSIZE]; + UINT64 offset; + + /* get track data */ + err = coupe_sdf_read_track(floppy, head, track, 0, &buffer, SDF_TRACKSIZE); + if (err) return err; + + /* get offset to the start of the sector */ + err = coupe_sdf_get_sector_offset(floppy, head, track, sector, &offset); + if (err) return err; + + /* get size */ + *sector_length = 128 << buffer[offset + 5]; + + return FLOPPY_ERROR_SUCCESS; +} + + +static floperr_t coupe_sdf_get_indexed_sector_info(floppy_image *floppy, + int head, int track, int sector_index, + int *cylinder, int *side, int *sector, UINT32 *sector_length, + unsigned long *flags) +{ + floperr_t err; + UINT8 buffer[SDF_TRACKSIZE]; + UINT64 offset; + + /* get track data */ + err = coupe_sdf_read_track(floppy, head, track, 0, &buffer, SDF_TRACKSIZE); + if (err) return err; + + /* get offset to the start of the sector */ + err = coupe_sdf_get_sector_offset(floppy, head, track, sector_index, &offset); + if (err) return err; + + /* extract data */ + if (cylinder) *cylinder = buffer[offset + 2]; + if (side) *side = buffer[offset + 3]; + if (sector) *sector = buffer[offset + 4]; + if (sector_length) *sector_length = 128 << buffer[offset + 5]; + if (flags) *flags = 0; /* TODO: read DAM or DDAM and determine flags */ + + return FLOPPY_ERROR_SUCCESS; +} + + +static floperr_t coupe_sdf_read_indexed_sector(floppy_image *floppy, + int head, int track, int sector, void *buffer, size_t buflen) +{ + floperr_t err; + UINT64 offset; + + err = coupe_sdf_get_total_sector_offset(floppy, head, track, sector, &offset); + if (err) return err; + + /* read sector data into buffer */ + floppy_image_read(floppy, buffer, offset + 8, buflen); + + return FLOPPY_ERROR_SUCCESS; +} + + +static floperr_t coupe_sdf_write_indexed_sector(floppy_image *floppy, + int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + floperr_t err; + UINT64 offset; + + err = coupe_sdf_get_total_sector_offset(floppy, head, track, sector, &offset); + if (err) return err; + + /* write buffer into image */ + floppy_image_write(floppy, buffer, offset + 8, buflen); + + return FLOPPY_ERROR_SUCCESS; +} + + +static void coupe_sdf_interpret_header(floppy_image *floppy, int *heads, int *tracks) +{ + UINT64 size = floppy_image_size(floppy); + + if (size % SDF_TRACKSIZE == 0) + { + *heads = 2; + *tracks = size / (SDF_TRACKSIZE * 2); + } + else + { + *heads = 0; + *tracks = 0; + } +} + + +FLOPPY_CONSTRUCT( coupe_sdf_construct ) +{ + struct FloppyCallbacks *callbacks; + struct sdf_tag *tag; + int heads, tracks; + + if (params) + { + /* create */ + return FLOPPY_ERROR_UNSUPPORTED; + } + else + { + /* load */ + coupe_sdf_interpret_header(floppy, &heads, &tracks); + } + + tag = (sdf_tag *)floppy_create_tag(floppy, sizeof(struct sdf_tag)); + + if (!tag) + return FLOPPY_ERROR_OUTOFMEMORY; + + tag->heads = heads; + tag->tracks = tracks; + + callbacks = floppy_callbacks(floppy); + callbacks->read_track = coupe_sdf_read_track; + callbacks->write_track = coupe_sdf_write_track; + callbacks->get_heads_per_disk = coupe_sdf_get_heads_per_disk; + callbacks->get_tracks_per_disk = coupe_sdf_get_tracks_per_disk; + callbacks->get_track_size = coupe_sdf_get_track_size; + callbacks->get_sector_length = coupe_sdf_get_sector_length; + callbacks->read_indexed_sector = coupe_sdf_read_indexed_sector; + callbacks->write_indexed_sector = coupe_sdf_write_indexed_sector; + callbacks->get_indexed_sector_info = coupe_sdf_get_indexed_sector_info; + + return FLOPPY_ERROR_SUCCESS; +} + + +FLOPPY_IDENTIFY( coupe_sdf_identify ) +{ + int heads, tracks; + + /* read header values */ + coupe_sdf_interpret_header(floppy, &heads, &tracks); + + /* check for sensible values */ + if (heads > 0 && heads < 3 && tracks > 0 && tracks < 84) + *vote = 100; + else + *vote = 0; + + return FLOPPY_ERROR_SUCCESS; +} diff --git a/src/lib/formats/coupedsk.h b/src/lib/formats/coupedsk.h new file mode 100644 index 00000000000..ce570a7df4e --- /dev/null +++ b/src/lib/formats/coupedsk.h @@ -0,0 +1,23 @@ +/************************************************************************* + + formats/coupedsk.h + + SAM Coupe disk image formats + +**************************************************************************/ + +#ifndef __COUPEDSK_H__ +#define __COUPEDSK_H__ + +#include "flopimg.h" + + +FLOPPY_CONSTRUCT( coupe_mgt_construct ); +FLOPPY_IDENTIFY( coupe_mgt_identify ); +FLOPPY_CONSTRUCT( coupe_sad_construct ); +FLOPPY_IDENTIFY( coupe_sad_identify ); +FLOPPY_CONSTRUCT( coupe_sdf_construct ); +FLOPPY_IDENTIFY( coupe_sdf_identify ); + + +#endif /* __COUPEDSK_H__ */ diff --git a/src/lib/formats/cpis_dsk.c b/src/lib/formats/cpis_dsk.c new file mode 100644 index 00000000000..17bfcd11221 --- /dev/null +++ b/src/lib/formats/cpis_dsk.c @@ -0,0 +1,93 @@ +/********************************************************************* + + formats/cpis_dsk.c + + Telenova Compis disk images + +*********************************************************************/ + +#include <string.h> + +#include "formats/cpis_dsk.h" +#include "formats/basicdsk.h" + + +static int compis_get_tracks_and_sectors(floppy_image *floppy, int *tracks, int *sectors) +{ + switch(floppy_image_size(floppy)) { + case 0x50000: /* 320 KB */ + *tracks = 40; + *sectors = 8; + break; + + case 0x5a000: /* 360 KB */ + *tracks = 40; + *sectors = 9; + break; + + case 0xa0000: /* 640 KB */ + *tracks = 80; + *sectors = 8; + break; + + case 0xb4000: /* 720 KB */ + *tracks = 80; + *sectors = 9; + break; + + case 0x12c000: /* 1200 KB */ + *tracks = 80; + *sectors = 15; + break; + + default: + return 0; + } + return 1; +} + + + +static FLOPPY_IDENTIFY(compis_dsk_identify) +{ + int dummy; + *vote = compis_get_tracks_and_sectors(floppy, &dummy, &dummy) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_CONSTRUCT(compis_dsk_construct) +{ + struct basicdsk_geometry geometry; + + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = 1; + geometry.first_sector_id = 1; + geometry.sector_length = 512; + + if (params) + { + /* create */ + geometry.tracks = option_resolution_lookup_int(params, PARAM_TRACKS); + geometry.sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + } + else + { + /* open */ + if (!compis_get_tracks_and_sectors(floppy, &geometry.tracks, &geometry.sectors)) + return FLOPPY_ERROR_INVALIDIMAGE; + } + + return basicdsk_construct(floppy, &geometry); +} + + + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( compis ) + FLOPPY_OPTION( compis_dsk, "dsk", "Compis floppy disk image", compis_dsk_identify, compis_dsk_construct, NULL, + TRACKS(40/[80]) + SECTORS(8/[9]/15)) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/cpis_dsk.h b/src/lib/formats/cpis_dsk.h new file mode 100644 index 00000000000..b16aa07a844 --- /dev/null +++ b/src/lib/formats/cpis_dsk.h @@ -0,0 +1,19 @@ +/********************************************************************* + + formats/cpis_dsk.h + + Telenova Compis disk images + +*********************************************************************/ + +#ifndef CPIS_DSK_H +#define CPIS_DSK_H + +#include "flopimg.h" + + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(compis); + +#endif /* CPIS_DSK_H */ diff --git a/src/mame/formats/cqm_dsk.c b/src/lib/formats/cqm_dsk.c index 76011ba970b..c8c335fd076 100644 --- a/src/mame/formats/cqm_dsk.c +++ b/src/lib/formats/cqm_dsk.c @@ -7,9 +7,7 @@ *********************************************************************/ #include <string.h> -#include "emu.h" -#include "imagedev/flopimg.h" -#include "imagedev/flopdrv.h" +#include "flopimg.h" #define CQM_HEADER_SIZE 133 diff --git a/src/lib/formats/csw_cas.c b/src/lib/formats/csw_cas.c new file mode 100644 index 00000000000..ec59399db8b --- /dev/null +++ b/src/lib/formats/csw_cas.c @@ -0,0 +1,329 @@ +/* +CSW format +---------- +Header Description + +Offset Value Type Description +0x00 (note) ASCII 22 bytes "Compressed Square Wave" signature +0x16 0x1A BYTE Terminator code +0x17 0x02 BYTE CSW major revision number +0x18 0x00 BYTE CSW minor revision number +0x19 DWORD Sample rate +0x1D DWORD Total number of pulses (after decompression) +0x21 BYTE Compression type 0x01: RLE 0x02: Z-RLE +0x22 BYTE Flags b0: initial polarity: if set, the signal starts at logical high +0x23 HDR BYTE Header extension length in bytes (0x00) +0x24 ASCII 16 bytes free use +0x34 BYTE Start of Header is HDR>0 + + + +*/ + +#include <string.h> + +#include "zlib.h" +#include "uef_cas.h" +#include "csw_cas.h" + + +#define CSW_WAV_FREQUENCY 44100 + +static const UINT8 CSW_HEADER[] = { "Compressed Square Wave" }; + +static UINT32 get_leuint32(const void *ptr) +{ + UINT32 value; + memcpy(&value, ptr, sizeof(value)); + return LITTLE_ENDIANIZE_INT32(value); +} + +static int mycaslen; + +static int csw_cas_to_wav_size( const UINT8 *casdata, int caslen ) { +UINT32 SampleRate; +UINT32 NumberOfPulses; +UINT8 MajorRevision; +UINT8 MinorRevision; +UINT8 CompressionType; +UINT8 Flags; +UINT8 HeaderExtensionLength; +UINT8 *gz_ptr = NULL; + +int total_size; +z_stream d_stream; +int err; +UINT8 *in_ptr; +int bsize=0; + + if ( memcmp( casdata, CSW_HEADER, sizeof(CSW_HEADER) ) ) { + logerror( "csw_cas_to_wav_size: cassette image has incompatible header\n" ); + goto cleanup; + } + + if (casdata[0x16]!=0x1a) { + logerror( "csw_cas_to_wav_size: Terminator Code Not Found\n" ); + goto cleanup; + } + + MajorRevision=casdata[0x17]; + MinorRevision=casdata[0x18]; + + logerror("Version %d : %d\n",MajorRevision,MinorRevision); + + if (casdata[0x17]!=2){ + logerror( "csw_cas_to_wav_size: Unsuported Major Version\n" ); + goto cleanup; + } + + SampleRate=get_leuint32(casdata+0x19); + logerror("Sample rate %d\n",SampleRate); + + NumberOfPulses=get_leuint32(casdata+0x1d); + logerror("Number Of Pulses %d\n",NumberOfPulses); + + + CompressionType=casdata[0x21]; + Flags=casdata[0x22]; + HeaderExtensionLength=casdata[0x23]; + + logerror("CompressionType %d Flast %d HeaderExtensionLength %d\n",CompressionType,Flags,HeaderExtensionLength); + + mycaslen=caslen; + //from here on down for now I am assuming it is compressed csw file. + in_ptr = (UINT8*) casdata+0x34+HeaderExtensionLength; + + gz_ptr = (UINT8*)malloc( 8 ); + + + d_stream.next_in = (unsigned char *)in_ptr; + d_stream.avail_in = caslen - ( in_ptr - casdata ); + d_stream.total_in=0; + + d_stream.next_out = gz_ptr; + d_stream.avail_out = 1; + d_stream.total_out=0; + + d_stream.zalloc = 0; + d_stream.zfree = 0; + d_stream.opaque = 0; + d_stream.data_type=0; + + err = inflateInit( &d_stream ); + if ( err != Z_OK ) { + logerror( "inflateInit2 error: %d\n", err ); + goto cleanup; + } + + + total_size=1; + do + { + d_stream.next_out = gz_ptr; + d_stream.avail_out=1; + err=inflate( &d_stream, Z_SYNC_FLUSH ); + if (err==Z_OK) { + bsize=gz_ptr[0]; + if (bsize==0) { + d_stream.avail_out=4; + d_stream.next_out = gz_ptr; + err=inflate( &d_stream, Z_SYNC_FLUSH ); + bsize=get_leuint32(gz_ptr); + } + total_size=total_size+bsize; + } + } + while (err==Z_OK); + + if ( err != Z_STREAM_END ) { + logerror( "inflate error: %d\n", err ); + goto cleanup; + } + + err = inflateEnd( &d_stream ); + if ( err != Z_OK ) { + logerror( "inflateEnd error: %d\n", err ); + goto cleanup; + } + + + + + + + if ( gz_ptr ) { + free( gz_ptr ); + gz_ptr = NULL; + } + + + + return total_size; + +cleanup: + if ( gz_ptr ) { + free( gz_ptr ); + gz_ptr = NULL; + } + return -1; + +} + +static int csw_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) { +UINT32 SampleRate; +UINT32 NumberOfPulses; +UINT8 CompressionType; +UINT8 Flags; +UINT8 HeaderExtensionLength; +INT8 Bit; + +UINT8 *gz_ptr = NULL; +int total_size; +z_stream d_stream; +int err; +UINT8 *in_ptr; +int bsize=0; +int i; + + + logerror("Length %d\n",length); + + SampleRate=get_leuint32(bytes+0x19); + logerror("Sample rate %d\n",SampleRate); + + NumberOfPulses=get_leuint32(bytes+0x1d); + logerror("Number Of Pulses %d\n",NumberOfPulses); + + CompressionType=bytes[0x21]; + Flags=bytes[0x22]; + HeaderExtensionLength=bytes[0x23]; + + if ((Flags&0)==0) + { + Bit=-100; + } else { + Bit=100; + } + + logerror("CompressionType %d Flast %d HeaderExtensionLength %d\n",CompressionType,Flags,HeaderExtensionLength); + + + //from here on down for now I am assuming it is compressed csw file. + in_ptr = (UINT8*) bytes+0x34+HeaderExtensionLength; + + gz_ptr = (UINT8*)malloc( 8 ); + + + d_stream.next_in = (unsigned char *)in_ptr; + d_stream.avail_in = mycaslen - ( in_ptr - bytes ); + d_stream.total_in=0; + + d_stream.next_out = gz_ptr; + d_stream.avail_out = 1; + d_stream.total_out=0; + + d_stream.zalloc = 0; + d_stream.zfree = 0; + d_stream.opaque = 0; + d_stream.data_type=0; + + err = inflateInit( &d_stream ); + if ( err != Z_OK ) { + logerror( "inflateInit2 error: %d\n", err ); + goto cleanup; + } + + total_size=0; + + do + { + d_stream.next_out = gz_ptr; + d_stream.avail_out=1; + err=inflate( &d_stream, Z_SYNC_FLUSH ); + if (err==Z_OK) { + bsize=gz_ptr[0]; + if (bsize==0) { + d_stream.avail_out=4; + d_stream.next_out = gz_ptr; + err=inflate( &d_stream, Z_SYNC_FLUSH ); + bsize=get_leuint32(gz_ptr); + } + for (i=0;i<bsize;i++) + { + buffer[total_size++]=Bit; + } + Bit=-Bit; + } + } + while (err==Z_OK); + + if ( err != Z_STREAM_END ) { + logerror( "inflate error: %d\n", err ); + goto cleanup; + } + + err = inflateEnd( &d_stream ); + if ( err != Z_OK ) { + logerror( "inflateEnd error: %d\n", err ); + goto cleanup; + } + + + + + + + if ( gz_ptr ) { + free( gz_ptr ); + gz_ptr = NULL; + } + + + + + return length; + + + + cleanup: + if ( gz_ptr ) { + free( gz_ptr ); + gz_ptr = NULL; + } + return -1; +} + + +static const struct CassetteLegacyWaveFiller csw_legacy_fill_wave = { + csw_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + csw_cas_to_wav_size, /* chunk_sample_calc */ + CSW_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t csw_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) { + return cassette_legacy_identify( cassette, opts, &csw_legacy_fill_wave ); +} + +static casserr_t csw_cassette_load( cassette_image *cassette ) { + return cassette_legacy_construct( cassette, &csw_legacy_fill_wave ); +} + +static const struct CassetteFormat csw_cassette_format = { + "csw", + csw_cassette_identify, + csw_cassette_load, + NULL +}; + +CASSETTE_FORMATLIST_START(csw_cassette_formats) + CASSETTE_FORMAT(csw_cassette_format) +CASSETTE_FORMATLIST_END + +CASSETTE_FORMATLIST_START(bbc_cassette_formats) + CASSETTE_FORMAT(csw_cassette_format) + CASSETTE_FORMAT(uef_cassette_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/csw_cas.h b/src/lib/formats/csw_cas.h new file mode 100644 index 00000000000..89e7800aa51 --- /dev/null +++ b/src/lib/formats/csw_cas.h @@ -0,0 +1,15 @@ +/* + csw_cas.h + + Format code for csw cassette files +*/ + +#ifndef CSW_CAS_H +#define CSW_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(csw_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(bbc_cassette_formats); + +#endif /* CSW_CAS_H */ diff --git a/src/lib/formats/d64_dsk.c b/src/lib/formats/d64_dsk.c new file mode 100644 index 00000000000..0515f8ecda8 --- /dev/null +++ b/src/lib/formats/d64_dsk.c @@ -0,0 +1,845 @@ +/********************************************************************* + + formats/d64_dsk.c + + Floppy format code for Commodore 1541/2040/8050 disk images + +*********************************************************************/ + +/* + + TODO: + + - fix MAX_ERROR_SECTORS, it is too small for d80/d82 + - write to disk + - disk errors 24, 25, 26, 28, 74 + - variable gaps + +*/ + +#define XTAL_16MHz 16000000 +#define XTAL_12MHz 12000000 +#include "g64_dsk.h" +#include "flopimg.h" +#include "d64_dsk.h" + +/*************************************************************************** + PARAMETERS +***************************************************************************/ + +#define LOG 1 + +#define MAX_HEADS 2 +#define MAX_TRACKS 84 +#define MAX_ERROR_SECTORS 802 // TODO this is too small for .d80 files +#define SECTOR_SIZE 256 +#define SECTOR_SIZE_GCR 368 + +#define INVALID_OFFSET 0xbadbad + +#define D64_SIZE_35_TRACKS 174848 +#define D64_SIZE_35_TRACKS_WITH_ERRORS 175531 +#define D64_SIZE_40_TRACKS 196608 +#define D64_SIZE_40_TRACKS_WITH_ERRORS 197376 +#define D64_SIZE_42_TRACKS 205312 +#define D64_SIZE_42_TRACKS_WITH_ERRORS 206114 +#define D67_SIZE_35_TRACKS 176640 +#define D71_SIZE_70_TRACKS 349696 +#define D71_SIZE_70_TRACKS_WITH_ERRORS 351062 +#define D80_SIZE_77_TRACKS 533248 +#define D82_SIZE_154_TRACKS 1066496 + +enum +{ + DOS1, + DOS2, + DOS25 +}; + +static const char *const DOS_VERSION[] = { "1.0", "2.0", "2.5" }; + +enum +{ + ERROR_00 = 1, + ERROR_20, /* header block not found */ + ERROR_21, /* no sync character */ + ERROR_22, /* data block not present */ + ERROR_23, /* checksum error in data block */ + ERROR_24, /* write verify (on format) UNIMPLEMENTED */ + ERROR_25, /* write verify error UNIMPLEMENTED */ + ERROR_26, /* write protect on UNIMPLEMENTED */ + ERROR_27, /* checksum error in header block */ + ERROR_28, /* write error UNIMPLEMENTED */ + ERROR_29, /* disk ID mismatch */ + ERROR_74, /* disk not ready (no device 1) UNIMPLEMENTED */ +}; + +static const char *const ERROR_CODE[] = { "00", "00", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "74" }; + +static const UINT8 bin_2_gcr[] = +{ + 0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17, + 0x09, 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15 +}; + +/* This could be of use if we ever implement saving in .d64 format, to convert back GCR -> d64 */ +/* +static const int gcr_2_bin[] = +{ + -1, -1, -1, -1, + -1, -1, -1, -1, + -1, 0x08, 0x00, 0x01, + -1, 0x0c, 0x04, 0x05, + -1, -1, 0x02, 0x03, + -1, 0x0f, 0x06, 0x07, + -1, 0x09, 0x0a, 0x0b, + -1, 0x0d, 0x0e, -1 +}; +*/ + +static const int DOS1_SECTORS_PER_TRACK[] = +{ + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 20, 20, 20, 20, 20, 20, 20, + 18, 18, 18, 18, 18, 18, + 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, + 17, 17 +}; + +static const int DOS1_SPEED_ZONE[] = +{ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0 +}; + +static const int DOS2_SECTORS_PER_TRACK[] = +{ + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 19, 19, 19, 19, 19, 19, 19, + 18, 18, 18, 18, 18, 18, + 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, + 17, 17 +}; + +static const int DOS25_SECTORS_PER_TRACK[] = +{ + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, /* 1-39 */ + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, /* 40-53 */ + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, /* 54-64 */ + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, /* 65-77 */ + 23, 23, 23, 23, 23, 23, 23 /* 78-84 */ +}; + +static const int DOS25_SPEED_ZONE[] = +{ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* 1-39 */ + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* 40-53 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 54-64 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 65-77 */ + 0, 0, 0, 0, 0, 0, 0 /* 78-84 */ +}; + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct d64dsk_tag +{ + int dos; /* CBM DOS version */ + int heads; /* number of physical heads */ + int tracks; /* number of physical tracks */ + int dos_tracks; /* number of logical tracks */ + int track_offset[MAX_HEADS][MAX_TRACKS]; /* offset within image for each physical track */ + UINT32 speed_zone[MAX_TRACKS]; /* speed zone for each physical track */ + bool has_errors; /* flag to check for available error codes */ + UINT8 error[MAX_ERROR_SECTORS]; /* error code for each logical sector */ + int error_offset[MAX_HEADS][MAX_TRACKS]; /* offset within error array for sector 0 of each logical track */ + + UINT8 id1, id2; /* DOS disk format ID */ +}; + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +INLINE struct d64dsk_tag *get_tag(floppy_image *floppy) +{ + return (d64dsk_tag *)floppy_tag(floppy); +} + +INLINE float get_dos_track(int track) +{ + return ((float)track / 2) + 1; +} + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + d64_get_heads_per_disk - returns the number + of heads in the disk image +-------------------------------------------------*/ + +static int d64_get_heads_per_disk(floppy_image *floppy) +{ + return get_tag(floppy)->heads; +} + +/*------------------------------------------------- + d64_get_tracks_per_disk - returns the number + of DOS tracks in the disk image +-------------------------------------------------*/ + +static int d64_get_tracks_per_disk(floppy_image *floppy) +{ + return get_tag(floppy)->tracks; +} + +/*------------------------------------------------- + d64_get_sectors_per_track - returns the number + of sectors per given track +-------------------------------------------------*/ + +static int d64_get_sectors_per_track(floppy_image *floppy, int head, int track) +{ + int sectors_per_track = 0; + + switch (get_tag(floppy)->dos) + { + case DOS1: sectors_per_track = DOS1_SECTORS_PER_TRACK[track / 2]; break; + case DOS2: sectors_per_track = DOS2_SECTORS_PER_TRACK[track / 2]; break; + case DOS25: sectors_per_track = DOS25_SECTORS_PER_TRACK[track]; break; + } + + return sectors_per_track; +} + +/*------------------------------------------------- + get_track_offset - returns the offset within + the disk image for a given track +-------------------------------------------------*/ + +static floperr_t get_track_offset(floppy_image *floppy, int head, int track, UINT64 *offset) +{ + struct d64dsk_tag *tag = get_tag(floppy); + UINT64 offs = 0; + + if ((track < 0) || (track >= tag->tracks)) + return FLOPPY_ERROR_SEEKERROR; + + offs = tag->track_offset[head][track]; + + if (offset) + *offset = offs; + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + d64_get_track_size - returns the track size +-------------------------------------------------*/ + +static UINT32 d64_get_track_size(floppy_image *floppy, int head, int track) +{ + struct d64dsk_tag *tag = get_tag(floppy); + + if (tag->track_offset[head][track] == INVALID_OFFSET) + return 0; + + /* determine number of sectors per track */ + int sectors_per_track = d64_get_sectors_per_track(floppy, head, track); + + /* allocate temporary GCR track data buffer */ + UINT32 track_length = sectors_per_track * SECTOR_SIZE_GCR; + + return track_length; +} + +/*------------------------------------------------- + get_sector_error_code - returns the error + code for the given sector +-------------------------------------------------*/ + +static int get_sector_error_code(floppy_image *floppy, int head, int dos_track, int sector) +{ + struct d64dsk_tag *tag = get_tag(floppy); + + if (!tag->has_errors) + return ERROR_00; + + int sector_error = tag->error[tag->error_offset[head][dos_track] + sector]; // TODO index out of bounds!!! + + if (sector_error != ERROR_00) + { + logerror("D64 error %s head %u track %u sector %u\n", ERROR_CODE[sector_error], head, dos_track, sector); + } + + return sector_error; +} + +/*------------------------------------------------- + gcr_double_2_gcr - GCR decodes given data +-------------------------------------------------*/ + +/* gcr_double_2_gcr takes 4 bytes (a, b, c, d) and shuffles their nibbles to obtain 5 bytes in dest */ +/* The result is basically res = (enc(a) << 15) | (enc(b) << 10) | (enc(c) << 5) | enc(d) + * with res being 5 bytes long and enc(x) being the GCR encode of x. + * In fact, we store the result as five separate bytes in the dest argument + */ + +/* + + Commodore GCR format + +Original Encoded +4 bits 5 bits + +0000 -> 01010 = 0x0a +0001 -> 01011 = 0x0b +0010 -> 10010 = 0x12 +0011 -> 10011 = 0x13 +0100 -> 01110 = 0x0e +0101 -> 01111 = 0x0f +0110 -> 10110 = 0x16 +0111 -> 10111 = 0x17 +1000 -> 01001 = 0x09 +1001 -> 11001 = 0x19 +1010 -> 11010 = 0x1a +1011 -> 11011 = 0x1b +1100 -> 01101 = 0x0d +1101 -> 11101 = 0x1d +1110 -> 11110 = 0x1e +1111 -> 10101 = 0x15 + +We use the encoded values in bytes because we use them to encode +groups of 4 bytes into groups of 5 bytes, below. + +*/ + +static void gcr_double_2_gcr(UINT8 a, UINT8 b, UINT8 c, UINT8 d, UINT8 *dest) +{ + UINT8 gcr[8]; + + /* Encode each nibble to 5 bits */ + gcr[0] = bin_2_gcr[a >> 4]; + gcr[1] = bin_2_gcr[a & 0x0f]; + gcr[2] = bin_2_gcr[b >> 4]; + gcr[3] = bin_2_gcr[b & 0x0f]; + gcr[4] = bin_2_gcr[c >> 4]; + gcr[5] = bin_2_gcr[c & 0x0f]; + gcr[6] = bin_2_gcr[d >> 4]; + gcr[7] = bin_2_gcr[d & 0x0f]; + + /* Re-order the encoded data to only keep the 5 lower bits of each byte */ + dest[0] = (gcr[0] << 3) | (gcr[1] >> 2); + dest[1] = (gcr[1] << 6) | (gcr[2] << 1) | (gcr[3] >> 4); + dest[2] = (gcr[3] << 4) | (gcr[4] >> 1); + dest[3] = (gcr[4] << 7) | (gcr[5] << 2) | (gcr[6] >> 3); + dest[4] = (gcr[6] << 5) | gcr[7]; +} + +/*------------------------------------------------- + d64_read_track - reads a full track from the + disk image +-------------------------------------------------*/ + +/* + + Commodore sector format + + SYNC FF * 5 + 08 + CHECKSUM sector ^ track ^ id2 ^ id1 + SECTOR 0..20 (2040), 0..28 (8050) + TRACK 1..35 (2040), 1..77 (8050), 1..70 (1571) + ID2 + ID1 + GAP 1 55 * 9 (2040), 55 * 8 (1541) + + SYNC FF * 5 + 07 + NEXT TRACK + NEXT SECTOR + 254 BYTES OF DATA + CHECKSUM + GAP 2 55 * 8..19 + +*/ + +static floperr_t d64_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) +{ + struct d64dsk_tag *tag = get_tag(floppy); + floperr_t err; + UINT64 track_offset; + + /* get track offset */ + err = get_track_offset(floppy, head, track, &track_offset); + + if (err) + return err; + + if (track_offset != INVALID_OFFSET) + { + UINT8 id1 = tag->id1; + UINT8 id2 = tag->id2; + int sectors_per_track; + UINT16 d64_track_size; + UINT8 *d64_track_data; + UINT16 gcr_track_size; + UINT8 *gcr_track_data; + UINT64 gcr_pos = 0; + + /* determine logical track number */ + int dos_track = get_dos_track(track); + + if (tag->dos == DOS25) + { + dos_track = track + 1; + } + + /* logical track numbers continue on the flip side */ + if (head == 1) dos_track += tag->dos_tracks; + + /* determine number of sectors per track */ + sectors_per_track = d64_get_sectors_per_track(floppy, head, track); + + /* allocate D64 track data buffer */ + d64_track_size = sectors_per_track * SECTOR_SIZE; + d64_track_data = (UINT8 *)alloca(d64_track_size); + + /* allocate temporary GCR track data buffer */ + gcr_track_size = sectors_per_track * SECTOR_SIZE_GCR; + gcr_track_data = (UINT8 *)alloca(gcr_track_size); + + if (buflen < gcr_track_size) { printf("D64 track buffer too small: %u!", (UINT32)buflen); exit(-1); } + + /* read D64 track data */ + floppy_image_read(floppy, d64_track_data, track_offset, d64_track_size); + + /* GCR encode D64 sector data */ + for (int sector = 0; sector < sectors_per_track; sector++) + { + // here we convert the sector data to gcr directly! + // IMPORTANT: errors in reading sectors can modify e.g. header info $01 & $05 + int sector_error = get_sector_error_code(floppy, head, track, sector); + + /* first we set the position at which sector data starts in the image */ + UINT64 d64_pos = sector * SECTOR_SIZE; + int i; + + /* + 1. Header sync FF FF FF FF FF (40 'on' bits, not GCR encoded) + 2. Header info 52 54 B5 29 4B 7A 5E 95 55 55 (10 GCR bytes) + 3. Header gap 55 55 55 55 55 55 55 55 55 (9 bytes, never read) + 4. Data sync FF FF FF FF FF (40 'on' bits, not GCR encoded) + 5. Data block 55...4A (325 GCR bytes) + 6. Inter-sector gap 55 55 55 55...55 55 (4 to 19 bytes, never read) + */ + + if (sector_error == ERROR_29) + id1 ^= 0xff; + + /* Header sync */ + if (sector_error != ERROR_21) + { + for (i = 0; i < 5; i++) + gcr_track_data[gcr_pos + i] = 0xff; + gcr_pos += 5; + } + + /* Header info */ + /* These are 8 bytes unencoded, which become 10 bytes encoded */ + // $00 - header block ID ($08) // this byte can be modified by error code 20 -> 0xff + // $01 - header block checksum (EOR of $02-$05) // this byte can be modified by error code 27 -> ^ 0xff + // $02 - Sector# of data block + // $03 - Track# of data block + UINT8 header_block_id = (sector_error == ERROR_20) ? 0xff : 0x08; + UINT8 header_block_checksum = sector ^ dos_track ^ id2 ^ id1; + + if (sector_error == ERROR_27) + header_block_checksum ^= 0xff; + + gcr_double_2_gcr(header_block_id, header_block_checksum, sector, dos_track, gcr_track_data + gcr_pos); + gcr_pos += 5; + + // $04 - Format ID byte #2 + // $05 - Format ID byte #1 + // $06 - $0F ("off" byte) + // $07 - $0F ("off" byte) + gcr_double_2_gcr(id2, id1, 0x0f, 0x0f, gcr_track_data + gcr_pos); + gcr_pos += 5; + + /* Header gap */ + for (i = 0; i < 9; i++) + gcr_track_data[gcr_pos + i] = 0x55; + gcr_pos += 9; + + /* Data sync */ + for (i = 0; i < 5; i++) + gcr_track_data[gcr_pos + i] = 0xff; + gcr_pos += 5; + + /* Data block */ + UINT8 data_block_id = (sector_error == ERROR_22) ? 0xff : 0x07; + + // we first need to calculate the checksum of the 256 bytes of the sector + UINT8 sector_checksum = d64_track_data[d64_pos]; + for (i = 1; i < 256; i++) + sector_checksum ^= d64_track_data[d64_pos + i]; + + if (sector_error == ERROR_23) + sector_checksum ^= 0xff; + + /* + $00 - data block ID ($07) + $01-100 - 256 bytes sector data + $101 - data block checksum (EOR of $01-100) + $102-103 - $00 ("off" bytes, to make the sector size a multiple of 5) + */ + gcr_double_2_gcr(data_block_id, d64_track_data[d64_pos], d64_track_data[d64_pos + 1], d64_track_data[d64_pos + 2], gcr_track_data + gcr_pos); + gcr_pos += 5; + + for (i = 1; i < 64; i++) + { + gcr_double_2_gcr(d64_track_data[d64_pos + 4 * i - 1], d64_track_data[d64_pos + 4 * i], + d64_track_data[d64_pos + 4 * i + 1], d64_track_data[d64_pos + 4 * i + 2], gcr_track_data + gcr_pos); + gcr_pos += 5; + } + + gcr_double_2_gcr(d64_track_data[d64_pos + 255], sector_checksum, 0x00, 0x00, gcr_track_data + gcr_pos); + gcr_pos += 5; + + /* Inter-sector gap */ + // "In tests that the author conducted on a real 1541 disk, gap sizes of 8 to 19 bytes were seen." + // Here we put 14 as an average... + for (i = 0; i < 14; i++) + gcr_track_data[gcr_pos + i] = 0x55; + gcr_pos += 14; + } + + /* copy GCR track data to buffer */ + memcpy(buffer, gcr_track_data, gcr_track_size); + } + else /* half tracks */ + { + /* set track length to 0 */ + memset(buffer, 0, buflen); + } + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + d64_write_track - writes a full track to the + disk image +-------------------------------------------------*/ + +static floperr_t d64_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen) +{ + return FLOPPY_ERROR_UNSUPPORTED; +} + +/*------------------------------------------------- + d64_identify - identifies the disk image +-------------------------------------------------*/ + +static void d64_identify(floppy_image *floppy, int *dos, int *heads, int *tracks, bool *has_errors) +{ + switch (floppy_image_size(floppy)) + { + /* 2040/3040 */ + case D67_SIZE_35_TRACKS: *dos = DOS1; *heads = 1; *tracks = 35; *has_errors = false; break; + + /* 4040/2031/1541/1551 */ + case D64_SIZE_35_TRACKS: *dos = DOS2; *heads = 1; *tracks = 35; *has_errors = false; break; + case D64_SIZE_35_TRACKS_WITH_ERRORS: *dos = DOS2; *heads = 1; *tracks = 35; *has_errors = true; break; + case D64_SIZE_40_TRACKS: *dos = DOS2; *heads = 1; *tracks = 40; *has_errors = false; break; + case D64_SIZE_40_TRACKS_WITH_ERRORS: *dos = DOS2; *heads = 1; *tracks = 40; *has_errors = true; break; + case D64_SIZE_42_TRACKS: *dos = DOS2; *heads = 1; *tracks = 42; *has_errors = false; break; + case D64_SIZE_42_TRACKS_WITH_ERRORS: *dos = DOS2; *heads = 1; *tracks = 42; *has_errors = true; break; + + /* 1571 */ + case D71_SIZE_70_TRACKS: *dos = DOS2; *heads = 2; *tracks = 35; *has_errors = false; break; + case D71_SIZE_70_TRACKS_WITH_ERRORS: *dos = DOS2; *heads = 2; *tracks = 35; *has_errors = true; break; + + /* 8050 */ + case D80_SIZE_77_TRACKS: *dos = DOS25; *heads = 1; *tracks = 77; *has_errors = false; break; + + /* 8250/SFD1001 */ + case D82_SIZE_154_TRACKS: *dos = DOS25; *heads = 2; *tracks = 77; *has_errors = false; break; + } +} + +/*------------------------------------------------- + FLOPPY_IDENTIFY( d64_dsk_identify ) +-------------------------------------------------*/ + +FLOPPY_IDENTIFY( d64_dsk_identify ) +{ + int dos = 0, heads, tracks; + bool has_errors = false; + + *vote = 0; + + d64_identify(floppy, &dos, &heads, &tracks, &has_errors); + + if (dos == DOS2 && heads == 1) + { + *vote = 100; + } + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + FLOPPY_IDENTIFY( d67_dsk_identify ) +-------------------------------------------------*/ + +FLOPPY_IDENTIFY( d67_dsk_identify ) +{ + *vote = 0; + + if (floppy_image_size(floppy) == D67_SIZE_35_TRACKS) + { + *vote = 100; + } + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + FLOPPY_IDENTIFY( d71_dsk_identify ) +-------------------------------------------------*/ + +FLOPPY_IDENTIFY( d71_dsk_identify ) +{ + int heads = 0, tracks = 0, dos = -1; + bool has_errors = false; + + *vote = 0; + + d64_identify(floppy, &dos, &heads, &tracks, &has_errors); + + if (dos == DOS2 && heads == 2) + { + *vote = 100; + } + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + FLOPPY_IDENTIFY( d80_dsk_identify ) +-------------------------------------------------*/ + +FLOPPY_IDENTIFY( d80_dsk_identify ) +{ + *vote = 0; + + if (floppy_image_size(floppy) == D80_SIZE_77_TRACKS) + { + *vote = 100; + } + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + FLOPPY_IDENTIFY( d82_dsk_identify ) +-------------------------------------------------*/ + +FLOPPY_IDENTIFY( d82_dsk_identify ) +{ + *vote = 0; + + if (floppy_image_size(floppy) == D82_SIZE_154_TRACKS) + { + *vote = 100; + } + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + FLOPPY_CONSTRUCT( d64_dsk_construct ) +-------------------------------------------------*/ + +FLOPPY_CONSTRUCT( d64_dsk_construct ) +{ + struct FloppyCallbacks *callbacks; + struct d64dsk_tag *tag; + UINT8 id[2]; + + int track_offset = 0; + int head, track; + + int heads = 0, dos_tracks = 0, dos = 0; + bool has_errors = 0; + int errors_size = 0; + + if (params) + { + /* create not supported */ + return FLOPPY_ERROR_UNSUPPORTED; + } + + tag = (struct d64dsk_tag *) floppy_create_tag(floppy, sizeof(struct d64dsk_tag)); + + if (!tag) return FLOPPY_ERROR_OUTOFMEMORY; + + /* identify image type */ + d64_identify(floppy, &dos, &heads, &dos_tracks, &has_errors); + + tag->dos = dos; + tag->heads = heads; + tag->tracks = MAX_TRACKS; + tag->dos_tracks = dos_tracks; + tag->has_errors = has_errors; + + if (LOG) + { + logerror("D64 size: %04x\n", (UINT32)floppy_image_size(floppy)); + logerror("D64 heads: %u\n", heads); + logerror("D64 tracks: %u\n", dos_tracks); + logerror("D64 DOS version: %s\n", DOS_VERSION[dos]); + logerror("D64 error codes: %s\n", has_errors ? "yes" : "no"); + } + + /* clear track data offsets */ + for (head = 0; head < MAX_HEADS; head++) + { + for (track = 0; track < MAX_TRACKS; track++) + { + tag->track_offset[head][track] = INVALID_OFFSET; + } + } + + /* determine track data offsets */ + for (head = 0; head < heads; head++) + { + for (track = 0; track < tag->tracks; track++) + { + if (dos == DOS25) + { + if (track >= dos_tracks) + { + /* track out of range */ + tag->track_offset[head][track] = INVALID_OFFSET; + } + else + { + tag->track_offset[head][track] = track_offset; + tag->error_offset[head][track] = errors_size; + + track_offset += DOS25_SECTORS_PER_TRACK[track] * SECTOR_SIZE; + /* also store an error entry for each sector */ + errors_size += DOS25_SECTORS_PER_TRACK[track]; + + if (LOG) + { + logerror("D64 head %u track %u data offset: %04x\n", head, track + 1, tag->track_offset[head][track]); + if (has_errors) logerror("D64 head %u track %u error offset: %04x\n", head, track + 1, tag->error_offset[head][track]); + } + } + } + else + { + if ((track % 2) || ((track / 2) >= dos_tracks)) + { + /* half track or out of range */ + tag->track_offset[head][track] = INVALID_OFFSET; + } + else + { + /* full track */ + tag->track_offset[head][track] = track_offset; + tag->error_offset[head][track] = errors_size; + + if (dos == DOS1) + { + track_offset += DOS1_SECTORS_PER_TRACK[track / 2] * SECTOR_SIZE; + /* also store an error entry for each sector */ + errors_size += DOS1_SECTORS_PER_TRACK[track / 2]; + } + else + { + track_offset += DOS2_SECTORS_PER_TRACK[track / 2] * SECTOR_SIZE; + /* also store an error entry for each sector */ + errors_size += DOS2_SECTORS_PER_TRACK[track / 2]; + } + + if (LOG) + { + logerror("D64 head %u track %.1f data offset: %04x\n", head, get_dos_track(track), tag->track_offset[head][track]); + if (has_errors) logerror("D64 head %u track %.1f error offset: %04x\n", head, get_dos_track(track), tag->error_offset[head][track]); + } + } + } + } + } + + /* determine speed zones */ + for (track = 0; track < tag->tracks; track++) + { + if (dos == DOS25) + { + tag->speed_zone[track] = DOS25_SPEED_ZONE[track]; + + if (LOG) logerror("D64 track %u speed zone: %u\n", track + 1, tag->speed_zone[track]); + } + else + { + tag->speed_zone[track] = DOS1_SPEED_ZONE[track / 2]; + + if (LOG) logerror("D64 track %.1f speed zone: %u\n", get_dos_track(track), tag->speed_zone[track]); + } + } + + /* read format ID from directory */ + /* + id1, id2 are the same for extended d64 (i.e. with error tables), for d67 and for d71 + + for d81 they are at track 40 bytes 0x17 & 0x18 + for d80 & d82 they are at track 39 bytes 0x18 & 0x19 + */ + if (dos == DOS25) + floppy_image_read(floppy, id, tag->track_offset[0][38] + 0x18, 2); + else + floppy_image_read(floppy, id, tag->track_offset[0][34] + 0xa2, 2); + + tag->id1 = id[0]; + tag->id2 = id[1]; + + if (LOG) logerror("D64 format ID: %02x%02x\n", id[0], id[1]); + + /* read errors */ + if (tag->has_errors) + { + if (LOG) logerror("D64 error blocks: %u %u\n", errors_size, track_offset); + floppy_image_read(floppy, tag->error, track_offset, errors_size); + } + else + { + memset(tag->error, ERROR_00, MAX_ERROR_SECTORS); + } + + /* set callbacks */ + callbacks = floppy_callbacks(floppy); + + callbacks->read_track = d64_read_track; + callbacks->write_track = d64_write_track; + callbacks->get_heads_per_disk = d64_get_heads_per_disk; + callbacks->get_tracks_per_disk = d64_get_tracks_per_disk; + callbacks->get_track_size = d64_get_track_size; + + return FLOPPY_ERROR_SUCCESS; +} diff --git a/src/lib/formats/d64_dsk.h b/src/lib/formats/d64_dsk.h new file mode 100644 index 00000000000..b5dc8d9d9ed --- /dev/null +++ b/src/lib/formats/d64_dsk.h @@ -0,0 +1,26 @@ +/********************************************************************* + + formats/d64_dsk.h + + Floppy format code for Commodore 1541/2040/8050 disk images + +*********************************************************************/ + +#ifndef __D64_DSK__ +#define __D64_DSK__ + +#include "flopimg.h" + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +FLOPPY_IDENTIFY( d64_dsk_identify ); +FLOPPY_IDENTIFY( d67_dsk_identify ); +FLOPPY_IDENTIFY( d71_dsk_identify ); +FLOPPY_IDENTIFY( d80_dsk_identify ); +FLOPPY_IDENTIFY( d82_dsk_identify ); + +FLOPPY_CONSTRUCT( d64_dsk_construct ); + +#endif diff --git a/src/lib/formats/d81_dsk.c b/src/lib/formats/d81_dsk.c new file mode 100644 index 00000000000..a448f51963d --- /dev/null +++ b/src/lib/formats/d81_dsk.c @@ -0,0 +1,131 @@ +/********************************************************************* + + formats/d81_dsk.c + + Floppy format code for Commodore 1581 disk images + +*********************************************************************/ + +#include "basicdsk.h" +#include "d81_dsk.h" + +/*************************************************************************** + PARAMETERS +***************************************************************************/ + +#define D81_SIZE 819200 + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + d81_translate_offset - translates the + physical offset to a logical offset within + the disk image +-------------------------------------------------*/ + +/* + + file offset | CBM logical | drive physical | specials + decimal sedecimal | track/sector | cyl head sec offs | + ------------------+--------------+-------------------+-------------- + 0 0x000000 | 01;00 | 00;01;01 | first block + 256 0x000100 | 01;01 | 00;01;01 +256 | + . . | . | . . | + 4864 0x001300 | 01;19 | 00;01;10 +256 | + 5120 0x001400 | 01;20 | 00;00;01 | + . . | . | . . | + 9984 0x002700 | 01;39 | 00;00;10 +256 | + 10240 0x002800 | 02;00 | 01;01;01 | + . . | . | . . | + 15360 0x003C00 | 02;20 | 01;00;01 | + . . | . | . . | + 20480 0x005000 | 03;00 | 02;01;01 | + . . | . | . . | + . . | . | . . | + 30729 0x007800 | 04;00 | 03;01;01 | + . . | . | . . | + . . | . | . . | + . . | . | . . | + 399360 0x061800 | 40;00 | 39;01;01 | disk header + 399616 0x061900 | 40;01 | 39;01;01 +256 | 1st BAM block + 399872 0x061A00 | 40;02 | 39;01;02 | 2nd BAM block + 400128 0x061B00 | 40;03 | 39;01;02 +256 | 1st dir block + . . | . | . . | + 409600 0x064000 | 41;00 | 40;01;01 | + . . | . | . . | + . . | . | . . | + . . | . | . . | + 808960 0x0C5800 | 80;00 | 79;01;01 | + . . | . | . . | + 813824 0x0C6B00 | 80;19 | 79;01;10 +256 | + 814080 0x0C6C00 | 80;20 | 79;00;01 | + . . | . | . . | + 818688 0x0C7E00 | 80;38 | 79;00;10 | + 818944 0x0C7F00 | 80;39 | 79;00;10 +256 | last block + +*/ + +static UINT64 d81_translate_offset(floppy_image *floppy, const struct basicdsk_geometry *geom, int track, int head, int sector) +{ + UINT64 offset = (track * 20) + (!head * 10) + sector; + + return offset; +} + +/*------------------------------------------------- + FLOPPY_IDENTIFY( d81_dsk_identify ) +-------------------------------------------------*/ + +FLOPPY_IDENTIFY( d81_dsk_identify ) +{ + *vote = (floppy_image_size(floppy) == D81_SIZE) ? 100 : 0; + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + FLOPPY_CONSTRUCT( d81_dsk_construct ) +-------------------------------------------------*/ + +/* +PER TRACK ORGANIZATION: + + Hex 4E written as a gap, with 10 sectors of data, with full gaps written for motor speed variation. + +PER SECTOR ORGANIZATION: + + MFM Encoding + 12 Bytes of Hex 00 + 3 Bytes of Hex A1 (Data Hex A1, Clock Hex 0A) + 1 Byte of Hex FE (ID Address Mark) + 1 Byte (Track Number) + 1 Byte (Side Number) + 1 Byte (Sector Number) + 1 Byte (Sector Length, 02 for 512 Byte Sectors) + 2 Bytes CRC (Cyclic Redundancy Check) + 22 Bytes of Hex 22 + 12 Bytes of Hex 00 + 3 Bytes of Hex A1 (Data Hex A1, Clock Hex 0A) + 1 Byte of Hex FB (Data Address Mark) + 512 Bytes of Data + 2 Bytes of CRC (Cyclic Redundancy Check) + 38 Bytes of Hex 4E +*/ + +FLOPPY_CONSTRUCT( d81_dsk_construct ) +{ + struct basicdsk_geometry geometry; + + memset(&geometry, 0, sizeof(geometry)); + + geometry.heads = 2; + geometry.first_sector_id = 1; + geometry.sector_length = 512; + geometry.tracks = 80; + geometry.sectors = 10; + geometry.translate_offset = d81_translate_offset; + + return basicdsk_construct(floppy, &geometry); +} diff --git a/src/lib/formats/d81_dsk.h b/src/lib/formats/d81_dsk.h new file mode 100644 index 00000000000..395ddba835c --- /dev/null +++ b/src/lib/formats/d81_dsk.h @@ -0,0 +1,21 @@ +/********************************************************************* + + formats/d81_dsk.h + + Floppy format code for Commodore 1581 disk images + +*********************************************************************/ + +#ifndef __D81_DSK__ +#define __D81_DSK__ + +#include "flopimg.h" + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +FLOPPY_IDENTIFY( d81_dsk_identify ); +FLOPPY_CONSTRUCT( d81_dsk_construct ); + +#endif diff --git a/src/mame/formats/d88_dsk.c b/src/lib/formats/d88_dsk.c index 1528fb1be58..845e6352c0b 100644 --- a/src/mame/formats/d88_dsk.c +++ b/src/lib/formats/d88_dsk.c @@ -27,9 +27,7 @@ * */ -#include "emu.h" -#include "imagedev/flopimg.h" -#include "imagedev/flopdrv.h" +#include "flopimg.h" #include "imageutl.h" #define D88_HEADER_LEN 0x2b0 diff --git a/src/lib/formats/dim_dsk.c b/src/lib/formats/dim_dsk.c new file mode 100644 index 00000000000..c6d195af6d7 --- /dev/null +++ b/src/lib/formats/dim_dsk.c @@ -0,0 +1,101 @@ +/********************************************************************* + + formats/dim_dsk.c + + DIM disk images + +*********************************************************************/ + +#include <string.h> + +#include "dim_dsk.h" +#include "basicdsk.h" + +FLOPPY_IDENTIFY(dim_dsk_identify) +{ + UINT8 dim_header[16]; + + floppy_image_read(floppy, (UINT8*)dim_header,0xab,16); + + if(strncmp((const char*)dim_header,"DIFC HEADER",11) == 0) + *vote = 100; + else + *vote = 0; + return FLOPPY_ERROR_SUCCESS; +} + +FLOPPY_CONSTRUCT(dim_dsk_construct) +{ + struct basicdsk_geometry geometry; + // DIM disk image header, most of this is guesswork + int tracks = 77; + int heads = 2; + int sectors = 8; // per track + int sectorlen = 1024; + int firstsector = 0x01; + UINT8 format_tmp; + int x; + UINT16 temp; + + if(params) + { + // create + return FLOPPY_ERROR_UNSUPPORTED; + } + + // Offset + 0 : disk format type (1 byte): + // 0 = 2HD / 2HDA (8 sector/track, 1024 bytes/sector, GAP#3 = 0x74) + // 1 = 2HS (9 sector/track, 1024 bytes/sector, GAP#3 = 0x39) + // 2 = 2HC (15 sector/track, 512 bytes/sector, GAP#3 = 0x54) + // 3 = 2HDE(68) (9 sector/track, 1024 bytes/sector, GAP#3 = 0x39) + // 9 = 2HQ (18 sector/track, 512 bytes/sector, GAP#3 = 0x54) + // 17 = N88-BASIC (26 sector/track, 256 bytes/sector, GAP#3 = 0x33) + // or (26 sector/track, 128 bytes/sector, GAP#3 = 0x1a) + // + floppy_image_read(floppy, &format_tmp,0,1); + + switch(format_tmp) + { + case 0x00: + sectors = 8; + sectorlen = 1024; + break; + case 0x01: + case 0x03: + sectors = 9; + sectorlen = 1024; + break; + case 0x02: + sectors = 15; + sectorlen = 512; + break; + case 0x09: + sectors = 18; + sectorlen = 512; + break; + case 0x11: + sectors = 26; + sectorlen = 256; + break; + } + tracks = 0; + for (x=0;x<86;x++) + { + floppy_image_read(floppy,&temp,(x*2)+1,2); + if(temp == 0x0101) + tracks++; + } + // TODO: expand on this basic implementation + + logerror("FDD: DIM image loaded - type %i, %i tracks, %i sectors per track, %i bytes per sector\n", format_tmp,tracks, sectors,sectorlen); + + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = heads; + geometry.first_sector_id = firstsector; + geometry.sector_length = sectorlen; + geometry.tracks = tracks+1; + geometry.sectors = sectors; + geometry.offset = 0x100; + return basicdsk_construct(floppy, &geometry); +} + diff --git a/src/lib/formats/dim_dsk.h b/src/lib/formats/dim_dsk.h new file mode 100644 index 00000000000..a889d468ad1 --- /dev/null +++ b/src/lib/formats/dim_dsk.h @@ -0,0 +1,19 @@ +/********************************************************************* + + formats/dim_dsk.h + + DIM disk images + +*********************************************************************/ + +#ifndef DIM_DSK_H +#define DIM_DSK_H + +#include "flopimg.h" + +/**************************************************************************/ + +FLOPPY_IDENTIFY(dim_dsk_identify); +FLOPPY_CONSTRUCT(dim_dsk_construct); + +#endif /* DIM_DSK_H */ diff --git a/src/mame/formats/dsk_dsk.c b/src/lib/formats/dsk_dsk.c index 3f5913f5050..f02f7d539d5 100644 --- a/src/mame/formats/dsk_dsk.c +++ b/src/lib/formats/dsk_dsk.c @@ -8,10 +8,8 @@ #include <string.h> -#include "emu.h" #include "imageutl.h" -#include "imagedev/flopimg.h" -#include "imagedev/flopdrv.h" +#include "flopimg.h" #define MV_CPC "MV - CPC" #define EXTENDED "EXTENDED" diff --git a/src/mame/formats/fdi_dsk.c b/src/lib/formats/fdi_dsk.c index cd727d6d52a..500db9357bc 100644 --- a/src/mame/formats/fdi_dsk.c +++ b/src/lib/formats/fdi_dsk.c @@ -14,10 +14,8 @@ */ -#include "emu.h" #include "imageutl.h" -#include "imagedev/flopimg.h" -#include "imagedev/flopdrv.h" +#include "flopimg.h" /*************************************************************************** PARAMETERS diff --git a/src/emu/imagedev/flopimg.c b/src/lib/formats/flopimg.c index da636092cf8..49f01553289 100644 --- a/src/emu/imagedev/flopimg.c +++ b/src/lib/formats/flopimg.c @@ -13,7 +13,8 @@ #include <assert.h> #include <limits.h> -#include "osdepend.h" +#include "osdcore.h" +#include "ioprocs.h" #include "flopimg.h" #include "pool.h" #include "imageutl.h" diff --git a/src/emu/imagedev/flopimg.h b/src/lib/formats/flopimg.h index ae693dfb9ec..aaf4508886f 100644 --- a/src/emu/imagedev/flopimg.h +++ b/src/lib/formats/flopimg.h @@ -9,9 +9,13 @@ #ifndef FLOPIMG_H #define FLOPIMG_H -#include "emu.h" +#include "osdcore.h" +#include "ioprocs.h" #include "opresolv.h" +#ifndef logerror +#define logerror printf +#endif /*************************************************************************** @@ -22,6 +26,12 @@ #define FLOPPY_FLAGS_READWRITE 0 #define FLOPPY_FLAGS_READONLY 1 +/* sector has a deleted data address mark */ +#define ID_FLAG_DELETED_DATA 0x0001 +/* CRC error in id field */ +#define ID_FLAG_CRC_ERROR_IN_ID_FIELD 0x0002 +/* CRC error in data field */ +#define ID_FLAG_CRC_ERROR_IN_DATA_FIELD 0x0004 /*************************************************************************** diff --git a/src/lib/formats/fm7_cas.c b/src/lib/formats/fm7_cas.c new file mode 100644 index 00000000000..c80708f8f3f --- /dev/null +++ b/src/lib/formats/fm7_cas.c @@ -0,0 +1,108 @@ +/* + * Fujitsu FM-7 series cassette handling + */ + +#include "fm7_cas.h" + +#define WAVE_HIGH 0x5a9e +#define WAVE_LOW -0x5a9e + +static int cas_size; + +static int fm7_fill_wave(INT16* buffer, UINT8 high, UINT8 low, int sample_pos) +{ + UINT16 data = (high << 8) + low; + int sample_count = 0; + int x = 0; + int count = (data & 0x7fff); + + if(data & 0x8000) + { + for(x=0;x<count;x++) + { + if(buffer) + buffer[sample_pos+x] = WAVE_HIGH; + } + } + else + { + for(x=0;x<count;x++) + { + if(buffer) + buffer[sample_pos+x] = WAVE_LOW; + } + } + + sample_count += count; + return sample_count; +} + +static int fm7_handle_t77(INT16* buffer, const UINT8* casdata) +{ + int sample_count = 0; + int data_pos = 16; + + if(memcmp(casdata, "XM7 TAPE IMAGE 0",16)) // header check + return -1; + + while(data_pos < cas_size) + { + sample_count += fm7_fill_wave(buffer,casdata[data_pos],casdata[data_pos+1],sample_count); + data_pos+=2; + } + + return sample_count; +} + +/******************************************************************* + Calculate the number of samples needed for this tape image +********************************************************************/ +static int fm7_cas_to_wav_size (const UINT8 *casdata, int caslen) +{ + cas_size = caslen; + + return fm7_handle_t77(NULL,casdata); +} + +/******************************************************************* + Generate samples for the tape image +********************************************************************/ +static int fm7_cas_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + return fm7_handle_t77(buffer,bytes); +} + +static const struct CassetteLegacyWaveFiller fm7_legacy_fill_wave = +{ + fm7_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + fm7_cas_to_wav_size, /* chunk_sample_calc */ + 125000, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t fm7_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &fm7_legacy_fill_wave); +} + + + +static casserr_t fm7_cas_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &fm7_legacy_fill_wave); +} + + +static const struct CassetteFormat fm7_cassette_format = { + "t77", + fm7_cas_identify, + fm7_cas_load, + NULL +}; + +CASSETTE_FORMATLIST_START(fm7_cassette_formats) + CASSETTE_FORMAT(fm7_cassette_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/fm7_cas.h b/src/lib/formats/fm7_cas.h new file mode 100644 index 00000000000..11d3438942b --- /dev/null +++ b/src/lib/formats/fm7_cas.h @@ -0,0 +1,12 @@ +/* + * Fujitsu FM-7 cassette format handling + */ + +#ifndef FM7_CAS_H_ +#define FM7_CAS_H_ + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN( fm7_cassette_formats ); + +#endif /*FM7_CAS_H_*/ diff --git a/src/mame/formats/fmsx_cas.c b/src/lib/formats/fmsx_cas.c index ab4a7a96ddd..ab4a7a96ddd 100644 --- a/src/mame/formats/fmsx_cas.c +++ b/src/lib/formats/fmsx_cas.c diff --git a/src/mame/formats/fmsx_cas.h b/src/lib/formats/fmsx_cas.h index 2639c7cb952..fe3e3d9d2f6 100644 --- a/src/mame/formats/fmsx_cas.h +++ b/src/lib/formats/fmsx_cas.h @@ -9,7 +9,7 @@ #ifndef FMSX_CAS_H #define FMSX_CAS_H -#include "imagedev/cassimg.h" +#include "cassimg.h" CASSETTE_FORMATLIST_EXTERN(fmsx_cassette_formats); diff --git a/src/lib/formats/g64_dsk.c b/src/lib/formats/g64_dsk.c new file mode 100644 index 00000000000..787468b6dca --- /dev/null +++ b/src/lib/formats/g64_dsk.c @@ -0,0 +1,295 @@ +/********************************************************************* + + formats/g64_dsk.c + + Floppy format code for Commodore 1541 GCR disk images + +*********************************************************************/ + +/* + + TODO: + + - speed zones + - writing + +*/ + +#define XTAL_16MHz 16000000 +#define XTAL_12MHz 12000000 +#include "g64_dsk.h" +#include "flopimg.h" + +/*************************************************************************** + PARAMETERS +***************************************************************************/ + +#define LOG 0 + +#define HEADER_LENGTH 0x2ac /* standard length for 84 half tracks */ +#define MAX_TRACKS 84 + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct g64dsk_tag +{ + int version; + int heads; /* number of physical heads */ + int tracks; /* number of physical tracks */ + UINT16 track_size; /* size of each track */ + UINT32 track_offset[MAX_TRACKS]; /* offset within data for each track */ + UINT32 speed_zone_offset[MAX_TRACKS]; /* offset within data for each track */ +}; + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +INLINE struct g64dsk_tag *get_tag(floppy_image *floppy) +{ + return (g64dsk_tag *)floppy_tag(floppy); +} + +INLINE float get_dos_track(int track) +{ + return ((float)track / 2) + 1; +} + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + g64_get_heads_per_disk - returns the number + of heads in the disk image +-------------------------------------------------*/ + +static int g64_get_heads_per_disk(floppy_image *floppy) +{ + return get_tag(floppy)->heads; +} + +/*------------------------------------------------- + g64_get_tracks_per_disk - returns the number + of DOS tracks in the disk image +-------------------------------------------------*/ + +static int g64_get_tracks_per_disk(floppy_image *floppy) +{ + return get_tag(floppy)->tracks; +} + +/*------------------------------------------------- + get_track_offset - returns the offset within + the disk image for a given track +-------------------------------------------------*/ + +static floperr_t get_track_offset(floppy_image *floppy, int head, int track, UINT64 *offset) +{ + struct g64dsk_tag *tag = get_tag(floppy); + UINT64 offs = 0; + + if ((track < 0) || (track >= tag->tracks)) + return FLOPPY_ERROR_SEEKERROR; + + offs = tag->track_offset[track]; + + if (offset) + *offset = offs; + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + g64_get_track_size - returns the track size +-------------------------------------------------*/ + +static UINT32 g64_get_track_size(floppy_image *floppy, int head, int track) +{ + /* get track offset */ + UINT64 track_offset; + floperr_t err = get_track_offset(floppy, head, track, &track_offset); + + if (err) + return 0; + + /* read track length */ + UINT8 size[2]; + floppy_image_read(floppy, &size, track_offset, 2); + UINT32 track_length = (size[1] << 8) | size[0]; + + return track_length; +} + +/*------------------------------------------------- + g64_read_track - reads a full track from the + disk image +-------------------------------------------------*/ + +static floperr_t g64_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) +{ + struct g64dsk_tag *tag = get_tag(floppy); + floperr_t err; + UINT64 track_offset; + UINT16 track_length = 0; + + /* get track offset */ + err = get_track_offset(floppy, head, track, &track_offset); + + if (err) + return err; + + if (!head && track_offset) + { + if (buflen < tag->track_size) { printf("G64 track buffer too small: %u!", (UINT32)buflen); exit(-1); } + + /* read track */ + floppy_image_read(floppy, buffer, track_offset + 2, tag->track_size); + } + else + { + /* set track length to 0 */ + memset(buffer, 0, buflen); + } + + if (LOG) logerror("G64 track %.1f length %u\n", get_dos_track(track), track_length); + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + g64_write_track - writes a full track to the + disk image +-------------------------------------------------*/ + +static floperr_t g64_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen) +{ + return FLOPPY_ERROR_UNSUPPORTED; +} + +/*------------------------------------------------- + FLOPPY_IDENTIFY( g64_dsk_identify ) +-------------------------------------------------*/ + +FLOPPY_IDENTIFY( g64_dsk_identify ) +{ + UINT8 header[8]; + + floppy_image_read(floppy, header, 0, 8); + + if (!strncmp((const char *)header, "GCR-1541", 8)) + { + *vote = 100; + } + else + { + *vote = 0; + } + + return FLOPPY_ERROR_SUCCESS; +} + +/*------------------------------------------------- + FLOPPY_CONSTRUCT( g64_dsk_construct ) +-------------------------------------------------*/ + +/* + + G64 File Header Format + + Bytes: $0000-0007: File signature "GCR-1541" + 0008: G64 version (presently only $00 defined) + 0009: Number of tracks in image (usually $54, decimal 84) + 000A-000B: Size of each stored track in bytes (usually 7928, or + $1EF8 in LO/HI format. + + Bytes: $000C-000F: Offset to stored track 1.0 ($000002AC, in LO/HI + format, see below for more) + 0010-0013: Offset to stored track 1.5 ($00000000) + 0014-0017: Offset to stored track 2.0 ($000021A6) + ... + 0154-0157: Offset to stored track 42.0 ($0004F8B6) + 0158-015B: Offset to stored track 42.5 ($00000000) + + Bytes: $015C-015F: Speed zone entry for track 1 ($03, in LO/HI format, + see below for more) + 0160-0163: Speed zone entry for track 1.5 ($03) + ... + 02A4-02A7: Speed zone entry for track 42 ($00) + 02A8-02AB: Speed zone entry for track 42.5 ($00) + + Bytes: $02AC-02AD: Actual size of stored track (7692 or $1E0C, in LO/HI + format) + 02AE-02AE+$1E0C: Track data + +*/ + +FLOPPY_CONSTRUCT( g64_dsk_construct ) +{ + struct FloppyCallbacks *callbacks; + struct g64dsk_tag *tag; + UINT8 header[HEADER_LENGTH]; + UINT64 pos = 0; + int i; + + if (params) + { + /* create not supported */ + return FLOPPY_ERROR_UNSUPPORTED; + } + + tag = (struct g64dsk_tag *) floppy_create_tag(floppy, sizeof(struct g64dsk_tag)); + + if (!tag) return FLOPPY_ERROR_OUTOFMEMORY; + + /* version */ + floppy_image_read(floppy, header, pos, 0x0c); pos += 0xc; + tag->version = header[8]; + if (LOG) logerror("G64 version: %u\n", tag->version); + + /* number of half tracks */ + tag->heads = 1; + tag->tracks = header[9]; + if (LOG) logerror("G64 tracks: %u\n", tag->tracks); + + /* size of each stored half track */ + tag->track_size = (header[11] << 8) | header[10]; + if (LOG) logerror("G64 track size: %04x\n", tag->track_size); + + /* data offsets */ + for (i = 0; i < tag->tracks; i++) + { + floppy_image_read(floppy, header, pos, 4); pos += 4; + tag->track_offset[i] = (header[3] << 24) | (header[2] << 16) | (header[1] << 8) | header[0]; + + if (LOG) logerror("G64 track %.1f data offset: %04x\n", get_dos_track(i), tag->track_offset[i]); + } + + /* speed zone offsets */ + for (i = 0; i < tag->tracks; i++) + { + floppy_image_read(floppy, header, pos, 4); pos += 4; + tag->speed_zone_offset[i] = (header[3] << 24) | (header[2] << 16) | (header[1] << 8) | header[0]; + + if (LOG) + { + if (tag->speed_zone_offset[i] < 4) + logerror("G64 track %.1f speed zone: %u\n", get_dos_track(i), tag->speed_zone_offset[i]); + else + logerror("G64 track %.1f speed zone offset: %04x\n", get_dos_track(i), tag->speed_zone_offset[i]); + } + } + + /* set callbacks */ + callbacks = floppy_callbacks(floppy); + + callbacks->read_track = g64_read_track; + callbacks->write_track = g64_write_track; + callbacks->get_heads_per_disk = g64_get_heads_per_disk; + callbacks->get_tracks_per_disk = g64_get_tracks_per_disk; + callbacks->get_track_size = g64_get_track_size; + + return FLOPPY_ERROR_SUCCESS; +} diff --git a/src/lib/formats/g64_dsk.h b/src/lib/formats/g64_dsk.h new file mode 100644 index 00000000000..20c5a6d253b --- /dev/null +++ b/src/lib/formats/g64_dsk.h @@ -0,0 +1,45 @@ +/********************************************************************* + + formats/g64_dsk.h + + Floppy format code for Commodore 1541 GCR disk images + +*********************************************************************/ + +#ifndef __G64_DSK__ +#define __G64_DSK__ + +#include "flopimg.h" + +/*************************************************************************** + MACROS / CONSTANTS +***************************************************************************/ + +#define G64_SYNC_MARK 0x3ff /* 10 consecutive 1-bits */ + +#define G64_BUFFER_SIZE 16384 + +const int C2040_BITRATE[] = +{ + XTAL_16MHz/16, /* tracks 1-17 */ + XTAL_16MHz/15, /* tracks 18-24 */ + XTAL_16MHz/14, /* tracks 25-30 */ + XTAL_16MHz/13 /* tracks 31-42 */ +}; + +const int C8050_BITRATE[] = +{ + XTAL_12MHz/2/16, /* tracks 1-39 */ + XTAL_12MHz/2/15, /* tracks 40-53 */ + XTAL_12MHz/2/14, /* tracks 54-65 */ + XTAL_12MHz/2/13 /* tracks 65-84 */ +}; + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +FLOPPY_IDENTIFY( g64_dsk_identify ); +FLOPPY_CONSTRUCT( g64_dsk_construct ); + +#endif diff --git a/src/lib/formats/gtp_cas.c b/src/lib/formats/gtp_cas.c new file mode 100644 index 00000000000..e3756c4dcf7 --- /dev/null +++ b/src/lib/formats/gtp_cas.c @@ -0,0 +1,200 @@ +/* + + Tape support for Glaksija GTP format + + Miodrag Milanovic +*/ +#include "gtp_cas.h" + + +#define GTP_WAV_FREQUENCY 44100 +#define WAVE_LOW -0x5a9e +#define WAVE_HIGH 0x5a9e +#define WAVE_NULL 0 + +#define GTP_BLOCK_STANDARD 0x00 +#define GTP_BLOCK_TURBO 0x01 +#define GTP_BLOCK_NAME 0x10 + +static INT16 wave_data; +static INT16 len; + +#define PULSE_WIDTH 30 +#define PERIOD_BASE 150 +#define PERIOD_1 75 +#define PERIOD_0 150 + +#define INTERBYTE_PAUSE 225 +#define INTERBLOCK_PAUSE 100000 + + +static void gtp_output_wave( INT16 **buffer, int length ) { + if ( buffer == NULL ) { + return; + } + + for( ; length > 0; length-- ) { + **buffer = wave_data; + *buffer = *buffer + 1; + } +} + + + +static int gtp_mod_1( INT16 **buffer ) +{ + wave_data = WAVE_LOW; + gtp_output_wave(buffer,PULSE_WIDTH); + wave_data = WAVE_HIGH; + gtp_output_wave(buffer,PULSE_WIDTH); + wave_data = WAVE_NULL; + gtp_output_wave(buffer,PERIOD_1 - 2 * PULSE_WIDTH); + wave_data = WAVE_LOW; + gtp_output_wave(buffer,PULSE_WIDTH); + wave_data = WAVE_HIGH; + gtp_output_wave(buffer,PULSE_WIDTH); + wave_data = WAVE_NULL; + gtp_output_wave(buffer,PERIOD_1 - 2 * PULSE_WIDTH); + + return PERIOD_1 * 2; +} + +static int gtp_mod_0( INT16 **buffer ) +{ + wave_data = WAVE_LOW; + gtp_output_wave(buffer,PULSE_WIDTH); + wave_data = WAVE_HIGH; + gtp_output_wave(buffer,PULSE_WIDTH); + wave_data = WAVE_NULL; + gtp_output_wave(buffer,PERIOD_0 - 2 * PULSE_WIDTH); + + return PERIOD_0; +} + +static int gtp_byte( INT16 **buffer, UINT8 val ) +{ + UINT8 b; + int j,size = 0; + for (j=0;j<8;j++) { + b = (val >> j) & 1; + if (b==0) { + size += gtp_mod_0(buffer); + } else { + size += gtp_mod_1(buffer); + } + } + return size; +} + +static int gtp_sync( INT16 **buffer ) +{ + int i; + int size = 0; + + for(i=0;i<100;i++) { + if (i!=0) { + // Interbyte pause + wave_data = WAVE_NULL; + gtp_output_wave(buffer,INTERBYTE_PAUSE); + size += INTERBYTE_PAUSE; + } + size += gtp_byte(buffer,0); + } + return size; +} + +static int gtp_cas_to_wav_size( const UINT8 *casdata, int caslen ) { + int size,n; + size = 0; + n = 0; + if (casdata == NULL) return -1; + while(n<caslen) { + int block_type = casdata[n]; + int block_size = casdata[n+2]*256 + casdata[n+1]; + n+=5; + if (block_type==GTP_BLOCK_STANDARD) { + // Interblock pause + size += INTERBLOCK_PAUSE; + size += 100 * (PERIOD_0 * 8 + INTERBYTE_PAUSE) - INTERBYTE_PAUSE; + size += (PERIOD_0 * 8 + INTERBYTE_PAUSE) * block_size; + } + n += block_size; + } + len = caslen; + return size; +} + +static int gtp_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) { + int i,size,n; + size = 0; + n = 0; + if (bytes == NULL) return -1; + while(n<len) + { + int block_type = bytes[n]; + int block_size = bytes[n+2]*256 + bytes[n+1]; + n+=5; + if (block_type==GTP_BLOCK_STANDARD) { + + // Interblock pause + wave_data = WAVE_NULL; + gtp_output_wave(&buffer,INTERBLOCK_PAUSE); + size += INTERBLOCK_PAUSE; + size += gtp_sync(&buffer); + + for (i=0;i<block_size;i++) { + // Interbyte pause + wave_data = WAVE_NULL; + gtp_output_wave(&buffer,INTERBYTE_PAUSE); + size += INTERBYTE_PAUSE; + + size += gtp_byte(&buffer,bytes[n]); + n++; + } + } else { + n += block_size; + } + } + return size; +} + + + + +static const struct CassetteLegacyWaveFiller gtp_legacy_fill_wave = { + gtp_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + gtp_cas_to_wav_size, /* chunk_sample_calc */ + GTP_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + + +static casserr_t gtp_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) { + return cassette_legacy_identify( cassette, opts, >p_legacy_fill_wave ); +} + + + +static casserr_t gtp_cassette_load( cassette_image *cassette ) { + return cassette_legacy_construct( cassette, >p_legacy_fill_wave ); +} + + + +static const struct CassetteFormat gtp_cassette_format = { + "gtp", + gtp_cassette_identify, + gtp_cassette_load, + NULL +}; + + +CASSETTE_FORMATLIST_START(gtp_cassette_formats) + CASSETTE_FORMAT(gtp_cassette_format) +CASSETTE_FORMATLIST_END + + diff --git a/src/lib/formats/gtp_cas.h b/src/lib/formats/gtp_cas.h new file mode 100644 index 00000000000..ee31a42caf6 --- /dev/null +++ b/src/lib/formats/gtp_cas.h @@ -0,0 +1,15 @@ +/* + + gtp_cas.h + + Format code for gtp cassette files +*/ + +#ifndef GTP_CAS_H +#define GTP_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(gtp_cassette_formats); + +#endif /* GTP_CAS_H */ diff --git a/src/lib/formats/hect_tap.c b/src/lib/formats/hect_tap.c new file mode 100644 index 00000000000..5032728224f --- /dev/null +++ b/src/lib/formats/hect_tap.c @@ -0,0 +1,305 @@ +/******************************************************************** + +Support for Micronique machine .K7 and *.FOR cassette images + +Note that the usual type for hector cassette is *.K7, + the *.FOR type is only for programming screen in forth format. + +You can find some *.K7 file on serveral server in France +(Micronique is a French factory) Enjoy ! + + jj.stacino@aliceadsl.fr + +Updated 3/1/10 : use real value for timing. +********************************************************************/ +#include "hect_tap.h" + + +#define SMPLO -32768 +#define SILENCE 0 +#define SMPHI 32767 + + +static int cas_size; + + +enum +{ + Header_cycles = 77, /* Valeur Th?orique 66 = 44100 * 1.5 / 1000 // mesur? sur jeu Formule1 = 1,75ms*/ + Zero_cycles = 27, /* Valeur Th?orique 17 = 44100 * 0.4 / 1000 // mesur? sur jeu Formule1 = 0,61ms*/ + Un_cycles = 50, /* Valeur Th?orique 40 = 44100 * 0.9 / 1000 // mesur? sur jeu Formule1 = 1,13ms*/ +}; + +/* Here I prefer use the value that I read on a real tape, and not the theorical value; note that these + value work best on my HRX... Yo_fr (jj.stacino@aliceadsl.fr) */ + +/******************************************************************* + Generate one high-low cycle of sample data +********************************************************************/ +INLINE int hector_tap_cycle(INT16 *buffer, int sample_pos, int high, int low) +{ + int i = 0; + + if ( buffer ) + { + while( i < high) + { + buffer[ sample_pos + i ] = SMPHI; + i++; + } + + while( i < high + low ) + { + buffer[ sample_pos + i ] = SMPLO; + i++; + } + } + return high + low; +} + + +INLINE int hector_tap_byte(INT16 *buffer, int sample_pos, UINT8 data) +{ +/* Writing an entire byte */ + int i, samples; + + samples = 0; + for ( i = 0; i < 8; i++ ) + { + if ( data & 0x01 ) + samples += hector_tap_cycle( buffer, sample_pos + samples, Un_cycles/2, Un_cycles/2 ); + else + samples += hector_tap_cycle( buffer, sample_pos + samples, Zero_cycles/2, Zero_cycles/2 ); + + data >>= 1; + } + return samples; +} + + +INLINE int hector_tap_synchro(INT16 *buffer, int sample_pos, int nb_synchro) +{ +/* Writing an entire byte */ + int i, samples; + + samples = 0; + for ( i = 0; i < nb_synchro ; i++ ) + samples += hector_tap_cycle( buffer, sample_pos + samples, Header_cycles/2, Header_cycles/2 ); + + return samples; +} + + +static int hector_handle_tap(INT16 *buffer, const UINT8 *casdata) +{ + int data_pos, sample_count/*, block_count*/; + int previous_block=0; + + data_pos = 0; + sample_count = 0; + /*block_count = 0;*/ + previous_block = 0; + + /* First 768 cycle of synchro */ + sample_count += hector_tap_synchro( buffer, sample_count, 768-4 ); + + /* on the entire file*/ + while( data_pos < cas_size ) + { + UINT16 block_size; + + if (previous_block == 0xFE) + /* Starting a block with 150 cycle of synchro to let time to Hector to do the job ! */ + sample_count += hector_tap_synchro( buffer, sample_count, 150 ); + else + /* Starting a block with 4 cycle of synchro */ + sample_count += hector_tap_synchro( buffer, sample_count, 4 ); + + if (data_pos>1) + previous_block = casdata[data_pos-1]; + + /* Handle block lenght on tape data */ + block_size = casdata[data_pos] ; + if (block_size==0) + block_size=256; + + /*block_count++;*/ + sample_count += hector_tap_byte(buffer, sample_count, casdata[data_pos] ); + data_pos++; + + /* Data samples */ + for ( ; block_size ; data_pos++, block_size-- ) + { + /* Make sure there are enough bytes left */ + if ( data_pos > cas_size ) + return -1; + + sample_count += hector_tap_byte( buffer, sample_count, casdata[data_pos] ); + + } + } + /*Finish by a zero*/ + sample_count += hector_tap_byte( buffer, sample_count, 0 ); + + return sample_count; +} +/******************************************************************* +//// FORTH DATA CASSETTE +*******************************************************************/ + + +static int hector_handle_forth_tap(INT16 *buffer, const UINT8 *casdata) +{ + int data_pos, sample_count/*, block_count*/; + /*int previous_block=0;*/ + + data_pos = 0; + sample_count = 0; + /*block_count = 0;*/ + /*previous_block = 0;*/ + + /* Out if len of file not modulo 822 octets */ + if ( (cas_size % 822) != 0 ) + return -1; + + /* on the entire file*/ + while( data_pos < cas_size ) + { + UINT16 block_size; + + /* Starting a block with 768 cycle of synchro*/ + sample_count += hector_tap_synchro( buffer, sample_count, 768 ); + + /* Handle block lenght on tape data */ + block_size = 822 ; /* Fixed size for the forth*/ + + /*block_count=0;*/ + + /* Data samples */ + for ( ; block_size ; data_pos++, block_size-- ) + { + /* Make sure there are enough bytes left */ + if ( data_pos > cas_size ) + return -1; + + sample_count += hector_tap_byte( buffer, sample_count, casdata[data_pos] ); + + } + } + + /*Finish by a zero*/ + sample_count += hector_tap_byte( buffer, sample_count, 0 ); + + return sample_count; +} +/******************************************************************* +///// END FORTH DATA CASSETTE +*******************************************************************/ + + +/******************************************************************* + Generate samples for the tape image +********************************************************************/ +static int hector_tap_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + return hector_handle_tap( buffer, bytes ); +} + + +/******************************************************************* + Calculate the number of samples needed for this tape image FORTH +********************************************************************/ +static int hector_tap_forth_to_wav_size(const UINT8 *casdata, int caslen) +{ + cas_size = caslen ; + + return hector_handle_forth_tap( NULL, casdata ); +} + +/******************************************************************* + Generate samples for the tape image FORTH +********************************************************************/ +static int hector_tap_forth_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + return hector_handle_forth_tap( buffer, bytes ); //forth removed here !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +} + + +/******************************************************************* + Calculate the number of samples needed for this tape image classical +********************************************************************/ +static int hector_tap_to_wav_size(const UINT8 *casdata, int caslen) +{ + cas_size = caslen ; + + return hector_handle_tap( NULL, casdata );//forth removed here !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +} + + +static const struct CassetteLegacyWaveFiller hector_legacy_fill_wave = +{ + hector_tap_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + hector_tap_to_wav_size, /* chunk_sample_calc */ + 44100, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static const struct CassetteLegacyWaveFiller hector_forth_legacy_fill_wave = +{ + hector_tap_forth_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + hector_tap_forth_to_wav_size, /* chunk_sample_calc */ + 44100, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + +static casserr_t hector_k7_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &hector_legacy_fill_wave); +} + + +static casserr_t hector_k7_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &hector_legacy_fill_wave); +} + +static casserr_t hector_k7forth_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &hector_forth_legacy_fill_wave); +} + + +static casserr_t hector_k7forth_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &hector_forth_legacy_fill_wave); +} + + +static const struct CassetteFormat hector_k7_format = +{ + "k7,cin", + hector_k7_identify, + hector_k7_load, + NULL +}; + +static const struct CassetteFormat hector_k7Forth_format = +{ + "for", + hector_k7forth_identify, + hector_k7forth_load, + NULL +}; + + +CASSETTE_FORMATLIST_START(hector_cassette_formats) + CASSETTE_FORMAT(hector_k7_format) + CASSETTE_FORMAT(hector_k7Forth_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/hect_tap.h b/src/lib/formats/hect_tap.h new file mode 100644 index 00000000000..893c3193510 --- /dev/null +++ b/src/lib/formats/hect_tap.h @@ -0,0 +1,17 @@ +/********************************************************************* + + hect_tap.h + + Format code for Micronique cassette files + +*********************************************************************/ + +#ifndef HECT_TAP_H +#define HECT_TAP_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(hector_cassette_formats); + +#endif /* HECT_TAP_H */ + diff --git a/src/lib/util/imageutl.c b/src/lib/formats/imageutl.c index b662792ba95..b662792ba95 100644 --- a/src/lib/util/imageutl.c +++ b/src/lib/formats/imageutl.c diff --git a/src/lib/util/imageutl.h b/src/lib/formats/imageutl.h index 1822b72a17a..1822b72a17a 100644 --- a/src/lib/util/imageutl.h +++ b/src/lib/formats/imageutl.h diff --git a/src/mame/formats/imd_dsk.c b/src/lib/formats/imd_dsk.c index af73817a0cd..77014717f1f 100644 --- a/src/mame/formats/imd_dsk.c +++ b/src/lib/formats/imd_dsk.c @@ -7,9 +7,7 @@ *********************************************************************/ #include <string.h> -#include "emu.h" -#include "imagedev/flopimg.h" -#include "imagedev/flopdrv.h" +#include "flopimg.h" struct imddsk_tag { diff --git a/src/emu/ioprocs.c b/src/lib/formats/ioprocs.c index 7413aa8756f..b4c1213c322 100644 --- a/src/emu/ioprocs.c +++ b/src/lib/formats/ioprocs.c @@ -1,5 +1,6 @@ #include <stdio.h> #include <string.h> +#include "osdcore.h" #include "ioprocs.h" diff --git a/src/emu/ioprocs.h b/src/lib/formats/ioprocs.h index cf91ab39bd7..f6c30866988 100644 --- a/src/emu/ioprocs.h +++ b/src/lib/formats/ioprocs.h @@ -10,7 +10,6 @@ #define IOPROCS_H #include <stdlib.h> -#include "emu.h" diff --git a/src/lib/formats/kim1_cas.c b/src/lib/formats/kim1_cas.c new file mode 100644 index 00000000000..290a0095c99 --- /dev/null +++ b/src/lib/formats/kim1_cas.c @@ -0,0 +1,194 @@ +#include "kim1_cas.h" + +#define SMPLO -32768 +#define SMPHI 32767 + + +static int cas_size; + + +INLINE int kim1_output_signal( INT16 *buffer, int sample_pos, int high ) +{ + int sample_count, i, j; + + sample_count = 0; + + if ( high ) + { + /* high frequency (~3600Hz) */ + for ( i = 0; i < 9; i++ ) + { + if ( buffer ) + { + for ( j = 0; j < 6; j++ ) + buffer[ sample_pos + sample_count + j ] = SMPHI; + } + sample_count += 6; + + if ( buffer ) + { + for ( j = 0; j < 6; j++ ) + buffer[ sample_pos + sample_count + j ] = SMPLO; + } + sample_count += 6; + } + } + else + { + /* low frequency (~2400Hz) */ + for ( i = 0; i < 6; i++ ) + { + if ( buffer ) + { + for ( j = 0; j < 9; j++ ) + buffer[ sample_pos + sample_count + j ] = SMPHI; + } + sample_count += 9; + + if ( buffer ) + { + for ( j = 0; j < 9; j++ ) + buffer[ sample_pos + sample_count + j ] = SMPLO; + } + sample_count += 9; + } + } + + return sample_count; +} + + +INLINE int kim1_output_byte( INT16 *buffer, int sample_pos, UINT8 byte ) +{ + int i; + int sample_count = 0; + + for ( i = 0; i < 8; i++ ) + { + sample_count += kim1_output_signal( buffer, sample_pos + sample_count, 1 ); + sample_count += kim1_output_signal( buffer, sample_pos + sample_count, byte & 0x01 ? 0 : 1 ); + sample_count += kim1_output_signal( buffer, sample_pos + sample_count, 0 ); + byte >>= 1; + } + return sample_count; +} + + +static int kim1_handle_kim(INT16 *buffer, const UINT8 *casdata) +{ + static const UINT8 encoding[16] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; + int i, data_pos, sample_count; + UINT16 size, address, file_id, checksum; + + if ( cas_size < 9 ) return -1; + if ( memcmp( casdata, "KIM1", 4 ) ) return -1; + + address = casdata[4] | ( casdata[5] << 8 ); + size = casdata[6] | ( casdata[7] << 8 ); + file_id = casdata[8]; + + data_pos = 9; + sample_count = 0; + checksum = casdata[4] + casdata[5]; + + /* First output a sync header: 100 x 16h */ + for ( i = 0; i < 100; i++ ) + sample_count += kim1_output_byte( buffer, sample_count, 0x16 ); + + /* Output end of sync: 2Ah */ + sample_count += kim1_output_byte( buffer, sample_count, 0x2A ); + + /* Output ID */ + sample_count += kim1_output_byte( buffer, sample_count, encoding[ file_id >> 4 ] ); + sample_count += kim1_output_byte( buffer, sample_count, encoding[ file_id & 0x0f ] ); + + /* Output starting address */ + sample_count += kim1_output_byte( buffer, sample_count, encoding[ ( address & 0xff ) >> 4 ] ); + sample_count += kim1_output_byte( buffer, sample_count, encoding[ address & 0x0f ] ); + sample_count += kim1_output_byte( buffer, sample_count, encoding[ address >> 12 ] ); + sample_count += kim1_output_byte( buffer, sample_count, encoding[ ( address >> 8 ) & 0x0f ] ); + + /* Output the data */ + while( data_pos < cas_size && data_pos < ( size + 9 ) ) + { + UINT8 data = casdata[data_pos]; + + sample_count += kim1_output_byte( buffer, sample_count, encoding[ data >> 4 ] ); + sample_count += kim1_output_byte( buffer, sample_count, encoding[ data & 0x0f ] ); + checksum += data; + data_pos++; + } + + /* Output end of data marker: 2Fh */ + sample_count += kim1_output_byte( buffer, sample_count, 0x2F ); + + /* Output checksum */ + sample_count += kim1_output_byte( buffer, sample_count, encoding[ ( checksum & 0xff ) >> 4 ] ); + sample_count += kim1_output_byte( buffer, sample_count, encoding[ checksum & 0x0f ] ); + sample_count += kim1_output_byte( buffer, sample_count, encoding[ checksum >> 12 ] ); + sample_count += kim1_output_byte( buffer, sample_count, encoding[ ( checksum >> 8 ) & 0x0f ] ); + + /* Output end of transmission marker: 2 x 04h */ + sample_count += kim1_output_byte( buffer, sample_count, 0x04 ); + sample_count += kim1_output_byte( buffer, sample_count, 0x04 ); + + return sample_count; +} + + +/******************************************************************* + Generate samples for the tape image +********************************************************************/ +static int kim1_kim_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + return kim1_handle_kim( buffer, bytes ); +} + + +/******************************************************************* + Calculate the number of samples needed for this tape image +********************************************************************/ +static int kim1_kim_to_wav_size(const UINT8 *casdata, int caslen) +{ + cas_size = caslen; + + return kim1_handle_kim( NULL, casdata ); +} + +static const struct CassetteLegacyWaveFiller kim1_kim_legacy_fill_wave = +{ + kim1_kim_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + kim1_kim_to_wav_size, /* chunk_sample_calc */ + 44100, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + +static casserr_t kim1_kim_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &kim1_kim_legacy_fill_wave); +} + + +static casserr_t kim1_kim_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &kim1_kim_legacy_fill_wave); +} + + +static const struct CassetteFormat kim1_kim_format = +{ + "kim,kim1", + kim1_kim_identify, + kim1_kim_load, + NULL +}; + + +CASSETTE_FORMATLIST_START(kim1_cassette_formats) + CASSETTE_FORMAT(kim1_kim_format) +CASSETTE_FORMATLIST_END + diff --git a/src/lib/formats/kim1_cas.h b/src/lib/formats/kim1_cas.h new file mode 100644 index 00000000000..e0e547a6a89 --- /dev/null +++ b/src/lib/formats/kim1_cas.h @@ -0,0 +1,17 @@ +/********************************************************************* + + kim1_cas.h + + Format code for KIM-1 casette files. + +*********************************************************************/ + +#ifndef __KIM1_CAS_H +#define __KIM1_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(kim1_cassette_formats); + +#endif /* __KIM1_CAS_H */ + diff --git a/src/lib/formats/lviv_lvt.c b/src/lib/formats/lviv_lvt.c new file mode 100644 index 00000000000..3f2ccce936b --- /dev/null +++ b/src/lib/formats/lviv_lvt.c @@ -0,0 +1,149 @@ +/* .LVT tape images */ +#include "lviv_lvt.h" + +#define WAVEENTRY_LOW -32768 +#define WAVEENTRY_HIGH 32767 + +#define LVIV_LVT_BIT_SAMPLES 60 +#define LVIV_LVT_HEADER_PILOT_SAMPLES 5190*60 +#define LVIV_LVT_HEADER_DATA_SAMPLES 16*11*60 +#define LVIV_LVT_PAUSE_SAMPLES 69370 +#define LVIV_LVT_BLOCK_PILOT_SAMPLES 1298*60 + +#define LVIV_LVT_HEADER_PILOT_LENGTH 5190 +#define LVIV_LVT_BLOCK_PILOT_LENGTH 1298 + +static INT16 *lviv_emit_level(INT16 *p, int count, int level) +{ + int i; + + for (i=0; i<count; i++) + { + *(p++) = level; + } + return p; +} + +static INT16* lviv_output_bit(INT16 *p, UINT8 b) +{ + if (b) + { + p = lviv_emit_level (p, 15, WAVEENTRY_HIGH); + p = lviv_emit_level (p, 15, WAVEENTRY_LOW); + p = lviv_emit_level (p, 15, WAVEENTRY_HIGH); + p = lviv_emit_level (p, 15, WAVEENTRY_LOW); + } + else + { + p = lviv_emit_level (p, 30, WAVEENTRY_HIGH); + p = lviv_emit_level (p, 30, WAVEENTRY_LOW); + } + return p; +} + +static INT16* lviv_output_byte(INT16 *p, UINT8 byte) +{ + int i; + + p = lviv_output_bit (p, 0); + + for (i=0; i<8; i++) + p = lviv_output_bit(p,(byte>>i) & 0x01); + + p = lviv_output_bit (p, 1); + p = lviv_output_bit (p, 1); + return p; +} + +/*************************************************************************************/ + +static int lviv_cassette_calculate_size_in_samples(const UINT8 *bytes, int length) +{ + int size; + + size = LVIV_LVT_HEADER_PILOT_SAMPLES + + LVIV_LVT_HEADER_DATA_SAMPLES + + LVIV_LVT_PAUSE_SAMPLES + + LVIV_LVT_BLOCK_PILOT_SAMPLES + + (length-0x10)*11*LVIV_LVT_BIT_SAMPLES; + + return size; +} + +/*************************************************************************************/ + +static int lviv_cassette_fill_wave(INT16 *buffer, int length, UINT8 *bytes) +{ + int i; + INT16 * p = buffer; + + int data_size; + + for (i=0; i<LVIV_LVT_HEADER_PILOT_LENGTH; i++) + p = lviv_output_bit (p, 1); + + for (i=0; i<10; i++) + p = lviv_output_byte (p, bytes[0x09]); + + for (i=0; i<6; i++) + p = lviv_output_byte (p, bytes[0x0a+i]); + + p = lviv_emit_level (p, LVIV_LVT_PAUSE_SAMPLES, WAVEENTRY_HIGH); + + for (i=0; i<LVIV_LVT_BLOCK_PILOT_LENGTH; i++) + p = lviv_output_bit (p, 1); + + data_size = length - ( LVIV_LVT_HEADER_PILOT_SAMPLES + + LVIV_LVT_HEADER_DATA_SAMPLES + + LVIV_LVT_PAUSE_SAMPLES + + LVIV_LVT_BLOCK_PILOT_SAMPLES ); + data_size/=660; + + for (i=0; i<data_size; i++) + p = lviv_output_byte (p, bytes[0x10+i]); + + return p - buffer; +} + + + +static const struct CassetteLegacyWaveFiller lviv_legacy_fill_wave = +{ + lviv_cassette_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + lviv_cassette_calculate_size_in_samples, /* chunk_sample_calc */ + 44100, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + + +static casserr_t lviv_lvt_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &lviv_legacy_fill_wave); +} + + + +static casserr_t lviv_lvt_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &lviv_legacy_fill_wave); +} + + + +static const struct CassetteFormat lviv_lvt_image_format = +{ + "lvt", + lviv_lvt_identify, + lviv_lvt_load, + NULL +}; + + + +CASSETTE_FORMATLIST_START(lviv_lvt_format) + CASSETTE_FORMAT(lviv_lvt_image_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/lviv_lvt.h b/src/lib/formats/lviv_lvt.h new file mode 100644 index 00000000000..9629e1024d0 --- /dev/null +++ b/src/lib/formats/lviv_lvt.h @@ -0,0 +1,16 @@ +/********************************************************************* + + lviv_lvt.h + + Format code for Lviv casette files + +*********************************************************************/ + +#ifndef LVIV_LVT_H +#define LVIV_LVT_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(lviv_lvt_format); + +#endif /* LVIV_LVT_H */ diff --git a/src/mame/formats/msx_dsk.c b/src/lib/formats/msx_dsk.c index 17cf288ef2b..17cf288ef2b 100644 --- a/src/mame/formats/msx_dsk.c +++ b/src/lib/formats/msx_dsk.c diff --git a/src/mame/formats/msx_dsk.h b/src/lib/formats/msx_dsk.h index 70d8311c1de..63914366e9c 100644 --- a/src/mame/formats/msx_dsk.h +++ b/src/lib/formats/msx_dsk.h @@ -9,7 +9,7 @@ #ifndef MSX_DSK_H #define MSX_DSK_H -#include "imagedev/flopimg.h" +#include "flopimg.h" /**************************************************************************/ diff --git a/src/lib/formats/mz_cas.c b/src/lib/formats/mz_cas.c new file mode 100644 index 00000000000..cdeaa7ec81e --- /dev/null +++ b/src/lib/formats/mz_cas.c @@ -0,0 +1,340 @@ +#include <string.h> +#include "mz_cas.h" + +#ifndef VERBOSE +#define VERBOSE 0 +#endif + +//#define LOG(N,M,A) +// if(VERBOSE>=N){ if( M )logerror("%11.6f: %-24s",machine.time().as_double(), (const char*)M ); logerror A; } + +#define LOG(N,M,A) \ + if(VERBOSE>=N){ if( M )printf("%-24s",(const char*)M ); printf A; } + +#define LO -32768 +#define HI +32767 + +#define SHORT_PULSE 2 +#define LONG_PULSE 4 + +#define BYTE_SAMPLES (LONG_PULSE+8*LONG_PULSE) + +#define SILENCE 8000 + +/* long gap and tape mark */ +#define LGAP 22000 +#define LTM_1 40 +#define LTM_0 40 +#define LTM_L 1 + +/* short gap and tape mark */ +#define SGAP 11000 +#define STM_1 20 +#define STM_0 20 +#define STM_L 1 + +static int fill_wave_1(INT16 *buffer, int offs) +{ + buffer[offs++] = HI; + buffer[offs++] = HI; + buffer[offs++] = LO; + buffer[offs++] = LO; + return LONG_PULSE; +} + +static int fill_wave_0(INT16 *buffer, int offs) +{ + buffer[offs++] = HI; + buffer[offs++] = LO; + return SHORT_PULSE; +} + +static int fill_wave_b(INT16 *buffer, int offs, int byte) +{ + int i, count = 0; + + /* data bits are preceded by a long pulse */ + count += fill_wave_1(buffer, offs + count); + + for( i = 7; i >= 0; i-- ) + { + if( (byte >> i) & 1 ) + count += fill_wave_1(buffer, offs + count); + else + count += fill_wave_0(buffer, offs + count); + } + return count; +} + +static int fill_wave(INT16 *buffer, int length, UINT8 *code) +{ + static INT16 *beg; + static UINT16 csum = 0; + static int header = 1, bytecount = 0; + int count = 0; + + if( code == CODE_HEADER ) + { + int i; + + /* is there insufficient space for the LGAP? */ + if( count + LGAP * SHORT_PULSE > length ) + return -1; + LOG(1,"mz700 fill_wave",("LGAP %d samples\n", LGAP * SHORT_PULSE)); + /* fill long gap */ + for( i = 0; i < LGAP; i++ ) + count += fill_wave_0(buffer, count); + + /* make a long tape mark */ + /* is there insufficient space for the LTM 1? */ + if( count + LTM_1 * LONG_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("LTM 1 %d samples\n", LTM_1 * LONG_PULSE)); + for( i = 0; i < LTM_1; i++ ) + count += fill_wave_1(buffer, count); + + /* is there insufficient space for the LTM 0? */ + if( count + LTM_0 * SHORT_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("LTM 0 %d samples\n", LTM_0 * SHORT_PULSE)); + for( i = 0; i < LTM_0; i++ ) + count += fill_wave_0(buffer, count); + + /* is there insufficient space for the L? */ + if( count + LTM_L * LONG_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("L %d samples\n", LONG_PULSE)); + count += fill_wave_1(buffer, count); + + /* reset header, bytecount and checksum */ + header = 1; + bytecount = 0; + csum = 0; + + /* HDR begins here */ + beg = buffer + count; + + return count; + } + + if( code == CODE_TRAILER ) + { + int i, file_length; + INT16 *end = buffer; + + /* is there insufficient space for the CHKF? */ + if( count + 2 * BYTE_SAMPLES > length ) + return -1; + LOG(1,"mz700_fill_wave",("CHKF 0x%04X\n", csum)); + count += fill_wave_b(buffer, count, csum >> 8); + count += fill_wave_b(buffer, count, csum & 0xff); + + /* is there insufficient space for the L */ + if( count + LTM_L * LONG_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("L\n")); + count += fill_wave_1(buffer, count); + + /* is there insufficient space for the 256S pulses? */ + if( count + 256 * SHORT_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("256S\n")); + for (i = 0; i < 256; i++) + count += fill_wave_0(buffer, count); + + file_length = (int)(end - beg) / sizeof(INT16); + /* is there insufficient space for the FILEC ? */ + if( count + file_length > length ) + return -1; + LOG(1,"mz700_fill_wave",("FILEC %d samples\n", file_length)); + memcpy(buffer + count, beg, file_length * sizeof(INT16)); + count += file_length; + + /* is there insufficient space for the CHKF ? */ + if( count + 2 * BYTE_SAMPLES > length ) + return -1; + LOG(1,"mz700_fill_wave",("CHKF 0x%04X\n", csum)); + count += fill_wave_b(buffer, count, csum >> 8); + count += fill_wave_b(buffer, count, csum & 0xff); + + /* is there insufficient space for the L ? */ + if( count + STM_L * LONG_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("L %d samples\n", LONG_PULSE)); + count += fill_wave_1(buffer, count); + + LOG(1,"mz700_fill_wave",("silence %d samples\n", SILENCE)); + /* silence at the end */ + for( i = 0; i < SILENCE; i++ ) + buffer[count++] = 0; + + return count; + } + + if( header == 1 && bytecount == 128 ) + { + int i, hdr_length; + INT16 *end = buffer; + + /* is there insufficient space for the CHKH ? */ + if( count + 2 * BYTE_SAMPLES > length ) + return -1; + LOG(1,"mz700_fill_wave",("CHKH 0x%04X\n", csum & 0xffff)); + count += fill_wave_b(buffer, count, (csum >> 8) & 0xff); + count += fill_wave_b(buffer, count, csum & 0xff); + + /* is there insufficient space for the L ? */ + if( count + LONG_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("L %d samples\n", LONG_PULSE)); + count += fill_wave_1(buffer, count); + + /* is there insufficient space for the 256S ? */ + if( count + 256 * SHORT_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("256S\n")); + for (i = 0; i < 256; i++) + count += fill_wave_0(buffer, count); + + hdr_length = (int)(end - beg) / sizeof(INT16); + /* is there insufficient space for the HDRC ? */ + if( count + hdr_length > length ) + return -1; + LOG(1,"mz700_fill_wave",("HDRC %d samples\n", hdr_length)); + memcpy(buffer + count, beg, hdr_length * sizeof(INT16)); + count += hdr_length; + + /* is there insufficient space for CHKH ? */ + if( count + 2 * BYTE_SAMPLES > length ) + return -1; + LOG(1,"mz700_fill_wave",("CHKH 0x%04X\n", csum & 0xffff)); + count += fill_wave_b(buffer, count, (csum >> 8) & 0xff); + count += fill_wave_b(buffer, count, csum & 0xff); + + /* is there insufficient space for the L ? */ + if( count + LONG_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("L %d samples\n", LONG_PULSE)); + count += fill_wave_1(buffer, count); + + /* is there sufficient space for the SILENCE? */ + if( count + SILENCE > length ) + return -1; + LOG(1,"mz700_fill_wave",("SILENCE %d samples\n", SILENCE)); + /* fill silence */ + for( i = 0; i < SILENCE; i++ ) + buffer[count++] = 0; + + /* is there sufficient space for the SGAP? */ + if( count + SGAP * SHORT_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("SGAP %d samples\n", SGAP * SHORT_PULSE)); + /* fill short gap */ + for( i = 0; i < SGAP; i++ ) + count += fill_wave_0(buffer, count); + + /* make a short tape mark */ + + /* is there sufficient space for the STM 1? */ + if( count + STM_1 * LONG_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("STM 1 %d samples\n", STM_1 * LONG_PULSE)); + for( i = 0; i < STM_1; i++ ) + count += fill_wave_1(buffer, count); + + /* is there sufficient space for the STM 0? */ + if( count + STM_0 * SHORT_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("STM 0 %d samples\n", STM_0 * SHORT_PULSE)); + for( i = 0; i < STM_0; i++ ) + count += fill_wave_0(buffer, count); + + /* is there sufficient space for the L? */ + if( count + STM_L * LONG_PULSE > length ) + return -1; + LOG(1,"mz700_fill_wave",("L %d samples\n", LONG_PULSE)); + count += fill_wave_1(buffer, count); + + bytecount = 0; + header = 0; + csum = 0; + + /* FILE begins here */ + beg = buffer + count; + } + + if( length < BYTE_SAMPLES ) + return -1; + + if (*code & 0x01) csum++; + if (*code & 0x02) csum++; + if (*code & 0x04) csum++; + if (*code & 0x08) csum++; + if (*code & 0x10) csum++; + if (*code & 0x20) csum++; + if (*code & 0x40) csum++; + if (*code & 0x80) csum++; + + bytecount++; + + count += fill_wave_b(buffer, count, *code); + + return count; +} + +#define MZ700_WAVESAMPLES_HEADER ( \ + LGAP * SHORT_PULSE + \ + LTM_1 * LONG_PULSE + \ + LTM_0 * SHORT_PULSE + \ + LTM_L * LONG_PULSE + \ + 2 * 2 * BYTE_SAMPLES + \ + SILENCE + \ + SGAP * SHORT_PULSE + \ + STM_1 * LONG_PULSE + \ + STM_0 * SHORT_PULSE + \ + STM_L * LONG_PULSE + \ + 2 * 2 * BYTE_SAMPLES) + + + + +static const struct CassetteLegacyWaveFiller mz700_legacy_fill_wave = +{ + fill_wave, /* fill_wave */ + 1, /* chunk_size */ + 2 * BYTE_SAMPLES, /* chunk_samples */ + NULL, /* chunk_sample_calc */ + 5120, /* sample_frequency */ + MZ700_WAVESAMPLES_HEADER, /* header_samples */ + 1 /* trailer_samples */ +}; + + + +static casserr_t mz700_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &mz700_legacy_fill_wave); +} + + + +static casserr_t mz700_cas_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &mz700_legacy_fill_wave); +} + + + +static const struct CassetteFormat mz700_cas_format = +{ + "m12,mzf", + mz700_cas_identify, + mz700_cas_load, + NULL +}; + + + +CASSETTE_FORMATLIST_START(mz700_cassette_formats) + CASSETTE_FORMAT(mz700_cas_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/mz_cas.h b/src/lib/formats/mz_cas.h new file mode 100644 index 00000000000..a06f557a8ec --- /dev/null +++ b/src/lib/formats/mz_cas.h @@ -0,0 +1,16 @@ +/********************************************************************* + + mz_cas.h + + Format code for MZ700 casette files + +*********************************************************************/ + +#ifndef MZ_CAS_H +#define MZ_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(mz700_cassette_formats); + +#endif /* MZ_CAS_H */ diff --git a/src/mame/formats/nes_dsk.c b/src/lib/formats/nes_dsk.c index 5beeab15f73..5beeab15f73 100644 --- a/src/mame/formats/nes_dsk.c +++ b/src/lib/formats/nes_dsk.c diff --git a/src/mame/formats/nes_dsk.h b/src/lib/formats/nes_dsk.h index 6d953830843..4ef5a5c7d92 100644 --- a/src/mame/formats/nes_dsk.h +++ b/src/lib/formats/nes_dsk.h @@ -9,7 +9,7 @@ #ifndef NES_DSK_H_ #define NES_DSK_H_ -#include "imagedev/flopimg.h" +#include "flopimg.h" FLOPPY_OPTIONS_EXTERN(nes_only); diff --git a/src/lib/formats/orao_cas.c b/src/lib/formats/orao_cas.c new file mode 100644 index 00000000000..5c198db243e --- /dev/null +++ b/src/lib/formats/orao_cas.c @@ -0,0 +1,120 @@ +/* + + Tape support for Orao TAP format + +*/ +#include "orao_cas.h" + + +#define ORAO_WAV_FREQUENCY 44100 +#define WAVE_HIGH -32768 +#define WAVE_LOW 0 + +#define ORAO_WAVE_ONE 24 +#define ORAO_WAVE_ZERO 11 + +#define ORAO_HEADER_SIZE 360 + +static INT16 wave_data; +static int len; + +static void orao_output_wave( INT16 **buffer, int length ) { + if ( buffer == NULL ) { + return; + } + + for( ; length > 0; length-- ) { + **buffer = wave_data; + *buffer = *buffer + 1; + } +} +static int orao_cas_to_wav_size( const UINT8 *casdata, int caslen ) { + int i,j,size; + UINT8 b; + + if (casdata == NULL) return -1; + if (caslen <= ORAO_HEADER_SIZE) { + return -1; + } + size = 0; + for (i=ORAO_HEADER_SIZE;i<caslen-ORAO_HEADER_SIZE;i++) { + for (j=0;j<8;j++) { + b = (casdata[i] >> j) & 1; + if (b==0) { + size += 2*ORAO_WAVE_ZERO; + } else { + size += 2*ORAO_WAVE_ONE; + } + } + } + len = caslen; + return size; +} + +static int orao_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) { + int i,j,size; + UINT8 b; + size = 0; + if (bytes == NULL) return -1; + for (i=ORAO_HEADER_SIZE;i<len-ORAO_HEADER_SIZE;i++) { + for (j=0;j<8;j++) { + b = (bytes[i] >> j) & 1; + if (b==0) { + wave_data = WAVE_LOW; + orao_output_wave(&buffer,ORAO_WAVE_ZERO); + wave_data = WAVE_HIGH; + orao_output_wave(&buffer,ORAO_WAVE_ZERO); + size += 2 *ORAO_WAVE_ZERO; + } else { + wave_data = WAVE_LOW; + orao_output_wave(&buffer,ORAO_WAVE_ONE); + wave_data = WAVE_HIGH; + orao_output_wave(&buffer,ORAO_WAVE_ONE); + size += 2 * ORAO_WAVE_ONE; + } + } + } + + return size; +} + + + + +static const struct CassetteLegacyWaveFiller orao_legacy_fill_wave = { + orao_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + orao_cas_to_wav_size, /* chunk_sample_calc */ + ORAO_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + + +static casserr_t orao_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) { + return cassette_legacy_identify( cassette, opts, &orao_legacy_fill_wave ); +} + + + +static casserr_t orao_cassette_load( cassette_image *cassette ) { + return cassette_legacy_construct( cassette, &orao_legacy_fill_wave ); +} + + + +static const struct CassetteFormat orao_cassette_format = { + "tap", + orao_cassette_identify, + orao_cassette_load, + NULL +}; + + +CASSETTE_FORMATLIST_START(orao_cassette_formats) + CASSETTE_FORMAT(orao_cassette_format) +CASSETTE_FORMATLIST_END + + diff --git a/src/lib/formats/orao_cas.h b/src/lib/formats/orao_cas.h new file mode 100644 index 00000000000..8e7bce9b752 --- /dev/null +++ b/src/lib/formats/orao_cas.h @@ -0,0 +1,15 @@ +/* + + orao_cas.h + + Format code for orao cassette files +*/ + +#ifndef ORAO_CAS_H +#define ORAO_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(orao_cassette_formats); + +#endif /* ORAO_CAS_H */ diff --git a/src/lib/formats/oric_dsk.c b/src/lib/formats/oric_dsk.c new file mode 100644 index 00000000000..3e716d88426 --- /dev/null +++ b/src/lib/formats/oric_dsk.c @@ -0,0 +1,316 @@ +/********************************************************************* + + formats/oric_dsk.c + + Oric disk images + +*********************************************************************/ + +#include <string.h> + +#include "imageutl.h" +#include "flopimg.h" +#include "oric_dsk.h" +#include "basicdsk.h" + + +#define mfm_disk_header_size 0x0100 +#define MFM_ID "MFM_DISK" + +#define TRACK_SIZE_MFM 0x1900 + +struct mfm_disk_sector_info +{ + int id_ptr; + int data_ptr; + int sector_size; + UINT8 ddam; +}; + +struct oricdsk_tag +{ + int tracks; + int heads; + int geometry; + int tracksize; + int num_sectors; + struct mfm_disk_sector_info sector_data[32]; +}; + + +static struct oricdsk_tag *get_tag(floppy_image *floppy) +{ + struct oricdsk_tag *tag; + tag = (oricdsk_tag *)floppy_tag(floppy); + return tag; +} + + +static FLOPPY_IDENTIFY(oric_dsk_identify) +{ + UINT8 header[mfm_disk_header_size]; + + floppy_image_read(floppy, header, 0, mfm_disk_header_size); + if ( memcmp( header, MFM_ID, 8 ) ==0) { + int heads = pick_integer_le(header, 8, 4); + int tracks = pick_integer_le(header, 12, 4); + + if (floppy_image_size(floppy)==((tracks*heads*TRACK_SIZE_MFM)+mfm_disk_header_size)) { + *vote = 100; + } else { + *vote = 0; + } + } else { + *vote = 0; + } + return FLOPPY_ERROR_SUCCESS; +} +static int oric_get_track_offset(floppy_image *floppy,int track, int head) +{ + if (get_tag(floppy)->geometry==1) { + return mfm_disk_header_size + (get_tag(floppy)->tracksize * track) + (head * get_tag(floppy)->tracksize * get_tag(floppy)->tracks); + } else { + return mfm_disk_header_size + (get_tag(floppy)->tracksize*((track * get_tag(floppy)->heads)+head)); + } +} + +static int oric_get_heads_per_disk(floppy_image *floppy) +{ + return get_tag(floppy)->heads; +} + +static int oric_get_tracks_per_disk(floppy_image *floppy) +{ + return get_tag(floppy)->tracks; +} + +static void mfm_info_cache_sector_info(floppy_image *floppy,int track,int head) +{ + UINT8 track_data[TRACK_SIZE_MFM]; + + /* initialise these with single density values if single density */ + UINT8 IdMark = 0x0fe; + UINT8 DataMark = 0x0fb; + UINT8 DeletedDataMark = 0x0f8; + + UINT8 SectorCount; + UINT8 SearchCode = 0; + UINT8 sector_number = 0; + int ptr = 0; + int track_offset = oric_get_track_offset(floppy,track,head); + floppy_image_read(floppy, track_data, track_offset, TRACK_SIZE_MFM); + SectorCount = 0; + + do + { + switch (SearchCode) + { + /* searching for id's */ + case 0: + { + /* found id mark? */ + if (track_data[ptr] == IdMark) + { + sector_number = track_data[ptr+3]-1; + /* store pointer to id mark */ + get_tag(floppy)->sector_data[sector_number].id_ptr = ptr + track_offset; + SectorCount++; + + /* grab N value - used to skip data in data field */ + get_tag(floppy)->sector_data[sector_number].sector_size = (1<< (track_data[ptr+4]+7)); + + /* skip past id field and crc */ + ptr+=7; + + /* now looking for data field */ + SearchCode = 1; + } + else + { + /* update position */ + ptr++; + } + } + break; + + /* searching for data id's */ + case 1: + { + /* found data or deleted data? */ + if ((track_data[ptr] == DataMark) || (track_data[ptr] == DeletedDataMark)) + { + /* yes */ + get_tag(floppy)->sector_data[sector_number].data_ptr = ptr + track_offset + 1; + get_tag(floppy)->sector_data[sector_number].ddam = (track_data[ptr] == DeletedDataMark) ? ID_FLAG_DELETED_DATA : 0; + + /* skip data field and id */ + ptr += get_tag(floppy)->sector_data[sector_number].sector_size + 3; + + /* now looking for id field */ + SearchCode = 0; + } + else + { + ptr++; + } + } + break; + + default: + break; + } + } + while (ptr < TRACK_SIZE_MFM); + get_tag(floppy)->num_sectors = SectorCount; + +} + +static floperr_t get_offset(floppy_image *floppy, int head, int track, int sector, int sector_is_index, UINT64 *offset) +{ + UINT64 offs; + + /* translate the sector to a raw sector */ + if (!sector_is_index) + { + sector -= 1; + } + mfm_info_cache_sector_info(floppy,track,head); + + /* check to see if we are out of range */ + if ((head < 0) || (head >= get_tag(floppy)->heads) || (track < 0) || (track >= get_tag(floppy)->tracks) + || (sector < 0) || (sector >=get_tag(floppy)->num_sectors)) + return FLOPPY_ERROR_SEEKERROR; + + offs = get_tag(floppy)->sector_data[sector].data_ptr; + if (offset) + *offset = offs; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_oric_read_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen) +{ + UINT64 offset; + floperr_t err; + err = get_offset(floppy, head, track, sector, sector_is_index, &offset); + if (err) + return err; + floppy_image_read(floppy, buffer, offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_oric_write_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam) +{ + UINT64 offset; + floperr_t err; + + err = get_offset(floppy, head, track, sector, sector_is_index, &offset); + if (err) + return err; + + floppy_image_write(floppy, buffer, offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t oric_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_oric_read_sector(floppy, head, track, sector, FALSE, buffer, buflen); +} + +static floperr_t oric_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_oric_write_sector(floppy, head, track, sector, FALSE, buffer, buflen, ddam); +} + +static floperr_t oric_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_oric_read_sector(floppy, head, track, sector, TRUE, buffer, buflen); +} + +static floperr_t oric_write_indexed_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_oric_write_sector(floppy, head, track, sector, TRUE, buffer, buflen, ddam); +} + +static floperr_t oric_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) +{ + floperr_t err; + err = get_offset(floppy, head, track, sector, FALSE, NULL); + if (err) + return err; + + if (sector_length) { + *sector_length = get_tag(floppy)->sector_data[sector].sector_size; + } + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t oric_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) +{ + floperr_t retVal; + + sector_index += 1; + + retVal = oric_get_sector_length(floppy, head, track, sector_index, sector_length); + if (sector_length!=NULL) { + *sector_length = get_tag(floppy)->sector_data[sector_index-1].sector_size; + } + if (cylinder) + *cylinder = track; + if (side) + *side = head; + if (sector) + *sector = sector_index; + if (flags) + *flags = get_tag(floppy)->sector_data[sector_index].ddam; + return retVal; +} + + +static FLOPPY_CONSTRUCT(oric_dsk_construct) +{ + struct FloppyCallbacks *callbacks; + struct oricdsk_tag *tag; + UINT8 header[mfm_disk_header_size]; + + floppy_image_read(floppy, header, 0, mfm_disk_header_size); + + tag = (struct oricdsk_tag *) floppy_create_tag(floppy, sizeof(struct oricdsk_tag)); + if (!tag) + return FLOPPY_ERROR_OUTOFMEMORY; + + tag->heads = pick_integer_le(header, 8, 4); + tag->tracks = pick_integer_le(header, 12, 4); + tag->geometry = pick_integer_le(header, 16, 4); + tag->tracksize = TRACK_SIZE_MFM; + memset(tag->sector_data,0,sizeof(tag->sector_data)); + + callbacks = floppy_callbacks(floppy); + callbacks->read_sector = oric_read_sector; + callbacks->write_sector = oric_write_sector; + callbacks->read_indexed_sector = oric_read_indexed_sector; + callbacks->write_indexed_sector = oric_write_indexed_sector; + callbacks->get_sector_length = oric_get_sector_length; + callbacks->get_heads_per_disk = oric_get_heads_per_disk; + callbacks->get_tracks_per_disk = oric_get_tracks_per_disk; + callbacks->get_indexed_sector_info = oric_get_indexed_sector_info; + return FLOPPY_ERROR_SUCCESS; +} +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( oric ) + FLOPPY_OPTION( oricmfm, "dsk", "Oric MFM floppy disk image", oric_dsk_identify, oric_dsk_construct, NULL, NULL) + FLOPPY_OPTION( oric, "dsk", "Oric disk image", basicdsk_identify_default, basicdsk_construct_default, NULL, + HEADS([2]) + TRACKS([80]) + SECTORS([9]) + SECTOR_LENGTH([512]) + FIRST_SECTOR_ID([1])) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/oric_dsk.h b/src/lib/formats/oric_dsk.h new file mode 100644 index 00000000000..58fc8336d91 --- /dev/null +++ b/src/lib/formats/oric_dsk.h @@ -0,0 +1,18 @@ +/********************************************************************* + + formats/oric_dsk.h + + Oric disk images + +*********************************************************************/ + +#ifndef ORIC_DSK_H +#define ORIC_DSK_H + +#include "flopimg.h" + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(oric); + +#endif /* ORIC_DSK_H */ diff --git a/src/lib/formats/oric_tap.c b/src/lib/formats/oric_tap.c new file mode 100644 index 00000000000..51a0aac7dc0 --- /dev/null +++ b/src/lib/formats/oric_tap.c @@ -0,0 +1,519 @@ +#include "oric_tap.h" + +#define ORIC_WAV_DEBUG 0 +#define LOG(x) do { if (ORIC_WAV_DEBUG) printf x; } while (0) + +/* this code based heavily on tap2wav by Fabrice Frances */ +#define ORIC_SYNC_BYTE 0x016 + +/* frequency of wave */ +/* tapes use 1200Hz and 2400Hz samples */ +#define ORIC_WAV_FREQUENCY 4800 + +/* 13 bits define a byte on the cassette */ +/* 1 start bit, 8 data bits, 1 parity bit and 3 stop bits */ +#define ORIC_BYTE_TO_BITS_ON_CASSETTE 13 + +#define ORIC_WAVESAMPLES_HEADER 3000 +#define ORIC_WAVESAMPLES_TRAILER 1000 + +enum +{ + ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE, + ORIC_CASSETTE_GOT_SYNC_BYTE, + ORIC_CASSETTE_READ_HEADER, + ORIC_CASSETTE_READ_FILENAME, + ORIC_CASSETTE_WRITE_DATA +}; + +#define WAVEENTRY_LOW -32768 +#define WAVEENTRY_HIGH 32767 +#define WAVEENTRY_NULL 0 + +#define ORIC_LEADER_LENGTH 512 + +struct oric_t +{ + int cassette_state; + int data_count; + int data_length; + int tap_size; +}; +static oric_t oric; + +/* to write a bit to the tape, the rom routines output either 4 periods at 1200 Hz for a 0 or 8 periods at 2400 Hz for a 1 */ +/* 4800 is twice 2400Hz */ + +/* 8 periods at 2400Hz */ +/* hi,lo, hi,lo, hi,lo, hi,lo */ + +static INT16 *oric_emit_level(INT16 *p, int count, INT16 wave_state) +{ + int i; + + for (i=0; i<count; i++) + { + *(p++) = wave_state; + } + return p; +} + +/* 4 periods at 1200Hz */ +static INT16* oric_output_bit(INT16 *p, UINT8 b) +{ + p = oric_emit_level(p, 1, WAVEENTRY_HIGH); + p = oric_emit_level(p, b ? 1 : 2, WAVEENTRY_LOW); + + return p; +} + +static int oric_get_bit_size_in_samples(UINT8 b) +{ + int count; + + count = 1; + + if (b) + { + count++; + } + else + { + count+=2; + } + + return count; +} + + +/* each byte on cassette is stored as: + + start bit 0 * 1 + data bits 8 * x (x is 0 or 1, and depends on data-bit value) + parity bit 1 * x (x is 0 or 1, and depends on the parity of the data bits) + stop bits 1 * 3 + + if data has even parity, parity bit will be 1. + if data has odd parity, parity bit will be 0. +*/ + +/* + 512 * data byte 0x016 -> leader + 1 * data byte 0x024 -> sync byte + 9 * data byte -> header + delay (of last pulse written) + x * data byte -> length + + + header structure: + 3 * ? -> ??? + 1 * ? -> ??? + 1 * x -> end address high byte + 1 * x -> end address low byte + 1 * x -> start address high byte + 1 * x -> start address low byte + 1 * ? -> ??? +*/ + +static int oric_calculate_byte_size_in_samples(UINT8 byte) +{ + int count; + int i; + UINT8 parity; + UINT8 data; + + count = 0; + + + /* start bit */ + count+=oric_get_bit_size_in_samples(0); + + /* set initial parity */ + parity = 1; + + /* data bits, written bit 0, bit 1...bit 7 */ + data = byte; + for (i=0; i<8; i++) + { + UINT8 data_bit; + + data_bit = data & 0x01; + + parity = parity + data_bit; + + count+=oric_get_bit_size_in_samples(data_bit); + data = data>>1; + } + + /* parity */ + count+=oric_get_bit_size_in_samples((parity & 0x01)); + + /* stop bits */ + count+=oric_get_bit_size_in_samples(1); + count+=oric_get_bit_size_in_samples(1); + count+=oric_get_bit_size_in_samples(1); + count+=oric_get_bit_size_in_samples(1); + + return count; +} + + +static INT16 *oric_output_byte(INT16 *p, UINT8 byte) +{ + int i; + UINT8 parity; + UINT8 data; + + /* start bit */ + p = oric_output_bit(p, 0); + + /* set initial parity */ + parity = 1; + + /* data bits, written bit 0, bit 1...bit 7 */ + data = byte; + for (i=0; i<8; i++) + { + UINT8 data_bit; + + data_bit = data & 0x01; + + parity = parity + data_bit; + + p = oric_output_bit(p, data_bit); + data = data>>1; + } + + /* parity */ + p = oric_output_bit(p, parity & 0x01); + + /* stop bits */ + p = oric_output_bit(p, 1); + p = oric_output_bit(p, 1); + p = oric_output_bit(p, 1); + p = oric_output_bit(p, 1); + + return p; +} + +static INT16 *oric_fill_pause(INT16 *p, int sample_count) +{ + int i; + + for (i=0; i<sample_count; i++) + { + *(p++) = WAVEENTRY_NULL; + } + + return p; +} + +static int oric_seconds_to_samples(float seconds) +{ + return (int)((float)seconds*(float)ORIC_WAV_FREQUENCY); +} + +/* length is length of .tap file! */ +static int oric_cassette_calculate_size_in_samples(const UINT8 *bytes, int length) +{ + unsigned char header[9]; + int count; + + const UINT8 *data_ptr; + int i; + UINT8 data; + + oric.tap_size = length; + + oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; + count = 0; + data_ptr = bytes; + + while (data_ptr<(bytes+length)) + { + data = data_ptr[0]; + data_ptr++; + + switch (oric.cassette_state) + { + case ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE: + { + if (data==ORIC_SYNC_BYTE) + { + logerror("found sync byte!\n"); + /* found first sync byte */ + oric.cassette_state = ORIC_CASSETTE_GOT_SYNC_BYTE; + } + } + break; + + case ORIC_CASSETTE_GOT_SYNC_BYTE: + { + if (data!=ORIC_SYNC_BYTE) + { + /* 0.25 second pause */ + count += oric_seconds_to_samples(0.25); + + logerror("found end of sync bytes!\n"); + + /* oric writes approx 512 bytes */ + /* found end of sync bytes */ + for (i=0; i<ORIC_LEADER_LENGTH; i++) + { + count+=oric_calculate_byte_size_in_samples(0x016); + } + + if (data==0x024) + { + //logerror("reading header!\n"); + count+=oric_calculate_byte_size_in_samples(0x024); + + oric.cassette_state = ORIC_CASSETTE_READ_HEADER; + oric.data_count = 0; + oric.data_length = 9; + } + } + } + break; + + case ORIC_CASSETTE_READ_HEADER: + { + header[oric.data_count] = data; + count+=oric_calculate_byte_size_in_samples(data); + + oric.data_count++; + + if (oric.data_count==oric.data_length) + { + //logerror("finished reading header!\n"); + oric.cassette_state = ORIC_CASSETTE_READ_FILENAME; + } + } + break; + + case ORIC_CASSETTE_READ_FILENAME: + { + count+=oric_calculate_byte_size_in_samples(data); + + /* got end of filename? */ + if (data==0) + { + UINT16 end, start; + logerror("got end of filename\n"); + + /* 100 1 bits to seperate header from data */ + for (i=0; i<100; i++) + { + count+=oric_get_bit_size_in_samples(1); + } + + oric.cassette_state = ORIC_CASSETTE_WRITE_DATA; + oric.data_count = 0; + + end = (((header[4] & 0x0ff)<<8) | (header[5] & 0x0ff)); + start = (((header[6] & 0x0ff)<<8) | (header[7] & 0x0ff)); + LOG(("start (from header): %02x\n",start)); + LOG(("end (from header): %02x\n",end)); + oric.data_length = end - start + 1; + } + } + break; + + case ORIC_CASSETTE_WRITE_DATA: + { + count+=oric_calculate_byte_size_in_samples(data); + oric.data_count++; + + if (oric.data_count==oric.data_length) + { + logerror("finished writing data!\n"); + oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; + } + } + break; + + } + } + + return count; +} + +/* length is length of sample buffer to fill! */ +static int oric_cassette_fill_wave(INT16 *buffer, int length, UINT8 *bytes) +{ + unsigned char header[9]; + UINT8 *data_ptr; + INT16 *p; + int i; + UINT8 data; + + p = buffer; + + + /* header and trailer act as pauses */ + /* the trailer is required so that the via sees the last bit of the last + byte */ + if (bytes == CODE_HEADER) { + for (i = 0; i < ORIC_WAVESAMPLES_HEADER; i++) + *(p++) = WAVEENTRY_NULL; + } + else if (bytes == CODE_TRAILER) { + for (i = 0; i < ORIC_WAVESAMPLES_TRAILER; i++) + *(p++) = WAVEENTRY_NULL; + } + else +{ + /* the length is the number of samples left in the buffer and NOT the number of bytes for the input file */ + length = length - ORIC_WAVESAMPLES_TRAILER; + + oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; + data_ptr = bytes; + + while ((data_ptr<(bytes + oric.tap_size)) && (p < (buffer+length)) ) + { + data = data_ptr[0]; + data_ptr++; + + switch (oric.cassette_state) + { + case ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE: + { + if (data==ORIC_SYNC_BYTE) + { + logerror("found sync byte!\n"); + /* found first sync byte */ + oric.cassette_state = ORIC_CASSETTE_GOT_SYNC_BYTE; + } + } + break; + + case ORIC_CASSETTE_GOT_SYNC_BYTE: + { + if (data!=ORIC_SYNC_BYTE) + { + /* 0.25 second pause */ + p = oric_fill_pause(p, oric_seconds_to_samples(0.25)); + + logerror("found end of sync bytes!\n"); + /* found end of sync bytes */ + for (i=0; i<ORIC_LEADER_LENGTH; i++) + { + p = oric_output_byte(p,0x016); + } + + if (data==0x024) + { + //logerror("reading header!\n"); + p = oric_output_byte(p,data); + oric.cassette_state = ORIC_CASSETTE_READ_HEADER; + oric.data_count = 0; + oric.data_length = 9; + } + } + } + break; + + case ORIC_CASSETTE_READ_HEADER: + { + header[oric.data_count] = data; + p = oric_output_byte(p, data); + oric.data_count++; + + if (oric.data_count==oric.data_length) + { + //logerror("finished reading header!\n"); + oric.cassette_state = ORIC_CASSETTE_READ_FILENAME; + } + } + break; + + case ORIC_CASSETTE_READ_FILENAME: + { + p = oric_output_byte(p, data); + + /* got end of filename? */ + if (data==0) + { + UINT16 end, start; + logerror("got end of filename\n"); + + /* oric includes a small delay, but I don't see + it being 1 bits */ + for (i=0; i<100; i++) + { + p = oric_output_bit(p,1); + } + + oric.cassette_state = ORIC_CASSETTE_WRITE_DATA; + oric.data_count = 0; + + end = (((header[4] & 0x0ff)<<8) | (header[5] & 0x0ff)); + start = (((header[6] & 0x0ff)<<8) | (header[7] & 0x0ff)); + LOG(("start (from header): %02x\n",start)); + LOG(("end (from header): %02x\n",end)); + oric.data_length = end - start + 1; + } + } + break; + + case ORIC_CASSETTE_WRITE_DATA: + { + p = oric_output_byte(p, data); + oric.data_count++; + + if (oric.data_count==oric.data_length) + { + logerror("finished writing data!\n"); + oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE; + } + } + break; + + } + } +} + return p - buffer; +} + + + +static const struct CassetteLegacyWaveFiller oric_legacy_fill_wave = +{ + oric_cassette_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + oric_cassette_calculate_size_in_samples, /* chunk_sample_calc */ + ORIC_WAV_FREQUENCY, /* sample_frequency */ + ORIC_WAVESAMPLES_HEADER, /* header_samples */ + ORIC_WAVESAMPLES_TRAILER /* trailer_samples */ +}; + + + +static casserr_t oric_tap_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &oric_legacy_fill_wave); +} + + + +static casserr_t oric_tap_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &oric_legacy_fill_wave); +} + + + +static const struct CassetteFormat oric_tap_format = +{ + "tap", + oric_tap_identify, + oric_tap_load, + NULL +}; + + + +CASSETTE_FORMATLIST_START(oric_cassette_formats) + CASSETTE_FORMAT(oric_tap_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/oric_tap.h b/src/lib/formats/oric_tap.h new file mode 100644 index 00000000000..2ec73b654fc --- /dev/null +++ b/src/lib/formats/oric_tap.h @@ -0,0 +1,16 @@ +/********************************************************************* + + oric_tap.h + + Format code for Oric TAP (*.tap) files + +*********************************************************************/ + +#ifndef ORIC_TAP_H +#define ORIC_TAP_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(oric_cassette_formats); + +#endif /* ORIC_TAP_H */ diff --git a/src/lib/formats/p6001_cas.c b/src/lib/formats/p6001_cas.c new file mode 100644 index 00000000000..ca3e0d4cd43 --- /dev/null +++ b/src/lib/formats/p6001_cas.c @@ -0,0 +1,93 @@ +/* + * Nec PC-6001 cassette format handling + */ + +#include "p6001_cas.h" + +#define WAVE_HIGH 0x5a9e +#define WAVE_LOW -0x5a9e + +static int cas_size; + +static int pc6001_fill_wave(INT16* buffer, UINT8 data, int sample_pos) +{ + int x; + int sample_count = 0; + + // one byte = 8 samples + for(x=0;x<8;x++) + { + if(buffer) + buffer[sample_pos+x] = ((data >> (7-x)) & 1) ? WAVE_HIGH : WAVE_LOW; + } + + sample_count+=8; + return sample_count; +} + +static int pc6001_handle_cas(INT16* buffer, const UINT8* casdata) +{ + int sample_count = 0; + int data_pos = 0; + + while(sample_count < cas_size) + { + sample_count += pc6001_fill_wave(buffer,casdata[data_pos],sample_count); + data_pos++; + } + + return sample_count; +} + +/******************************************************************* + Calculate the number of samples needed for this tape image +********************************************************************/ +static int pc6001_cas_to_wav_size (const UINT8 *casdata, int caslen) +{ + cas_size = caslen*8; + + return pc6001_handle_cas(NULL,casdata); +} + +/******************************************************************* + Generate samples for the tape image +********************************************************************/ +static int pc6001_cas_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + return pc6001_handle_cas(buffer,bytes); +} + +static const struct CassetteLegacyWaveFiller pc6001_legacy_fill_wave = +{ + pc6001_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + pc6001_cas_to_wav_size, /* chunk_sample_calc */ + 8000, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t pc6001_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &pc6001_legacy_fill_wave); +} + + + +static casserr_t pc6001_cas_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &pc6001_legacy_fill_wave); +} + + +static const struct CassetteFormat pc6001_cassette_format = { + "cas", + pc6001_cas_identify, + pc6001_cas_load, + NULL +}; + +CASSETTE_FORMATLIST_START(pc6001_cassette_formats) + CASSETTE_FORMAT(pc6001_cassette_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/p6001_cas.h b/src/lib/formats/p6001_cas.h new file mode 100644 index 00000000000..a79a3a149b1 --- /dev/null +++ b/src/lib/formats/p6001_cas.h @@ -0,0 +1,12 @@ +/* + * Nec PC-6001 cassette format handling + */ + +#ifndef PC6001_CAS_H_ +#define PC6001_CAS_H_ + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN( pc6001_cassette_formats ); + +#endif /*PC6001_CAS_H_*/ diff --git a/src/lib/formats/pc_dsk.c b/src/lib/formats/pc_dsk.c new file mode 100644 index 00000000000..cf42d7ef6e8 --- /dev/null +++ b/src/lib/formats/pc_dsk.c @@ -0,0 +1,137 @@ +/********************************************************************* + + formats/pc_dsk.c + + PC disk images + +*********************************************************************/ + +#include <string.h> + +#include "formats/pc_dsk.h" +#include "formats/basicdsk.h" + +struct pc_disk_sizes +{ + UINT32 image_size; + int sectors; + int heads; +}; + + + +static const struct pc_disk_sizes disk_sizes[] = +{ + { 8*1*40*512, 8, 1}, /* 5 1/4 inch double density single sided */ + { 8*2*40*512, 8, 2}, /* 5 1/4 inch double density */ + { 9*1*40*512, 9, 1}, /* 5 1/4 inch double density single sided */ + { 9*2*40*512, 9, 2}, /* 5 1/4 inch double density */ + {10*2*40*512, 10, 2}, /* 5 1/4 inch double density single sided */ + { 9*2*80*512, 9, 2}, /* 80 tracks 5 1/4 inch drives rare in PCs */ + { 9*2*80*512, 9, 2}, /* 3 1/2 inch double density */ + {15*2*80*512, 15, 2}, /* 5 1/4 inch high density (or japanese 3 1/2 inch high density) */ + {18*2*80*512, 18, 2}, /* 3 1/2 inch high density */ + {36*2*80*512, 36, 2} /* 3 1/2 inch enhanced density */ +}; + + + +static floperr_t pc_dsk_compute_geometry(floppy_image *floppy, struct basicdsk_geometry *geometry) +{ + int i; + UINT64 size; + + memset(geometry, 0, sizeof(*geometry)); + size = floppy_image_size(floppy); + + for (i = 0; i < ARRAY_LENGTH(disk_sizes); i++) + { + if (disk_sizes[i].image_size == size) + { + geometry->sectors = disk_sizes[i].sectors; + geometry->heads = disk_sizes[i].heads; + geometry->sector_length = 512; + geometry->first_sector_id = 1; + geometry->tracks = (int) (size / disk_sizes[i].sectors / disk_sizes[i].heads / geometry->sector_length); + return FLOPPY_ERROR_SUCCESS; + } + } + + if (size >= 0x1a) + { + /* + * get info from boot sector. + * not correct on all disks + */ + UINT8 scl, spt, heads; + floppy_image_read(floppy, &scl, 0x0c, 1); + floppy_image_read(floppy, &spt, 0x18, 1); + floppy_image_read(floppy, &heads, 0x1A, 1); + + if (size == ((UINT64) scl) * spt * heads * 0x200) + { + geometry->sectors = spt; + geometry->heads = heads; + geometry->sector_length = 512; + geometry->first_sector_id = 1; + geometry->tracks = scl; + return FLOPPY_ERROR_SUCCESS; + } + } + + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_IDENTIFY(pc_dsk_identify) +{ + floperr_t err; + struct basicdsk_geometry geometry; + + err = pc_dsk_compute_geometry(floppy, &geometry); + if (err) + return err; + + *vote = geometry.heads ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_CONSTRUCT(pc_dsk_construct) +{ + floperr_t err; + struct basicdsk_geometry geometry; + + if (params) + { + /* create */ + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = option_resolution_lookup_int(params, PARAM_HEADS); + geometry.tracks = option_resolution_lookup_int(params, PARAM_TRACKS); + geometry.sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + geometry.first_sector_id = 1; + geometry.sector_length = 512; + } + else + { + /* open */ + err = pc_dsk_compute_geometry(floppy, &geometry); + if (err) + return err; + } + + return basicdsk_construct(floppy, &geometry); +} + + + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( pc ) + FLOPPY_OPTION( pc_dsk, "dsk,ima,img,ufi,360", "PC floppy disk image", pc_dsk_identify, pc_dsk_construct, NULL, + HEADS([1]-2) + TRACKS(40/[80]) + SECTORS(8/[9]/10/15/18/36)) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/pc_dsk.h b/src/lib/formats/pc_dsk.h new file mode 100644 index 00000000000..1180f0bb228 --- /dev/null +++ b/src/lib/formats/pc_dsk.h @@ -0,0 +1,19 @@ +/********************************************************************* + + formats/pc_dsk.h + + PC disk images + +*********************************************************************/ + +#ifndef PC_DSK_H +#define PC_DSK_H + +#include "flopimg.h" + + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(pc); + +#endif /* PC_DSK_H */ diff --git a/src/lib/formats/pmd_pmd.c b/src/lib/formats/pmd_pmd.c new file mode 100644 index 00000000000..2d6cf67f378 --- /dev/null +++ b/src/lib/formats/pmd_pmd.c @@ -0,0 +1,119 @@ +/* .PMD tape images */ +#include "pmd_pmd.h" + +#define WAVEENTRY_LOW -32768 +#define WAVEENTRY_HIGH 32767 + +#define PMD85_WAV_FREQUENCY 7200 +#define PMD85_TIMER_FREQUENCY 1200 +#define PMD85_BIT_LENGTH (PMD85_WAV_FREQUENCY/PMD85_TIMER_FREQUENCY) +#define PMD85_PILOT_BITS (PMD85_TIMER_FREQUENCY*3) +#define PMD85_PAUSE_BITS (PMD85_TIMER_FREQUENCY/2) +#define PMD85_HEADER_BYTES 63 +#define PMD85_BITS_PER_BYTE 11 + +static INT16 *pmd85_emit_level(INT16 *p, int count, int level) +{ + int i; + + for (i=0; i<count; i++) *(p++) = level; + + return p; +} + +static INT16* pmd85_output_bit(INT16 *p, UINT8 bit) +{ + if (bit) + { + p = pmd85_emit_level (p, PMD85_BIT_LENGTH/2, WAVEENTRY_LOW); + p = pmd85_emit_level (p, PMD85_BIT_LENGTH/2, WAVEENTRY_HIGH); + } + else + { + p = pmd85_emit_level (p, PMD85_BIT_LENGTH/2, WAVEENTRY_HIGH); + p = pmd85_emit_level (p, PMD85_BIT_LENGTH/2, WAVEENTRY_LOW); + } + return p; +} + +static INT16* pmd85_output_byte(INT16 *p, UINT8 byte) +{ + int i; + + /* start */ + p = pmd85_output_bit (p, 0); + + /* data */ + for (i=0; i<8; i++) + p = pmd85_output_bit(p,(byte>>(7-i)) & 0x01); + + /* stop */ + p = pmd85_output_bit (p, 1); + p = pmd85_output_bit (p, 1); + + return p; +} + +static int pmd85_cassette_calculate_size_in_samples(const UINT8 *bytes, int length) +{ + return PMD85_BIT_LENGTH * (length * PMD85_BITS_PER_BYTE + PMD85_PILOT_BITS + PMD85_PAUSE_BITS); +} + +static int pmd85_cassette_fill_wave(INT16 *buffer, int length, UINT8 *bytes) +{ + int i; + INT16 * p = buffer; + + int data_size = ((length/PMD85_BIT_LENGTH-PMD85_PILOT_BITS-PMD85_PAUSE_BITS)/PMD85_BITS_PER_BYTE)-PMD85_HEADER_BYTES; + + /* pilot */ + for (i=0; i<PMD85_PILOT_BITS; i++) + p = pmd85_output_bit(p, 1); + + /* header */ + for (i=0; i<PMD85_HEADER_BYTES; i++) + p = pmd85_output_byte(p, bytes[i]); + + /* pause */ + for (i=0; i<PMD85_PAUSE_BITS; i++) + p = pmd85_output_bit(p, 1); + + /* data */ + for (i=0; i<data_size; i++) + p = pmd85_output_byte(p, bytes[i+PMD85_HEADER_BYTES]); + + return p - buffer; +} + +static const struct CassetteLegacyWaveFiller pmd85_legacy_fill_wave = +{ + pmd85_cassette_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + pmd85_cassette_calculate_size_in_samples, /* chunk_sample_calc */ + PMD85_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t pmd85_pmd_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &pmd85_legacy_fill_wave); +} + +static casserr_t pmd85_pmd_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &pmd85_legacy_fill_wave); +} + +static const struct CassetteFormat pmd85_pmd_image_format = +{ + "pmd", + pmd85_pmd_identify, + pmd85_pmd_load, + NULL +}; + +CASSETTE_FORMATLIST_START(pmd85_pmd_format) + CASSETTE_FORMAT(pmd85_pmd_image_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/pmd_pmd.h b/src/lib/formats/pmd_pmd.h new file mode 100644 index 00000000000..d33f286fa0e --- /dev/null +++ b/src/lib/formats/pmd_pmd.h @@ -0,0 +1,16 @@ +/********************************************************************* + + pmd_pmd.h + + Format code for PMD-85 casette files + +*********************************************************************/ + +#ifndef PMD_PMD_H +#define PMD_PMD_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(pmd85_pmd_format); + +#endif /* PMD_PMD_H */ diff --git a/src/lib/formats/primoptp.c b/src/lib/formats/primoptp.c new file mode 100644 index 00000000000..74fdce1d93c --- /dev/null +++ b/src/lib/formats/primoptp.c @@ -0,0 +1,247 @@ +/* .PTP Microkey Primo tape images */ + + +#include "primoptp.h" + + +#define PRIMO_WAVEENTRY_LOW -32768 +#define PRIMO_WAVEENTRY_HIGH 32767 +#define PRIMO_WAVEENTRY_ZERO 0 + +#define PRIMO_WAV_FREQUENCY 22050 +#define PRIMO_BIT_1_PERIOD (312*2*0.000001) +#define PRIMO_BIT_0_PERIOD (3*PRIMO_BIT_1_PERIOD) + +#define PRIMO_BIT_1_LENGTH (PRIMO_BIT_1_PERIOD*PRIMO_WAV_FREQUENCY) +#define PRIMO_BIT_0_LENGTH (PRIMO_BIT_0_PERIOD*PRIMO_WAV_FREQUENCY) +/* +#define PRIMO_BIT_1_LENGTH 6 +#define PRIMO_BIT_0_LENGTH 16 +*/ +#define PRIMO_PAUSE_LENGTH 2000 +#define PRIMO_FILE_PILOT_LENGTH ((4*PRIMO_BIT_1_LENGTH + 4*PRIMO_BIT_0_LENGTH)*512) +#define PRIMO_BLOCK_PILOT_LENGTH ((8*PRIMO_BIT_1_LENGTH)*96 + (5*PRIMO_BIT_1_LENGTH + 3*PRIMO_BIT_0_LENGTH)*3) + +static UINT32 primo_tape_image_length; + +static INT16 *primo_emit_level(INT16 *p, int count, int level) +{ + int i; + + for (i=0; i<count; i++) *(p++) = level; + + return p; +} + +static INT16* primo_output_bit(INT16 *p, UINT8 bit) +{ + if (bit) + { + p = primo_emit_level (p, PRIMO_BIT_1_LENGTH/2, PRIMO_WAVEENTRY_HIGH); + p = primo_emit_level (p, PRIMO_BIT_1_LENGTH/2, PRIMO_WAVEENTRY_LOW); + } + else + { + p = primo_emit_level (p, PRIMO_BIT_0_LENGTH/2, PRIMO_WAVEENTRY_HIGH); + p = primo_emit_level (p, PRIMO_BIT_0_LENGTH/2, PRIMO_WAVEENTRY_LOW); + } + return p; +} + +static INT16* primo_output_byte(INT16 *p, UINT8 byte) +{ + int i; + + for (i=0; i<8; i++) + p = primo_output_bit(p,(byte>>(7-i)) & 0x01); + + return p; +} + +static UINT32 primo_cassette_calculate_number_of_1(const UINT8 *bytes, UINT16 length) +{ + int i,j; + + UINT32 number_of_1 = 0; + + for (i=0; i<length; i++) + for (j=0; j<8; j++) + if ((bytes[i]>>j)&0x01) + number_of_1++; + + return number_of_1; +} + +static int primo_cassette_calculate_size_in_samples(const UINT8 *bytes, int length) +{ + int i = 0, j = 0; + + UINT8 *b = (UINT8*) bytes; + + UINT32 file_size = 0; + UINT16 block_size = 0; + + UINT32 number_of_1 = 0; + UINT32 number_of_0 = 0; + UINT32 size_in_samples = 0; + + primo_tape_image_length = length; + + while (i < length) + { + size_in_samples += PRIMO_PAUSE_LENGTH; + logerror ("Samples (pause): %d\n", size_in_samples); + + size_in_samples += PRIMO_FILE_PILOT_LENGTH; + logerror ("Samples (file pilot): %d\n", size_in_samples); + + /* file size with header */ + file_size = *(b+1) + *(b+2)*256; + b += 3; + logerror ("File size (with header): %d\n", file_size); + + /* b is now set on the first data byte of file + it means first byte (type) of block */ + + j = 0; + while (j < file_size-3) + { + size_in_samples += PRIMO_BLOCK_PILOT_LENGTH; + logerror ("Samples (block pilot): %d\n", size_in_samples); + + /* block size without header but including CRC byte */ + block_size = *(b+1) + *(b+2)*256; + + /* b is set on the first byte of block data */ + b += 3; + + number_of_1 = primo_cassette_calculate_number_of_1(b, block_size); + number_of_0 = (block_size)*8-number_of_1; + + size_in_samples += number_of_1 * PRIMO_BIT_1_LENGTH; + size_in_samples += number_of_0 * PRIMO_BIT_0_LENGTH; + + logerror ("Samples (block data): %d\n", size_in_samples); + + logerror ("\tBlock size: %d\n", block_size); + + b += block_size; + + /* b is set on the first header byte of next block or file */ + + j += block_size+3; + } + + i += file_size; + } + + return size_in_samples; +} + +static int primo_cassette_fill_wave(INT16 *buffer, int length, UINT8 *bytes) +{ + + int i = 0, j = 0, k; + + INT16 *p = buffer; + UINT8 *b = bytes; + + UINT32 file_size = 0; + UINT16 block_size = 0; + + logerror ("Image size: %d\n", length); + + while (i < primo_tape_image_length) + { + logerror ("Beginning Primo file\n"); + /* pause */ + p = primo_emit_level (p, PRIMO_PAUSE_LENGTH, PRIMO_WAVEENTRY_ZERO); + + logerror ("Samples (pause): %ld\n", (long int)(p-buffer)); + + /* file pilot */ + for (k=0; k<512; k++) + p = primo_output_byte (p, 0xaa); + + logerror ("Samples (file pilot): %ld\n", (long int)(p-buffer)); + + /* file size with header */ + file_size = *(b+1) + *(b+2)*256; + b += 3; + + logerror ("File size: %d\n", file_size); + + /* b is now set on the first data byte of file + it means first byte (block type) of block header */ + + j = 0; + while (j < file_size-3) + { + + /* block pilot */ + for (k=0; k<96; k++) + p = primo_output_byte (p, 0xff); + for (k=0; k<3; k++) + p = primo_output_byte (p, 0xd3); + + logerror ("Samples (block pilot): %ld\n", (long int)(p-buffer)); + + /* block size without header but including CRC byte */ + block_size = *(b+1) + *(b+2)*256; + b += 3; + + for (k=0; k<block_size; k++) + p = primo_output_byte (p, *(b+k)); + + logerror ("Samples (block data): %ld\n", (long int)(p-buffer)); + + b += block_size; + + /* b is now set on the first header byte of next block or file */ + + j += block_size+3; + } + + logerror ("Primo file finished\n"); + logerror ("i = %d\n", i); + i += file_size; + logerror ("i = %d\n", i); + } + + logerror ("End of fill_wave/n"); + + return p - buffer; +} + +static const struct CassetteLegacyWaveFiller primo_legacy_fill_wave = +{ + primo_cassette_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + primo_cassette_calculate_size_in_samples, /* chunk_sample_calc */ + PRIMO_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t primo_ptp_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &primo_legacy_fill_wave); +} + +static casserr_t primo_ptp_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &primo_legacy_fill_wave); +} + +static const struct CassetteFormat primo_ptp_image_format = +{ + "ptp", + primo_ptp_identify, + primo_ptp_load, + NULL +}; + +CASSETTE_FORMATLIST_START(primo_ptp_format) + CASSETTE_FORMAT(primo_ptp_image_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/primoptp.h b/src/lib/formats/primoptp.h new file mode 100644 index 00000000000..f934d637819 --- /dev/null +++ b/src/lib/formats/primoptp.h @@ -0,0 +1,16 @@ +/********************************************************************* + + primoptp.h + + Format code for Primo casette files + +*********************************************************************/ + +#ifndef PRIMOPTP_H +#define PRIMOPTP_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN( primo_ptp_format ); + +#endif /* PRIMOPTP_H */ diff --git a/src/lib/formats/rk_cas.c b/src/lib/formats/rk_cas.c new file mode 100644 index 00000000000..8f7f5ca86c8 --- /dev/null +++ b/src/lib/formats/rk_cas.c @@ -0,0 +1,310 @@ +/* + + Tape support for RK format + +*/ +#include "rk_cas.h" + + +#define RK_WAV_FREQUENCY 44000 +#define WAVE_HIGH 32767 +#define WAVE_LOW -32768 + +#define RK_HEADER_LEN 256 + +#define RK_SIZE_20 20 +#define RK_SIZE_22 22 +#define RK_SIZE_60 60 + +static int data_size; + +static INT16 *rk_emit_level(INT16 *p, int count, int level) +{ + int i; + + for (i=0; i<count; i++) + { + *(p++) = level; + } + return p; +} + +static INT16* rk_output_bit(INT16 *p, UINT8 b,int bitsize) +{ + if (b) + { + p = rk_emit_level (p, bitsize, WAVE_HIGH); + p = rk_emit_level (p, bitsize, WAVE_LOW); + } + else + { + p = rk_emit_level (p, bitsize, WAVE_LOW); + p = rk_emit_level (p, bitsize, WAVE_HIGH); + + } + return p; +} + +static INT16* rk_output_byte(INT16 *p, UINT8 byte,int bitsize) +{ + int i; + for (i=7; i>=0; i--) + p = rk_output_bit(p,(byte>>i) & 0x01, bitsize); + + return p; +} + + +static int rk20_cas_to_wav_size( const UINT8 *casdata, int caslen ) { + data_size = caslen; + return (RK_HEADER_LEN * 8 * 2 + 8*2 + caslen * 8 * 2) * RK_SIZE_20; +} + +static int rk22_cas_to_wav_size( const UINT8 *casdata, int caslen ) { + data_size = caslen; + return (RK_HEADER_LEN * 8 * 2 + 8*2 + caslen * 8 * 2) * RK_SIZE_22; +} + +static int rk60_cas_to_wav_size( const UINT8 *casdata, int caslen ) { + data_size = caslen; + return (RK_HEADER_LEN * 8 * 2 + 8*2 + caslen * 8 * 2) * RK_SIZE_60; +} + +static int gam_cas_to_wav_size( const UINT8 *casdata, int caslen ) { + data_size = caslen; + return (RK_HEADER_LEN * 8 * 2 + caslen * 8 * 2) * RK_SIZE_20; +} + +static int rk20_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) { + int i; + INT16 * p = buffer; + + for (i=0; i<RK_HEADER_LEN; i++) { + p = rk_output_byte (p, 0x00, RK_SIZE_20 ); + } + + p = rk_output_byte (p, 0xE6, RK_SIZE_20); + + for (i=0; i<data_size; i++) + p = rk_output_byte (p, bytes[i], RK_SIZE_20); + + return p - buffer; +} + +static int rk22_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) { + int i; + INT16 * p = buffer; + + for (i=0; i<RK_HEADER_LEN; i++) { + p = rk_output_byte (p, 0x00, RK_SIZE_22 ); + } + + p = rk_output_byte (p, 0xE6, RK_SIZE_22); + + for (i=0; i<data_size; i++) + p = rk_output_byte (p, bytes[i], RK_SIZE_22); + + return p - buffer; +} + +static int rk60_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) { + int i; + INT16 * p = buffer; + + for (i=0; i<RK_HEADER_LEN; i++) { + p = rk_output_byte (p, 0x00, RK_SIZE_60 ); + } + + p = rk_output_byte (p, 0xE6, RK_SIZE_60); + + for (i=0; i<data_size; i++) + p = rk_output_byte (p, bytes[i], RK_SIZE_60); + + return p - buffer; +} + +static int gam_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) { + int i; + INT16 * p = buffer; + + for (i=0; i<RK_HEADER_LEN; i++) { + p = rk_output_byte (p, 0x00, RK_SIZE_20 ); + } + + for (i=0; i<data_size; i++) + p = rk_output_byte (p, bytes[i], RK_SIZE_20); + + return p - buffer; +} + +static const struct CassetteLegacyWaveFiller rk20_legacy_fill_wave = { + rk20_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + rk20_cas_to_wav_size, /* chunk_sample_calc */ + RK_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t rk20_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) { + return cassette_legacy_identify( cassette, opts, &rk20_legacy_fill_wave ); +} + +static casserr_t rk20_cassette_load( cassette_image *cassette ) { + return cassette_legacy_construct( cassette, &rk20_legacy_fill_wave ); +} + +static const struct CassetteLegacyWaveFiller rk22_legacy_fill_wave = { + rk22_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + rk22_cas_to_wav_size, /* chunk_sample_calc */ + RK_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t rk22_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) { + return cassette_legacy_identify( cassette, opts, &rk22_legacy_fill_wave ); +} + +static casserr_t rk22_cassette_load( cassette_image *cassette ) { + return cassette_legacy_construct( cassette, &rk22_legacy_fill_wave ); +} + +static const struct CassetteLegacyWaveFiller gam_legacy_fill_wave = { + gam_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + gam_cas_to_wav_size, /* chunk_sample_calc */ + RK_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t gam_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) { + return cassette_legacy_identify( cassette, opts, &gam_legacy_fill_wave ); +} + +static casserr_t gam_cassette_load( cassette_image *cassette ) { + return cassette_legacy_construct( cassette, &gam_legacy_fill_wave ); +} + +static const struct CassetteLegacyWaveFiller rk60_legacy_fill_wave = { + rk60_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + rk60_cas_to_wav_size, /* chunk_sample_calc */ + RK_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t rk60_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) { + return cassette_legacy_identify( cassette, opts, &rk60_legacy_fill_wave ); +} + +static casserr_t rk60_cassette_load( cassette_image *cassette ) { + return cassette_legacy_construct( cassette, &rk60_legacy_fill_wave ); +} + +static const struct CassetteFormat rku_cassette_format = { + "rku", + rk20_cassette_identify, + rk20_cassette_load, + NULL +}; + +static const struct CassetteFormat rk8_cassette_format = { + "rk8", + rk60_cassette_identify, + rk60_cassette_load, + NULL +}; + +static const struct CassetteFormat rks_cassette_format = { + "rks", + rk20_cassette_identify, + rk20_cassette_load, + NULL +}; + +static const struct CassetteFormat rko_cassette_format = { + "rko", + rk20_cassette_identify, + rk20_cassette_load, + NULL +}; + +static const struct CassetteFormat rkr_cassette_format = { + "rk,rkr", + rk20_cassette_identify, + rk20_cassette_load, + NULL +}; + +static const struct CassetteFormat rka_cassette_format = { + "rka", + rk20_cassette_identify, + rk20_cassette_load, + NULL +}; + +static const struct CassetteFormat rkm_cassette_format = { + "rkm", + rk22_cassette_identify, + rk22_cassette_load, + NULL +}; + +static const struct CassetteFormat rkp_cassette_format = { + "rkp", + rk20_cassette_identify, + rk20_cassette_load, + NULL +}; + +static const struct CassetteFormat gam_cassette_format = { + "gam,g16,pki", + gam_cassette_identify, + gam_cassette_load, + NULL +}; + +CASSETTE_FORMATLIST_START(rku_cassette_formats) + CASSETTE_FORMAT(rku_cassette_format) +CASSETTE_FORMATLIST_END + +CASSETTE_FORMATLIST_START(rk8_cassette_formats) + CASSETTE_FORMAT(rk8_cassette_format) +CASSETTE_FORMATLIST_END + +CASSETTE_FORMATLIST_START(rks_cassette_formats) + CASSETTE_FORMAT(rks_cassette_format) +CASSETTE_FORMATLIST_END + +CASSETTE_FORMATLIST_START(rko_cassette_formats) + CASSETTE_FORMAT(rko_cassette_format) +CASSETTE_FORMATLIST_END + +CASSETTE_FORMATLIST_START(rkr_cassette_formats) + CASSETTE_FORMAT(rkr_cassette_format) + CASSETTE_FORMAT(gam_cassette_format) +CASSETTE_FORMATLIST_END + +CASSETTE_FORMATLIST_START(rka_cassette_formats) + CASSETTE_FORMAT(rka_cassette_format) +CASSETTE_FORMATLIST_END + +CASSETTE_FORMATLIST_START(rkm_cassette_formats) + CASSETTE_FORMAT(rkm_cassette_format) +CASSETTE_FORMATLIST_END + +CASSETTE_FORMATLIST_START(rkp_cassette_formats) + CASSETTE_FORMAT(rkp_cassette_format) +CASSETTE_FORMATLIST_END + +CASSETTE_FORMATLIST_START(gam_cassette_formats) + CASSETTE_FORMAT(gam_cassette_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/rk_cas.h b/src/lib/formats/rk_cas.h new file mode 100644 index 00000000000..71a8ae34860 --- /dev/null +++ b/src/lib/formats/rk_cas.h @@ -0,0 +1,24 @@ +/* + + rk_cas.h + + Format code for rk cassette files +*/ + +#ifndef RK_CAS_H +#define RK_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(rku_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(rk8_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(rks_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(rko_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(rkr_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(rka_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(rkm_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(rkp_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(gam_cassette_formats); + +#endif /* RK_CAS_H */ + diff --git a/src/lib/formats/smx_dsk.c b/src/lib/formats/smx_dsk.c new file mode 100644 index 00000000000..85941db7a61 --- /dev/null +++ b/src/lib/formats/smx_dsk.c @@ -0,0 +1,159 @@ +/********************************************************************* + + formats/smx_dsk.c + + Specialist MX disk images + +*********************************************************************/ + +#include <string.h> + +#include "smx_dsk.h" +#include "basicdsk.h" + +static FLOPPY_IDENTIFY(smx_dsk_identify) +{ + *vote = (floppy_image_size(floppy) == 819200) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + +static int smx_get_heads_per_disk(floppy_image *floppy) +{ + return 2; +} + +static int smx_get_tracks_per_disk(floppy_image *floppy) +{ + return 80; +} + +static UINT64 smx_translate_offset(floppy_image *floppy, + int track, int head, int sector) +{ + return (track * 1024 * 5 * 2) + (head * 1024 * 5) + 1024 * sector; +} + +static floperr_t get_offset(floppy_image *floppy, int head, int track, int sector, int sector_is_index, UINT64 *offset) +{ + UINT64 offs; + /* translate the sector to a raw sector */ + if (!sector_is_index) + { + sector -= 1; + } + /* check to see if we are out of range */ + if ((head < 0) || (head >= 2) || (track < 0) || (track >= 80) + || (sector < 0) || (sector >= 6)) + return FLOPPY_ERROR_SEEKERROR; + + offs = smx_translate_offset(floppy, track, head, sector); + if (offset) + *offset = offs; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_smx_read_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen) +{ + UINT64 offset; + floperr_t err; + err = get_offset(floppy, head, track, sector, sector_is_index, &offset); + if (err) + return err; + + floppy_image_read(floppy, buffer, offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_smx_write_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam) +{ + UINT64 offset; + floperr_t err; + + err = get_offset(floppy, head, track, sector, sector_is_index, &offset); + if (err) + return err; + + floppy_image_write(floppy, buffer, offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t smx_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_smx_read_sector(floppy, head, track, sector, FALSE, buffer, buflen); +} + +static floperr_t smx_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_smx_write_sector(floppy, head, track, sector, FALSE, buffer, buflen, ddam); +} + +static floperr_t smx_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_smx_read_sector(floppy, head, track, sector, TRUE, buffer, buflen); +} + +static floperr_t smx_write_indexed_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_smx_write_sector(floppy, head, track, sector, TRUE, buffer, buflen, ddam); +} + +static floperr_t smx_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) +{ + floperr_t err; + err = get_offset(floppy, head, track, sector, FALSE, NULL); + if (err) + return err; + + if (sector_length) { + *sector_length = 1024; + } + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t smx_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) +{ + sector_index += 1; + if (cylinder) + *cylinder = track; + if (side) + *side = head; + if (sector) + *sector = sector_index; + if (flags) + /* TODO: read DAM or DDAM and determine flags */ + *flags = 0; + return smx_get_sector_length(floppy, head, track, sector_index, sector_length); +} + + +static FLOPPY_CONSTRUCT(smx_dsk_construct) +{ + struct FloppyCallbacks *callbacks; + callbacks = floppy_callbacks(floppy); + callbacks->read_sector = smx_read_sector; + callbacks->write_sector = smx_write_sector; + callbacks->read_indexed_sector = smx_read_indexed_sector; + callbacks->write_indexed_sector = smx_write_indexed_sector; + callbacks->get_sector_length = smx_get_sector_length; + callbacks->get_heads_per_disk = smx_get_heads_per_disk; + callbacks->get_tracks_per_disk = smx_get_tracks_per_disk; + callbacks->get_indexed_sector_info = smx_get_indexed_sector_info; + + return FLOPPY_ERROR_SUCCESS; +} + + + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( specimx ) + FLOPPY_OPTION( smx_dsk, "odi", "Specialist MX floppy disk image", smx_dsk_identify, smx_dsk_construct, NULL, NULL) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/smx_dsk.h b/src/lib/formats/smx_dsk.h new file mode 100644 index 00000000000..19149f3e229 --- /dev/null +++ b/src/lib/formats/smx_dsk.h @@ -0,0 +1,18 @@ +/********************************************************************* + + formats/smx_dsk.h + + SVI318 disk images + +*********************************************************************/ + +#ifndef SMX_DSK_H +#define SMX_DSK_H + +#include "flopimg.h" + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(specimx); + +#endif /* SVI_DSK_H */ diff --git a/src/lib/formats/sorc_dsk.c b/src/lib/formats/sorc_dsk.c new file mode 100644 index 00000000000..c23c0b9e02b --- /dev/null +++ b/src/lib/formats/sorc_dsk.c @@ -0,0 +1,157 @@ +/********************************************************************* + + formats/exi_dsk.c + + Exidy Sorcerer floppy-disk images + +*********************************************************************/ + +#include <string.h> + +#include "sorc_dsk.h" +#include "basicdsk.h" + +static FLOPPY_IDENTIFY(exi_dsk_identify) +{ + *vote = (floppy_image_size(floppy) == 332640) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + +static int exi_get_heads_per_disk(floppy_image *floppy) +{ + return 1; +} + +static int exi_get_tracks_per_disk(floppy_image *floppy) +{ + return 77; +} + +static UINT64 exi_translate_offset(floppy_image *floppy, int track, int head, int sector) +{ + return 13+270*(16*track+sector); +} + +static floperr_t get_offset(floppy_image *floppy, int head, int track, int sector, int sector_is_index, UINT64 *offset) +{ + UINT64 offs; + /* translate the sector to a raw sector */ + if (!sector_is_index) + { + sector -= 1; + } + /* check to see if we are out of range */ + if ((head != 0) || (track < 0) || (track >= 77) || (sector < 0) || (sector >= 16)) + return FLOPPY_ERROR_SEEKERROR; + + offs = exi_translate_offset(floppy, track, head, sector); + if (offset) + *offset = offs; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_exi_read_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen) +{ + UINT64 offset; + floperr_t err; + err = get_offset(floppy, head, track, sector, sector_is_index, &offset); + if (err) + return err; + + floppy_image_read(floppy, buffer, offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_exi_write_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam) +{ + UINT64 offset; + floperr_t err; + + err = get_offset(floppy, head, track, sector, sector_is_index, &offset); + if (err) + return err; + + floppy_image_write(floppy, buffer, offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t exi_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_exi_read_sector(floppy, head, track, sector, FALSE, buffer, buflen); +} + +static floperr_t exi_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_exi_write_sector(floppy, head, track, sector, FALSE, buffer, buflen, ddam); +} + +static floperr_t exi_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_exi_read_sector(floppy, head, track, sector, TRUE, buffer, buflen); +} + +static floperr_t exi_write_indexed_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_exi_write_sector(floppy, head, track, sector, TRUE, buffer, buflen, ddam); +} + +static floperr_t exi_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) +{ + floperr_t err; + err = get_offset(floppy, head, track, sector, FALSE, NULL); + if (err) + return err; + + if (sector_length) { + *sector_length = 256; + } + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t exi_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) +{ + sector_index += 1; + if (cylinder) + *cylinder = track; + if (side) + *side = head; + if (sector) + *sector = sector_index; + if (flags) + /* TODO: read DAM or DDAM and determine flags */ + *flags = 0; + return exi_get_sector_length(floppy, head, track, sector_index, sector_length); +} + + +static FLOPPY_CONSTRUCT(exi_dsk_construct) +{ + struct FloppyCallbacks *callbacks; + callbacks = floppy_callbacks(floppy); + callbacks->read_sector = exi_read_sector; + callbacks->write_sector = exi_write_sector; + callbacks->read_indexed_sector = exi_read_indexed_sector; + callbacks->write_indexed_sector = exi_write_indexed_sector; + callbacks->get_sector_length = exi_get_sector_length; + callbacks->get_heads_per_disk = exi_get_heads_per_disk; + callbacks->get_tracks_per_disk = exi_get_tracks_per_disk; + callbacks->get_indexed_sector_info = exi_get_indexed_sector_info; + + return FLOPPY_ERROR_SUCCESS; +} + + + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( exidy ) + FLOPPY_OPTION( exi_dsk, "dsk", "Exidy Sorcerer floppy disk image", exi_dsk_identify, exi_dsk_construct, NULL, NULL) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/sorc_dsk.h b/src/lib/formats/sorc_dsk.h new file mode 100644 index 00000000000..9e7c17c8ace --- /dev/null +++ b/src/lib/formats/sorc_dsk.h @@ -0,0 +1,18 @@ +/********************************************************************* + + formats/sorc_dsk.h + + Exidy Sorcerer disk images + +*********************************************************************/ + +#ifndef EXI_DSK_H +#define EXI_DSK_H + +#include "flopimg.h" + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(exidy); + +#endif /* EXI_DSK_H */ diff --git a/src/lib/formats/sord_cas.c b/src/lib/formats/sord_cas.c new file mode 100644 index 00000000000..cab593d66e8 --- /dev/null +++ b/src/lib/formats/sord_cas.c @@ -0,0 +1,138 @@ +/************************************************************************** + + sord_cas.c + + Format code for Sord M5 cassette files + +**************************************************************************/ + +#include <string.h> +#include "sord_cas.h" + +#define SORDM5_WAVESAMPLES_HEADER 1 +#define SORDM5_WAVESAMPLES_TRAILER 1 +#define SORDM5_WAVESAMPLES_BLOCK 1 + +static const UINT8 SORDM5_CAS_HEADER[6] = { 'S', 'O', 'R', 'D', 'M', '5'}; + +static const struct CassetteModulation sordm5_cas_modulation = +{ + CASSETTE_MODULATION_SINEWAVE, + 1575.0 - 300, 1575.0, 1575.0 + 300, + 3150.0 - 600, 3150.0, 3150.0 + 600 +}; + +static UINT8 cassette_image_read_uint8( cassette_image *cassette, UINT64 offset) +{ + UINT8 data; + cassette_image_read( cassette, &data, offset, 1); + return data; +} + +static casserr_t sordm5_tap_identify( cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_modulation_identify( cassette, &sordm5_cas_modulation, opts); +} + +static casserr_t sordm5_tap_load( cassette_image *cassette) +{ + casserr_t err; + UINT64 image_size; + double time_index = 0.0; + double time_displacement; + UINT8 header[16]; + UINT64 image_pos; + UINT8 block_type, byte, bit; + UINT32 block_size, i, j; + size_t filler_length; + // init + image_size = cassette_image_size(cassette); + image_pos = 0; + // read/check header + if (image_size < 16) return CASSETTE_ERROR_INVALIDIMAGE; + cassette_image_read( cassette, &header, image_pos, 16); + image_pos += 16; + if (memcmp( header, SORDM5_CAS_HEADER, sizeof(SORDM5_CAS_HEADER)) != 0) return CASSETTE_ERROR_INVALIDIMAGE; + // add silence (header) + err = cassette_put_sample( cassette, 0, time_index, SORDM5_WAVESAMPLES_HEADER, 0); + if (err) return err; + time_index += SORDM5_WAVESAMPLES_HEADER; + // process blocks + while (image_pos < image_size) + { + // read block type + block_type = cassette_image_read_uint8( cassette, image_pos + 0); + if ((block_type != 'H') && (block_type != 'D')) return CASSETTE_ERROR_INVALIDIMAGE; + // read block size + block_size = cassette_image_read_uint8( cassette, image_pos + 1); + if (block_size == 0) block_size = 0x100; + block_size += 3; + // add silence (header block) + if (block_type == 'H') + { + err = cassette_put_sample( cassette, 0, time_index, SORDM5_WAVESAMPLES_BLOCK, 0); + if (err) return err; + time_index += SORDM5_WAVESAMPLES_BLOCK; + } + // add sync + if (block_type == 'H') filler_length = 2.4 * (3150 / 8); else filler_length = 0.15 * (3150 / 8); + err = cassette_put_modulated_filler(cassette, 0, time_index, 0xFF, filler_length, &sordm5_cas_modulation, &time_displacement); + if (err) return err; + time_index += time_displacement; + // process block + for (i=0;i<block_size;i++) + { + // read byte + byte = cassette_image_read_uint8( cassette, image_pos + i); + // calc/check checksum + #if 0 + if (i == block_size) + { + if (byte != (crc & 0xFF)) return CASSETTE_ERROR_INVALIDIMAGE; + } + if (i > 2) crc += byte; + #endif + // process byte + for (j=0;j<10;j++) + { + // get bit + if (j < 2) + { + bit = ((j==0)?1:0); + } + else + { + bit = (byte >> (j-2)) & 1; + } + // add bit + err = cassette_put_modulated_data_bit( cassette, 0, time_index, bit, &sordm5_cas_modulation, &time_displacement); + if (err) return err; + time_index += time_displacement; + } + } + // mark end of block + err = cassette_put_modulated_data_bit( cassette, 0, time_index, 1, &sordm5_cas_modulation, &time_displacement); + if (err) return err; + time_index += time_displacement; + // next block + image_pos += block_size; + } + // add silence (trailer) + err = cassette_put_sample( cassette, 0, time_index, SORDM5_WAVESAMPLES_TRAILER, 0); + if (err) return err; + time_index += SORDM5_WAVESAMPLES_TRAILER; + // + return CASSETTE_ERROR_SUCCESS; +} + +static const struct CassetteFormat sordm5_cas_format = +{ + "cas", + sordm5_tap_identify, + sordm5_tap_load, + NULL +}; + +CASSETTE_FORMATLIST_START(sordm5_cassette_formats) + CASSETTE_FORMAT(sordm5_cas_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/sord_cas.h b/src/lib/formats/sord_cas.h new file mode 100644 index 00000000000..92c079f6cdd --- /dev/null +++ b/src/lib/formats/sord_cas.h @@ -0,0 +1,16 @@ +/********************************************************************* + + sord_cas.h + + Format code for Sord M5 cassette files + +*********************************************************************/ + +#ifndef SORD_CAS_H +#define SORD_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(sordm5_cassette_formats); + +#endif /* SORD_CAS_H */ diff --git a/src/lib/formats/svi_cas.c b/src/lib/formats/svi_cas.c new file mode 100644 index 00000000000..08e3915ac1c --- /dev/null +++ b/src/lib/formats/svi_cas.c @@ -0,0 +1,209 @@ +#include "svi_cas.h" + +#define CAS_PERIOD_0 (37) +#define CAS_PERIOD_1 (18) +#define CAS_HEADER_PERIODS (1600) +#define CAS_EMPTY_SAMPLES (24220) +#define CAS_INIT_SAMPLES (200) + +static const UINT8 CasHeader[17] = +{ + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x7f +}; + +#define SMPLO -32768 +#define SMPHI 32767 + +static int cas_size; + +/******************************************************************* + Generate samples for the tape image +********************************************************************/ +static int svi_cas_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + int cas_pos, samples_pos, n, i; + + cas_pos = 17; + samples_pos = 0; + + /* write CAS_INIT_SAMPLES of silence */ + n = CAS_INIT_SAMPLES; while (n--) buffer[samples_pos++] = 0; + + while (samples_pos < sample_count && cas_pos < cas_size) + { + /* write CAS_HEADER_PERIODS of header */ + for (i=0;i<CAS_HEADER_PERIODS;i++) + { + /* write a "0" */ + n = !(i % 4) ? 21 : 18; + while (n--) buffer[samples_pos++] = SMPHI; + n = 19; while (n--) buffer[samples_pos++] = SMPLO; + /* write a "1" */ + n = 9; while (n--) buffer[samples_pos++] = SMPHI; + n = 9; while (n--) buffer[samples_pos++] = SMPLO; + } + + /* write "0x7f" */ + /* write a "0" */ + n = 21; while (n--) buffer[samples_pos++] = SMPHI; + n = 19; while (n--) buffer[samples_pos++] = SMPLO; + + for (i=0;i<7;i++) + { + /* write a "1" */ + n = 9; while (n--) buffer[samples_pos++] = SMPHI; + n = 9; while (n--) buffer[samples_pos++] = SMPLO; + } + + while (samples_pos < sample_count && cas_pos < cas_size) + { + n = 21; while (n--) buffer[samples_pos++] = SMPHI; + n = 19; while (n--) buffer[samples_pos++] = SMPLO; + + for (i=0;i<8;i++) + { + int bit = (bytes[cas_pos] & (0x80 >> i) ); + + /* write this one bit */ + if (bit) + { + /* write a "1" */ + n = 9; while (n--) buffer[samples_pos++] = SMPHI; + n = 9; while (n--) buffer[samples_pos++] = SMPLO; + } + else + { + /* write a "0" */ + n = 18; while (n--) buffer[samples_pos++] = SMPHI; + n = 19; while (n--) buffer[samples_pos++] = SMPLO; + } + } + + cas_pos++; + + /* check if we've hit a new header (or end of block) */ + if ( (cas_pos + 17) < cas_size) + { + if (!memcmp (bytes + cas_pos, CasHeader, 17) ) + { + cas_pos += 17; + + /* end of block marker */ + n = CAS_EMPTY_SAMPLES; while (n--) buffer[samples_pos++] = SMPHI; + + break; /* falls back to loop above; plays header again */ + } + } + } + } + + /* end of block marker */ + n = CAS_EMPTY_SAMPLES; while (n--) buffer[samples_pos++] = SMPHI; + + return samples_pos; +} + + +/******************************************************************* + Calculate the number of samples needed for this tape image +********************************************************************/ +static int svi_cas_to_wav_size(const UINT8 *casdata, int caslen) +{ + int cas_pos, samples_pos, size, i; + + if (caslen < 17) return -1; + if (memcmp (casdata, CasHeader, sizeof (CasHeader) ) ) return -1; + + cas_size = caslen; + + cas_pos = 17; + + samples_pos = CAS_INIT_SAMPLES; + + while (cas_pos < caslen) + { + size = CAS_HEADER_PERIODS * ( CAS_PERIOD_0 + CAS_PERIOD_1 ) + + ( CAS_HEADER_PERIODS / 4 ) * 3; + + samples_pos += size; + + samples_pos += 21 + 19 + 7 * CAS_PERIOD_1; + + while (cas_pos < caslen) + { + samples_pos += 21; + samples_pos += 19; + + for (i=0;i<8;i++) + { + int bit = (casdata[cas_pos] & (0x80 >> i) ); + + samples_pos += bit ? CAS_PERIOD_1 : CAS_PERIOD_0; + } + + cas_pos++; + + /* check if we've hit a new header (or end of block) */ + if ( (cas_pos + 17) < caslen) + { + if (!memcmp (casdata + cas_pos, CasHeader, 17) ) + { + cas_pos += 17; + + /* end of block marker */ + samples_pos += CAS_EMPTY_SAMPLES; + + break; /* falls back to loop above; plays header again */ + } + } + } + } + + /* end of block marker */ + samples_pos += CAS_EMPTY_SAMPLES; + + return samples_pos; +} + + +static const struct CassetteLegacyWaveFiller svi_legacy_fill_wave = +{ + svi_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + svi_cas_to_wav_size, /* chunk_sample_calc */ + 44100, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + + +static casserr_t svi_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &svi_legacy_fill_wave); +} + + + +static casserr_t svi_cas_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &svi_legacy_fill_wave); +} + + + +static const struct CassetteFormat svi_cas_format = +{ + "cas", + svi_cas_identify, + svi_cas_load, + NULL +}; + + + +CASSETTE_FORMATLIST_START(svi_cassette_formats) + CASSETTE_FORMAT(svi_cas_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/svi_cas.h b/src/lib/formats/svi_cas.h new file mode 100644 index 00000000000..bb2590b45c5 --- /dev/null +++ b/src/lib/formats/svi_cas.h @@ -0,0 +1,17 @@ +/********************************************************************* + + svi_cas.h + + Format code for SVI casette files + +*********************************************************************/ + +#ifndef SVI_CAS_H +#define SVI_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(svi_cassette_formats); + +#endif /* SVI_CAS_H */ + diff --git a/src/lib/formats/svi_dsk.c b/src/lib/formats/svi_dsk.c new file mode 100644 index 00000000000..747e5ea76d0 --- /dev/null +++ b/src/lib/formats/svi_dsk.c @@ -0,0 +1,171 @@ +/********************************************************************* + + formats/svi_dsk.c + + SVI318 disk images + +*********************************************************************/ + +#include <string.h> + +#include "svi_dsk.h" +#include "basicdsk.h" + +static FLOPPY_IDENTIFY(svi_dsk_identify) +{ + *vote = ((floppy_image_size(floppy) == (172032)) || (floppy_image_size(floppy) == (346112))) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + +static int svi_get_heads_per_disk(floppy_image *floppy) +{ + return (floppy_image_size(floppy) == (172032)) ? 1 : 2; +} + +static int svi_get_tracks_per_disk(floppy_image *floppy) +{ + return 40; +} + +static UINT64 svi_translate_offset(floppy_image *floppy, + int track, int head, int sector) +{ + UINT64 o; + if ((track==0) && (head==0)) + o = sector*128; + else + o = ((track*((floppy_image_size(floppy) == (172032)) ? 1 : 2)+head)*17+sector)*256-2048; /* (17*256)-(18*128)=2048 */ + + return o; +} + +static floperr_t get_offset(floppy_image *floppy, int head, int track, int sector, int sector_is_index, UINT64 *offset) +{ + UINT64 offs; + /* translate the sector to a raw sector */ + if (!sector_is_index) + { + sector -= 1; + } + /* check to see if we are out of range */ + if ((head < 0) || (head >= ((floppy_image_size(floppy) == (172032)) ? 1 : 2)) || (track < 0) || (track >= 40) + || (sector < 0) || (sector >= ((head==0 && track==0) ? 18 : 17))) + return FLOPPY_ERROR_SEEKERROR; + + offs = svi_translate_offset(floppy, track, head, sector); + if (offset) + *offset = offs; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_svi_read_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen) +{ + UINT64 offset; + floperr_t err; + err = get_offset(floppy, head, track, sector, sector_is_index, &offset); + if (err) + return err; + + floppy_image_read(floppy, buffer, offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_svi_write_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam) +{ + UINT64 offset; + floperr_t err; + + err = get_offset(floppy, head, track, sector, sector_is_index, &offset); + if (err) + return err; + + floppy_image_write(floppy, buffer, offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t svi_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_svi_read_sector(floppy, head, track, sector, FALSE, buffer, buflen); +} + +static floperr_t svi_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_svi_write_sector(floppy, head, track, sector, FALSE, buffer, buflen, ddam); +} + +static floperr_t svi_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_svi_read_sector(floppy, head, track, sector, TRUE, buffer, buflen); +} + +static floperr_t svi_write_indexed_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_svi_write_sector(floppy, head, track, sector, TRUE, buffer, buflen, ddam); +} + +static floperr_t svi_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) +{ + floperr_t err; + err = get_offset(floppy, head, track, sector, FALSE, NULL); + if (err) + return err; + + if (sector_length) { + *sector_length = (head==0 && track==0) ? 128 : 256; + } + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t svi_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) +{ + sector_index += 1; + if (cylinder) + *cylinder = track; + if (side) + *side = head; + if (sector) + *sector = sector_index; + if (flags) + /* TODO: read DAM or DDAM and determine flags */ + *flags = 0; + return svi_get_sector_length(floppy, head, track, sector_index, sector_length); +} + + +static FLOPPY_CONSTRUCT(svi_dsk_construct) +{ + struct FloppyCallbacks *callbacks; + callbacks = floppy_callbacks(floppy); + callbacks->read_sector = svi_read_sector; + callbacks->write_sector = svi_write_sector; + callbacks->read_indexed_sector = svi_read_indexed_sector; + callbacks->write_indexed_sector = svi_write_indexed_sector; + callbacks->get_sector_length = svi_get_sector_length; + callbacks->get_heads_per_disk = svi_get_heads_per_disk; + callbacks->get_tracks_per_disk = svi_get_tracks_per_disk; + callbacks->get_indexed_sector_info = svi_get_indexed_sector_info; + + return FLOPPY_ERROR_SUCCESS; +} + + + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( svi318 ) + FLOPPY_OPTION( svi_dsk, "dsk", "SVI-318 floppy disk image", svi_dsk_identify, svi_dsk_construct, NULL, NULL) + FLOPPY_OPTION( svi_cpm, "dsk", "SVI-728 DSDD CP/M disk image", basicdsk_identify_default, basicdsk_construct_default, NULL, + HEADS([2]) + TRACKS([40]) + SECTORS([17]) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID([1])) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/svi_dsk.h b/src/lib/formats/svi_dsk.h new file mode 100644 index 00000000000..9a1c55460b1 --- /dev/null +++ b/src/lib/formats/svi_dsk.h @@ -0,0 +1,18 @@ +/********************************************************************* + + formats/svi_dsk.h + + SVI318 disk images + +*********************************************************************/ + +#ifndef SVI_DSK_H +#define SVI_DSK_H + +#include "flopimg.h" + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(svi318); + +#endif /* SVI_DSK_H */ diff --git a/src/mame/formats/td0_dsk.c b/src/lib/formats/td0_dsk.c index 6c18ad02581..c20e20c46f8 100644 --- a/src/mame/formats/td0_dsk.c +++ b/src/lib/formats/td0_dsk.c @@ -13,9 +13,7 @@ */ #include <string.h> -#include "emu.h" -#include "imagedev/flopimg.h" -#include "imagedev/flopdrv.h" +#include "flopimg.h" #define BUFSZ 512 // new input buffer @@ -675,7 +673,7 @@ FLOPPY_CONSTRUCT( td0_dsk_construct ) if (!tag) return FLOPPY_ERROR_OUTOFMEMORY; - tag->data = global_alloc_array_clear(UINT8,floppy_image_size(floppy)); + tag->data = (UINT8*)malloc(floppy_image_size(floppy)); if (tag->data==NULL) { return FLOPPY_ERROR_OUTOFMEMORY; } @@ -762,7 +760,7 @@ FLOPPY_CONSTRUCT( td0_dsk_construct ) FLOPPY_DESTRUCT( td0_dsk_destruct ) { struct td0dsk_tag *tag = get_tag(floppy); - global_free(tag->data); + free(tag->data); tag->data = NULL; return FLOPPY_ERROR_SUCCESS; -}
\ No newline at end of file +} diff --git a/src/lib/formats/thom_cas.c b/src/lib/formats/thom_cas.c new file mode 100644 index 00000000000..e4e404c229c --- /dev/null +++ b/src/lib/formats/thom_cas.c @@ -0,0 +1,787 @@ +/********************************************************************** + + Copyright (C) Antoine Mine' 2006 + + Thomson 8-bit computers + +**********************************************************************/ + +#include <math.h> + +#include "pool.h" +#include "cassimg.h" +#include "thom_cas.h" + + +/***************************** configuration **************************/ + +#define K7_SPEED_HACK 0 +/* When set to 1, bits extracted from k7 files are not transformed into a + raw wave. It saves some time & memory but disables the ability to "hear" the + cassette. + This does not affect raw wave files. + Also, we do not do this for k5 files because the the corresponding wave + files are more compact anyway. */ +/* It must be set accordingly in machine/thomson.c */ + +#define VERBOSE 0 /* 0, 1 or 2 */ + + +/***************************** utilities **************************/ + +#define PRINT(x) printf x + +#define LOG(x) do { if (VERBOSE > 0) printf x; } while (0) +#define VLOG(x) do { if (VERBOSE > 1) printf x; } while (0) + + +#define TO7_BIT_LENGTH 0.001114 + +#define TO7_FREQ_CASS_0 4500. +#define TO7_FREQ_CASS_1 6300. + +#define TO7_PERIOD_CASS_0 ( 1. / TO7_FREQ_CASS_0 ) +#define TO7_PERIOD_CASS_1 ( 1. / TO7_FREQ_CASS_1 ) + +static UINT32 to7_k7_bitsize; +static UINT8* to7_k7_bits; + +#define MO5_BIT_LENGTH 0.000833 +#define MO5_HBIT_LENGTH (MO5_BIT_LENGTH / 2.) + +/************************* k7 format *************************/ + +/* The k7 format is widespread among existing Thomson emulators. + It represents the stream of bytes returned by the BIOS cassette routine, + and so, is used by emulators that bypass the BIOS routine. + It is generally hacked from the raw image more or less by hand + (in particular, to by-pass special loaders and copy-protection schemes). + + As we run the original BIOS routine, we need to hack back the k7 file + into the original stream. + + In theory, the byte stream is a sequence of blocks of the form: + - 2-byte header: 01 3C + - 1-byte type + 00 = start of file, the block contains the filename + 01 = chunk of file data + FF = end of file + - 1-byte size + - size bytes of data + - one byte of CRC + + These are separated by 0xff fillers. + + There are also a variety of non-standard blocks, and we try to handle them. + + Our main problem is to guess where the places where the motor will be + cut-off and introduce there sequences of 1 bit for synchronization. + + Note: as a nice effect, if our routine succeeds, it constructs a wave + file that should be usable on a real computer. +*/ + + + +static const struct CassetteModulation to7_k7_modulation = +{ + CASSETTE_MODULATION_SQUAREWAVE, + 4000.0, 4500.0, 5000.0, + 5500.0, 6300.0, 7500.0 +}; + + + +static casserr_t to7_k7_identify ( cassette_image *cass, struct CassetteOptions *opts ) +{ + casserr_t e = cassette_modulation_identify( cass, &to7_k7_modulation, opts ); + return e; +} + + + +static casserr_t to7_k7_load( cassette_image *cass ) +{ +#if ! K7_SPEED_HACK + static const INT8 square_wave[] = { -128, 127 }; + double time = 0.; +#endif + size_t size = cassette_image_size( cass ), pos = 0; + int i, sz, sz2, bitmax = 1024, invalid = 0; + UINT8 in, typ, block[264]; + + LOG (( "to7_k7_load: start conversion, size=%li\n", (long)size )); + PRINT (( "to7_k7_load: open cassette, length: %li bytes\n", (long) size )); + + if ( to7_k7_bits ) + { + free( to7_k7_bits ); + to7_k7_bits = NULL; + } + + to7_k7_bitsize = 0; + to7_k7_bits = (UINT8*)malloc(bitmax ); + +/* store one period */ +#if K7_SPEED_HACK +#define K7_PUT( PERIOD ) +#else +#define K7_PUT( PERIOD ) \ + do \ + { \ + casserr_t err; \ + err = cassette_put_samples( cass, 0, time, (PERIOD), 2, 1, \ + square_wave, CASSETTE_WAVEFORM_8BIT ); \ + if ( err ) \ + return err; \ + time += (PERIOD); \ + } while (0) +#endif + +/* store one bit */ +#define K7_PUT_BIT( BIT ) \ + do \ + { \ + int b; \ + if ( BIT ) \ + { \ + for ( b = 0; b < 7; b++ ) \ + K7_PUT( TO7_PERIOD_CASS_1 ); \ + } \ + else \ + { \ + for ( b = 0; b < 5; b++ ) \ + K7_PUT( TO7_PERIOD_CASS_0 ); \ + } \ + if ( to7_k7_bitsize + 1 >= bitmax ) \ + { \ + UINT8* a = (UINT8*)malloc(bitmax * 2); \ + memcpy ( a, to7_k7_bits, bitmax ); \ + bitmax *= 2; \ + to7_k7_bits = a; \ + } \ + to7_k7_bits[ to7_k7_bitsize++ ] = (BIT); \ + } while (0) + +/* store one byte, with start / stop bits */ +#define K7_PUT_BYTE( BYTE ) \ + do \ + { \ + UINT8 x; \ + K7_PUT_BIT( 0 ); \ + for ( x = 0; x < 8; x++ ) \ + K7_PUT_BIT( ( (BYTE) >> x ) & 1 ); \ + K7_PUT_BIT( 1 ); \ + K7_PUT_BIT( 1 ); \ + } while (0) + +#define K7_FILL_1( SIZE ) \ + do \ + { \ + if ( (SIZE) > 0 ) { \ + int ii; \ + LOG (( "to7_k7_load: 1-filler size=%i bitstart=%i\n", \ + (SIZE), to7_k7_bitsize )); \ + for ( ii = 0; ii < (SIZE); ii++ ) K7_PUT_BIT( 1 ); \ + } \ + } while (0) + +#define K7_FILL_ff( SIZE ) \ + do \ + { \ + if ( (SIZE) > 0 ) \ + { \ + int ii; \ + LOG (( "to7_k7_load: 0xff filler size=%i bitstart=%i\n", (SIZE), to7_k7_bitsize )); \ + for ( ii = 0; ii < (SIZE); ii++ ) \ + K7_PUT_BYTE( 0xff ); \ + } \ + } while (0) + + /* check format */ + cassette_image_read( cass, block, 0, 64 ); + for ( i = 3; ; i++ ) + { + if ( ( i >= size ) || ( i >= 64 ) ) + { + /* ? */ + PRINT (( "to7_k7_load: WARNING: this does not look like a MO or TO cassette.\n" )); + break; + } + else if ( ( block[i-3] == 0x01 ) && ( block[i-2] == 0x3c ) && ( block[i-1] == 0x5a ) && ! block[i] ) + { + /* MO */ + PRINT (( "to7_k7_load: WARNING: this looks like a MO cassette, not a TO one.\n" )); + break; + } + else if ( ( block[i-3] == 0xff ) && ( block[i-2] == 0x01 ) && ( block[i-1] == 0x3c ) && ! block[i] ) + { + /* TO */ + break; + } + } + + /* skip to first 0xff filler */ + for ( sz = 0; pos < size; pos++, sz++ ) + { + cassette_image_read( cass, &in, pos, 1 ); + if ( in == 0xff ) + break; + } + if ( sz > 0 ) + LOG (( "to7_k7_load: skip %i trash bytes\n", sz )); + + /* loop over regular blocks */ + while ( pos < size ) + { + + rebounce: + /* skip 0xff filler */ + for ( sz = 0; pos < size; pos++, sz++ ) + { + cassette_image_read( cass, &in, pos, 1 ); + /* actually, we are bit laxist and treat as 0xff bytes with at least + 5 bits out of 8 set to 1 + */ + for ( i = 0; in; in >>= 1 ) + i += (in & 1); + if ( i < 5 ) + break; + } + + /* get block header */ + if ( pos + 4 > size ) + { + pos -= sz; + break; + } + cassette_image_read( cass, block, pos, 4 ); + typ = block[2]; + sz2 = block[3]+1; + if ( block[0] != 0x01 || block[1] != 0x3c || ( typ != 0x00 && typ != 0x01 && typ != 0xff ) ) + { + pos -= sz; + break; + } + pos += 4; + + /* get block */ + cassette_image_read( cass, block+4, pos, sz2 ); + pos += sz2; + + /* 1-filler and 0xff-filler */ + if ( typ == 0 || typ == 0xff ) + K7_FILL_1( 1000 ); + K7_FILL_ff( sz ); + + /* put block */ + LOG (( "to7_k7_load: block off=$%x type=$%02X size=%i bitstart=%i\n", (int) pos-sz2-4, typ, sz2, to7_k7_bitsize )); + VLOG (( "to7_k7_load: data:" )); + for ( i = 0; i < sz2 + 4; i ++) + { + VLOG (( " $%02X", block[i] )); + K7_PUT_BYTE( block[i] ); + } + VLOG (( "\n" )); + + /* if it is a directory enty, says so to the user */ + if ( typ == 0 ) + { + char name[] = "01234567.ABC"; + UINT8 t = block[15]; + UINT8 u = block[16]; + int p = (to7_k7_bitsize - sz2 - 4 - sz) * TO7_BIT_LENGTH; + memcpy( name, block+4, 8 ); + memcpy( name+9, block+12, 3 ); + for ( i = 0; name[i]; i++) + { + if ( name[i] < ' ' || name[i] >= 127 ) + name[i] = '?'; + } + PRINT (( "to7_k7_load: file \"%s\" type=%s,%s at %imn %is\n", + name, + (t==0) ? "bas" : (t==1) ? "dat" : (t==2) ? "bin" : "???", + (u == 0) ? "a" : (u == 0xff) ? "b" : "?", + p / 60, p % 60 )); + } + + /* extra 1-fillers */ + if ( typ == 0 || typ == 0xff ) + K7_FILL_1( 1000 ); + } + + /* trailing data with invalid block structure + => dump it in a raw form, but stay alert for hidden block starts + */ + if ( pos < size ) + { + invalid++; + LOG (( "to7_k7_load: trailing raw bytes off=$%x bitstart=%i\n", (int) pos, to7_k7_bitsize )); + + /* put block */ + for (; pos < size; pos++ ) + { + cassette_image_read( cass, &in, pos, 1 ); + for ( sz = 0; pos < size && in == 0xff; sz++ ) + { + pos++; + cassette_image_read( cass, &in, pos, 1 ); + } + if ( invalid < 10 && sz > 4 && in == 0x01 && pos + 4 <= size ) + { + UINT8 in1,in2; + cassette_image_read( cass, &in1, pos+1, 1 ); + cassette_image_read( cass, &in2, pos+2, 1 ); + if ( (in1 == 0x3c) && ((in2 == 0x00) || (in2 == 0x01) ) ) + { + /* seems we have a regular block hidden in raw data => rebounce */ + LOG (( "to7_k7_load: hidden regular block found\n" )); + pos -= sz; + goto rebounce; + } + if ( ( ( in1 == 0x3d ) && ( in2 == 0 ) ) || ( ( in1 == 0x57 ) && ( in2 == 0x49 ) ) ) + { + /* special block (Infogrames) => just prepend filler */ + K7_FILL_1 ( 500 ); + LOG (( "to7_k7_load: special $%02X $%02X $%02X block found off=$%x bitstart=%i\n", in, in1, in2, (int) pos, to7_k7_bitsize )); + } + } + for ( i = 0; i < sz; i++ ) + K7_PUT_BYTE( 0xff ); + K7_PUT_BYTE( in ); + } + } + + if ( invalid ) + PRINT (( "to7_k7_load: WARNING: the k7 has an unknown structure and may not work\n" )); + + sz = to7_k7_bitsize * TO7_BIT_LENGTH; + PRINT (( "to7_k7_load: cassette length: %imn %is (%i samples)\n", sz / 60, sz % 60, to7_k7_bitsize )); + + return CASSETTE_ERROR_SUCCESS; +} + + + +static const struct CassetteFormat to7_k7 = +{ "k7", to7_k7_identify, to7_k7_load, NULL /* no save */ }; + + + +/********************* TO7 WAV format ************************/ + + + +static casserr_t to7_wav_identify ( cassette_image *cass, + struct CassetteOptions *opts ) +{ + casserr_t e = wavfile_format.identify( cass, opts ); + return e; +} + + + +static casserr_t to7_wav_load ( cassette_image *cass ) +{ + casserr_t e = wavfile_format.load( cass ); + struct CassetteInfo info; + double len; + + if ( to7_k7_bits ) + { + free( to7_k7_bits ); + to7_k7_bits = NULL; + } + + if ( e != CASSETTE_ERROR_SUCCESS ) + return e; + + cassette_get_info( cass, &info ); + + len = (double) info.sample_count / info.sample_frequency; + + PRINT (( "to7_wav_load: loading cassette, length %imn %is, %i Hz, %i bps, %i bits\n", + (int) len / 60, (int) len % 60, + info.sample_frequency, info.bits_per_sample, + (int) (len / TO7_BIT_LENGTH) )); + + return CASSETTE_ERROR_SUCCESS; +} + + + +static casserr_t to7_wav_save ( cassette_image *cass, const struct CassetteInfo *info ) +{ + int len = info->sample_count / info->sample_frequency; + PRINT (( "to7_wav_save: saving cassette, length %imn %is, %i Hz, %i bps\n", len / 60, len % 60, info->sample_frequency, info->bits_per_sample )); + return wavfile_format.save( cass, info ); +} + + + +/* overloaded wav: dump info */ +static const struct CassetteFormat to7_wav = +{ "wav", to7_wav_identify, to7_wav_load, to7_wav_save }; + + + +/************************* k5 format *************************/ + + + +static casserr_t mo5_k5_identify ( cassette_image *cass, + struct CassetteOptions *opts ) +{ + opts -> bits_per_sample = 8; + opts -> channels = 1; + opts -> sample_frequency = 22100;//11050; + return CASSETTE_ERROR_SUCCESS; +} + + + +/* As the k7 format, the k5 format represents the stream of bytes returned by + the BIOS cassette routine and need to be hacked back into the original + cassette wave. + + The format of blocks is a bit different from that of TO7 cassettes: + - 2-byte header is: 3C 5A + - 1-byte type + 00 = start of file, the block contains the filename + 01 = chunk of file data + FF = end of file + - 1-byte size + - size - 1 bytes of data (255 bytes of data if size = 0) + - one byte of CRC: sum from (and including) header up to (and including) + CRC byte should be 0 modulo 256 + + They are separated by 0x01 filler bytes (generally 10 of them), + for synchronisation. There are also extra 0 filler bits between certain + kinds of blocks, when the motor is supposed to go off and back on. +*/ + +static casserr_t mo5_k5_load( cassette_image *cass ) +{ + size_t size = cassette_image_size( cass ), pos = 0; + int i, sz, sz2, hbit = 0; + UINT8 in, in2, in3, typ, block[264], sum; + int invalid = 0, hbitsize = 0, dcmoto = 0; + + LOG (( "mo5_k5_load: start conversion, size=%li\n", (long)size )); + PRINT (( "mo5_k5_load: open cassette, length: %li bytes\n", (long) size )); + + /* store a half-bit */ +#define K5_PUT_HBIT \ + do \ + { \ + cassette_put_sample ( cass, 0, hbitsize * MO5_HBIT_LENGTH, MO5_HBIT_LENGTH, (hbit ? 1 : -1) << 30 ); \ + hbitsize++; \ + } while ( 0 ) + + /* store one bit */ +#define K5_PUT_BIT( BIT ) \ + do \ + { \ + if ( BIT ) \ + { \ + K5_PUT_HBIT; \ + hbit = !hbit; \ + K5_PUT_HBIT; \ + } \ + else \ + { \ + K5_PUT_HBIT; \ + K5_PUT_HBIT; \ + } \ + hbit = !hbit; \ + } while (0) + + /* store one byte, no start / stop bit, converse order from TO7 */ +#define K5_PUT_BYTE( BYTE ) \ + do \ + { \ + UINT8 b = BYTE; \ + int x; \ + for ( x = 0; x < 8; x++ ) \ + K5_PUT_BIT( (b >> (7 - x)) & 1 ); \ + } while (0) + + /* store filler */ +#define K5_FILL_0( SIZE ) \ + do \ + { \ + if ( (SIZE) > 0 ) \ + { \ + int j; \ + LOG (( "mo5_k5_load: 0-filler size=%i hbitstart=%i\n", (SIZE), hbitsize )); \ + for ( j = 0; j < (SIZE); j++ ) \ + K5_PUT_BIT( 0 ); \ + } \ + } while (0) + +#define K5_FILL_01( SIZE ) \ + do \ + { \ + if ( (SIZE) > 0 ) \ + { \ + int j; \ + LOG (( "mo5_k5_load: 0x01 filler size=%i bitstart=%i\n", (SIZE), hbitsize )); \ + for ( j = 0; j < (SIZE); j++ ) \ + K5_PUT_BYTE( 0x01 ); \ + } \ + } while (0) + + /* check format */ + cassette_image_read( cass, block, 0, 64 ); + for ( i = 3; ; i++ ) + { + if ( ( i >= size ) || ( i >= 64 ) ) + { + /* ? */ + PRINT (( "to5_k5_load: WARNING: this does not look like a MO or TO cassette.\n" )); + break; + } + else if ( ( block[i-3] == 0x01 ) && ( block[i-2] == 0x3c ) && ( block[i-1] == 0x5a ) && ! block[i] ) + { + /* MO */ + break; + } + else if ( ( block[i-3] == 0xff ) && ( block[i-2] == 0x01 ) && ( block[i-1] == 0x3c ) && ! block[i] ) + { + /* TO */ + PRINT (( "to5_k5_load: WARNING: this looks like a TO cassette, not a MO one.\n" )); + break; + } + } + + cassette_image_read( cass, block, pos, 6 ); + if ( ! memcmp( block, "DCMOTO", 6 ) || ! memcmp( block, "DCMO5", 5 ) || ! memcmp( block, "DCMO6", 5 ) ) + dcmoto = 1; + + /* loop over regular blocks */ + while ( pos < size ) + { + + rebounce: + /* skip DCMOTO header*/ + if ( dcmoto ) + { + cassette_image_read( cass, block, pos, 6 ); + if ( ! memcmp( block, "DCMOTO", 6 ) ) + { + LOG (( "mo5_k5_load: DCMOTO signature found at off=$%x\n", (int)pos )); + pos += 6; + } + else if ( ! memcmp( block, "DCMO", 4 ) ) + { + LOG (( "mo5_k5_load: DCMO* signature found at off=$%x\n", (int)pos )); + pos += 5; + } + } + + /* skip 0x01 filler */ + for ( sz = 0; pos < size; pos++, sz++ ) + { + cassette_image_read( cass, &in, pos, 1 ); + if ( in != 0x01 ) + break; + } + + /* get block header */ + if ( pos + 4 > size ) + { + pos -= sz; + break; + } + cassette_image_read( cass, block, pos, 4 ); + typ = block[2]; + sz2 = (UINT8) (block[3]-1); + if ( block[0] != 0x3c || block[1] != 0x5a || ( typ != 0x00 && typ != 0x01 && typ != 0xff ) || pos+sz2 > size ) + { + pos -= sz; + break; + } + pos += 4; + + /* get block */ + cassette_image_read( cass, block+4, pos, sz2 ); + pos += sz2; + + /* 0-fillers and 0x01-fillers */ + if ( typ == 0 || typ == 0xff ) + K5_FILL_0( 1200 ); + else + K5_FILL_0( 300 ); /* for MO6 */ + K5_FILL_01( sz < 10 ? 10 : sz ); + + /* put block */ + LOG (( "mo5_k5_load: block off=$%x type=$%02X size=%i hbitstart=%i\n", (int) pos-sz2-4, typ, sz2, hbitsize )); + VLOG (( "mo5_k5_load: data:" )); + for ( i = 0; i < sz2 + 4; i ++) + { + VLOG (( " $%02X", block[i] )); + K5_PUT_BYTE( block[i] ); + } + VLOG (( "\n" )); + + /* checksum */ + for ( i = 0, sum = 0; i < sz2; i++ ) + sum += block[i+4]; + if ( sum ) + LOG(( "mo5_k5_load: invalid checksum $%02X (should be 0)\n", sum )); + + /* if it is a directory enty, says so to the user */ + if ( typ == 0 ) + { + char name[] = "01234567.ABC"; + UINT8 t = block[15]; + UINT8 u = block[16]; + int p = (hbitsize - sz2 - 4 - sz) * MO5_HBIT_LENGTH; + memcpy( name, block+4, 8 ); + memcpy( name+9, block+12, 3 ); + for ( i = 0; name[i]; i++) + { + if ( name[i] < ' ' || name[i] >= 127 ) + name[i] = '?'; + } + PRINT (( "mo5_k5_load: file \"%s\" type=%s,%s at %imn %is\n", + name, + (t==0) ? "bas" : (t==1) ? "dat" : (t==2) ? "bin" : "???", + (u == 0) ? "a" : (u == 0xff) ? "b" : "?", + p / 60, p % 60 )); + } + + /* 0-fillers */ + if ( typ == 0xff || typ == 0x00 ) + K5_FILL_0( 1800 ); + } + + /* dump trailing bytes, but also look for beginings of blocks */ + if ( pos < size ) + { + invalid++; + K5_FILL_0( 1200 ); + LOG (( "mo5_k5_load: trailing trash off=$%x size=%i hbitstart=%i\n", (int) pos, (int) (size-pos), hbitsize )); + for ( ; pos < size; pos++ ) + { + cassette_image_read( cass, &in, pos, 1 ); + if ( dcmoto && in=='D' ) + { + /* skip DCMOTO header*/ + cassette_image_read( cass, block, pos, 6 ); + if ( ! memcmp( block, "DCMOTO", 6 ) ) + { + LOG (( "mo5_k5_load: DCMOTO signature found at off=$%x\n", (int)pos )); + pos += 6; + cassette_image_read( cass, &in, pos, 1 ); + } + else if ( ! memcmp( block, "DCMO", 4 ) ) + { + LOG (( "mo5_k5_load: DCMO* signature found at off=$%x\n", (int)pos )); + pos += 5; + cassette_image_read( cass, &in, pos, 1 ); + } + } + for ( sz = 0; pos < size && in == 0x01; sz++ ) + { + pos++; + cassette_image_read( cass, &in, pos, 1 ); + } + if ( sz > 6 ) + { + cassette_image_read( cass, &in2, pos+1, 1 ); + cassette_image_read( cass, &in3, pos+2, 1 ); + if ( invalid < 10 && in == 0x3c && in2 == 0x5a && (in3 == 0x00 || in3 == 0x01 || in3 == 0xff ) ) + { + /* regular block found */ + LOG (( "mo5_k5_load: hidden regular block found\n" )); + pos -= sz; + goto rebounce; + } + if ( invalid < 10 && sz > 6 && ( (in == 0x3c && in2 == 0x5a) || (in == 0xc3 && in2 == 0x5a) || (in == 0xc3 && in2 == 0x3c) || (in == 0x87 && in2 == 0x4a) ) ) + { + /* special block found */ + K5_FILL_0( 1200 ); + LOG (( "mo5_k5_load: special block $%02X $%02X found off=$%x hbitstart=%i\n", in, in2, (int) pos-sz, hbitsize )); + } + } + + VLOG (( "mo5_k5_load: special data:" )); + for ( i = 0; i < sz; i++ ) + { + K5_PUT_BYTE( 0x01 ); + VLOG (( " $01" )); + } + K5_PUT_BYTE( in ); + VLOG (( " $%02X\n", in )); + } + } + + if ( invalid ) + PRINT (( "mo5_k5_load: WARNING: the k5 has an unknown structure and may not work\n" )); + + sz = hbitsize * MO5_HBIT_LENGTH; + PRINT (( "mo5_k5_load: cassette length: %imn %is (%i half-bits)\n", sz / 60, sz % 60, hbitsize )); + + return CASSETTE_ERROR_SUCCESS; +} + + + +static const struct CassetteFormat mo5_k5 = +{ "k5,k7", mo5_k5_identify, mo5_k5_load, NULL /* no save */ }; + + +/********************* MO5 WAV format ************************/ + + + +static casserr_t mo5_wav_identify ( cassette_image *cass, + struct CassetteOptions *opts ) +{ + casserr_t e = wavfile_format.identify( cass, opts ); + return e; +} + + + +static casserr_t mo5_wav_load ( cassette_image *cass ) +{ + casserr_t e = wavfile_format.load( cass ); + struct CassetteInfo info; + int len; + if ( e == CASSETTE_ERROR_SUCCESS ) + { + cassette_get_info( cass, &info ); + len = info.sample_count / info.sample_frequency; + PRINT (( "mo5_wav_load: loading cassette, length %imn %is, %i Hz, %i bps\n", len / 60, len % 60, info.sample_frequency, info.bits_per_sample )); + } + return e; +} + + + +static casserr_t mo5_wav_save ( cassette_image *cass, const struct CassetteInfo *info ) +{ + int len = info->sample_count / info->sample_frequency; + PRINT (( "mo5_wav_save: saving cassette, length %imn %is, %i Hz, %i bps\n", len / 60, len % 60, info->sample_frequency, info->bits_per_sample )); + return wavfile_format.save( cass, info ); +} + + + +/* overloaded wav: dump info */ +static const struct CassetteFormat mo5_wav = +{ "wav", mo5_wav_identify, mo5_wav_load, mo5_wav_save }; + + + +/********************* formats ************************/ + + +const struct CassetteFormat *const to7_cassette_formats[] = +{ &to7_wav, &to7_k7, NULL }; + + +const struct CassetteFormat *const mo5_cassette_formats[] = +{ &mo5_wav, &mo5_k5, NULL }; diff --git a/src/lib/formats/thom_cas.h b/src/lib/formats/thom_cas.h new file mode 100644 index 00000000000..892615aae52 --- /dev/null +++ b/src/lib/formats/thom_cas.h @@ -0,0 +1,22 @@ +/********************************************************************** + + Copyright (C) Antoine Mine' 2006 + + Thomson 8-bit computers + +**********************************************************************/ + +#ifndef THOM_CAS +#define THOM_CAS + +#include "cassimg.h" + +/******************** TO7 ************************/ + +CASSETTE_FORMATLIST_EXTERN(to7_cassette_formats); + +/******************** MO5 ************************/ + +CASSETTE_FORMATLIST_EXTERN(mo5_cassette_formats); + +#endif diff --git a/src/lib/formats/thom_dsk.c b/src/lib/formats/thom_dsk.c new file mode 100644 index 00000000000..6e301cb6ac3 --- /dev/null +++ b/src/lib/formats/thom_dsk.c @@ -0,0 +1,391 @@ +/********************************************************************* + + formats/thom_dsk.c + + Thomson disk images + + Based on work of Antoine Mine' + +*********************************************************************/ + +#include <string.h> +#include "thom_dsk.h" +#include "basicdsk.h" + +static const int sap_magic_num = 0xB3; /* simple XOR crypt */ + + +static const char sap_header[] = + "\001SYSTEME D'ARCHIVAGE PUKALL S.A.P. " + "(c) Alexandre PUKALL Avril 1998"; + + +static const UINT16 sap_crc[] = +{ + 0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387, + 0x8408, 0x9489, 0xa50a, 0xb58b, 0xc60c, 0xd68d, 0xe70e, 0xf78f, +}; + +struct sap_dsk_tag +{ + int tracks; + int sector_size; + int sector_pos[80][16]; /* remember sector position in file */ +}; + +static UINT16 thom_sap_crc( UINT8* data, int size ) +{ + int i; + UINT16 crc = 0xffff, crc2; + for ( i = 0; i < size; i++ ) + { + crc2 = ( crc >> 4 ) ^ sap_crc[ ( crc ^ data[i] ) & 15 ]; + crc = ( crc2 >> 4 ) ^ sap_crc[ ( crc2 ^ (data[i] >> 4) ) & 15 ]; + } + return crc; +} + +static struct sap_dsk_tag *get_tag(floppy_image *floppy) +{ + struct sap_dsk_tag *tag; + tag = (sap_dsk_tag *)floppy_tag(floppy); + return tag; +} + + +static FLOPPY_IDENTIFY(sap_dsk_identify) +{ + char header[0x100]; + floppy_image_read(floppy, header, 0, sizeof(sap_header)); + if (!memcmp( header, sap_header, sizeof(sap_header) ) ) + { + *vote= 100; + } else { + *vote = 0; + } + return FLOPPY_ERROR_SUCCESS; +} +static int sap_get_heads_per_disk(floppy_image *floppy) +{ + return 1; +} + +static int sap_get_tracks_per_disk(floppy_image *floppy) +{ + return get_tag(floppy)->tracks; +} + + +static floperr_t get_offset(floppy_image *floppy, int head, int track, int sector, int sector_is_index, UINT64 *offset) +{ + UINT64 offs; + struct sap_dsk_tag *tag = get_tag(floppy); + /* translate the sector to a raw sector */ + if (!sector_is_index) + { + sector -= 1; + } + /* check to see if we are out of range */ + if ((head < 0) || (head >= 1) || (track < 0) || (track >=tag->tracks) + || (sector < 0) || (sector >= 16)) + return FLOPPY_ERROR_SEEKERROR; + + offs = tag->sector_pos[track][sector]; + if (offs <= 0 ) + return FLOPPY_ERROR_SEEKERROR; + + if (offset) + *offset = offs; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_sap_read_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen) +{ + UINT64 offset; + floperr_t err; + int i; + UINT8 *buf; + err = get_offset(floppy, head, track, sector, sector_is_index, &offset); + if (err) + return err; + + floppy_image_read(floppy, buffer, offset+4, buflen); + buf = (UINT8*)buffer; + for (i=0;i<buflen;i++) { + buf[i] ^= sap_magic_num; + } + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_sap_write_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam) +{ + UINT64 offset; + floperr_t err; + UINT8 buf[256+6]; + UINT16 crc; + int i; + err = get_offset(floppy, head, track, sector, sector_is_index, &offset); + if (err) + return err; + + /* buf = 4-byte header + sector + 2-byte CRC */ + floppy_image_read(floppy, buf, offset, 4); + for (i=0;i<buflen;i++) { + buf[i+4] = ((UINT8*)buffer)[i]; + } + crc = thom_sap_crc( buf, buflen+4 ); + buf[buflen+4] = crc >> 8; + buf[buflen+5] = crc & 0xff; + for (i=0;i<buflen;i++) { + buf[i+4] ^= sap_magic_num; + } + floppy_image_write(floppy, buf, offset, buflen+6); + + return FLOPPY_ERROR_SUCCESS; +} + + +static floperr_t sap_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_sap_read_sector(floppy, head, track, sector, FALSE, buffer, buflen); +} + +static floperr_t sap_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_sap_write_sector(floppy, head, track, sector, FALSE, buffer, buflen, ddam); +} + +static floperr_t sap_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_sap_read_sector(floppy, head, track, sector, TRUE, buffer, buflen); +} + +static floperr_t sap_write_indexed_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_sap_write_sector(floppy, head, track, sector, TRUE, buffer, buflen, ddam); +} + +static floperr_t sap_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) +{ + floperr_t err; + err = get_offset(floppy, head, track, sector, FALSE, NULL); + if (err) + return err; + + if (sector_length) { + *sector_length = get_tag(floppy)->sector_size; + } + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t sap_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) +{ + floperr_t err; + UINT8 header[4]; + UINT64 offset = 0; + sector_index += 1; + err = get_offset(floppy, head, track, sector_index, FALSE, &offset); + + floppy_image_read(floppy, header, offset, 4); + if (cylinder) + *cylinder = header[2]; + if (side) + *side = head; + if (sector) + *sector = header[3]; + if (sector_length) + *sector_length = get_tag(floppy)->sector_size; + if (flags) + /* TODO: read DAM or DDAM and determine flags */ + *flags = 0; + return err; +} + +static floperr_t sap_post_format(floppy_image *floppy, option_resolution *params) +{ + int track,sector; + int pos; + UINT8 buf[256], header[4]; + struct sap_dsk_tag *tag; + tag = (struct sap_dsk_tag *) floppy_create_tag(floppy, sizeof(struct sap_dsk_tag)); + + /* default options */ + if ( !tag->tracks ) + { + tag->tracks = 80; + tag->sector_size = 256; + } + + /* create SAP file header */ + floppy_image_write( floppy, sap_header, 0, 66 ); + + for ( track = 0; track < 80; track++ ) + for ( sector = 0; sector < 16; sector++ ) + tag->sector_pos[track][sector] = 0; + + /* create all sectors with valid header and CRC */ + memset(buf, 0xe5, 256); + pos = 0x42; + header[0] = (tag->sector_size==128) ? 1 : 0; + header[1] = 0; + for ( track = 0, pos = 0x42; track < tag->tracks; track++ ) + for ( sector = 0; sector < 16; sector++, pos += tag->sector_size + 6 ) { + tag->sector_pos[track][sector] = pos; + header[2] = track; + header[3] = sector + 1; + floppy_image_write(floppy, header, pos, 4); + sap_write_indexed_sector( floppy, 0, track, sector, buf, tag->sector_size, 0 ); + } + + return FLOPPY_ERROR_SUCCESS; +} + + +static FLOPPY_CONSTRUCT(sap_dsk_construct) +{ + struct FloppyCallbacks *callbacks; + struct sap_dsk_tag *tag; + int i,j; + UINT8 fmt; + tag = (struct sap_dsk_tag *) floppy_create_tag(floppy, sizeof(struct sap_dsk_tag)); + if (!tag) + return FLOPPY_ERROR_OUTOFMEMORY; + + /* guess format */ + floppy_image_read(floppy, &fmt, 0x42, 1); + if ( fmt==1 ) tag->sector_size = 128; else tag->sector_size = 256; + + /* start with an empty offset table */ + tag->tracks = 0; + for ( i = 0; i < 80; i++ ) + for ( j = 0; j < 16; j++ ) + tag->sector_pos[i][j] = 0; + + /* count tracks & fill sector offset table */ + for ( i = 0x42; i+4 < floppy_image_size(floppy); i += tag->sector_size + 6 ) // CRC 2 bytes + 4 bytes sector header + { + UINT8 sector, track; + floppy_image_read(floppy, &track, i+2, 1); + floppy_image_read(floppy, §or, i+3, 1); + if ( track >= 80 || sector < 1 || sector > 16 ) continue; + if ( track > tag->tracks ) tag->tracks = track+1; + tag->sector_pos[track][sector-1] = i; + } + callbacks = floppy_callbacks(floppy); + callbacks->read_sector = sap_read_sector; + callbacks->write_sector = sap_write_sector; + callbacks->read_indexed_sector = sap_read_indexed_sector; + callbacks->write_indexed_sector = sap_write_indexed_sector; + callbacks->get_sector_length = sap_get_sector_length; + callbacks->get_heads_per_disk = sap_get_heads_per_disk; + callbacks->get_tracks_per_disk = sap_get_tracks_per_disk; + callbacks->get_indexed_sector_info = sap_get_indexed_sector_info; + callbacks->post_format = sap_post_format; + + return FLOPPY_ERROR_SUCCESS; +} + +static FLOPPY_IDENTIFY(qdd_dsk_identify) +{ + *vote = (floppy_image_size(floppy) == (51200)) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + +/* fixed interlacing map for QDDs */ +static int thom_qdd_map[400]; + +static int qdd_translate_sector(floppy_image *floppy, int sector) +{ + return thom_qdd_map[sector-1]; +} + +static void thom_qdd_compute_map ( void ) +{ + /* this map is hardcoded in the QDD BIOS */ + static const int p[6][4] = + { + { 20, 2, 14, 8 }, { 21, 19, 13, 7 }, + { 22, 18, 12, 6 }, { 23, 17, 11, 5 }, + { 24, 16, 10, 4 }, { 1, 15, 9, 3 } + }; + static const int q[4] = { 0, 8, 4, 12 }; + int t, s; + for ( t = 0; t < 24; t++ ) + { + for ( s = 0; s < 16; s++ ) + { + thom_qdd_map[ t*16 + s ] = p[ t/4 ][ s%4 ] * 16 + (s/4) + 4*(t%4); + } + } + for ( s = 0; s < 16; s++ ) + { + thom_qdd_map[ 24*16 + s ] = q[ s%4 ] + (s/4); + } +} + +static FLOPPY_CONSTRUCT(qdd_dsk_construct) +{ + struct basicdsk_geometry geometry; + + thom_qdd_compute_map(); + + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = 1; + geometry.first_sector_id = 1; + geometry.sector_length = 128; + geometry.tracks = 1; + geometry.sectors = 400; + geometry.translate_sector = qdd_translate_sector; + return basicdsk_construct(floppy, &geometry); +} + + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START(thomson) + + FLOPPY_OPTION(fdmfm2, "fd", "Thomson FD (MFM) 80 tracks disk image (3\"1/2 DD)", basicdsk_identify_default, basicdsk_construct_default, NULL, + HEADS([1]) + TRACKS([80]) + SECTORS([16]) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID([1])) + + FLOPPY_OPTION(fdmfm, "fd", "Thomson FD (MFM) 40 tracks disk image (5\"1/4 DD)", basicdsk_identify_default, basicdsk_construct_default, NULL, + HEADS([1]) + TRACKS([40]) + SECTORS([16]) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID([1])) + + FLOPPY_OPTION(fd2, "fd", "Thomson FD (FM) 80 tracks disk image (3\"1/2 SD)", basicdsk_identify_default, basicdsk_construct_default, NULL, + HEADS([1]) + TRACKS([80]) + SECTORS([16]) + SECTOR_LENGTH([128]) + FIRST_SECTOR_ID([1])) + + FLOPPY_OPTION(fd, "fd", "Thomson FD (FM) 40 tracks disk image (5\"1/4 SD)", basicdsk_identify_default, basicdsk_construct_default, NULL, + HEADS([1]) + TRACKS([40]) + SECTORS([16]) + SECTOR_LENGTH([128]) + FIRST_SECTOR_ID([1])) + + FLOPPY_OPTION(sap,"sap", "Thomson SAP floppy disk image", sap_dsk_identify, sap_dsk_construct, NULL, NULL) + + FLOPPY_OPTION(qdd,"qd", "Thomson QDD floppy disk image (2\"8 SD)", qdd_dsk_identify, qdd_dsk_construct, NULL, + HEADS([1]) + TRACKS([1]) + SECTORS([400]) + SECTOR_LENGTH([128]) + FIRST_SECTOR_ID([0])) + +FLOPPY_OPTIONS_END + diff --git a/src/lib/formats/thom_dsk.h b/src/lib/formats/thom_dsk.h new file mode 100644 index 00000000000..1935baac4d9 --- /dev/null +++ b/src/lib/formats/thom_dsk.h @@ -0,0 +1,14 @@ +#ifndef THOM_DSK +#define THOM_DSK + +#include "flopimg.h" + +/* recognized image formats: + - .fd one-side 5"1/4 (single and double density), 3"1/2 + - .qd one-side QDD + - .sap one-side double-density 5"1/4, 3"1/2 +*/ + +FLOPPY_OPTIONS_EXTERN(thomson); + +#endif diff --git a/src/lib/formats/ti99_dsk.c b/src/lib/formats/ti99_dsk.c new file mode 100644 index 00000000000..b50aac6a919 --- /dev/null +++ b/src/lib/formats/ti99_dsk.c @@ -0,0 +1,2037 @@ +/********************************************************************* + * + * formats/ti99_dsk.c + * + * TI99 and Geneve disk images + * Sector Dump and Track Dump format + * + * used by TI-99/4, TI-99/4A, TI-99/8, SGCPU ("TI-99/4P"), and Geneve + * + * The Sector Dump Format is also known as v9t9 (named after the first TI + * emulator to use this format). It is a contiguous sequence of sector contents + * without track data. The first sector of the disk is located at the start of + * the image, while the last sector is at its end. + * + * The Track Dump Format is also known as pc99 (again, named after the first + * TI emulator to use this format). It is a contiguous sequence of track + * contents, containing all information including address marks and CRC, but it + * does not contain clock signals. Therefore, the format requires that the + * address marks be at the same positions within each track. + * + * Both formats allow for a broad range of medium sizes. All sectors are 256 + * bytes long. The most common formats are 9 sectors per track, single-sided, + * 40 tracks, which yields 90 KiB of sector data (known as SSSD), and 18 + * sectors per track, double-sided, and 40 tracks, which is 360 KiB (known as + * DSDD). There are rare occurances of 8/16 sectors/track + * (prototypical TI double-density controller) and 35 track media. Newer + * controllers and ROMs allow for up to 36 sectors per track and 80 tracks on + * both sides, which is 2,88 MiB (DSHD80). + * + * Different to earlier implementations, these format implementations provide + * full track read/write capabilities. For the SDF format, the missing track + * information is recreated, and a common interleave is assumed. + * + * Note that in principle, writing tracks may change track sizes. + * As neither one of the formats contains meta-information about track lengths, + * and as the image consists of a contiguous sequence of the tracks, we + * cannot allow different track sizes currently. Accordingly, the track size + * cannot be changed after the image has been created by imgtool. + * Hence, also sector count per tracks and recording type (FM or MFM) cannot be + * changed without breaking the image. + * For example, MFM disks with 18 sectors per track can only be reformatted to + * MFM/18. Moreover, while this could be handled in a future revision, + * the number of tracks should also remain unchanged. + * + * You can use blank image files (filled with zeros or arbitrary data) and use + * the Disk Manager cartridge to properly format them. Also, this can be done + * by imgtool. The image file must match the format length (representing the + * space on the physical medium). + * + * 80 track drive handling + * + * For the TI system there may be a situation where 40 track disks are used + * in 80 track drives. In these cases, the disk driver applies double-stepping, + * that is, it advances the head twice in order to seek the next track. We + * emulate this by dividing the track number by 2, which means that two tracks + * are mapped to one (0,1 -> 0; 2,3 -> 1; 4,5 -> 2; ...; 78,79 -> 39) + * + * Technical detail: The size of the track must be given before actually + * starting write_track as the controller implementation uses buffered + * transfer. + * + * Michael Zapf, Feb 2010 + * + * TODO: Link to imgtool. imgtool still uses its own format creation + * + * FIXME: If image is broken with good first track and defect higher tracks, + * tdf_guess_geometry fails to define a geometry. The guessing should + * always be done. + ********************************************************************/ + +#include <string.h> +#include <assert.h> +#include <time.h> + +#include "osdepend.h" +#include "imageutl.h" +#include "ti99_dsk.h" + +#define SECTOR_SIZE 256 +#define TI99_IDAM_LENGTH 7 + +/* + Determines whether we are using 80 track drives. This variable is + necessary unless we get information about the dip + switch settings. It is set by the disk controller implementations + (bwg, hfdc, ti_fdc). +*/ +static int use_80_track_drives = FALSE; + +struct ti99dsk_geometry +{ + UINT8 sides; + UINT8 tracksperside; + UINT8 secspertrack; + UINT8 density; +}; + +struct ti99dsk_tag +{ + int heads; + int tracks; + int sectors; + int track_size; // Complete track; to be calculated + int format; + int first_idam; + int dam_offset; +}; + +/* + Parametrizes the format to use 80 track drives. For TI systems we + need to be able to emulate 40 track drives and 80 track drives. + This is set in the configuration, but the format has no direct access. + So as a preliminary solution we use this global function which sets the + flag. This function is called by the disk controller implementations + (bwg, hfdc, ti_fdc). +*/ +void ti99_set_80_track_drives(int use80) +{ + use_80_track_drives = use80; +} + +#define TI99_DSK_TAG "ti99dsktag" +#define TI99DSK_BLOCKNOTFOUND -1 + +/* + Searches a block containing number * byte, starting at the given + position. Returns the position of the first byte of the block. +*/ +static int find_block(const UINT8 *buffer, int start, int stop, UINT8 byte, size_t number) +{ + int i = start; + size_t current = number; + while (i < stop && current > 0) + { + if (buffer[i++] != byte) + { + current = number; + } + else + { + current--; + } + } + if (current==0) + { + return i - number; + } + else + return TI99DSK_BLOCKNOTFOUND; +} + +/* + Queried by flopdrv. Instead of the image tracks, we return the tracks + of the floppy drive. We assume we always have two heads. +*/ +static int ti99_get_heads_per_disk(floppy_image *floppy) +{ +// struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); +// return tag->heads; + return 2; +} + +/* + Queried by flopdrv. Instead of the image tracks, we return the tracks + of the floppy drive. +*/ +static int ti99_get_tracks_per_disk(floppy_image *floppy) +{ + int drivetracks = 42; + + // This may fail on startup; therefore we use a special function in + // in the floppy controller implementations to explicitly set the geometry. + if (use_80_track_drives) + { + drivetracks = 83; + } + return drivetracks; +} + +static floperr_t ti99_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) +{ + *sector_length=SECTOR_SIZE; + return FLOPPY_ERROR_SUCCESS; +} + +/* + Return the track size. We do not allow different track + sizes, so we store the track size in the tag. +*/ +static UINT32 ti99_get_track_size(floppy_image *floppy, int head, int track) +{ + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + return tag->track_size; +} + +/* +static void create_vib(UINT8 *sector0, option_resolution *params) +{ + const char *name = "UNNAMED "; + int sides, tracksperside,secspertrack,totalsectors,ausize,allocbytes,i; + + enum { NAME=0, TOTALSECS=10, SECSPERTRACK=11, SIG=12, PROT=16, TRACKS=17, SIDES=18, DENSITY=19, EXT=20, ALLOC=56 }; + + sides = option_resolution_lookup_int(params, PARAM_HEADS); + tracksperside = option_resolution_lookup_int(params, PARAM_TRACKS); + secspertrack = option_resolution_lookup_int(params, PARAM_SECTORS); + + totalsectors = sides * tracksperside * secspertrack; + + memcpy(§or0[NAME], name, 10); + + sector0[TOTALSECS] = (totalsectors>>8)&0xff; + sector0[TOTALSECS+1] = (totalsectors&0xff); + + sector0[SECSPERTRACK] = secspertrack; + sector0[SIG] = 'D'; + sector0[SIG+1] = 'S'; + sector0[SIG+2] = 'K'; + + sector0[PROT] = ' '; + sector0[TRACKS] = tracksperside; + sector0[SIDES] = sides; + sector0[DENSITY] = (secspertrack<10)? 0x01 : 0x02; + + for (i=EXT; i < ALLOC; i++) + sector0[i] = 0; + + ausize = (totalsectors / 1441) + 1; + + allocbytes = totalsectors / ausize / 8; + + for (i=ALLOC; i < ALLOC+allocbytes; i++) + sector0[i] = 0; + + while (i++ < SECTOR_SIZE) + sector0[i] = 0xff; +} +*/ +/* ========================================================================= + Sector dump format + =========================================================================*/ + +static floperr_t ti99_sdf_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen); +static floperr_t ti99_sdf_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam); + +/* ----------------------------------------------------------------------- + * Guess the geometry of the sector dump disk. + * + * Sector size is always 256 bytes. + * + * Common formats: + * 90 KiB = 40 tracks / 1 side / 9 sectors = SSSD + * 180 KiB = 40 tracks / 2 side / 9 sectors = DSSD (most common) + * = 40 tracks / 1 side / 18 sectors = SSDD (rare) + * = 80 tracks / 1 side / 9 sectors = SSSD80 (rare) + * 360 KiB = 40 tracks / 2 side / 18 sectors = DSDD (most common) + * = 80 tracks / 2 side / 9 sectors = DSSD80 (rare) + * = 80 tracks / 1 side / 18 sectors = SSDD80 (rare) + * = 40 tracks / 1 side / 36 sectors = SSHD (rare) + * 720 KiB = 80 tracks / 2 side / 18 sectors = DSDD80 (most common) + * 1440 KiB= 80 tracks / 2 side / 36 sectors = DSHD80 (most common) + * + * Moreover, there may be formats with 35 tracks (ancient) and 8 sectors/track. + * We only check for the 8 sector formats. + * 160 KiB = 40 tracks / 1 side / 16 sectors = SSDD8 + * 320 KiB = 40 tracks / 2 side / 16 sectors = DSDD8 + * + * The Volume Information Block may contain suitable information, but not + * before the disk is formatted and initialized. + * + * returns 100 if recognized, 0 if not recognized + * ----------------------------------------------------------------------- */ + +static int ti99_sdf_guess_geometry(floppy_image *floppy, UINT64 size, + struct ti99dsk_geometry *geometry) +{ + int totsecs; + typedef struct ti99_vib + { + char name[10]; // volume name (10 characters, pad with spaces) + UINT8 totsecsMSB; // disk length in sectors (big-endian) (usually 360, 720 or 1440) + UINT8 totsecsLSB; + UINT8 secspertrack; // sectors per track (usually 9 (FM) or 18 (MFM)) + UINT8 id[3]; // String "DSK" + UINT8 protection; // 'P' if disk is protected, ' ' otherwise. + UINT8 tracksperside; // tracks per side (usually 40) + UINT8 sides; // sides (1 or 2) + UINT8 density; // 0,1 (FM) or 2,3,4 (MFM) + UINT8 res[36]; // Empty for traditional disks, or up to 3 directory pointers + UINT8 abm[200]; // allocation bitmap: a 1 for each sector in use (sector 0 is LSBit of byte 0, + // sector 7 is MSBit of byte 0, sector 8 is LSBit of byte 1, etc.) + } ti99_vib; + + ti99_vib vib; + UINT32 file_size; + struct ti99dsk_geometry dummy_geometry; + + if (geometry) + memset(geometry, 0, sizeof(*geometry)); + else + geometry = &dummy_geometry; + + if (size > 0xFFFFFFFF) + return 0; + + file_size = (UINT32) size; + + // Read the Volume Information Block + floppy_image_read(floppy, &vib, 0, sizeof(vib)); + + // If we have read the sector successfully, let us parse it + totsecs = (vib.totsecsMSB << 8) | vib.totsecsLSB; + geometry->secspertrack = vib.secspertrack; + if (geometry->secspertrack == 0) + // Some images might be like this, because the original SSSD + // TI controller always assumes 9. + geometry->secspertrack = 9; + geometry->tracksperside = vib.tracksperside; + if (geometry->tracksperside == 0) + // Some images are like this, because the original SSSD TI controller + // always assumes 40. + geometry->tracksperside = 40; + geometry->sides = vib.sides; + if (geometry->sides == 0) + // Some images are like this, because the original SSSD TI controller + // always assumes that tracks beyond 40 are on side 2. */ + geometry->sides = totsecs / (geometry->secspertrack * geometry->tracksperside); + geometry->density = vib.density; + // check that the format makes sense + if (((geometry->secspertrack * geometry->tracksperside * geometry->sides) == totsecs) + && (geometry->density <= 4) && (totsecs >= 2) && (! memcmp(vib.id, "DSK", 3)) + && (file_size == totsecs*256)) + { + logerror("SDF/VIB consistent; tracks = %d, heads = %d, sectors = %d\n", geometry->tracksperside, geometry->sides, geometry->secspertrack); + return 100; + } + logerror("SDF/VIB not consistent; guessing format\n"); + + // So that was not consistent. We guess the size from the file size + // and assume that the VIB did not contain reliable data. For the + // ambiguous case we choose the most common format. + + switch (file_size) + { + case 1*40*9*256: + // 90kbytes: SSSD + case 0: + /*default:*/ + geometry->sides = 1; + geometry->tracksperside = 40; + geometry->secspertrack = 9; + geometry->density = 1; + break; + + case 2*40*9*256: + // 180kbytes: either DSSD or 18-sector-per-track SSDD. + // We assume DSSD since DSSD is more common and is supported by + // the original TI SD disk controller. + geometry->sides = 2; + geometry->tracksperside = 40; + geometry->secspertrack = 9; + geometry->density = 1; + break; + + case 1*40*16*256: + // 160kbytes: 16-sector-per-track SSDD (standard format for TI + // DD disk controller prototype, and the TI hexbus disk + // controller?) */ + geometry->sides = 1; + geometry->tracksperside = 40; + geometry->secspertrack = 16; + geometry->density = 2; + break; + + case 2*40*16*256: + // 320kbytes: 16-sector-per-track DSDD (standard format for TI + // DD disk controller prototype, and TI hexbus disk + // controller?) + geometry->sides = 2; + geometry->tracksperside = 40; + geometry->secspertrack = 16; + geometry->density = 2; + break; + + case 2*40*18*256: + // 360kbytes: 18-sector-per-track DSDD (standard format for most + // third-party DD disk controllers, but reportedly not supported by + // the original TI DD disk controller prototype) + geometry->sides = 2; + geometry->tracksperside = 40; + geometry->secspertrack = 18; + geometry->density = 2; + break; + + case 2*80*18*256: + // 720kbytes: 18-sector-per-track 80-track DSDD (Myarc only) + geometry->sides = 2; + geometry->tracksperside = 80; + geometry->secspertrack = 18; + geometry->density = 2; + break; + + case 2*80*36*256: + // 1.44Mbytes: DSHD (Myarc only) + geometry->sides = 2; + geometry->tracksperside = 80; + geometry->secspertrack = 36; + geometry->density = 3; + break; + + default: + logerror("Unrecognized disk image geometry\n"); + return 0; + } + + logerror("SDF geometry guess: tracks = %d, heads = %d, sectors = %d\n", geometry->tracksperside, geometry->sides, geometry->secspertrack); + return 100; +} + +/* + Figure out the interleave for the sector arrangement in the track. For + SDF format this is required for the read_track function. + + Interleave: + 0 7 5 3 1 8 6 4 2; + start with 0, 3, 6, 0, 3, 6, ... + 0 11 4 15 8 1 12 5 16 9 2 13 6 17 10 3 14 7 + start with 0 for every track + + Rare formats: 8 sectors + 0 3 6 1 4 7 2 5 (i*3)%8 + + 16 sectors + 0 9 2 11 4 13 6 15 8 1 10 3 12 5 14 7 (i*9)%16 + + 36 sectors + 0 11 22 ... 3 14 25 (i*11)%36 +*/ +static void guess_interleave(int sectors, int *step, int *initial) +{ + switch (sectors) + { + case 8: + *step = 3; + /* initial is the offset from track to track+1 of the starting sector number */ + *initial = 0; + break; + case 9: + *step = 7; + *initial = 3; + break; + case 16: + *step = 9; + *initial = 0; + break; + case 18: + *step = 11; + *initial = 0; + break; + case 36: + *step = 11; + *initial = 0; + break; + default: + *step = 11; + *initial = 0; + break; + } +} + +/* + Delivers a whole track. As the image consists only of + sector data, we must reconstruct the track data and + then insert the sector data. + + The track layout is exactly the Track Dump Format, except that the + CRC values must be correctly calculated. + + Total length (FM) = 247 + sectors*334; (sectors=8 or 9) + Total length (MFM) = 752 + sectors*340; (sectors=16 or 18) +*/ +static floperr_t ti99_sdf_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) +{ + int sector; + int startstep; + int step; + int position=0; + unsigned short crc; + int i; + floperr_t err; + int imgtrack = track; + + UINT8 *trackdata = (UINT8*)buffer; + + /* Determine from the image geometry whether we have FM or MFM */ + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + + // Do we have a 40 track disk in an 80 track drive? In that case we + // double the "width" of the tracks. */ + if (use_80_track_drives && tag->tracks<=40) + { + imgtrack = track / 2; + } + + guess_interleave(tag->sectors, &step, &startstep); + + if (tag->sectors < 10) /* must be FM */ + { + /* Write lead-in. */ + memset(trackdata, 0x00, 16); + position += 16; + for (i=0; i < tag->sectors; i++) + { + /* Create interleaving */ + sector = (i*step + imgtrack*startstep) % tag->sectors; + + memset(&trackdata[position], 0x00, 6); + position += 6; + /* Set IDAM */ + trackdata[position++] = 0xfe; + trackdata[position++] = imgtrack; + trackdata[position++] = head; + trackdata[position++] = sector; + trackdata[position++] = 0x01; + /* Calculate CRC16. Preset to 0xffff (what is 0xcdb4?) */ + crc = ccitt_crc16(0xffff, &trackdata[position-(TI99_IDAM_LENGTH-2)], TI99_IDAM_LENGTH - 2); + trackdata[position++] = (crc>>8)&0xff; + trackdata[position++] = crc & 0xff; + + /* Write Gap2 */ + memset(&trackdata[position], 0xff, 11); + position += 11; + memset(&trackdata[position], 0x00, 6); + position += 6; + /* Write DAM */ + trackdata[position++] = 0xfb; + + /* Locate the sector content in the image and load it. */ + err = ti99_sdf_read_sector(floppy, head, imgtrack, sector, &trackdata[position], SECTOR_SIZE); + if (err) + return err; + + position += SECTOR_SIZE; + + /* Set CRC16 */ + crc = ccitt_crc16(0xffff, &trackdata[position-(SECTOR_SIZE+1)], SECTOR_SIZE+1); + trackdata[position++] = (crc>>8)&0xff; + trackdata[position++] = crc & 0xff; + + /* Write Gap3 */ + memset(&trackdata[position], 0xff, 45); + position += 45; + } + /* Write lead-out */ + memset(&trackdata[position], 0xff, 231); + position += 231; + } + else /* we have MFM */ + { + /* Write lead-in. */ + memset(trackdata, 0x4e, 40); + position += 40; + for (i=0; i < tag->sectors; i++) + { + sector = (i*step + imgtrack*startstep) % tag->sectors; + + memset(&trackdata[position], 0x00, 10); + position += 10; + /* Write sync */ + memset(&trackdata[position], 0xa1, 3); + position += 3; + /* Set IDAM */ + trackdata[position++] = 0xfe; + trackdata[position++] = imgtrack; + trackdata[position++] = head; + trackdata[position++] = sector; + trackdata[position++] = 0x01; + /* Set CRC16 */ + crc = ccitt_crc16(0xffff, &trackdata[position-TI99_IDAM_LENGTH+2], TI99_IDAM_LENGTH - 2); + trackdata[position++] = (crc>>8)&0xff; + trackdata[position++] = crc & 0xff; + + /* Write Gap2 */ + memset(&trackdata[position], 0x4e, 22); + position += 22; + memset(&trackdata[position], 0x00, 12); + position += 12; + /* Write sync */ + memset(&trackdata[position], 0xa1, 3); + position += 3; + /* Write DAM */ + trackdata[position++] = 0xfb; + + /* Locate the sector content in the image and load it. */ + err = ti99_sdf_read_sector(floppy, head, track, sector, &trackdata[position], SECTOR_SIZE); + if (err) + return err; + + position += SECTOR_SIZE; + + /* Set CRC16 */ + crc = ccitt_crc16(0xffff, &trackdata[position-SECTOR_SIZE-1], SECTOR_SIZE+1); + trackdata[position++] = (crc>>8)&0xff; + trackdata[position++] = crc & 0xff; + + /* Write Gap3 */ + memset(&trackdata[position], 0x4e, 24); + position += 24; + } + /* Write lead-out */ + memset(&trackdata[position], 0x4e, 712); + position += 712; + } + return FLOPPY_ERROR_SUCCESS; +} + +/* + Writes a whole track. As the sector dump format does not include track + data, we have to extract the sector contents and to write them + in sequence into the image. All the rest (gaps, crc, sync bytes) are + dropped here. + Bytes written to locations beyond the image end will be droppped + silently (as if the medium is unwritable from that point). +*/ +static floperr_t ti99_sdf_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen) +{ + int current_pos = 0; + UINT8 *track_image; + int leadin, gap1, gap2; + int is_fm, found; + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + int imgtrack = track; + + // Find out if we are going to write an FM or MFM track. Note that the + // original TI controller is known to have a bug in the formatting + // routine (using a MOVB instead of a MOV instruction when setting the + // pointer to the track data), outputting false bytes at the + // beginning of the track. So we do not rely on absolute positions + // (and neither does the controller). + track_image = (UINT8*)buffer; + + if (use_80_track_drives && tag->tracks<=40) + { + imgtrack = track / 2; + } + + /* Only search in the first 100 bytes for the start. */ + leadin = 40; + gap1 = 10; + gap2 = 12; + is_fm = FALSE; + + current_pos = find_block(track_image, 0, 100, 0x4e, leadin); + + // In case of defect formats, we continue as far as possible. This + // may lead to sectors not being written. */ + if (current_pos==TI99DSK_BLOCKNOTFOUND) + { + /* Not MFM, possibly FM image */ + /* Again, find Lead-in */ + leadin = 16; + current_pos = find_block(track_image, 0, 100, 0x00, leadin); + if (current_pos==TI99DSK_BLOCKNOTFOUND) + { + /* If neither, forget about this process completely. */ + logerror("Cannot find lead-in for track %d, head %d.\n", track, head); + return FLOPPY_ERROR_INVALIDIMAGE; + } + gap1 = 6; + gap2 = 6; + is_fm = TRUE; + } + + found = FALSE; + /* Get behind lead-in */ + current_pos += leadin; + while (current_pos < buflen) + { + /* We must find the address block to determine the sector. */ + int new_pos = find_block(track_image, current_pos, buflen, 0x00, gap1); + if (new_pos==TI99DSK_BLOCKNOTFOUND) + { + /* Forget about the rest. */ + if (found) break; /* we were already successful, so all ok */ + logerror("Cannot find gap1 for track %d, head %d.\n", track, head); + return FLOPPY_ERROR_INVALIDIMAGE; + } + found = TRUE; + + if (!is_fm) + new_pos += 3; /* skip sync bytes in MFM */ + + if (track_image[new_pos + gap1]==0xfe) + { + current_pos = new_pos + gap1 + 1; + /* IDAM found. */ + int wtrack = track_image[current_pos]; + int whead = track_image[current_pos+1]; + int sector = track_image[current_pos+2]; + if (wtrack == imgtrack && whead == head) + { + /* We skip the first part of gap2. */ + new_pos = find_block(track_image, current_pos, buflen, 0x00, gap2); + if (current_pos==TI99DSK_BLOCKNOTFOUND) + { + logerror("Cannot find gap2 for track %d, head %d.\n", imgtrack, head); + return FLOPPY_ERROR_INVALIDIMAGE; + } + else + { + new_pos += gap2; + if (!is_fm) + new_pos += 3; /* skip sync */ + current_pos = new_pos; + if (track_image[current_pos]==0xfb) + { + /* DAM found. We now write the sector content to the image. */ + ti99_sdf_write_sector(floppy, head, track, sector, &track_image[current_pos+1], SECTOR_SIZE, 0); + current_pos += SECTOR_SIZE; + } + else + logerror("DAM not found. Not writing write sector %d\n", sector); + /* else not found, ignore this sector. */ + } + } + else + { + // else the sector head data do not match the + // current track and head. Ignore the sector. + logerror("Wrong track: wtrack=%d, imgtrack=%d, whead=%d, imghead=%d\n",wtrack,imgtrack, whead,head); + } + } + else + { + /* One step forward. Retry. */ + current_pos++; + } + } + return FLOPPY_ERROR_SUCCESS; +} + +/* + This method creates a properly formatted image. + + For a proper image, we need to format all tracks, and to create a + minimal filesystem: + + name = "UNNAMED " + number of sectors + sectors/track + "DSK" + protection = 0x00 + tracks/side + sides + density (0,1 = FM, 2 = MFM) + 0x14 - 0x37 : 0x00 + Allocation bitmap: 0xFF except for the first #sectors/AU size + + AU size = 1 (360 - 1440 sectors) + = 2 (1441 - 2880 sectors) + = 4 (2881 - 5760 sectors) + = 8 (5760 - 11520 sectors) + + sector 0 = + sector 1 = 256* 0x00 +*/ +/* static floperr_t ti99_sdf_format_track(floppy_image *floppy, int head, int track, option_resolution *params) +{ + UINT8 *sector0 = (UINT8*)malloc(SECTOR_SIZE); + create_vib(sector0, params); + free(sector0); + if (true) + { + printf("not implemented\n"); + return FLOPPY_ERROR_SEEKERROR; + } + return FLOPPY_ERROR_SUCCESS; +} +*/ + +/* + Get the offset from the start of the image file. In this format, logical + tracks on two-sided disks are arranged as follows (example for 40 + tracks; accordingly for 35 or 80) + + head-track + 0-0 + 0-1 + 0-2 + 0-3 + ... + 0-39 + 1-39 + 1-38 + 1-37 + ... + 1-0 + Note that we take the imgtrack, which may be half of the drive track. +*/ +static floperr_t ti99_sdf_get_offset(floppy_image *floppy, int head, int imgtrack, int sector, UINT64 *offset) +{ + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + + if ((head < 0) || (head >= tag->heads) + || (imgtrack < 0) || (imgtrack >= tag->tracks) + || (sector < 0) || (sector > tag->sectors)) + return FLOPPY_ERROR_SEEKERROR; + + if (head == 0) /* track numbers increasing towards inner track */ + { + *offset = (imgtrack * tag->sectors + sector) * SECTOR_SIZE; + } + else /* track numbers increasing towards outer track */ + { + *offset = (((2*tag->tracks)-1-imgtrack) * tag->sectors + sector) * SECTOR_SIZE; + } + return FLOPPY_ERROR_SUCCESS; +} + +/* + Read one sector at the specified position. +*/ +static floperr_t ti99_sdf_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + floperr_t err; + UINT64 offset; + int imgtrack = track; + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + + if (use_80_track_drives && tag->tracks<=40) + { + imgtrack = track / 2; + } + + err = ti99_sdf_get_offset(floppy, head, imgtrack, sector, &offset); + if (err) + { + return err; + } + floppy_image_read(floppy, buffer, offset, SECTOR_SIZE); + + return FLOPPY_ERROR_SUCCESS; +} + +/* + For the SDF format, sectors are always numbered in ascending order, + starting with the lowest numbered sector in each track. In other words, + we have nothing to calculate here. +*/ +static floperr_t ti99_sdf_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return ti99_sdf_read_sector(floppy, head, track, sector, buffer, buflen); +} + +/* + Write one sector at the specified position. +*/ +static floperr_t ti99_sdf_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + UINT64 offset; + floperr_t err; + int imgtrack = track; + + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + + if (use_80_track_drives && tag->tracks<=40) + { + imgtrack = track / 2; + } + + err = ti99_sdf_get_offset(floppy, head, imgtrack, sector, &offset); + if (err) + { + return err; + } + floppy_image_write(floppy, buffer, offset, SECTOR_SIZE); + + return FLOPPY_ERROR_SUCCESS; +} + +/* + See above; indexing and numbering coincide. +*/ +static floperr_t ti99_sdf_write_indexed_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return ti99_sdf_write_sector(floppy, head, track, sector, buffer, buflen, ddam); +} + +/* + Required for ReadAddress command +*/ +static floperr_t ti99_sdf_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) +{ + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + + if (use_80_track_drives && tag->tracks<=40) + { + track = track / 2; + } + + if (sector_length) + *sector_length=SECTOR_SIZE; + if (cylinder) + *cylinder = track; + if (side) + *side = head; + if (sector) + *sector = sector_index; + if (flags) + *flags = 0; + if (sector_index > tag->sectors) + { + return FLOPPY_ERROR_SEEKERROR; + } + + return FLOPPY_ERROR_SUCCESS; +} + +/* + Try to identify the image as an SDF format image. +*/ +static FLOPPY_IDENTIFY(ti99_sdf_identify) +{ + UINT64 size; + size = floppy_image_size(floppy); + *vote = ti99_sdf_guess_geometry(floppy, size, NULL); + logerror("SDF voting %d\n", *vote); + return FLOPPY_ERROR_SUCCESS; +} + +/* + Create the SDF data structures and set the callbacks. +*/ +static FLOPPY_CONSTRUCT(ti99_sdf_construct) +{ + struct ti99dsk_geometry geometry; + struct FloppyCallbacks *callbacks; + struct ti99dsk_tag *tag; + logerror("Reading image as SDF\n"); + + if (params) + { + /* create */ + memset(&geometry, 0, sizeof(geometry)); + geometry.sides = option_resolution_lookup_int(params, PARAM_HEADS); + geometry.tracksperside = option_resolution_lookup_int(params, PARAM_TRACKS); + geometry.secspertrack = option_resolution_lookup_int(params, PARAM_SECTORS); + + /* We don't have headers for geometry */ + /* check for usage in imgtool - we want to be able to create useful disks */ + } + else + { + /* load */ + if (ti99_sdf_guess_geometry(floppy, floppy_image_size(floppy), &geometry)<50) + return FLOPPY_ERROR_INVALIDIMAGE; + } + + assert(geometry.sides); + assert(geometry.tracksperside); + assert(geometry.secspertrack); + + tag = (struct ti99dsk_tag *) floppy_create_tag(floppy, sizeof(struct ti99dsk_tag)); + + if (!tag) + return FLOPPY_ERROR_OUTOFMEMORY; + + tag->heads = geometry.sides; + tag->tracks = geometry.tracksperside; + tag->sectors = geometry.secspertrack; + if (tag->sectors < 10) + /* FM mode */ + tag->track_size = 16 + tag->sectors*334 + 231; + else + tag->track_size = 40 + tag->sectors*340 + 712; + + /* set up format callbacks */ + callbacks = floppy_callbacks(floppy); + callbacks->read_sector = ti99_sdf_read_sector; + callbacks->write_sector = ti99_sdf_write_sector; + callbacks->read_indexed_sector = ti99_sdf_read_indexed_sector; + callbacks->write_indexed_sector = ti99_sdf_write_indexed_sector; + callbacks->read_track = ti99_sdf_read_track; + callbacks->write_track = ti99_sdf_write_track; +/* callbacks->format_track = ti99_sdf_format_track; */ + callbacks->get_track_size = ti99_get_track_size; + /* post format unset */ + callbacks->get_heads_per_disk = ti99_get_heads_per_disk; + callbacks->get_tracks_per_disk = ti99_get_tracks_per_disk; + callbacks->get_sector_length = ti99_get_sector_length; + callbacks->get_indexed_sector_info = ti99_sdf_get_indexed_sector_info; + + return FLOPPY_ERROR_SUCCESS; +} + +/* ----------------------------------------------------------------------- + * Track Dump Format, aka PC99 format + * This format includes all track data, including address marks and gaps. + * However, CRC is not stored; instead, a f7f7 is stored at that location. + * Two track images exist, one for FM and one for MFM format. + * + * In this implementation, however, we do calculate the CRC. + * + * All tracks have exactly the same size, so offsets are calculated easily. + * + * This format is a precise map of the data contents of the track; + * however, it does not store clock patterns. + * + * The TDF format does not have a special index part, so any sector on it + * must be searched (as done by the disk controller) + * + * Interesting detail: The original TI disk controller has a bug which has + * just recently been discovered (using MESS!): The lead-in has another 9 + * bytes as a prefix; this is due to a wrongly set pointer in the format + * routine. Strictly speaking, it produces invalid track images. + * The original hardware does not care, however, so neither should we. + * To keep the emulation realistic (and to allow for a + * possible future extension with clock bits), we search for the + * beginning of the track. From there on, however, we accept no further + * deviations. This may make the resulting format unusable with the PC99 + * emulator, but it only appears when formatting with the TI disk + * controller anyway. If the image is created with another disk controller or + * using imgtool, the format is correctly created. + * + * FM track image + * + * Lead-in: 16* 00 + * Pre-ID gap: 6* 00 ---+ + * IDAM: fe | + * Track: tt | + * Head: hh | + * Sector: ss | + * Size: 01 | + * CRC: 2* f7 +--- repeat 9 times (8 in rare cases) + * Gap2: 11* ff | + * 6* 00 | + * DAM: fb | + * Content: 256* xx | + * CRC: 2* f7 | + * Gap3: 45* ff ---+ + * Lead-out: 231* ff + * + * -------------- + * + * MFM track image + * + * Lead-in: 40* 4e + * Pre-ID gap: 10* 00 ---+ + * Sync: 3* a1 | + * IDAM: fe | + * Track: tt | + * Head: hh | + * Sector: ss | + * Size: 01 | + * CRC: 2* f7 +--- repeat 18 times (16 in rare cases) + * Gap2: 22* 4e | + * 12* 00 | + * Sync: 3* a1 | + * DAM: fb | + * Content: 256* xx | + * CRC: 2* f7 | + * Gap3: 24* 4e ---+ + * Lead-out: 712* 4e + * + * ------------------ + * The tracks are located on the image as follows (different to SDF!): + * + * head-track + * 0-0 + * 0-1 + * 0-2 + * 0-3 + * ... + * 0-39 + * 1-0 + * 1-1 + * 1-2 + * ... + * 1-39 + * ----------------------------------------------------------------------- */ + +#define TI99_FM 1 +#define TI99_MFM 2 + +/* For the emulation of the clock pattern, we add a 9th bit. */ +#define TI99_DAM 0x1fb +#define TI99_IDAM 0x1fe + +#define TI99_FM_DAM_OFFSET 24 +#define TI99_MFM_DAM_OFFSET 44 + +#define TI99_FM_TRACK_ADD 247 +#define TI99_MFM_TRACK_ADD 752 + +#define TI99_FM_TOTAL_SECTOR 334 +#define TI99_MFM_TOTAL_SECTOR 340 + +/* + Determine offset of the first IDAM. This is the base of the simulation + of the clock bytes. We use a heuristic to guess where the track begins + and simply check whether we can locate the first IDAM and DAM. Return + value is the offset to the first IDAM. +*/ +static floperr_t determine_offset(int format, UINT8 *track, int *offset) +{ + floperr_t retval; + int current_pos; + + if (format==TI99_FM) + { +// printf("Trying FM\n"); + // We are seeking the lead-in and pre-id gap from + // position 0 to position 50. This is just to make sure that + // if there are bad bytes at the beginning (original TI controller) + // we will skip them. + current_pos = find_block(track, 0, 50, 0x00, 22); + if (current_pos==TI99DSK_BLOCKNOTFOUND) + { + logerror("Lead-in not found\n"); + retval = FLOPPY_ERROR_SEEKERROR; + } + else + { + current_pos += 22; + if (track[current_pos]==0xfe /* IDAM */ && + track[current_pos+4]==0x01 /* sector length, always 256 for TI */ && + track[current_pos+24]==0xfb) /* DAM */ + { + /* We're pretty sure this is the beginning. */ + *offset = current_pos; + retval = FLOPPY_ERROR_SUCCESS; + } + else + { + logerror("IDAM or DAM not found\n"); + retval = FLOPPY_ERROR_SEEKERROR; + } + } + } + else + { + /* MFM */ +// printf("Trying MFM\n"); + int track_start = find_block(track, 0, 100, 0x4e, 40); + if (track_start==TI99DSK_BLOCKNOTFOUND) + { + logerror("Lead-in not found\n"); + retval = FLOPPY_ERROR_SEEKERROR; + } + else + { + track_start += 40; + current_pos = find_block(track, track_start, track_start+10, 0x00, 10); + if (current_pos==TI99DSK_BLOCKNOTFOUND) + { + logerror("Pre-gap not found\n"); + retval = FLOPPY_ERROR_SEEKERROR; + } + else + { + current_pos += 10; + if (track[current_pos] == 0xa1 /* First sync */ && + track[current_pos+3] == 0xfe /* IDAM */ && + track[current_pos+44] == 0xa1 /* First sync */ && + track[current_pos+47] == 0xfb) /* DAM */ + { + /* We're pretty sure this is the beginning. */ + *offset = current_pos + 3; + retval = FLOPPY_ERROR_SUCCESS; + } + else + { + logerror("Sync/IDAM/DAM not found\n"); + retval = FLOPPY_ERROR_SEEKERROR; + } + } + } + } +/* printf("TDF offset = 0x%x\n", *offset); */ + return retval; +} + +/* + Gets a byte from the track. In this format we do not have a clock + signal, so we will simulate it. Thus, this function returns a 9-bit + value, with the first bit set as a marker for an address mark. + + This may be a little more complicated than necessary, but it correctly + emulates the behavior of the controller (and it allows for an + alternative format which actually supports clock and data). +*/ +static int read_byte(int format, int first_idam, UINT8 *track, int position) +{ + int totalseclen; + int dam_offset; + int byte; + + /* Check whether we are at any IDAM or DAM in this track. */ + if (format==TI99_FM) + { + totalseclen = TI99_FM_TOTAL_SECTOR; + dam_offset = TI99_FM_DAM_OFFSET; + } + else + { + totalseclen = TI99_MFM_TOTAL_SECTOR; + dam_offset = TI99_MFM_DAM_OFFSET; + } + + int curpos = (position - first_idam) % totalseclen; + if (curpos==0 && track[position]==0xfe) + byte = TI99_IDAM; + else + { + if (curpos==dam_offset && track[position]==0xfb) + byte = TI99_DAM; + else + byte = track[position] & 0xff; + } + return byte; +} + +/* + Determine the position of this track in the image. +*/ +static floperr_t ti99_tdf_get_offset(floppy_image *floppy, int head, int imgtrack, UINT64 *offset) +{ + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + + if ((head < 0) || (head >= tag->heads) || (imgtrack < 0) || (imgtrack >= tag->tracks)) + return FLOPPY_ERROR_SEEKERROR; + + // For head 0, tracks increase from 0 to tracks-1; then for head 1, + // tracks also increase from 0 to tracks-1. */ + *offset = head * (tag->tracks * tag->track_size) + imgtrack * tag->track_size; + + return FLOPPY_ERROR_SUCCESS; +} + +/* + Reads a track. We just copy the contents from the format and recreate + the CRC if it is the "blank" CRC as F7F7. + This method assumes that the track division be done already. +*/ +static floperr_t ti99_tdf_read_track_internal(floppy_image *floppy, int head, int imgtrack, UINT64 offset, void *buffer, size_t buflen) +{ + floperr_t err; + UINT64 track_offset; + int first_idam = 0; + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + int i, byte, crc; + + UINT8 *track_data = (UINT8*)buffer; + + err = ti99_tdf_get_offset(floppy, head, imgtrack, &track_offset); + if (err) + return err; + + floppy_image_read(floppy, buffer, track_offset, buflen); + + // Rebuild the CRCs. PC99 did not store the CRC but put + // F7F7 in its place. + // The first CRC is at position IDAM + 5 + // The second CRC is at position IDAM + 0x119 (FM) or +0x12d (MFM) + // All this is repeated for each sector in the track + + if (determine_offset(tag->format, track_data, &first_idam)==FLOPPY_ERROR_SEEKERROR) + return FLOPPY_ERROR_SEEKERROR; + + i = 0; + while (i < buflen) + { + byte = read_byte(tag->format, first_idam, track_data, i); + if (byte == TI99_IDAM) + { + // Do we have a valid CRCs already? Then this image + // already contains proper CRCs handling. Do not recreate them. + if (track_data[i+5] != 0xf7 || track_data[i+6] != 0xf7) + return FLOPPY_ERROR_SUCCESS; + + crc = ccitt_crc16(0xffff, &track_data[i], 5); + track_data[i+5] = (crc>>8) & 0xff; + track_data[i+6] = (crc & 0xff); + } + else + { + if (byte == TI99_DAM) + { + crc = ccitt_crc16(0xffff, &track_data[i], SECTOR_SIZE+1); + track_data[i+SECTOR_SIZE+1] = (crc>>8) & 0xff; + track_data[i+SECTOR_SIZE+2] = (crc & 0xff); + } + } + i++; + } +// dump_contents(track_data, buflen); + return FLOPPY_ERROR_SUCCESS; +} + +/* + Reads a track. We just copy the contents from the format, without + changes. +*/ +static floperr_t ti99_tdf_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) +{ + int imgtrack = track; + + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + if (use_80_track_drives && tag->tracks<=40) + { + imgtrack = track / 2; + } + return ti99_tdf_read_track_internal(floppy, head, imgtrack, offset, buffer, buflen); +} + +/* + Writes a track. +*/ +static floperr_t ti99_tdf_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen) +{ + floperr_t err; + UINT64 track_offset; + int imgtrack = track; + + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + if (use_80_track_drives && tag->tracks<=40) + { + imgtrack = track / 2; + } + + err = ti99_tdf_get_offset(floppy, head, imgtrack, &track_offset); + if (err) + return err; + + // According to the TDF format, we don't need the CRC but replace it + // with F7F7. For now we keep it and see whether PC99 can cope with + // that. (Otherwise we would have to copy the const buffer). */ + floppy_image_write(floppy, buffer, offset + track_offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + +/* +static floperr_t ti99_tdf_format_track(floppy_image *floppy, int head, int track, option_resolution *params) +{ + int sectors; + int sector_length; + int interleave; + int first_sector_id; + sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); + interleave = option_resolution_lookup_int(params, PARAM_INTERLEAVE); + first_sector_id = option_resolution_lookup_int(params, PARAM_FIRST_SECTOR_ID); + return FLOPPY_ERROR_SUCCESS; +} +*/ + +/* + Get a sector from a track. This function takes a complete track, + searches for the sector, and passes a pointer to the sector contents + back. + As the original TI controller creates bad bytes at the start of a track + we do *not* assume a simple layout. The strategy is to search the + lead-in of the track, and from this offset, step through the track + in search for the sector. Since we do not have index marks (we cannot + distinguish them from ordinary data, as we do not store the clock), we + do not attempt to detect IDAM or DAM. +*/ +static floperr_t ti99_tdf_seek_sector_in_track(floppy_image *floppy, int head, int track, int sector, UINT8 *track_data, UINT8 **sector_data) +{ + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + + int byte; + int state; + enum { SEARCHIDAM, SEARCHDAM }; + + state = SEARCHIDAM; + if (tag->first_idam==0) /* should we check for every track? */ + { + if (determine_offset(tag->format, track_data, &tag->first_idam)==FLOPPY_ERROR_SEEKERROR) + { + logerror("could not determine offset\n"); + return FLOPPY_ERROR_SEEKERROR; + } + } + + for (int i=0; i < tag->track_size; i++) + { + byte = read_byte(tag->format, tag->first_idam, track_data, i); + + switch (state) + { + case SEARCHIDAM: + /* search for the IDAM */ + if (byte==TI99_IDAM) + { + if ((track_data[i+1]==track) && (track_data[i+2]==head) && (track_data[i+3]==sector)) + state = SEARCHDAM; + else + state = SEARCHIDAM; + } + break; + case SEARCHDAM: + if (byte==TI99_DAM) + { + *sector_data = &track_data[i+1]; + return FLOPPY_ERROR_SUCCESS; + } + break; + } + } + logerror("Sector not found.\n"); + return FLOPPY_ERROR_SEEKERROR; +} + +static floperr_t ti99_tdf_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + floperr_t err; + UINT8 *sector_data; + UINT8 *track_data; + int imgtrack = track; + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + + track_data = (UINT8*)malloc(tag->track_size); + + if (use_80_track_drives && tag->tracks<=40) + { + imgtrack = track / 2; + } + + err = ti99_tdf_read_track_internal(floppy, head, imgtrack, 0, track_data, tag->track_size); + if (err) + return err; + + err = ti99_tdf_seek_sector_in_track(floppy, head, imgtrack, sector, track_data, §or_data); + if (err) + return err; + /* verify CRC? */ + + memcpy(buffer, sector_data, SECTOR_SIZE); + free(track_data); + return FLOPPY_ERROR_SUCCESS; +} + +/* + Writes a sector into the track. Note that the indexed_write method + may be used instead of this method, according to the implementation + of flopimg/flopdrv. +*/ +static floperr_t ti99_tdf_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + floperr_t err; + UINT8 *sector_data; + UINT8 *track_data; + UINT64 track_offset; + int imgtrack = track; + UINT64 offset; + UINT8 crc_field[2]; + int crc; + + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + + track_data = (UINT8*)malloc(tag->track_size); + if (use_80_track_drives && tag->tracks<=40) + { + imgtrack = track / 2; + } + + err = ti99_tdf_read_track_internal(floppy, head, imgtrack, 0, track_data, tag->track_size); + if (err) + return err; + err = ti99_tdf_seek_sector_in_track(floppy, head, imgtrack, sector, track_data, §or_data); + if (err) + return err; + + err = ti99_tdf_get_offset(floppy, head, imgtrack, &track_offset); + if (err) + return err; + + offset = track_offset + (sector_data - track_data); + + floppy_image_write(floppy, buffer, offset, SECTOR_SIZE); + + // Recalculate CRC. We init the CRC with bf84 (DAM fb is + // already included; would be ffff without) + crc = ccitt_crc16(0xbf84, (UINT8*)buffer, SECTOR_SIZE); + crc_field[0] = (crc>>8) & 0xff; + crc_field[1] = (crc & 0xff); + floppy_image_write(floppy, crc_field, offset+SECTOR_SIZE, 2); + + return FLOPPY_ERROR_SUCCESS; +} + +static floperr_t ti99_tdf_write_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, const void *buffer, size_t buflen, int ddam) +{ + /* Read track, head */ + int byte; + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + UINT8 *track_data; + UINT8 *sector_data; + UINT64 track_offset; + UINT64 offset; + UINT8 crc_field[2]; + + int crc; + int imgtrack = track; + + int i; + floperr_t err; + + track_data = (UINT8*)malloc(tag->track_size); + + if (use_80_track_drives && tag->tracks<=40) + { + imgtrack = track / 2; + } + + err = ti99_tdf_read_track_internal(floppy, head, imgtrack, 0, track_data, tag->track_size); + if (err) + { + free(track_data); + return err; + } + + /* Search for the sector_index-th sector. */ + if (tag->first_idam==0) /* should we check for every track? */ + { + if (determine_offset(tag->format, track_data, &tag->first_idam)==FLOPPY_ERROR_SEEKERROR) + { + free(track_data); + return FLOPPY_ERROR_SEEKERROR; + } + } + + /* Search for the IDAM of the n-th sector */ + i=0; + while (i < tag->track_size && sector_index>=0) + { + byte = read_byte(tag->format, tag->first_idam, track_data, i); + if (byte == TI99_IDAM) + { + sector_index--; + } + i++; + } + i--; + + /* Not that many sectors in this track? */ + if (sector_index>0) + { + free(track_data); + return FLOPPY_ERROR_SEEKERROR; + } + + /* Find the DAM. */ + while (i < tag->track_size) + { + byte = read_byte(tag->format, tag->first_idam, track_data, i); + if (byte == TI99_DAM) + break; + else + i++; + } + + /* There was no DAM. Image seems to be broken. */ + if (i == tag->track_size) + { + free(track_data); + return FLOPPY_ERROR_SEEKERROR; + } + + /* Sector data is right after the DAM. */ + sector_data = &track_data[i+1]; + + /* Get the position of the track in the image. */ + err = ti99_tdf_get_offset(floppy, head, imgtrack, &track_offset); + if (err) + return err; + + offset = track_offset + (sector_data - track_data); + + /* Write the sector data at that position. */ + floppy_image_write(floppy, buffer, offset, SECTOR_SIZE); + + // Recalculate CRC. We init the CRC with bf84 (DAM fb is + // already included; would be ffff without) + crc = ccitt_crc16(0xbf84, (UINT8*)buffer, SECTOR_SIZE); + crc_field[0] = (crc>>8) & 0xff; + crc_field[1] = (crc & 0xff); + floppy_image_write(floppy, crc_field, offset+SECTOR_SIZE, 2); + + /* Free the temporary buffer */ + free(track_data); + + return FLOPPY_ERROR_SUCCESS; +} + + +static floperr_t ti99_tdf_find_indexed_sector(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, void *buffer, unsigned long *flags) +{ + /* Read track, head */ + int byte = 0; + struct ti99dsk_tag *tag = (ti99dsk_tag*)floppy_tag(floppy); + UINT8 *track_data; + int imgtrack = track; + int i; + floperr_t err; + floperr_t retval; + track_data = (UINT8*)malloc(tag->track_size); + + if (use_80_track_drives && tag->tracks<=40) + { + imgtrack = track / 2; + } + + err = ti99_tdf_read_track_internal(floppy, head, imgtrack, 0, track_data, tag->track_size); + if (err) + { + free(track_data); + return err; + } + + /* Search for the sector_index-th sector. */ + + if (tag->first_idam==0) /* should we check for every track? */ + { + if (determine_offset(tag->format, track_data, &tag->first_idam)==FLOPPY_ERROR_SEEKERROR) + { + free(track_data); + return FLOPPY_ERROR_SEEKERROR; + } + } + + i=0; + sector_index = sector_index+1; + while (i++ < tag->track_size && sector_index>0) + { + byte = read_byte(tag->format, tag->first_idam, track_data, i); + if (byte == TI99_IDAM) + { + sector_index--; + } + } + + retval = FLOPPY_ERROR_SEEKERROR; + if (sector_index==0) + { + /* If desired, return the ID field */ + if (cylinder) + { + *cylinder = track_data[i]; + // printf("cylinder=%d", *cylinder); + + } + if (side) + { + *side = track_data[i+1]; + // printf(", side=%d", *side); + } + if (sector) + { + *sector = track_data[i+2]; + // printf(", sector=%d", *sector); + } + if (sector_length) + { + *sector_length = 128 << track_data[i+3]; + // printf(", sector_length=%d\n", *sector_length); + } + if (flags) + { + *flags = 0; + } + retval = FLOPPY_ERROR_SUCCESS; + + /* If desired, return the sector contents. */ + if (buffer) + { + while (i++ < tag->track_size) + { + byte = read_byte(tag->format, tag->first_idam, track_data, i); + if (byte == TI99_DAM) + break; + } + if (byte == TI99_DAM) + { + memcpy(buffer, &track_data[i+1], SECTOR_SIZE); + } + else + { + retval = FLOPPY_ERROR_SEEKERROR; + } + } + } + free(track_data); + return retval; +} + +/* + Required for ReadAddress command. This function gets the 1st, 2nd, 3rd, + sector (and so on) from the track, not sector 1, 2, 3. That is, the + returned sector info depends on the interleave and the start sector. +*/ +static floperr_t ti99_tdf_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) +{ + return ti99_tdf_find_indexed_sector(floppy, head, track, sector_index, cylinder, side, sector, sector_length, NULL, flags); +} + +static floperr_t ti99_tdf_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return ti99_tdf_find_indexed_sector(floppy, head, track, sector, (int*)NULL, (int*)NULL, (int*)NULL, (UINT32*)NULL, buffer, (unsigned long*)NULL); +} + + +/* + Guess the geometry of the disk. + 1. Check whether the disk is formatted, and if so, whether it is + a FM or a MFM disk + 2. If this fails, guess from the file size which format it should have + + SDF formats have sizes which are multiples of 10KiB. + TDF: full sector size FM (MFM) = 334 (340) bytes + additional track data FM (MFM) = 247 (752) bytes + + => check whether size is dividable by 40 (or 35) and by 2 + => get track size + => subtract track data + => divide by 334 or 340 + => number of sectors should be 8, 9, 16, 18, 32, or 36 + + SSDD is 274880, but DSSD is 260240, so they differ (unlike in the SDF format) + Still, we cannot tell apart single-sided 80 track formats from double-sided + 40 tracks. We assume the latter. Moreover, we only attempt to detect + 9/18/36 sectors (no 8/16) and 40 tracks (no 35). + + SSSD40 = 130120 + DSSD = 260240 + SSDD = 274880 + DSDD = 549760 + DSHD = 1039360 + DSDD80 = 1099520 + DSHD80 = 2078720 +*/ +static int ti99_tdf_guess_geometry(floppy_image *floppy, UINT64 size, + struct ti99dsk_geometry *geometry) +{ + int idamcnt, state, track, head, i, totalseclen = 0, format = 0, byte, tracklength, trackadd = 0; + int first_idam = 0; + UINT8 *track_data; + /*int offset;*/ + + struct ti99dsk_geometry dummy_geometry; + + if (geometry) + memset(geometry, 0, sizeof(*geometry)); + else + geometry = &dummy_geometry; + + /* Allocate enough bytes to hold the longest supported track. */ + track_data = (UINT8*)malloc(13000); + floppy_image_read(floppy, track_data, 0, 13000); + + if (determine_offset(TI99_MFM, track_data, &first_idam)==FLOPPY_ERROR_SEEKERROR) + { + /* MFM failed. */ + if (determine_offset(TI99_FM, track_data, &first_idam)==FLOPPY_ERROR_SEEKERROR) + { + logerror("IDAM not found. Unformatted disk.\n"); + + // FM failed as well. Disk is not formatted. We assume + // a format that fits into the space provided by the file. + // We only give a moderate vote in this case, so SDF + // make take it. + + free(track_data); + if (size < 130120 || size > 2078720) + { + logerror("Unknown format size: %d\n", (int)size); + return 0; + } + if (size >= 130120 && size < 260240) + { + /* SSSD */ + geometry->sides = 1; + geometry->tracksperside = 40; + geometry->secspertrack = 9; + geometry->density = 1; + return 50; + } + if (size >= 260240 && size < 274880) + { + /* DSSD */ + geometry->sides = 2; + geometry->tracksperside = 40; + geometry->secspertrack = 9; + geometry->density = 1; + return 50; + } + if (size >= 274880 && size < 549760) + { + /* SSDD */ + geometry->sides = 1; + geometry->tracksperside = 40; + geometry->secspertrack = 18; + geometry->density = 2; + return 50; + } + if (size >= 549760 && size < 1039360) + { + /* DSDD */ + geometry->sides = 2; + geometry->tracksperside = 40; + geometry->secspertrack = 18; + geometry->density = 2; + return 50; + } + if (size >= 1039360 && size < 1099520) + { + /* DSHD */ + geometry->sides = 2; + geometry->tracksperside = 40; + geometry->secspertrack = 36; + geometry->density = 2; + return 50; + } + if (size >= 1099520 && size < 2078720) + { + /* DSDD80 */ + geometry->sides = 2; + geometry->tracksperside = 80; + geometry->secspertrack = 18; + geometry->density = 2; + return 50; + } + if (size == 2078720) + { + /* DSHD80 */ + geometry->sides = 2; + geometry->tracksperside = 80; + geometry->secspertrack = 36; + geometry->density = 2; + return 50; + } + } + else + { + /* FM format */ + totalseclen = TI99_FM_TOTAL_SECTOR; + trackadd = TI99_FM_TRACK_ADD; /* additional track data (gaps etc.) */ + geometry->density = 1; + format = TI99_FM; + } + } + else + { + /* MFM format */ + totalseclen = TI99_MFM_TOTAL_SECTOR; + trackadd = TI99_MFM_TRACK_ADD; + geometry->density = 2; + format = TI99_MFM; + } + + /* Count the number of IDAMs on track 0, head 0 */ + idamcnt = 0; + state = 0; + track = 0; + head = 0; + i=0; + + while (i++ < 13000 && head == 0 && track == 0) + { + byte = read_byte(format, first_idam, track_data, i); + switch (state) + { + case 0: + if (byte==TI99_IDAM) + state = 1; + break; + case 1: + track = byte; + state = 2; + break; + case 2: + head = byte; + state = 3; + break; + case 3: + idamcnt++; + state = 0; + } + } + logerror("Determined %d sectors\n", idamcnt); + + // Now calculate the geometry + // The track size, in theory, may change due to reformatting, + // and it may also vary between tracks, but the TDF does not support + // varying track lengths. So every track has the same length as track 0. + // Also, we don't yet know whether the drive and the disk have the + // same track count. For 80 track drives and 40 track disks, we need + // double-stepping; this will be known as soon as the controller is + // initialized; we need to check that in the access methods. + + tracklength = idamcnt * totalseclen + trackadd; + /*offset = 0;*/ + + // Read the last track. We assume that all tracks are properly + // formatted; otherwise, the above calculations would already fail. + // That is, the last track in the image tells us whether it is for + // head 0 or for head 1. + + geometry->sides = 1; + floppy_image_read(floppy, track_data, size-tracklength, tracklength); + if (determine_offset(format, track_data, &first_idam)==FLOPPY_ERROR_SEEKERROR) + { + /* error ... what now? */ + logerror("Error when reading last track. Image broken.\n"); + free(track_data); + return 50; + } + else + { + if (track_data[first_idam+2]==0) + { + geometry->sides = 1; + } + else + { + if (track_data[first_idam+2]==1) + { + geometry->sides = 2; + } + else + { + logerror("Error: Last track has invalid first IDAM: head = %d.\n", track_data[first_idam+2]); + } + + } + } + + + geometry->tracksperside = (size / tracklength) / geometry->sides; + geometry->secspertrack = idamcnt; + + if (geometry->tracksperside < 35 || geometry->tracksperside > 80) + { + logerror("Unsupported track count: %d\n", geometry->tracksperside); + free(track_data); + return 0; + } + + free(track_data); + return 100; +} + +static FLOPPY_IDENTIFY(ti99_tdf_identify) +{ + UINT64 size; + size = floppy_image_size(floppy); + *vote = ti99_tdf_guess_geometry(floppy, size, NULL); + logerror("TDF voting %d\n", *vote); + return FLOPPY_ERROR_SUCCESS; +} + +static FLOPPY_CONSTRUCT(ti99_tdf_construct) +{ + struct ti99dsk_geometry geometry; + struct FloppyCallbacks *callbacks; + struct ti99dsk_tag *tag; + logerror("Reading image as TDF\n"); + if (params) + { + /* create */ + memset(&geometry, 0, sizeof(geometry)); +/* + geometry.sides = option_resolution_lookup_int(params, PARAM_HEADS); + geometry.tracksperside = option_resolution_lookup_int(params, PARAM_TRACKS); + geometry.secspertrack = option_resolution_lookup_int(params, PARAM_SECTORS); +*/ + } + else + { + /* load */ + if (ti99_tdf_guess_geometry(floppy, floppy_image_size(floppy), &geometry)<50) + return FLOPPY_ERROR_INVALIDIMAGE; + } + + assert(geometry.sides); + assert(geometry.tracksperside); + assert(geometry.secspertrack); + + tag = (struct ti99dsk_tag *) floppy_create_tag(floppy, sizeof(struct ti99dsk_tag)); + + tag->heads = geometry.sides; + tag->tracks = geometry.tracksperside; + + if (geometry.density==TI99_FM) + tag->track_size = geometry.secspertrack * TI99_FM_TOTAL_SECTOR + TI99_FM_TRACK_ADD; + else + tag->track_size = geometry.secspertrack * TI99_MFM_TOTAL_SECTOR + TI99_MFM_TRACK_ADD; + + tag->format = geometry.density; + tag->first_idam = 0; /* let's find out later */ + tag->dam_offset = 0; + + /* set up format callbacks */ + callbacks = floppy_callbacks(floppy); + callbacks->read_sector = ti99_tdf_read_sector; + callbacks->write_sector = ti99_tdf_write_sector; + callbacks->read_indexed_sector = ti99_tdf_read_indexed_sector; + callbacks->write_indexed_sector = ti99_tdf_write_indexed_sector; + callbacks->read_track = ti99_tdf_read_track; + callbacks->write_track = ti99_tdf_write_track; +/* callbacks->format_track = ti99_tdf_format_track; */ + callbacks->get_track_size = ti99_get_track_size; + /* post format unset */ + callbacks->get_heads_per_disk = ti99_get_heads_per_disk; + callbacks->get_tracks_per_disk = ti99_get_tracks_per_disk; + callbacks->get_sector_length = ti99_get_sector_length; + callbacks->get_indexed_sector_info = ti99_tdf_get_indexed_sector_info; + + return FLOPPY_ERROR_SUCCESS; +} + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( ti99 ) + FLOPPY_OPTION( ti99_sdf, "dsk", "TI99 sector dump (v9t9)", ti99_sdf_identify, ti99_sdf_construct, NULL, + HEADS([1]-2) + TRACKS(35-[40]-80) + SECTORS(8/9/16/[18]/36) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID(0)) + FLOPPY_OPTION( ti99_tdf, "dsk,dtk", "TI99 track dump (pc99)", ti99_tdf_identify, ti99_tdf_construct, NULL, + TRACKS(35-[40]-80) + SECTORS(8/9/16/[18]/36) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID(0)) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/ti99_dsk.h b/src/lib/formats/ti99_dsk.h new file mode 100644 index 00000000000..f0ee227bb11 --- /dev/null +++ b/src/lib/formats/ti99_dsk.h @@ -0,0 +1,19 @@ +/********************************************************************* + + formats/ti99_dsk.c + + TI99 and Geneve disk images + + Header file to be included by drivers which use these floppy options +*********************************************************************/ + +#ifndef TI99_DSK_H +#define TI99_DSK_H + +#include "flopimg.h" + +FLOPPY_OPTIONS_EXTERN(ti99); + +void ti99_set_80_track_drives(int use80); + +#endif /* TI99_DSK_H */ diff --git a/src/lib/formats/trd_dsk.c b/src/lib/formats/trd_dsk.c new file mode 100644 index 00000000000..55d56381066 --- /dev/null +++ b/src/lib/formats/trd_dsk.c @@ -0,0 +1,50 @@ +/********************************************************************* + + formats/trd_dsk.c + + TRD disk images + +*********************************************************************/ + +#include <string.h> + +#include "trd_dsk.h" +#include "basicdsk.h" + + +static FLOPPY_IDENTIFY(trd_dsk_identify) +{ + *vote = 100; + return FLOPPY_ERROR_SUCCESS; +} + +static FLOPPY_CONSTRUCT(trd_dsk_construct) +{ + struct basicdsk_geometry geometry; + UINT8 data[1]; + int heads; + int cylinders; + + floppy_image_read( floppy, data, 0x8e3 , 1 ); + + /* guess geometry of disk */ + heads = data[0] & 0x08 ? 1 : 2; + cylinders = data[0] & 0x01 ? 40 : 80; + + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = heads; + geometry.first_sector_id = 1; + geometry.sector_length = 256; + geometry.tracks = cylinders; + geometry.sectors = 16; + return basicdsk_construct(floppy, &geometry); +} + + + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( trd ) + FLOPPY_OPTION( trd_dsk, "trd", "TRD floppy disk image", trd_dsk_identify, trd_dsk_construct, NULL, NULL) +FLOPPY_OPTIONS_END + diff --git a/src/lib/formats/trd_dsk.h b/src/lib/formats/trd_dsk.h new file mode 100644 index 00000000000..d778aa56d0d --- /dev/null +++ b/src/lib/formats/trd_dsk.h @@ -0,0 +1,18 @@ +/********************************************************************* + + formats/trd_dsk.h + + TRD disk images + +*********************************************************************/ + +#ifndef TRD_DSK_H +#define TRD_DSK_H + +#include "flopimg.h" + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(trd); + +#endif /* TRD_DSK_H */ diff --git a/src/lib/formats/trs_cas.c b/src/lib/formats/trs_cas.c new file mode 100644 index 00000000000..dac68647699 --- /dev/null +++ b/src/lib/formats/trs_cas.c @@ -0,0 +1,134 @@ +/******************************************************************** + +Support for TRS80 .cas cassette images + +********************************************************************/ + +#include "formats/trs_cas.h" + +#define SILENCE 0 +#define SMPLO -32768 +#define SMPHI 32767 + + +static int cas_size; + + +/******************************************************************* + Generate one high-low cycle of sample data +********************************************************************/ +INLINE int trs80l2_cas_cycle(INT16 *buffer, int sample_pos, int silence, int high, int low) +{ + int i = 0; + + if ( buffer ) + { + while( i < silence ) + { + buffer[ sample_pos + i ] = SILENCE; + i++; + } + while( i < silence + high) + { + buffer[ sample_pos + i ] = SMPHI; + i++; + } + + while( i < silence + high + low ) + { + buffer[ sample_pos + i ] = SMPLO; + i++; + } + } + return silence + high + low; +} + + +static int trs80l2_handle_cas(INT16 *buffer, const UINT8 *casdata) +{ + int data_pos, sample_count; + + data_pos = 0; + sample_count = 0; + + while( data_pos < cas_size ) + { + UINT8 data = casdata[data_pos]; + int i; + + for ( i = 0; i < 8; i++ ) + { + /* Signal code */ + sample_count += trs80l2_cas_cycle( buffer, sample_count, 33, 6, 6 ); + + /* Bit code */ + if ( data & 0x80 ) + sample_count += trs80l2_cas_cycle( buffer, sample_count, 33, 6, 6 ); + else + sample_count += trs80l2_cas_cycle( buffer, sample_count, 33 + 6 + 6, 0, 0 ); + + data <<= 1; + } + + data_pos++; + } + return sample_count; +} + + +/******************************************************************* + Generate samples for the tape image +********************************************************************/ +static int trs80l2_cas_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + return trs80l2_handle_cas( buffer, bytes ); +} + + +/******************************************************************* + Calculate the number of samples needed for this tape image +********************************************************************/ +static int trs80l2_cas_to_wav_size(const UINT8 *casdata, int caslen) +{ + cas_size = caslen; + + return trs80l2_handle_cas( NULL, casdata ); +} + +static const struct CassetteLegacyWaveFiller trs80l2_cas_legacy_fill_wave = +{ + trs80l2_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + trs80l2_cas_to_wav_size, /* chunk_sample_calc */ + 44100, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + + +static casserr_t trs80l2_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &trs80l2_cas_legacy_fill_wave); +} + + +static casserr_t trs80l2_cas_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &trs80l2_cas_legacy_fill_wave); +} + + +static const struct CassetteFormat trs80l2_cas_format = +{ + "cas", + trs80l2_cas_identify, + trs80l2_cas_load, + NULL +}; + + +CASSETTE_FORMATLIST_START(trs80l2_cassette_formats) + CASSETTE_FORMAT(trs80l2_cas_format) +CASSETTE_FORMATLIST_END + diff --git a/src/lib/formats/trs_cas.h b/src/lib/formats/trs_cas.h new file mode 100644 index 00000000000..d8ca4bb64a4 --- /dev/null +++ b/src/lib/formats/trs_cas.h @@ -0,0 +1,17 @@ +/********************************************************************* + + trs_cas.h + + Format code for TRS80 (level 2) casette files. + +*********************************************************************/ + +#ifndef __TRS_CAS_H +#define __TRS_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(trs80l2_cassette_formats); + +#endif /* __TRS_CAS_H */ + diff --git a/src/lib/formats/trs_dsk.c b/src/lib/formats/trs_dsk.c new file mode 100644 index 00000000000..e2fc74c3a3e --- /dev/null +++ b/src/lib/formats/trs_dsk.c @@ -0,0 +1,77 @@ +#include <string.h> +#include "trs_dsk.h" +#include "basicdsk.h" +#include "coco_dsk.h" + +/* ----------------------------------------------------------------------- + * JV1 (Jeff Vavasour 1) format + * + * Used by Jeff Vavasour's TRS-80 Emulators + * + * Very straight basic disk; 1 head, 10 sectors, 256 sector length + * ----------------------------------------------------------------------- */ + +#define TRS80_JV1_HEADS 1 +#define TRS80_JV1_SECTORS 10 +#define TRS80_JV1_SECTORLENGTH 256 +#define TRS80_JV1_FIRSTSECTORID 0 + +static FLOPPY_IDENTIFY( trs80_jv1_identify ) +{ + UINT64 size; + size = floppy_image_size(floppy); + *vote = (size % (TRS80_JV1_HEADS * TRS80_JV1_SECTORS * TRS80_JV1_SECTORLENGTH)) + ? 0 : 100; + return FLOPPY_ERROR_SUCCESS; +} + + +static UINT64 trs80_jv1_get_ddam(floppy_image *floppy, const struct basicdsk_geometry *geom, int track, int head, int sector) +{ + // directory track is protected + if ((track==17) && (head==0)) { + return ID_FLAG_DELETED_DATA; + } + return 0; +} + +static FLOPPY_CONSTRUCT( trs80_jv1_construct ) +{ + struct basicdsk_geometry geometry; + + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = TRS80_JV1_HEADS; + geometry.sectors = TRS80_JV1_SECTORS; + geometry.first_sector_id = TRS80_JV1_FIRSTSECTORID; + geometry.sector_length = TRS80_JV1_SECTORLENGTH; + geometry.get_ddam = trs80_jv1_get_ddam; + + if (params) + { + /* create */ + geometry.tracks = option_resolution_lookup_int(params, PARAM_TRACKS); + } + else + { + /* load */ + geometry.tracks = (int) (floppy_image_size(floppy) / geometry.heads + / geometry.sectors / geometry.sector_length); + } + return basicdsk_construct(floppy, &geometry); +} + + + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( trs80 ) + FLOPPY_OPTION( trs80_jv1, "dsk", "TRS-80 JV1 disk image", trs80_jv1_identify, trs80_jv1_construct, NULL, + TRACKS([35]-255)) + FLOPPY_OPTION( trs80_dmk, "dsk,dmk", "TRS-80 DMK disk image", coco_dmk_identify, coco_dmk_construct, NULL, + HEADS([1]-2) + TRACKS([35]-255) + SECTORS(1-[10]-18) + SECTOR_LENGTH(128/[256]/512/1024/2048/4096/8192) + INTERLEAVE(0-[6]-17) + FIRST_SECTOR_ID([0]-1)) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/trs_dsk.h b/src/lib/formats/trs_dsk.h new file mode 100644 index 00000000000..6e589c8c6a3 --- /dev/null +++ b/src/lib/formats/trs_dsk.h @@ -0,0 +1,18 @@ +/********************************************************************* + + formats/trs_dsk.h + + TRS-80 disk images + +*********************************************************************/ + +#ifndef TRS_DSK_H +#define TRS_DSK_H + +#include "flopimg.h" + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN( trs80 ); + +#endif /* TRS_DSK_H */ diff --git a/src/lib/formats/tzx_cas.c b/src/lib/formats/tzx_cas.c new file mode 100644 index 00000000000..da8084160b2 --- /dev/null +++ b/src/lib/formats/tzx_cas.c @@ -0,0 +1,677 @@ +/* + +TZX (currently spectrum only) and spectrum TAP cassette format support by Wilbert Pol + +TODO: + Add support for the remaining block types: + case 0x15: Direct Recording + case 0x18: CSW Recording + case 0x19: Generalized Data Block + case 0x21: Group Start + case 0x22: Group End + case 0x23: Jump To Block + case 0x24: Loop Start + case 0x25: Loop End + case 0x26: Call Sequence + case 0x27: Return From Sequence + case 0x28: Select Block + case 0x2A: Stop Tape if in 48K Mode + case 0x2B: Set signal level + case 0x5A: Merge Block + Add support for the deprecated block types? Only if there is some image which need them: + case 0x16: C64 ROM type data block + case 0x17: C64 turbo tape data block + case 0x34: Emulation info + case 0x40: Snapshot block + +Notes: + +TZX format specification lists +8064 pulses for a header block and 3220 for a data block + +but the documentaiton on worldofspectrum lists +8063 pulses for a header block and 3223 for a data block + +see http://www.worldofspectrum.org/faq/reference/48kreference.htm#TapeDataStructure + +We are currently using the numbers from the TZX specification... + +*/ + +#include "tzx_cas.h" + + +#define TZX_WAV_FREQUENCY 44100 +#define WAVE_LOW -0x5a9e +#define WAVE_HIGH 0x5a9e +#define WAVE_NULL 0 + +#define SUPPORTED_VERSION_MAJOR 0x01 + +#define INITIAL_MAX_BLOCK_COUNT 256 +#define BLOCK_COUNT_INCREMENTS 256 + +static const UINT8 TZX_HEADER[8] = { 'Z','X','T','a','p','e','!',0x1a }; + +/* + Global variables + + Initialized by tzx_cas_get_wave_size, used (and cleaned up) by tzx_cas_fill_wave + */ + +static INT16 wave_data = 0; +static int block_count = 0; +static UINT8** blocks = NULL; +static float t_scale = 1; /* for scaling T-states to the 4MHz CPC */ + +static void toggle_wave_data(void) +{ + if (wave_data == WAVE_LOW) + { + wave_data = WAVE_HIGH; + } + else + { + wave_data = WAVE_LOW; + } +} + +static void tzx_cas_get_blocks( const UINT8 *casdata, int caslen ) +{ + int pos = sizeof(TZX_HEADER) + 2; + int max_block_count = INITIAL_MAX_BLOCK_COUNT; + + blocks = (UINT8**)malloc(max_block_count); + memset(blocks,0,max_block_count); + block_count = 0; + + while (pos < caslen) + { + UINT32 datasize; + UINT8 blocktype = casdata[pos]; + + if (block_count == max_block_count) + { + void *old_blocks = blocks; + int old_max_block_count = max_block_count; + printf("realoc"); + max_block_count = max_block_count + BLOCK_COUNT_INCREMENTS; + blocks = (UINT8**)malloc(max_block_count); // SHOULD NOT BE USING auto_alloc_array() + memset(blocks,0,max_block_count); + memcpy(blocks, old_blocks, old_max_block_count * sizeof(UINT8*)); + if (blocks) free(old_blocks); + } + + blocks[block_count] = (UINT8*)&casdata[pos]; + + pos += 1; + + switch (blocktype) + { + case 0x10: + pos += 2; + datasize = casdata[pos] + (casdata[pos + 1] << 8); + pos += 2 + datasize; + break; + case 0x11: + pos += 0x0f; + datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16); + pos += 3 + datasize; + break; + case 0x12: + pos += 4; + break; + case 0x13: + datasize = casdata[pos]; + pos += 1 + 2 * datasize; + break; + case 0x14: + pos += 7; + datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16); + pos += 3 + datasize; + break; + case 0x15: + pos += 5; + datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16); + pos += 3 + datasize; + break; + case 0x20: case 0x23: case 0x24: + pos += 2; + break; + case 0x21: case 0x30: + datasize = casdata[pos]; + pos += 1 + datasize; + break; + case 0x22: case 0x25: case 0x27: + break; + case 0x26: + datasize = casdata[pos] + (casdata[pos + 1] << 8); + pos += 2 + 2 * datasize; + break; + case 0x28: case 0x32: + datasize = casdata[pos] + (casdata[pos + 1] << 8); + pos += 2 + datasize; + break; + case 0x31: + pos += 1; + datasize = casdata[pos]; + pos += 1 + datasize; + break; + case 0x33: + datasize = casdata[pos]; + pos += 1 + 3 * datasize; + break; + case 0x34: + pos += 8; + break; + case 0x35: + pos += 0x10; + datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16) + (casdata[pos + 3] << 24); + pos += 4 + datasize; + break; + case 0x40: + pos += 1; + datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16); + pos += 3 + datasize; + break; + case 0x5A: + pos += 9; + break; + default: + datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16) + (casdata[pos + 3] << 24); + pos += 4 + datasize; + break; + } + + block_count++; + } +} + +INLINE int millisec_to_samplecount( int millisec ) +{ + return (int) (millisec * ((double)TZX_WAV_FREQUENCY / 1000.0)); +} + +INLINE int tcycles_to_samplecount( int tcycles ) +{ + return (int) ((0.5 + (((double)TZX_WAV_FREQUENCY / 3500000) * (double)tcycles)) * t_scale); +} + +static void tzx_output_wave( INT16 **buffer, int length ) +{ + if (buffer == NULL) + { + return; + } + + for ( ; length > 0; length--) + { + **buffer = wave_data; + *buffer = *buffer + 1; + } +} + +static int tzx_cas_handle_block( INT16 **buffer, const UINT8 *bytes, int pause, int data_size, int pilot, int pilot_length, int sync1, int sync2, int bit0, int bit1, int bits_in_last_byte ) +{ + int pilot_samples = tcycles_to_samplecount(pilot); + int sync1_samples = tcycles_to_samplecount(sync1); + int sync2_samples = tcycles_to_samplecount(sync2); + int bit0_samples = tcycles_to_samplecount(bit0); + int bit1_samples = tcycles_to_samplecount(bit1); + int data_index; + int size = 0; + + /* Uncomment this to include into error.log a fully detailed analysis of each block */ +// logerror("tzx_cas_block_size: pilot_length = %d, pilot_samples = %d, sync1_samples = %d, sync2_samples = %d, bit0_samples = %d, bit1_samples = %d\n", pilot_length, pilot_samples, sync1_samples, sync2_samples, bit0_samples, bit1_samples); + + /* PILOT */ + for ( ; pilot_length > 0; pilot_length--) + { + tzx_output_wave(buffer, pilot_samples); + size += pilot_samples; + toggle_wave_data(); + } + /* SYNC1 */ + if (sync1_samples > 0) + { + tzx_output_wave(buffer, sync1_samples); + size += sync1_samples; + toggle_wave_data(); + } + /* SYNC2 */ + if (sync2_samples > 0) + { + tzx_output_wave(buffer, sync2_samples); + size += sync2_samples; + toggle_wave_data(); + } + /* data */ + for (data_index = 0; data_index < data_size; data_index++) + { + UINT8 byte = bytes[data_index]; + int bits_to_go = (data_index == (data_size - 1)) ? bits_in_last_byte : 8; + + for ( ; bits_to_go > 0; byte <<= 1, bits_to_go--) + { + int bit_samples = (byte & 0x80) ? bit1_samples : bit0_samples; + tzx_output_wave(buffer, bit_samples); + size += bit_samples; + toggle_wave_data(); + tzx_output_wave(buffer, bit_samples); + size += bit_samples; + toggle_wave_data(); + } + } + /* pause */ + if (pause > 0) + { + int start_pause_samples = millisec_to_samplecount(1); + int rest_pause_samples = millisec_to_samplecount(pause - 1); + + tzx_output_wave(buffer, start_pause_samples); + size += start_pause_samples; + wave_data = WAVE_LOW; + tzx_output_wave(buffer, rest_pause_samples); + size += rest_pause_samples; + } + return size; +} + +static void ascii_block_common_log( const char *block_type_string, UINT8 block_type ) +{ + logerror("%s (type %02x) encountered.\n", block_type_string, block_type); + logerror("This block contains info on the .tzx file you are loading.\n"); + logerror("Please include the following info in your bug reports, if the image has issue in M.E.S.S.\n"); +} + +static const char *const archive_ident[] = +{ + "Full title", + "Software house/publisher", + "Author(s)", + "Year of publication", + "Language", + "Game/utility type", + "Price", + "Protection scheme/loader", + "Origin", +}; + +static const char *const hw_info[] = +{ + "Tape runs on this machine / this hardware", + "Tape needs this machine / this hardware", + "Tape runs on this machine / this hardware, but does not require its special features", + "Tape does not run on this machine / this hardware", +}; + +/* Will go through blocks and calculate number of samples needed. +If buffer is not NULL the sample data will also be written. */ +static int tzx_cas_do_work( INT16 **buffer ) +{ + int current_block = 0; + int size = 0; + + wave_data = WAVE_LOW; + + while (current_block < block_count) + { + int pause_time; + int data_size, text_size, total_size, i; + int pilot, pilot_length, sync1, sync2; + int bit0, bit1, bits_in_last_byte; + UINT8 *cur_block = blocks[current_block]; + UINT8 block_type = cur_block[0]; + + /* Uncomment this to include into error.log a list of the types each block */ +// logerror("tzx_cas_fill_wave: block %d, block_type %02x\n", current_block, block_type); + + switch (block_type) + { + case 0x10: /* Standard Speed Data Block (.TAP block) */ + pause_time = cur_block[1] + (cur_block[2] << 8); + data_size = cur_block[3] + (cur_block[4] << 8); + pilot_length = (cur_block[5] == 0x00) ? 8064 : 3220; + size += tzx_cas_handle_block(buffer, &cur_block[5], pause_time, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8); + current_block++; + break; + case 0x11: /* Turbo Loading Data Block */ + pilot = cur_block[1] + (cur_block[2] << 8); + sync1 = cur_block[3] + (cur_block[4] << 8); + sync2 = cur_block[5] + (cur_block[6] << 8); + bit0 = cur_block[7] + (cur_block[8] << 8); + bit1 = cur_block[9] + (cur_block[10] << 8); + pilot_length = cur_block[11] + (cur_block[12] << 8); + bits_in_last_byte = cur_block[13]; + pause_time = cur_block[14] + (cur_block[15] << 8); + data_size = cur_block[16] + (cur_block[17] << 8) + (cur_block[18] << 16); + size += tzx_cas_handle_block(buffer, &cur_block[19], pause_time, data_size, pilot, pilot_length, sync1, sync2, bit0, bit1, bits_in_last_byte); + current_block++; + break; + case 0x12: /* Pure Tone */ + pilot = cur_block[1] + (cur_block[2] << 8); + pilot_length = cur_block[3] + (cur_block[4] << 8); + size += tzx_cas_handle_block(buffer, cur_block, 0, 0, pilot, pilot_length, 0, 0, 0, 0, 0); + current_block++; + break; + case 0x13: /* Sequence of Pulses of Different Lengths */ + for (data_size = 0; data_size < cur_block[1]; data_size++) + { + pilot = cur_block[2 + 2 * data_size] + (cur_block[3 + 2 * data_size] << 8); + size += tzx_cas_handle_block(buffer, cur_block, 0, 0, pilot, 1, 0, 0, 0, 0, 0); + } + current_block++; + break; + case 0x14: /* Pure Data Block */ + bit0 = cur_block[1] + (cur_block[2] << 8); + bit1 = cur_block[3] + (cur_block[4] << 8); + bits_in_last_byte = cur_block[5]; + pause_time = cur_block[6] + (cur_block[7] << 8); + data_size = cur_block[8] + (cur_block[9] << 8) + (cur_block[10] << 16); + size += tzx_cas_handle_block(buffer, &cur_block[11], pause_time, data_size, 0, 0, 0, 0, bit0, bit1, bits_in_last_byte); + current_block++; + break; + case 0x20: /* Pause (Silence) or 'Stop the Tape' Command */ + pause_time = cur_block[1] + (cur_block[2] << 8); + if (pause_time == 0) + { + /* pause = 0 is used to let an emulator automagically stop the tape + in MESS we do not do that, so we insert a 5 second pause. */ + pause_time = 5000; + } + size += tzx_cas_handle_block(buffer, cur_block, pause_time, 0, 0, 0, 0, 0, 0, 0, 0); + current_block++; + break; + case 0x16: /* C64 ROM Type Data Block */ // Deprecated in TZX 1.20 + case 0x17: /* C64 Turbo Tape Data Block */ // Deprecated in TZX 1.20 + case 0x34: /* Emulation Info */ // Deprecated in TZX 1.20 + case 0x40: /* Snapshot Block */ // Deprecated in TZX 1.20 + logerror("Deprecated block type (%02x) encountered.\n", block_type); + logerror("Please look for an updated .tzx file.\n"); + current_block++; + break; + case 0x30: /* Text Description */ + ascii_block_common_log("Text Description Block", block_type); + for (data_size = 0; data_size < cur_block[1]; data_size++) + logerror("%c", cur_block[2 + data_size]); + logerror("\n"); + current_block++; + break; + case 0x31: /* Message Block */ + ascii_block_common_log("Message Block", block_type); + logerror("Expected duration of the message display: %02x\n", cur_block[1]); + logerror("Message: \n"); + for (data_size = 0; data_size < cur_block[2]; data_size++) + { + logerror("%c", cur_block[3 + data_size]); + if (cur_block[3 + data_size] == 0x0d) + logerror("\n"); + } + logerror("\n"); + current_block++; + break; + case 0x32: /* Archive Info */ + ascii_block_common_log("Archive Info Block", block_type); + total_size = cur_block[1] + (cur_block[2] << 8); + text_size = 0; + for (data_size = 0; data_size < cur_block[3]; data_size++) // data_size = number of text blocks, in this case + { + if (cur_block[4 + text_size] < 0x09) + logerror("%s: \n", archive_ident[cur_block[4 + text_size]]); + else + logerror("Comment(s): \n"); + + for (i = 0; i < cur_block[4 + text_size + 1]; i++) + { + logerror("%c", cur_block[4 + text_size + 2 + i]); + } + text_size += 2 + i; + } + logerror("\n"); + if (text_size != total_size) + logerror("Malformed Archive Info Block (Text length different from the declared one).\n Please verify your tape image.\n"); + current_block++; + break; + case 0x33: /* Hardware Type */ + ascii_block_common_log("Hardware Type Block", block_type); + for (data_size = 0; data_size < cur_block[1]; data_size++) // data_size = number of hardware blocks, in this case + { + logerror("Hardware Type %02x - Hardware ID %02x - ", cur_block[2 + data_size * 3], cur_block[2 + data_size * 3 + 1]); + logerror("%s \n ", hw_info[cur_block[2 + data_size * 3 + 2]]); + } + current_block++; + break; + case 0x35: /* Custom Info Block */ + ascii_block_common_log("Custom Info Block", block_type); + for (data_size = 0; data_size < 10; data_size++) + { + logerror("%c", cur_block[1 + data_size]); + } + logerror(":\n"); + text_size = cur_block[11] + (cur_block[12] << 8) + (cur_block[13] << 16) + (cur_block[14] << 24); + for (data_size = 0; data_size < text_size; data_size++) + logerror("%c", cur_block[15 + data_size]); + logerror("\n"); + current_block++; + break; + case 0x5A: /* "Glue" Block */ + logerror("Glue Block (type %02x) encountered.\n", block_type); + logerror("Please use a .tzx handling utility to split the merged tape files.\n"); + current_block++; + break; + case 0x15: /* Direct Recording */ + case 0x18: /* CSW Recording */ + case 0x19: /* Generalized Data Block */ + case 0x21: /* Group Start */ + case 0x22: /* Group End */ + case 0x23: /* Jump To Block */ + case 0x24: /* Loop Start */ + case 0x25: /* Loop End */ + case 0x26: /* Call Sequence */ + case 0x27: /* Return From Sequence */ + case 0x28: /* Select Block */ + case 0x2A: /* Stop Tape if in 48K Mode */ + case 0x2B: /* Set signal level */ + default: + logerror("Unsupported block type (%02x) encountered.\n", block_type); + current_block++; + break; + } + } + return size; +} + +static int tzx_cas_to_wav_size( const UINT8 *casdata, int caslen ) +{ + int size = 0; + + /* Header size plus major and minor version number */ + if (caslen < 10) + { + logerror("tzx_cas_to_wav_size: cassette image too small\n"); + goto cleanup; + } + + /* Check for correct header */ + if (memcmp(casdata, TZX_HEADER, sizeof(TZX_HEADER))) + { + logerror("tzx_cas_to_wav_size: cassette image has incompatible header\n"); + goto cleanup; + } + + /* Check major version number in header */ + if (casdata[0x08] > SUPPORTED_VERSION_MAJOR) + { + logerror("tzx_cas_to_wav_size: unsupported version\n"); + goto cleanup; + } + + tzx_cas_get_blocks(casdata, caslen); + + logerror("tzx_cas_to_wav_size: %d blocks found\n", block_count); + + if (block_count == 0) + { + logerror("tzx_cas_to_wav_size: no blocks found!\n"); + goto cleanup; + } + + size = tzx_cas_do_work(NULL); + + return size; + +cleanup: + return -1; +} + +static int tzx_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) +{ + INT16 *p = buffer; + int size = 0; + t_scale = 1.0; + size = tzx_cas_do_work(&p); + return size; +} + +static int cdt_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) +{ + INT16 *p = buffer; + int size = 0; + t_scale = (40 / 35); /* scale to 4MHz */ + size = tzx_cas_do_work(&p); + return size; +} + +static int tap_cas_to_wav_size( const UINT8 *casdata, int caslen ) +{ + int size = 0; + const UINT8 *p = casdata; + + while (p < casdata + caslen) + { + int data_size = p[0] + (p[1] << 8); + int pilot_length = (p[2] == 0x00) ? 8064 : 3220; /* TZX specification */ +// int pilot_length = (p[2] == 0x00) ? 8063 : 3223; /* worldofspectrum */ + logerror("tap_cas_to_wav_size: Handling TAP block containing 0x%X bytes", data_size); + p += 2; + size += tzx_cas_handle_block(NULL, p, 1000, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8); + logerror(", total size is now: %d\n", size); + p += data_size; + } + return size; +} + +static int tap_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) +{ + INT16 *p = buffer; + int size = 0; + + while (size < length) + { + int data_size = bytes[0] + (bytes[1] << 8); + int pilot_length = (bytes[2] == 0x00) ? 8064 : 3220; /* TZX specification */ +// int pilot_length = (bytes[2] == 0x00) ? 8063 : 3223; /* worldofspectrum */ + logerror("tap_cas_fill_wave: Handling TAP block containing 0x%X bytes\n", data_size); + bytes += 2; + size += tzx_cas_handle_block(&p, bytes, 1000, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8); + bytes += data_size; + } + return size; +} + +static const struct CassetteLegacyWaveFiller tzx_legacy_fill_wave = +{ + tzx_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + tzx_cas_to_wav_size, /* chunk_sample_calc */ + TZX_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static const struct CassetteLegacyWaveFiller tap_legacy_fill_wave = +{ + tap_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + tap_cas_to_wav_size, /* chunk_sample_calc */ + TZX_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static const struct CassetteLegacyWaveFiller cdt_legacy_fill_wave = +{ + cdt_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + tzx_cas_to_wav_size, /* chunk_sample_calc */ + TZX_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t tzx_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) +{ + return cassette_legacy_identify(cassette, opts, &tzx_legacy_fill_wave); +} + +static casserr_t tap_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) +{ + return cassette_legacy_identify(cassette, opts, &tap_legacy_fill_wave); +} + +static casserr_t cdt_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) +{ + return cassette_legacy_identify(cassette, opts, &cdt_legacy_fill_wave); +} + +static casserr_t tzx_cassette_load( cassette_image *cassette ) +{ + return cassette_legacy_construct(cassette, &tzx_legacy_fill_wave); +} + +static casserr_t tap_cassette_load( cassette_image *cassette ) +{ + return cassette_legacy_construct(cassette, &tap_legacy_fill_wave); +} + +static casserr_t cdt_cassette_load( cassette_image *cassette ) +{ + return cassette_legacy_construct(cassette, &cdt_legacy_fill_wave); +} + +static const struct CassetteFormat tzx_cassette_format = +{ + "tzx", + tzx_cassette_identify, + tzx_cassette_load, + NULL +}; + +static const struct CassetteFormat tap_cassette_format = +{ + "tap,blk", + tap_cassette_identify, + tap_cassette_load, + NULL +}; + +static const struct CassetteFormat cdt_cassette_format = +{ + "cdt", + cdt_cassette_identify, + cdt_cassette_load, + NULL +}; + +CASSETTE_FORMATLIST_START(tzx_cassette_formats) + CASSETTE_FORMAT(tzx_cassette_format) + CASSETTE_FORMAT(tap_cassette_format) +CASSETTE_FORMATLIST_END + +CASSETTE_FORMATLIST_START(cdt_cassette_formats) + CASSETTE_FORMAT(cdt_cassette_format) +CASSETTE_FORMATLIST_END + diff --git a/src/lib/formats/tzx_cas.h b/src/lib/formats/tzx_cas.h new file mode 100644 index 00000000000..5f8c4caf661 --- /dev/null +++ b/src/lib/formats/tzx_cas.h @@ -0,0 +1,16 @@ +/* + + tzx_cas.h + + Format code for tzx cassette files +*/ + +#ifndef TZX_CAS_H +#define TZX_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(tzx_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(cdt_cassette_formats); + +#endif /* TZX_CAS_H */ diff --git a/src/lib/formats/uef_cas.c b/src/lib/formats/uef_cas.c new file mode 100644 index 00000000000..f049346eeae --- /dev/null +++ b/src/lib/formats/uef_cas.c @@ -0,0 +1,333 @@ +/* + +UEF format support (for electron driver) + +The UEF format supports gzipped images, i'm doing the gunzip step during uef_cas_to_wav_size +because that is when the length of the orignal file is known. This is needed to determine +the size of memory to alloc for the decoding. + +Not nice, but it works... + +*/ + +#include <string.h> +#include <math.h> + +#include "zlib.h" +#include "uef_cas.h" + + +#define UEF_WAV_FREQUENCY 4800 +#define WAVE_LOW -32768 +#define WAVE_HIGH 32767 +#define WAVE_NULL 0 + +static const UINT8 UEF_HEADER[10] = { 0x55, 0x45, 0x46, 0x20, 0x46, 0x69, 0x6c, 0x65, 0x21, 0x00 }; + +/* + bytes are stored as + start bit 1 * 0 + data bits 8 * X + stop bit 1 * 1 +*/ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +static const UINT8* skip_gz_header( const UINT8 *p ) { + UINT8 method, flags; + /* skip initial 1f 8b header */ + p += 2; + /* get method and flags */ + method = *p; p++; + flags = *p; p++; + if ( method != Z_DEFLATED || ( flags & RESERVED ) != 0 ) { + return NULL; + } + /* Skip time, xflags and OS code */ + p += 6; + + /* Skip the extra field */ + if ( ( flags & EXTRA_FIELD ) != 0 ) { + int len = ( p[1] << 8 ) | p[0]; + p += 2 + len; + } + /* Skip the original file name */ + if ( ( flags & ORIG_NAME ) != 0 ) { + for ( ; *p; p++ ); + } + /* Skip the .gz file comment */ + if ( ( flags & COMMENT ) != 0 ) { + for( ; *p; p++ ); + } + /* Skip the header crc */ + if ( ( flags & HEAD_CRC ) != 0 ) { + p += 2; + } + return p; +} + +static UINT8 *gz_ptr = NULL; + +static float get_uef_float( const UINT8 *Float) +{ + int Mantissa; + float Result; + int Exponent; + + /* assume a four byte array named Float exists, where Float[0] + was the first byte read from the UEF, Float[1] the second, etc */ + + /* decode mantissa */ + Mantissa = Float[0] | (Float[1] << 8) | ((Float[2]&0x7f)|0x80) << 16; + + Result = (float)Mantissa; + Result = (float)ldexp(Result, -23); + + /* decode exponent */ + Exponent = ((Float[2]&0x80) >> 7) | (Float[3]&0x7f) << 1; + Exponent -= 127; + Result = (float)ldexp(Result, Exponent); + + /* flip sign if necessary */ + if(Float[3]&0x80) + Result = -Result; + + /* floating point number is now in 'Result' */ + return Result; +} + +static int uef_cas_to_wav_size( const UINT8 *casdata, int caslen ) { + int pos, size; + + if ( casdata[0] == 0x1f && casdata[1] == 0x8b ) { + int err; + z_stream d_stream; + int inflate_size = ( casdata[ caslen - 1 ] << 24 ) | ( casdata[ caslen - 2 ] << 16 ) | ( casdata[ caslen - 3 ] << 8 ) | casdata[ caslen - 4 ]; + const UINT8 *in_ptr = skip_gz_header( casdata ); + + if ( in_ptr == NULL ) { + goto cleanup; + } + gz_ptr = (UINT8 *)malloc( inflate_size ); + + d_stream.zalloc = 0; + d_stream.zfree = 0; + d_stream.opaque = 0; + d_stream.next_in = (unsigned char *)in_ptr; + d_stream.avail_in = caslen - ( in_ptr - casdata ); + d_stream.next_out = gz_ptr; + d_stream.avail_out = inflate_size; + + err = inflateInit2( &d_stream, -MAX_WBITS ); + if ( err != Z_OK ) { + logerror( "inflateInit2 error: %d\n", err ); + goto cleanup; + } + err = inflate( &d_stream, Z_NO_FLUSH ); + if ( err != Z_STREAM_END && err != Z_OK ) { + logerror( "inflate error: %d\n", err ); + goto cleanup; + } + err = inflateEnd( &d_stream ); + if ( err != Z_OK ) { + logerror( "inflateEnd error: %d\n", err ); + goto cleanup; + } + caslen = inflate_size; + casdata = gz_ptr; + } + + if ( caslen < 18 ) { + logerror( "uef_cas_to_wav_size: cassette image too small\n" ); + goto cleanup; + } + if ( memcmp( casdata, UEF_HEADER, sizeof(UEF_HEADER) ) ) { + logerror( "uef_cas_to_wav_size: cassette image has incompatible header\n" ); + goto cleanup; + } + + logerror( "UEF: Determinig tape size\n" ); + size = 0; + pos = sizeof(UEF_HEADER) + 2; + while( pos < caslen ) { + int chunk_type = ( casdata[pos+1] << 8 ) | casdata[pos]; + int chunk_length = ( casdata[pos+5] << 24 ) | ( casdata[pos+4] << 16 ) | ( casdata[pos+3] << 8 ) | casdata[pos+2]; + int baud_length; + + pos += 6; + switch( chunk_type ) { + case 0x0100: /* implicit start/stop bit data block */ + size += ( chunk_length * 10 ) * 4; + break; + case 0x0101: /* multiplexed data block */ + case 0x0103: + logerror( "Unsupported chunk type: %04x\n", chunk_type ); + break; + case 0x0102: /* explicit tape data block */ + size += ( ( chunk_length * 10 ) - casdata[pos] ) * 4; + break; + case 0x0104: + logerror( "Unsupported chunk type: %04x\n", chunk_type ); + break; + case 0x0110: + baud_length = ( casdata[pos+1] << 8 ) | casdata[pos]; + size += baud_length * 2; + break; + case 0x0111: + logerror( "Unsupported chunk type: %04x\n", chunk_type ); + break; + case 0x0112: + baud_length = ( casdata[pos+1] << 8 ) | casdata[pos]; + size += baud_length * 2 ; + break; + case 0x0116: + size += get_uef_float(casdata+pos)*UEF_WAV_FREQUENCY; + break; + case 0x0113: + case 0x0114: + case 0x0115: + case 0x0120: + logerror( "Unsupported chunk type: %04x\n", chunk_type ); + break; + } + pos += chunk_length; + } + size = 2 * size; + return size; + +cleanup: + if ( gz_ptr ) { + free( gz_ptr ); + gz_ptr = NULL; + } + return -1; +} + +static INT16* uef_cas_fill_bit( INT16 *buffer, int bit ) { + if ( bit ) { + *buffer = WAVE_LOW; buffer++; + *buffer = WAVE_HIGH; buffer++; + *buffer = WAVE_LOW; buffer++; + *buffer = WAVE_HIGH; buffer++; + } else { + *buffer = WAVE_LOW; buffer++; + *buffer = WAVE_LOW; buffer++; + *buffer = WAVE_HIGH; buffer++; + *buffer = WAVE_HIGH; buffer++; + } + return buffer; +} + +static int uef_cas_fill_wave( INT16 *buffer, int length, UINT8 *bytes ) { + int pos; + INT16 *p = buffer; + + if ( bytes[0] == 0x1f && bytes[1] == 0x8b ) { + if ( gz_ptr == NULL ) { + return 1; + } + bytes = gz_ptr; + } + + pos = sizeof(UEF_HEADER) + 2; + length = length / 2; + while( length > 0 ) { + int chunk_type = ( bytes[pos+1] << 8 ) | bytes[pos]; + int chunk_length = ( bytes[pos+5] << 24 ) | ( bytes[pos+4] << 16 ) | ( bytes[pos+3] << 8 ) | bytes[pos+2]; + + int baud_length, i, j; + UINT8 *c; + pos += 6; + switch( chunk_type ) { + case 0x0100: /* implicit start/stop bit data block */ + for( j = 0; j < chunk_length; j++ ) { + UINT8 byte = bytes[pos+j]; + p = uef_cas_fill_bit( p, 0 ); + for( i = 0; i < 8; i++ ) { + p = uef_cas_fill_bit( p, byte & 1 ); + byte = byte >> 1; + } + p = uef_cas_fill_bit( p, 1 ); + length -= ( 10 * 4 ); + } + break; + case 0x0101: /* multiplexed data block */ + case 0x0103: + logerror( "Unsupported chunk type: %04x\n", chunk_type ); + break; + case 0x0102: /* explicit tape data block */ + j = ( chunk_length * 10 ) - bytes[pos]; + c = bytes + pos; + while( j ) { + UINT8 byte = *c; + for( i = 0; i < 8 && i < j; i++ ) { + p = uef_cas_fill_bit( p, byte & 1 ); + byte = byte >> 1; + j--; + } + c++; + } + break; + case 0x0110: + for( baud_length = ( ( bytes[pos+1] << 8 ) | bytes[pos] ) ; baud_length; baud_length-- ) { + *p = WAVE_LOW; p++; + *p = WAVE_HIGH; p++; + length -= 2; + } + break; + case 0x0112: + for( baud_length = ( ( bytes[pos+1] << 8 ) | bytes[pos] ) ; baud_length; baud_length-- ) { + *p = WAVE_NULL; p++; + *p = WAVE_NULL; p++; + length -= 2; + } + break; + case 0x0116: + for( baud_length = (get_uef_float(bytes+pos)*UEF_WAV_FREQUENCY); baud_length; baud_length-- ) { + *p = WAVE_NULL; p++; + length -= 1; + } + break; + + + } + pos += chunk_length; + } + return p - buffer; +} + +static const struct CassetteLegacyWaveFiller uef_legacy_fill_wave = { + uef_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + uef_cas_to_wav_size, /* chunk_sample_calc */ + UEF_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t uef_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) { + return cassette_legacy_identify( cassette, opts, &uef_legacy_fill_wave ); +} + +static casserr_t uef_cassette_load( cassette_image *cassette ) { + return cassette_legacy_construct( cassette, &uef_legacy_fill_wave ); +} + +const struct CassetteFormat uef_cassette_format = { + "uef", + uef_cassette_identify, + uef_cassette_load, + NULL +}; + +CASSETTE_FORMATLIST_START(uef_cassette_formats) + CASSETTE_FORMAT(uef_cassette_format) +CASSETTE_FORMATLIST_END + diff --git a/src/lib/formats/uef_cas.h b/src/lib/formats/uef_cas.h new file mode 100644 index 00000000000..6c77d63a9a3 --- /dev/null +++ b/src/lib/formats/uef_cas.h @@ -0,0 +1,16 @@ +/* + uef_cas.h + + Format code for uef cassette files +*/ + +#ifndef UEF_CAS_H +#define UEF_CAS_H + +#include "cassimg.h" + +extern const struct CassetteFormat uef_cassette_format; + +CASSETTE_FORMATLIST_EXTERN(uef_cassette_formats); + +#endif /* UEF_CAS_H */ diff --git a/src/lib/formats/vg5k_cas.c b/src/lib/formats/vg5k_cas.c new file mode 100644 index 00000000000..68ae6b4a026 --- /dev/null +++ b/src/lib/formats/vg5k_cas.c @@ -0,0 +1,234 @@ +/******************************************************************** + + Support for VG-5000 .k7 cassette images + +********************************************************************/ +#include "vg5k_cas.h" + + +#define SMPLO -32768 +#define SILENCE 0 +#define SMPHI 32767 + + +static int k7_size; + +/******************************************************************* + Generate one high-low cycle of sample data +********************************************************************/ +INLINE int vg5k_cas_cycle(INT16 *buffer, int sample_pos, int len) +{ + int i = 0; + + if (buffer) + { + while( i < len) + { + buffer[ sample_pos + i ] = SMPHI; + i++; + } + + while( i < len * 2 ) + { + buffer[ sample_pos + i ] = SMPLO; + i++; + } + } + return len * 2; +} + + +/******************************************************************* + Generate n samples of silence +********************************************************************/ +INLINE int vg5k_cas_silence(INT16 *buffer, int sample_pos, int len) +{ + int i = 0; + + if ( buffer ) + for( i = 0; i < len; i++) + buffer[ sample_pos + i ] = SILENCE; + + return len; +} + + +/******************************************************************* + Generate the end-byte samples +********************************************************************/ +INLINE int vg5k_cas_eob(INT16 *buffer, int sample_pos) +{ + int i, samples = 0; + + for (i = 0; i < 4; i++) + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5); + + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 10); + + return samples; +} + + +INLINE int vg5k_cas_byte(INT16 *buffer, int sample_pos, UINT8 data) +{ +/* Writing an entire byte */ + int i, samples; + + samples = 0; + for ( i = 0; i < 8; i++ ) + { + if ( data & 0x01 ) + { + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5 ); + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5 ); + } + else + { + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 10 ); + } + + data >>= 1; + } + return samples; +} + + +/******************************************************************* + Generate n sample of synchro +********************************************************************/ +INLINE int vg5k_k7_synchro(INT16 *buffer, int sample_pos, int len) +{ + + int i, samples = 0; + + for ( i = 0; i < len ; i++ ) + samples += vg5k_cas_cycle( buffer, sample_pos + samples, 5); + + samples += vg5k_cas_eob( buffer, sample_pos + samples); + + return samples; +} + + +static int vg5k_handle_tap(INT16 *buffer, const UINT8 *casdata) +{ + int data_pos, sample_count; + + data_pos = 0; + sample_count = 0; + + /* file has to start with an head block */ + if (casdata[0] != 0xd3 || casdata[1] != 0xd3 || casdata[2] != 0xd3) + return -1; + + /* on the entire file*/ + while( data_pos < k7_size ) + { + UINT16 block_size = 0; + + /* Identify type of block */ + if (casdata[data_pos] == 0xd3) + { + /* head block have fixed size of 32 byte */ + block_size = 0x20; + + /* 1 sec of silence before the head block */ + sample_count += vg5k_cas_silence(buffer, sample_count, 44100); + + /* head block starts with 30000 samples of synchro */ + sample_count += vg5k_k7_synchro( buffer, sample_count, 30000 ); + } + else if (casdata[data_pos] == 0xd6) + { + /* data block size is defined in head block */ + block_size = (casdata[data_pos - 4] | casdata[data_pos - 3]<<8) + 20; + + /* 10000 samples of silence before the data block */ + sample_count += vg5k_cas_silence(buffer, sample_count, 10000); + + /* data block starts with 7200 samples of synchro */ + sample_count += vg5k_k7_synchro( buffer, sample_count, 7200); + } + else + { + /* tries to handle files that do not respect the size declared in the head block */ + while (data_pos < k7_size && casdata[data_pos] != 0xd3 && casdata[data_pos] != 0xd6) + data_pos++; + } + + /* Data samples */ + for ( ; block_size ; data_pos++, block_size-- ) + { + /* Make sure there are enough bytes left */ + if (data_pos > k7_size) + return -1; + + sample_count += vg5k_cas_byte( buffer, sample_count, casdata[data_pos] ); + + /* generate the end-byte samples */ + sample_count += vg5k_cas_eob( buffer, sample_count); + } + } + + /* Finish with 10000 samples of silence */ + sample_count += vg5k_cas_silence(buffer, sample_count, 10000); + + return sample_count; +} + + +/******************************************************************* + Generate samples for the tape image +********************************************************************/ +static int vg5k_k7_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + return vg5k_handle_tap(buffer, bytes); +} + + +/******************************************************************* + Calculate the number of samples needed for this tape image classical +********************************************************************/ +static int vg5k_k7_to_wav_size(const UINT8 *casdata, int caslen) +{ + k7_size = caslen ; + + return vg5k_handle_tap( NULL, casdata ); +} + + +static const struct CassetteLegacyWaveFiller vg5k_legacy_fill_wave = +{ + vg5k_k7_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + vg5k_k7_to_wav_size, /* chunk_sample_calc */ + 44100, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t vg5k_k7_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &vg5k_legacy_fill_wave); +} + + +static casserr_t vg5k_k7_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &vg5k_legacy_fill_wave); +} + + +static const struct CassetteFormat vg5k_k7_format = +{ + "k7", + vg5k_k7_identify, + vg5k_k7_load, + NULL +}; + + +CASSETTE_FORMATLIST_START(vg5k_cassette_formats) + CASSETTE_FORMAT(vg5k_k7_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/vg5k_cas.h b/src/lib/formats/vg5k_cas.h new file mode 100644 index 00000000000..2686893a31b --- /dev/null +++ b/src/lib/formats/vg5k_cas.h @@ -0,0 +1,15 @@ +/********************************************************************* + + vg5k_cas.h + +*********************************************************************/ + +#ifndef VG5K_CAS_H +#define VG5K_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(vg5k_cassette_formats); + +#endif /* VG5K_CAS_H */ + diff --git a/src/lib/formats/vt_cas.c b/src/lib/formats/vt_cas.c new file mode 100644 index 00000000000..0923b6c3213 --- /dev/null +++ b/src/lib/formats/vt_cas.c @@ -0,0 +1,222 @@ +#include "formats/vt_cas.h" + +/********************************************************************* + vtech 1/2 agnostic +*********************************************************************/ + +#define SILENCE 8000 + +static int generic_fill_wave(INT16 *buffer, int length, UINT8 *code, int bitsamples, int bytesamples, int lo, INT16 *(*fill_wave_byte)(INT16 *buffer, int byte)) +{ + static int nullbyte; + + if( code == CODE_HEADER ) + { + int i; + + if( length < SILENCE ) + return -1; + for( i = 0; i < SILENCE; i++ ) + *buffer++ = 0; + + nullbyte = 0; + + return SILENCE; + } + + if( code == CODE_TRAILER ) + { + int i; + + /* silence at the end */ + for( i = 0; i < length; i++ ) + *buffer++ = 0; + return length; + } + + if( length < bytesamples ) + return -1; + + buffer = fill_wave_byte(buffer, *code); + + if( !nullbyte && *code == 0 ) + { + int i; + for( i = 0; i < 2*bitsamples; i++ ) + *buffer++ = lo; + nullbyte = 1; + return bytesamples + 2 * bitsamples; + } + + return bytesamples; +} + + +/********************************************************************* + vtech 1 +*********************************************************************/ + +#define V1_LO -32768 +#define V1_HI +32767 + +#define V1_BITSAMPLES 6 +#define V1_BYTESAMPLES 8*V1_BITSAMPLES + +static INT16 *vtech1_fill_wave_byte(INT16 *buffer, int byte) +{ + int i; + + for( i = 7; i >= 0; i-- ) + { + *buffer++ = V1_HI; /* initial cycle */ + *buffer++ = V1_LO; + if( (byte >> i) & 1 ) + { + *buffer++ = V1_HI; /* two more cycles */ + *buffer++ = V1_LO; + *buffer++ = V1_HI; + *buffer++ = V1_LO; + } + else + { + *buffer++ = V1_HI; /* one slow cycle */ + *buffer++ = V1_HI; + *buffer++ = V1_LO; + *buffer++ = V1_LO; + } + } + return buffer; +} + +static int vtech1_cassette_fill_wave(INT16 *buffer, int length, UINT8 *code) +{ + return generic_fill_wave(buffer, length, code, V1_BITSAMPLES, V1_BYTESAMPLES, V1_LO, vtech1_fill_wave_byte); +} + +static const struct CassetteLegacyWaveFiller vtech1_legacy_fill_wave = +{ + vtech1_cassette_fill_wave, /* fill_wave */ + 1, /* chunk_size */ + V1_BYTESAMPLES, /* chunk_samples */ + NULL, /* chunk_sample_calc */ + 600*V1_BITSAMPLES, /* sample_frequency */ + 600*V1_BITSAMPLES, /* header_samples */ + 600*V1_BITSAMPLES /* trailer_samples */ +}; + +static casserr_t vtech1_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &vtech1_legacy_fill_wave); +} + +static casserr_t vtech1_cas_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &vtech1_legacy_fill_wave); +} + +static const struct CassetteFormat vtech1_cas_format = +{ + "cas", + vtech1_cas_identify, + vtech1_cas_load, + NULL +}; + +CASSETTE_FORMATLIST_START(vtech1_cassette_formats) + CASSETTE_FORMAT(vtech1_cas_format) +CASSETTE_FORMATLIST_END + +/********************************************************************* + vtech 2 +*********************************************************************/ + +#define VT2_LO -20000 +#define VT2_HI +20000 + +#define VT2_BITSAMPLES 18 +#define VT2_BYTESAMPLES 8*VT2_BITSAMPLES + +static const INT16 vtech2_bit0[VT2_BITSAMPLES] = +{ + /* short cycle, long cycles */ + VT2_HI,VT2_HI,VT2_HI,VT2_LO,VT2_LO,VT2_LO, + VT2_HI,VT2_HI,VT2_HI,VT2_HI,VT2_HI,VT2_HI, + VT2_LO,VT2_LO,VT2_LO,VT2_LO,VT2_LO,VT2_LO +}; + +static const INT16 vtech2_bit1[VT2_BITSAMPLES] = +{ + /* three short cycle */ + VT2_HI,VT2_HI,VT2_HI,VT2_LO,VT2_LO,VT2_LO, + VT2_HI,VT2_HI,VT2_HI,VT2_LO,VT2_LO,VT2_LO, + VT2_HI,VT2_HI,VT2_HI,VT2_LO,VT2_LO,VT2_LO +}; + + +static INT16 *vtech2_fill_wave_bit(INT16 *buffer, int bit) +{ + int i; + if( bit ) + { + for( i = 0; i < VT2_BITSAMPLES; i++ ) + *buffer++ = vtech2_bit1[i]; + } + else + { + for( i = 0; i < VT2_BITSAMPLES; i++ ) + *buffer++ = vtech2_bit0[i]; + } + return buffer; +} + + +static INT16 *vtech2_fill_wave_byte(INT16 *buffer, int byte) +{ + buffer = vtech2_fill_wave_bit(buffer, (byte >> 7) & 1); + buffer = vtech2_fill_wave_bit(buffer, (byte >> 6) & 1); + buffer = vtech2_fill_wave_bit(buffer, (byte >> 5) & 1); + buffer = vtech2_fill_wave_bit(buffer, (byte >> 4) & 1); + buffer = vtech2_fill_wave_bit(buffer, (byte >> 3) & 1); + buffer = vtech2_fill_wave_bit(buffer, (byte >> 2) & 1); + buffer = vtech2_fill_wave_bit(buffer, (byte >> 1) & 1); + buffer = vtech2_fill_wave_bit(buffer, (byte >> 0) & 1); + return buffer; +} + +static int vtech2_cassette_fill_wave(INT16 *buffer, int length, UINT8 *code) +{ + return generic_fill_wave(buffer, length, code, VT2_BITSAMPLES, VT2_BYTESAMPLES, VT2_LO, vtech2_fill_wave_byte); +} + +static const struct CassetteLegacyWaveFiller vtech2_legacy_fill_wave = +{ + vtech2_cassette_fill_wave, /* fill_wave */ + 1, /* chunk_size */ + VT2_BYTESAMPLES, /* chunk_samples */ + NULL, /* chunk_sample_calc */ + 600*VT2_BITSAMPLES, /* sample_frequency */ + 600*VT2_BITSAMPLES, /* header_samples */ + 600*VT2_BITSAMPLES /* trailer_samples */ +}; + +static casserr_t vtech2_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &vtech2_legacy_fill_wave); +} + +static casserr_t vtech2_cas_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &vtech2_legacy_fill_wave); +} + +static const struct CassetteFormat vtech2_cas_format = +{ + "cas", + vtech2_cas_identify, + vtech2_cas_load, + NULL +}; + +CASSETTE_FORMATLIST_START(vtech2_cassette_formats) + CASSETTE_FORMAT(vtech2_cas_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/vt_cas.h b/src/lib/formats/vt_cas.h new file mode 100644 index 00000000000..2dae9a2ad33 --- /dev/null +++ b/src/lib/formats/vt_cas.h @@ -0,0 +1,17 @@ +/********************************************************************* + + vt_cas.h + + Format code for VTech casette files + +*********************************************************************/ + +#ifndef VT_CAS_H +#define VT_CAS_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(vtech1_cassette_formats); +CASSETTE_FORMATLIST_EXTERN(vtech2_cassette_formats); + +#endif /* VT_CAS_H */ diff --git a/src/lib/formats/vt_dsk.c b/src/lib/formats/vt_dsk.c new file mode 100644 index 00000000000..a14d6fea1d9 --- /dev/null +++ b/src/lib/formats/vt_dsk.c @@ -0,0 +1,54 @@ +/********************************************************************* + + formats/vt_dsk.c + + VTech Laser/VZ disk images + +*********************************************************************/ + +#include <stdio.h> +#include <string.h> +#include "formats/vt_dsk.h" +#include "formats/basicdsk.h" + +static FLOPPY_IDENTIFY(vz_identify) +{ + UINT64 size = floppy_image_size(floppy); + *vote = ((size == 98560) || (size == 99200) || (size == 99184)) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + +static FLOPPY_CONSTRUCT(vz_construct) +{ + struct basicdsk_geometry geometry; + memset(&geometry, 0, sizeof(geometry)); + + if (params) + { + geometry.heads = option_resolution_lookup_int(params, PARAM_HEADS); + geometry.tracks = option_resolution_lookup_int(params, PARAM_TRACKS); + geometry.sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + geometry.first_sector_id = option_resolution_lookup_int(params, PARAM_FIRST_SECTOR_ID); + geometry.sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); + } + else + { + geometry.heads = 1; + geometry.tracks = 40; + geometry.sectors = 16; + geometry.first_sector_id = 0; + geometry.sector_length = floppy_image_size(floppy)/geometry.tracks/geometry.sectors; + } + + return basicdsk_construct(floppy, &geometry); +} + + +FLOPPY_OPTIONS_START(vz) + FLOPPY_OPTION(vtech1, "dsk", "Laser/VZ disk image", vz_identify, vz_construct, NULL, + HEADS([1]) + TRACKS([40]) + SECTORS([16]) + SECTOR_LENGTH([154]) + FIRST_SECTOR_ID([0])) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/vt_dsk.h b/src/lib/formats/vt_dsk.h new file mode 100644 index 00000000000..4fce0f9cbb5 --- /dev/null +++ b/src/lib/formats/vt_dsk.h @@ -0,0 +1,16 @@ +/********************************************************************* + + formats/vt_dsk.h + + Tandy Color Computer / Dragon disk images + +*********************************************************************/ + +#ifndef VT_DSK_H +#define VT_DSK_H + +#include "flopimg.h" + +FLOPPY_OPTIONS_EXTERN(vz); + +#endif /* VT_DSK_H */ diff --git a/src/emu/imagedev/wavfile.c b/src/lib/formats/wavfile.c index aa8d17e8a74..aa8d17e8a74 100644 --- a/src/emu/imagedev/wavfile.c +++ b/src/lib/formats/wavfile.c diff --git a/src/emu/imagedev/wavfile.h b/src/lib/formats/wavfile.h index ab769de5537..ab769de5537 100644 --- a/src/emu/imagedev/wavfile.h +++ b/src/lib/formats/wavfile.h diff --git a/src/lib/formats/x1_tap.c b/src/lib/formats/x1_tap.c new file mode 100644 index 00000000000..8bd35b2c202 --- /dev/null +++ b/src/lib/formats/x1_tap.c @@ -0,0 +1,127 @@ +/* + * + * Sharp X1 TAP image format + * + * "New" header format: + * 0x00: Index - must be "TAPE" (4 bytes) + * 0x04: Image title. (null-terminated string, 17 bytes) + * 0x15: Reserved (5 bytes) + * 0x1a: Write protect (bit 4, 1 byte) + * 0x1b: Format (bit 0, 1 byte) + * if bit 0 is high, uses "speed limit sampling method" + * 0x1c: Sample rate, per bit (in Hz, 4 bytes) + * 0x20: Tape data size, in bits (4 bytes) + * 0x24: Tape position (4 bytes, usually 0) + * 0x28: Tape data (data size / 8) + * + * "Old" header format: + * 0x00: Sampling rate (4 bytes) + * + */ + +#include "x1_tap.h" + +#define WAVE_HIGH 0x5a9e +#define WAVE_LOW -0x5a9e + +static int cas_size; +static int samplerate; + +static int x1_fill_wave(INT16* buffer, UINT8 data, int sample_pos) +{ + int x; + int sample_count = 0; + + // one byte = 8 samples + for(x=0;x<8;x++) + { + if(buffer) + buffer[sample_pos+x] = (data & (1 << (7-x))) ? WAVE_HIGH : WAVE_LOW; + sample_count++; + } + + return sample_count; +} + +static int x1_handle_tap(INT16* buffer, const UINT8* casdata) +{ + int sample_count = 0; + int data_pos = 0x28; + + if(memcmp(casdata, "TAPE",4)) // header check + { + logerror("TAP: image is not a 'new' format TAP image\n"); + return -1; + } + if(samplerate != 8000) + { + logerror("TAP: images that are not 8000Hz are not yet supported\n"); + return -1; + } + + while(sample_count < cas_size) + { + sample_count += x1_fill_wave(buffer,casdata[data_pos],sample_count); + data_pos++; + } + + return sample_count; +} + +/******************************************************************* + Calculate the number of samples needed for this tape image +********************************************************************/ +static int x1_cas_to_wav_size (const UINT8 *casdata, int caslen) +{ + UINT32 ret; + + ret = casdata[0x20] | (casdata[0x21] << 8) | (casdata[0x22] << 16) | (casdata[0x23] << 24); + cas_size = ret; + + samplerate = casdata[0x1c] | (casdata[0x1d] << 8) | (casdata[0x1e] << 16) | (casdata[0x1f] << 24); + + return ret; +} + +/******************************************************************* + Generate samples for the tape image +********************************************************************/ +static int x1_cas_fill_wave(INT16 *buffer, int sample_count, UINT8 *bytes) +{ + return x1_handle_tap(buffer,bytes); +} + +static const struct CassetteLegacyWaveFiller x1_legacy_fill_wave = +{ + x1_cas_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + x1_cas_to_wav_size, /* chunk_sample_calc */ + 8000, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t x1_cas_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &x1_legacy_fill_wave); +} + + + +static casserr_t x1_cas_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &x1_legacy_fill_wave); +} + + +static const struct CassetteFormat x1_cassette_format = { + "tap", + x1_cas_identify, + x1_cas_load, + NULL +}; + +CASSETTE_FORMATLIST_START(x1_cassette_formats) + CASSETTE_FORMAT(x1_cassette_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/x1_tap.h b/src/lib/formats/x1_tap.h new file mode 100644 index 00000000000..94d9a671fd8 --- /dev/null +++ b/src/lib/formats/x1_tap.h @@ -0,0 +1,14 @@ +/* + * + * Sharp X1 TAP image format + * + */ + +#ifndef X1_TAP_H_ +#define X1_TAP_H_ + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN( x1_cassette_formats ); + +#endif /*X1_TAP_H_*/ diff --git a/src/lib/formats/z80ne_dsk.c b/src/lib/formats/z80ne_dsk.c new file mode 100644 index 00000000000..c35af51fa4d --- /dev/null +++ b/src/lib/formats/z80ne_dsk.c @@ -0,0 +1,847 @@ +/********************************************************************* + + formats/z80ne_dsk.c + + Nuova Elettronica Z80NE disk images + +*********************************************************************/ + +#include "z80ne_dsk.h" +#include "basicdsk.h" +#include "imageutl.h" + +/* ----------------------------------------------------------------------- + * DMK file format + * + * David M. Keil's disk image format is aptly called an 'on disk' image + * format. This means that whatever written to the disk is encoded into + * the image file. IDAMS, sector headers, trailing CRCs, and intra-sector + * spacing. * + * + * HEADER DESCRIPTION: + * + * Offset Bytes Field Description + * ------ ----- ------------ ----------- + * 0 1 write_prot 0xff = Write Protected, 0x00 = R/W + * 1 1 tracks Number of tracks + * 2 2 track_length Bytes per track on DMK file (little endian) + * 4 1 disk_options Miscellaneous flags + * bit 0-3: Unused + * bit 4: Sides on DMK file + * 1=single, 0=double + * bit 5: Unused + * bit 6: Single Density DMK file + * 1=single (bytes written once + * on IDAMs) + * 0=double (bytes written + * two times for SD IDAMs) + * 5 7 reserved Reserved for future use + * 12 4 real_disk_code If this is 0x12345678 (little endian) + * then access a real disk drive + * (unsupported) + * + * Each track begins with a track TOC, consisting of 64 little endian 16-bit + * integers. Each integer has the following format: + * bit 0-13: Offset from beginning of track to 'FE' byte of IDAM + * Note these are always sorted from first to last. All empty + * entries are 0x00 + * bit 14: Undefined (reserved) + * bit 15: Sector double density (0=SD 1=DD) + * If not set and SD DMK flag is set then bytes are written + * once. + * ----------------------------------------------------------------------- */ + +struct dmk_tag +{ + int heads; + int tracks; + UINT32 track_size; +}; + +/* DMK file structure + * See WD1711 documentation + */ +/* DMK file header */ +#define DMK_HEADER_LEN 16 /* DMK header */ + /* For every track (40) */ + #define DMK_TOC_LEN 128 /* 64 16-bit relative offsets to IDAMs */ + #define DMK_GAP1_LEN 32 /* GAP1 (26 bytes 0xFF and 6 bytes 0x00) */ + + /* For every sector (10) 301 bytes */ + #define DMK_IDAM_LEN 7 /* IDAM (0xFE, Track, 0x00, Sector, 0x00, CRChi, CRClo) */ + #define DMK_ID_GAP_LEN 17 /* GAP2 (11 bytes 0xFF and 6 bytes 0x00) */ + #define DMK_DAM_LEN 1 /* Data Address Mark (0xFB) */ + #define DMK_DATA_LEN 256 /* Data */ + #define DMK_DATA_CRC_LEN 2 /* Data CRC (CRChi, CRClo) */ + #define DMK_DATA_GAP_LEN 18 /* GAP3 (12 bytes 0xFF and 6 bytes 0x00) (not for last sector) */ + + #define DMK_EXTRA_TRACK_LENGTH (176) /* GAP4 (192 bytes 0xFF) */ +/* End of DMK file structure */ + +#define dmk_idam_type(x) (x)[0] +#define dmk_idam_track(x) (x)[1] +#define dmk_idam_side(x) (x)[2] +#define dmk_idam_sector(x) (x)[3] +#define dmk_idam_sectorlength(x) (x)[4] +#define dmk_idam_crc(x) (((x)[5] << 8) + (x)[6]) +#define dmk_idam_set_crc(x, crc) (x)[5] = ((crc) >> 8); (x)[6] = ((crc) >> 0); + +static const char needle_data[] = "\x00\x00\x00\x00\x00\x00\x00\x00\xFB\xFB"; +static const char needle_deleted_data_fa[] = "\x00\x00\x00\x00\x00\x00\x00\x00\xFA\xFA"; +static const char needle_deleted_data_f8[] = "\x00\x00\x00\x00\x00\x00\x00\x00\xF8\xF8"; + + +static struct dmk_tag *get_dmk_tag(floppy_image *floppy) +{ + return (dmk_tag *)floppy_tag(floppy); +} + + +static floperr_t z80ne_dmk_get_track_offset(floppy_image *floppy, int head, int track, UINT64 *offset) +{ + struct dmk_tag *tag = get_dmk_tag(floppy); + + if ((head < 0) || (head >= tag->heads) || (track < 0) || (track >= tag->tracks)) + return FLOPPY_ERROR_SEEKERROR; + + *offset = track; + *offset *= tag->heads; + *offset += head; + *offset *= tag->track_size; + *offset += DMK_HEADER_LEN; + return FLOPPY_ERROR_SUCCESS; +} + + +static UINT32 z80ne_dmk_min_track_size(int sectors, int sector_length) +{ + int sector_physical_length; + sector_physical_length = (DMK_IDAM_LEN + DMK_ID_GAP_LEN + DMK_DAM_LEN + sector_length + DMK_DATA_CRC_LEN + DMK_DATA_GAP_LEN); + return DMK_TOC_LEN + 2 * (DMK_GAP1_LEN + (sectors * sector_physical_length) - DMK_DATA_GAP_LEN + DMK_EXTRA_TRACK_LENGTH); +} + + + +static floperr_t z80ne_dmk_read_track(floppy_image *floppy, int head, int track, UINT64 offset, void *buffer, size_t buflen) +{ + floperr_t err; + UINT64 track_offset; + + err = z80ne_dmk_get_track_offset(floppy, head, track, &track_offset); + if (err) + return err; + + floppy_image_read(floppy, buffer, offset + track_offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t z80ne_dmk_write_track(floppy_image *floppy, int head, int track, UINT64 offset, const void *buffer, size_t buflen) +{ + floperr_t err; + UINT64 track_offset; + + err = z80ne_dmk_get_track_offset(floppy, head, track, &track_offset); + if (err) + return err; + + floppy_image_write(floppy, buffer, offset + track_offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t z80ne_dmk_get_track_data_offset(floppy_image *floppy, int head, int track, UINT64 *offset) +{ + *offset = DMK_TOC_LEN + 1; + return FLOPPY_ERROR_SUCCESS; +} + + +static floperr_t z80ne_dmk_format_track(floppy_image *floppy, int head, int track, option_resolution *params) +{ + int sectors; + int sector_length; + int interleave; + int first_sector_id; + floperr_t err; + int physical_sector; + int logical_sector; + int track_position; + UINT16 idam_offset; + UINT16 crc; + UINT8 *track_data; + void *track_data_v; + UINT32 max_track_size; + int *sector_map = NULL; + UINT8 *local_sector = NULL; + int local_sector_size; + int sector_position; + int i; + + err = FLOPPY_ERROR_SUCCESS; + + sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); + interleave = option_resolution_lookup_int(params, PARAM_INTERLEAVE); + first_sector_id = option_resolution_lookup_int(params, PARAM_FIRST_SECTOR_ID); + + max_track_size = get_dmk_tag(floppy)->track_size; + + if (sectors > DMK_TOC_LEN/2) + { + err = FLOPPY_ERROR_INTERNAL; + goto done; + } + + if (max_track_size < z80ne_dmk_min_track_size(sectors, sector_length)) + { + err = FLOPPY_ERROR_NOSPACE; + goto done; + } + + err = floppy_load_track(floppy, head, track, TRUE, &track_data_v, NULL); + if (err) + goto done; + track_data = (UINT8 *) track_data_v; + + /* set up sector map */ + sector_map = (int*)malloc(sectors * sizeof(*sector_map)); + if (!sector_map) + { + err = FLOPPY_ERROR_OUTOFMEMORY; + goto done; + } + memset(sector_map, 0xFF, sectors * sizeof(*sector_map)); + + physical_sector = 0; + for (logical_sector = 0; logical_sector < sectors; logical_sector++) + { + while(sector_map[physical_sector] >= 0) + { + physical_sector++; + physical_sector %= sectors; + } + + sector_map[physical_sector] = logical_sector + first_sector_id; + physical_sector += interleave + 1; + physical_sector %= sectors; + } + + /* set up a local physical sector template */ + local_sector_size = (DMK_IDAM_LEN + + DMK_ID_GAP_LEN + + DMK_DAM_LEN + + sector_length + + DMK_DATA_CRC_LEN + + DMK_DATA_GAP_LEN); + local_sector = (UINT8*)malloc(local_sector_size); + if (!local_sector) + { + err = FLOPPY_ERROR_OUTOFMEMORY; + goto done; + } + memset(local_sector, 0x00, local_sector_size); + + /* set up track table of contents */ + physical_sector = 0; + + /* write GAP1 (track lead in) */ + memset(&track_data[DMK_TOC_LEN], 0xFF, 52); + memset(&track_data[DMK_TOC_LEN+52], 0x00, 12); + track_position = DMK_TOC_LEN + DMK_GAP1_LEN * 2; + + /* + * prepare template in local sector + */ + + /* IDAM */ + sector_position = 0; + dmk_idam_type( &local_sector[sector_position]) = 0xFE; + dmk_idam_track( &local_sector[sector_position]) = track; + dmk_idam_side( &local_sector[sector_position]) = head; + dmk_idam_sector( &local_sector[sector_position]) = 0; /* replace in sector instances */ + dmk_idam_sectorlength( &local_sector[sector_position]) = compute_log2(sector_length / 128); + dmk_idam_set_crc( &local_sector[sector_position], 0); /* replace in sector instances */ + sector_position += 7; + + /* GAP2 (ID GAP) */ + memset(&local_sector[sector_position], 0xFF, 11); + sector_position += 11; + memset(&local_sector[sector_position], 0x00, 6); + sector_position += 6; + + /* sector body */ + local_sector[sector_position] = 0xFB; + memset(&local_sector[sector_position + 1], floppy_get_filler(floppy), sector_length); + crc = ccitt_crc16(0xffff, &local_sector[sector_position], sector_length + 1); + local_sector[sector_position + sector_length + 1] = (UINT8) (crc >> 8); + local_sector[sector_position + sector_length + 2] = (UINT8) (crc >> 0); + sector_position += sector_length + 3; + + /* GAP3 (Data GAP) */ + memset(&local_sector[sector_position], 0xFF, 12); + sector_position += 12; + memset(&local_sector[sector_position], 0x00, 6); + sector_position += 6; + + /* + * start of track data + */ + while(physical_sector < DMK_TOC_LEN/2) + { + if (physical_sector >= sectors) + { + /* no more sectors */ + idam_offset = 0; + } + else + { + /* this is a sector */ + idam_offset = track_position; + logical_sector = sector_map[physical_sector]; + + /* update IDAM in local sector template */ + dmk_idam_sector( &local_sector[0]) = logical_sector; /* update sector number in sector instance */ + crc = ccitt_crc16(0xffff, &local_sector[0], DMK_IDAM_LEN - 2); /* and recalculate crc */ + dmk_idam_set_crc( &local_sector[0], crc); + + /* write local sector in track doubling every byte (DMK double density mode) */ + for(i=0; i<local_sector_size; i++) + track_data[track_position + i*2] = track_data[track_position + (i*2) + 1] = local_sector[i]; + track_position += (2 * local_sector_size); + if (physical_sector == (sectors-1)) /* on last sector remove GAP3 */ + track_position -= (2 * DMK_DATA_GAP_LEN); + } + + /* write the TOC entry */ + track_data[physical_sector * 2 + 0] = (UINT8) (idam_offset >> 0); + track_data[physical_sector * 2 + 1] = (UINT8) (idam_offset >> 8); + + physical_sector++; + } + + /* write track footer GAP4 */ + //assert(max_track_size >= (UINT32)track_position); + memset(&track_data[track_position], 0xFF, max_track_size - track_position); + +done: + if (sector_map) + free(sector_map); + if (local_sector) + free(local_sector); + + return err; +} + + + +static int z80ne_dmk_get_heads_per_disk(floppy_image *floppy) +{ + return get_dmk_tag(floppy)->heads; +} + + + +static int z80ne_dmk_get_tracks_per_disk(floppy_image *floppy) +{ + return get_dmk_tag(floppy)->tracks; +} + + + +static UINT32 z80ne_dmk_get_track_size(floppy_image *floppy, int head, int track) +{ + return get_dmk_tag(floppy)->track_size; +} + +static UINT8 * my_memmem(UINT8 *haystack, size_t haystacklen, + UINT8 *needle, size_t needlelen) +{ + register UINT8 *h = haystack; + register UINT8 *n = needle; + register size_t hl = haystacklen; + register size_t nl = needlelen; + register size_t i; + + if (nl == 0) return haystack; /* The first occurrence of the empty string is deemed to occur at + the beginning of the string. */ + if (hl < nl) + return NULL; + + for( i = 0; (i < hl) && (i + nl <= hl ); i++ ) + if (h[i] == *n) /* first match */ + if ( !memcmp(&h[i]+1, n + 1, nl - 1) ) + return (haystack+i); /* returns a pointer to the substring */ + + return (UINT8 *)NULL; /* not found */ +} + + + +static floperr_t z80ne_dmk_seek_sector_in_track(floppy_image *floppy, int head, int track, int sector, int sector_is_index, int dirtify, UINT8 **sector_data, UINT32 *sector_length) +{ + struct dmk_tag *tag = get_dmk_tag(floppy); + floperr_t err; + size_t idam_offset = 0; + UINT16 calculated_crc; + size_t i, j; + size_t offs; + /*int state;*/ + UINT8 *track_data; + void *track_data_v; + size_t track_length; + size_t sec_len; + UINT8 *local_idam = NULL; + int local_idam_size; + UINT8 *sec_data; + + + err = FLOPPY_ERROR_SUCCESS; + + err = floppy_load_track(floppy, head, track, dirtify, &track_data_v, &track_length); + if (err) + goto done; + track_data = (UINT8 *) track_data_v; + + /* set up a local IDAM space */ + local_idam_size = (DMK_IDAM_LEN + + DMK_ID_GAP_LEN + + DMK_DAM_LEN + + DMK_DATA_GAP_LEN); + local_idam = (UINT8*)malloc(local_idam_size); + if (!local_idam) + { + err = FLOPPY_ERROR_OUTOFMEMORY; + goto done; + } + memset(local_idam, 0x00, local_idam_size); + + /* search for matching IDAM */ + for (i = 0; i < DMK_TOC_LEN / 2; i++) + { + idam_offset = track_data[i * 2 + 1]; + idam_offset <<= 8; + idam_offset |= track_data[i * 2 + 0]; + idam_offset &= 0x3FFF; + + if (idam_offset == 0) + { + /* we've reached the end of the road */ + i = DMK_TOC_LEN; + break; + } + + if ((idam_offset + DMK_IDAM_LEN * 2) >= tag->track_size) + continue; + + /* overflowing the track? */ + if ((idam_offset + local_idam_size * 2) >= tag->track_size) + { + err = FLOPPY_ERROR_SEEKERROR; + goto done; + } + + /* create a local copy of sector of IDAM */ + for(j = 0; j < (DMK_IDAM_LEN + DMK_ID_GAP_LEN + DMK_DAM_LEN); j++) + local_idam[j] = track_data[idam_offset + j*2]; + + /* from now on all operations are done in local copy of IDAM */ + calculated_crc = ccitt_crc16(0xffff, &local_idam[0], DMK_IDAM_LEN - 2); + + /* check IDAM integrity and check for matching sector */ + if (calculated_crc == dmk_idam_crc(&local_idam[0])) + { + if (sector_is_index) + { + /* the sector is indexed; decrement the index and go */ + if (sector-- == 0) + break; + } + else + { + /* check for matching sector ID */ + if (sector == dmk_idam_sector(&local_idam[0])) + break; + } + } + } + + if (i >= DMK_TOC_LEN / 2) + { + err = FLOPPY_ERROR_SEEKERROR; + goto done; + } + + /* we found a matching sector ID */ + /*state = 0;*/ + offs = idam_offset + 2 * DMK_IDAM_LEN; + + /* find pattern 0x0000FB; this represents the start of a data sector */ + sec_data = my_memmem(&track_data[offs], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_data, 10); + if(!sec_data) + { + /* find pattern 0x0000FA; this represents the start of a deleted data sector (used for system sectors) */ + sec_data = my_memmem(&track_data[offs], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_deleted_data_fa, 10); + } + if(!sec_data) + { + /* find pattern 0x0000F8; this represents the start of a deleted data sector (used for system sectors) */ + sec_data = my_memmem(&track_data[offs], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_deleted_data_f8, 10); + } + + if(sec_data) + sec_data += 10; + else + { + err = FLOPPY_ERROR_SEEKERROR; + goto done; + } + offs = sec_data - &track_data[offs]; + + sec_len = 128 << dmk_idam_sectorlength(&local_idam[0]); + + if ((offs + 2 * sec_len) > track_length) + { + err = FLOPPY_ERROR_INVALIDIMAGE; + goto done; + } + + if (sector_data) + *sector_data = sec_data; + if (sector_length) + *sector_length = sec_len; +done: + if (local_idam) + free(local_idam); + return err; +} + + + +static floperr_t z80ne_dmk_get_sector_length(floppy_image *floppy, int head, int track, int sector, UINT32 *sector_length) +{ + return z80ne_dmk_seek_sector_in_track(floppy, head, track, sector, FALSE, FALSE, NULL, sector_length); +} + + + +static floperr_t z80ne_dmk_get_indexed_sector_info(floppy_image *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, UINT32 *sector_length, unsigned long *flags) +{ + floperr_t err; + size_t idam_offset; + UINT8 *track_data; + void *track_data_v; + unsigned long dam; + + if (sector_index * 2 >= DMK_TOC_LEN) + return FLOPPY_ERROR_SEEKERROR; + + err = floppy_load_track(floppy, head, track, FALSE, &track_data_v, NULL); + if (err) + return err; + track_data = (UINT8 *) track_data_v; + + idam_offset = track_data[sector_index * 2 + 1]; + idam_offset <<= 8; + idam_offset |= track_data[sector_index * 2 + 0]; + idam_offset &= 0x3FFF; + + if (idam_offset == 0) + return FLOPPY_ERROR_SEEKERROR; + + /* determine type of data sector */ + /* find pattern 0x0000FB; this represents the start of a data sector */ + if ( my_memmem(&track_data[idam_offset+14], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_data, 10) ) + dam = 0; + /* find pattern 0x0000FA; this represents the start of a deleted data sector (used for system sectors) */ + else if ( my_memmem(&track_data[idam_offset+14], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_deleted_data_fa, 10) ) + dam = ID_FLAG_DELETED_DATA; + /* find pattern 0x0000F8; this represents the start of a deleted data sector (used for system sectors) */ + else if ( my_memmem(&track_data[idam_offset+14], 4 * (DMK_ID_GAP_LEN + DMK_DAM_LEN), (UINT8 *)needle_deleted_data_f8, 10) ) + dam = ID_FLAG_DELETED_DATA; + else + return FLOPPY_ERROR_SEEKERROR; + + if (cylinder) + *cylinder = (&track_data[idam_offset])[2]; + if (side) + *side = (&track_data[idam_offset])[4]; + if (sector) + *sector = (&track_data[idam_offset])[6]; + if (sector_length) + *sector_length = 128 << (&track_data[idam_offset])[8]; + if (flags) + *flags = dam; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_z80ne_dmk_read_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen) +{ + floperr_t err; + UINT32 sector_length; + UINT16 crc_on_disk; + UINT16 calculated_crc; + UINT8 *sector_data = NULL; + UINT8 *local_sector = NULL; + int local_sector_size; + int i; + + err = FLOPPY_ERROR_SUCCESS; + + /* get sector length and data pointer */ + err = z80ne_dmk_seek_sector_in_track(floppy, head, track, sector, sector_is_index, FALSE, §or_data, §or_length); + if (err) + goto done; + + /* set up a local physical sector space (DAM + data + crc + GAP) */ + local_sector_size = (DMK_DAM_LEN + sector_length + DMK_DATA_CRC_LEN + DMK_DATA_GAP_LEN); + local_sector = (UINT8*)malloc(local_sector_size); + if (!local_sector) + { + err = FLOPPY_ERROR_OUTOFMEMORY; + goto done; + } + memset(local_sector, 0x00, local_sector_size); + + /* get sector data */ + /* create a local copy of sector data including DAM (for crc calculation) */ + for(i = 0; i < (DMK_DAM_LEN + sector_length + DMK_DATA_CRC_LEN); i++) + local_sector[i] = (sector_data - 2)[i*2]; + + crc_on_disk = local_sector[sector_length + 0 + 1]; + crc_on_disk <<= 8; + crc_on_disk += local_sector[sector_length + 1 + 1]; + + /* crc calculated including DAM */ + calculated_crc = ccitt_crc16(0xffff, local_sector, sector_length+1); + if (calculated_crc != crc_on_disk) + { + err = FLOPPY_ERROR_INVALIDIMAGE; + goto done; + } + + /* copy local sector data excluding DAM */ + memcpy(buffer, local_sector+1, MIN(sector_length, buflen)); + + done: + if (local_sector) + free(local_sector); + return err; +} + + + +static floperr_t internal_z80ne_dmk_write_sector(floppy_image *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam) +{ + floperr_t err; + UINT32 sector_length; + UINT8 *sector_data; + UINT16 crc; + UINT8 *local_sector = NULL; + int local_sector_size; + int i; + + err = FLOPPY_ERROR_SUCCESS; + + /* get sector length */ + err = z80ne_dmk_seek_sector_in_track(floppy, head, track, sector, sector_is_index, TRUE, §or_data, §or_length); + if (err) + goto done; + + /* set up a local physical sector space */ + local_sector_size = (DMK_DAM_LEN + sector_length + DMK_DATA_CRC_LEN + DMK_DATA_GAP_LEN); + local_sector = (UINT8*)malloc(local_sector_size); + if (!local_sector) + { + err = FLOPPY_ERROR_OUTOFMEMORY; + goto done; + } + memset(local_sector, 0x00, local_sector_size); + if(!ddam) + local_sector[0] = 0xFB; + else + local_sector[0] = 0xFA; + + if (buflen > sector_length) + { + err = FLOPPY_ERROR_INTERNAL; + goto done; + } + + memcpy(&local_sector[1], buffer, buflen); + + crc = ccitt_crc16(0xffff, local_sector, DMK_DAM_LEN + sector_length); + local_sector[DMK_DAM_LEN + sector_length + 0] = crc >> 8; + local_sector[DMK_DAM_LEN + sector_length + 1] = crc >> 0; + + /* write local sector in track doubling every byte (DMK double density mode) */ + /* write DAM or DDAM too */ + sector_data -= 2; + for(i=0; i < (DMK_DAM_LEN + sector_length + DMK_DATA_CRC_LEN); i++) + sector_data[i*2] = sector_data[(i*2)+1] = local_sector[i]; + + done: + if (local_sector) + free(local_sector); + return err; +} + + + +static floperr_t z80ne_dmk_read_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_z80ne_dmk_read_sector(floppy, head, track, sector, FALSE, buffer, buflen); +} + +static floperr_t z80ne_dmk_write_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_z80ne_dmk_write_sector(floppy, head, track, sector, FALSE, buffer, buflen, ddam); +} + +static floperr_t z80ne_dmk_read_indexed_sector(floppy_image *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_z80ne_dmk_read_sector(floppy, head, track, sector, TRUE, buffer, buflen); +} + +static floperr_t z80ne_dmk_write_indexed_sector(floppy_image *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_z80ne_dmk_write_sector(floppy, head, track, sector, TRUE, buffer, buflen, ddam); +} + + + +static void z80ne_dmk_interpret_header(floppy_image *floppy, int *heads, int *tracks, int * sectors, int *track_size) +{ + UINT8 header[DMK_HEADER_LEN + DMK_TOC_LEN]; + int i; + int idam_offset; + int sects; + + floppy_image_read(floppy, header, 0, DMK_HEADER_LEN + DMK_TOC_LEN); + + if (tracks) + *tracks = header[1]; + if (heads) + *heads = (header[4] & 0x10) ? 1 : 2; + if (track_size) + *track_size = ((int) header[3]) * 0x100 + header[2]; + if(sectors) + { + /* count sectors on first track */ + sects = 0; + for (i = 0; i < DMK_TOC_LEN / 2; i++) + { + idam_offset = header[DMK_HEADER_LEN + i * 2 + 1]; + idam_offset <<= 8; + idam_offset |= header[DMK_HEADER_LEN + i * 2 + 0]; + idam_offset &= 0x3FFF; + + if (idam_offset == 0) + break; + else + sects++; + } + *sectors = sects; + } + +} + + + +FLOPPY_CONSTRUCT(z80ne_dmk_construct) +{ + struct FloppyCallbacks *callbacks; + struct dmk_tag *tag; + UINT8 header[DMK_HEADER_LEN]; + int heads, tracks, track_size, sectors, sector_length; + + if (params) + { + heads = option_resolution_lookup_int(params, PARAM_HEADS); + tracks = option_resolution_lookup_int(params, PARAM_TRACKS); + sectors = option_resolution_lookup_int(params, PARAM_SECTORS); + sector_length = option_resolution_lookup_int(params, PARAM_SECTOR_LENGTH); + + track_size = z80ne_dmk_min_track_size(sectors, sector_length); + + memset(header, 0, sizeof(header)); + header[1] = tracks; + header[2] = track_size >> 0; + header[3] = track_size >> 8; + header[4] = (heads == 2) ? 0x00 : 0x10; + + floppy_image_write(floppy, header, 0, sizeof(header)); + } + else + { + z80ne_dmk_interpret_header(floppy, &heads, &tracks, §ors, &track_size); + } + + tag = (dmk_tag*)floppy_create_tag(floppy, sizeof(struct dmk_tag)); + if (!tag) + return FLOPPY_ERROR_OUTOFMEMORY; + tag->heads = heads; + tag->track_size = track_size; + tag->tracks = tracks; + + callbacks = floppy_callbacks(floppy); + callbacks->read_track = z80ne_dmk_read_track; + callbacks->write_track = z80ne_dmk_write_track; + callbacks->get_track_data_offset = z80ne_dmk_get_track_data_offset; + callbacks->format_track = z80ne_dmk_format_track; + callbacks->get_heads_per_disk = z80ne_dmk_get_heads_per_disk; + callbacks->get_tracks_per_disk = z80ne_dmk_get_tracks_per_disk; + callbacks->get_track_size = z80ne_dmk_get_track_size; + callbacks->get_sector_length = z80ne_dmk_get_sector_length; + callbacks->get_indexed_sector_info = z80ne_dmk_get_indexed_sector_info; + callbacks->read_sector = z80ne_dmk_read_sector; + callbacks->write_sector = z80ne_dmk_write_sector; + callbacks->read_indexed_sector = z80ne_dmk_read_indexed_sector; + callbacks->write_indexed_sector = z80ne_dmk_write_indexed_sector; + + return FLOPPY_ERROR_SUCCESS; +} + + + +FLOPPY_IDENTIFY(z80ne_dmk_identify) +{ + int heads, tracks, sectors, track_size; + UINT64 size, expected_size; + + size = floppy_image_size(floppy); + z80ne_dmk_interpret_header(floppy, &heads, &tracks, §ors, &track_size); + expected_size = DMK_HEADER_LEN + (heads * tracks * track_size); + *vote = (size == expected_size) ? 90 : 0; + *vote += (sectors == 10) ? 10 : 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +/* ----------------------------------------------------------------------- */ + +FLOPPY_OPTIONS_START( z80ne ) + /* + * Single side + * Single density + * FM, 125 kbit/s, + * 40 tracks, track ID: 0..39 + * 10 sectors/track, sector ID: 0..9 + * 256 bytes/sector + * Interleave 1 (sector ID sequence on track: 0 5 1 6 2 7 3 8 4 9) + * 3125 byte/track unformatted + * Rotation speed 300 rpm (5 rps) + */ + FLOPPY_OPTION( z80ne_dmk, "zmk", "Z80NE DMK disk image", z80ne_dmk_identify, z80ne_dmk_construct, NULL, + HEADS([1]) + TRACKS([40]) + SECTORS([10]) + SECTOR_LENGTH([256]) + INTERLEAVE([1]) + FIRST_SECTOR_ID([0])) +FLOPPY_OPTIONS_END diff --git a/src/lib/formats/z80ne_dsk.h b/src/lib/formats/z80ne_dsk.h new file mode 100644 index 00000000000..baa8b9cdadf --- /dev/null +++ b/src/lib/formats/z80ne_dsk.h @@ -0,0 +1,22 @@ +/********************************************************************* + + formats/z80ne_dsk.h + + Nuova Elettronica Z80NE disk images + +*********************************************************************/ + +#ifndef Z80NE_DSK_H +#define Z80NE_DSK_H + +#include "flopimg.h" + + +/**************************************************************************/ + +FLOPPY_OPTIONS_EXTERN(z80ne); + +FLOPPY_IDENTIFY(z80ne_dmk_identify); +FLOPPY_CONSTRUCT(z80ne_dmk_construct); + +#endif /* Z80NE_DSK_H */ diff --git a/src/lib/formats/zx81_p.c b/src/lib/formats/zx81_p.c new file mode 100644 index 00000000000..ad813509e4e --- /dev/null +++ b/src/lib/formats/zx81_p.c @@ -0,0 +1,293 @@ +/***************************************************************************** + +Taken from nocash ZX81 docs by Martin Korth. + +ZX81 Cassette File Structure + 5 seconds pilot + 1-127 bytes name (bit7 set in last char) + LEN bytes data, loaded to address 4009h, LEN=(4014h)-4009h. + 1 pulse video retrace signal (only if display was enabled) +The data field contains the system area, the basic program, the video memory, +and VARS area. + +ZX80 Cassette File Structure + 5 seconds pilot + LEN bytes data, loaded to address 4000h, LEN=(400Ah)-4000h. +ZX80 files do not have filenames, and video memory is not included in the file. + +Bits and Bytes +Each byte consists of 8 bits (MSB first) without any start and stop bits, +directly followed by the next byte. A "0" bit consists of four high pulses, +a "1" bit of nine pulses, either one followed by a silence period. + 0: /\/\/\/\________ + 1: /\/\/\/\/\/\/\/\/\________ +Each pulse is split into a 150us High period, and 150us Low period. The +duration of the silence between each bit is 1300us. The baud rate is thus 400 +bps (for a "0" filled area) downto 250 bps (for a "1" filled area). Average +medium transfer rate is approx. 307 bps (38 bytes/sec) for files that contain +50% of "0" and "1" bits each. + +*****************************************************************************/ + +#include "zx81_p.h" + + +#define WAVEENTRY_LOW -32768 +#define WAVEENTRY_HIGH 32767 +#define WAVEENTRY_ZERO 0 + +#define ZX81_WAV_FREQUENCY 44100 + +/* all following are in samples */ +#define ZX81_PULSE_LENGTH 16 +#define ZX81_PAUSE_LENGTH 56 +#define ZX81_PILOT_LENGTH 220500 + +#define ZX81_LOW_BIT_LENGTH (ZX81_PULSE_LENGTH*4+ZX81_PAUSE_LENGTH) +#define ZX81_HIGH_BIT_LENGTH (ZX81_PULSE_LENGTH*9+ZX81_PAUSE_LENGTH) + +#define ZX81_START_LOAD_ADDRESS 0x4009 +#define ZX80_START_LOAD_ADDRESS 0x4000 +#define ZX81_DATA_LENGTH_OFFSET 0x0b +#define ZX80_DATA_LENGTH_OFFSET 0x04 + +static UINT8 zx_file_name[128]; +static UINT16 real_data_length = 0; +static UINT8 zx_file_name_length = 0; + +/* common functions */ + +static INT16 *zx81_emit_level(INT16 *p, int count, int level) +{ + int i; + + for (i=0; i<count; i++) *(p++) = level; + + return p; +} + +static INT16* zx81_emit_pulse(INT16 *p) +{ + p = zx81_emit_level (p, ZX81_PULSE_LENGTH/8, WAVEENTRY_LOW); + p = zx81_emit_level (p, ZX81_PULSE_LENGTH/8, WAVEENTRY_LOW); + p = zx81_emit_level (p, ZX81_PULSE_LENGTH/8, WAVEENTRY_ZERO); + p = zx81_emit_level (p, ZX81_PULSE_LENGTH/8, WAVEENTRY_HIGH); + p = zx81_emit_level (p, ZX81_PULSE_LENGTH/8, WAVEENTRY_HIGH); + p = zx81_emit_level (p, ZX81_PULSE_LENGTH/8, WAVEENTRY_ZERO); + p = zx81_emit_level (p, ZX81_PULSE_LENGTH/8, WAVEENTRY_LOW); + p = zx81_emit_level (p, ZX81_PULSE_LENGTH/8, WAVEENTRY_LOW); + + return p; +} + +static INT16* zx81_emit_pause(INT16 *p) +{ + p = zx81_emit_level (p, ZX81_PAUSE_LENGTH, WAVEENTRY_ZERO); + + return p; +} + +static INT16* zx81_output_bit(INT16 *p, UINT8 bit) +{ + int i; + + if (bit) + for (i=0; i<9; i++) + p = zx81_emit_pulse (p); + else + for (i=0; i<4; i++) + p = zx81_emit_pulse (p); + + p = zx81_emit_pause(p); + + return p; +} + +static INT16* zx81_output_byte(INT16 *p, UINT8 byte) +{ + int i; + + for (i=0; i<8; i++) + p = zx81_output_bit(p,(byte>>(7-i)) & 0x01); + + return p; +} + +static UINT16 zx81_cassette_calculate_number_of_1(const UINT8 *bytes, UINT16 length) +{ + UINT16 number_of_1 = 0; + int i,j; + + for (i=0; i<length; i++) + for (j=0; j<8; j++) + if ((bytes[i]>>j)&0x01) + number_of_1++; + + return number_of_1; +} + +/* ZX-81 functions */ + +static const UINT8 zx81_chars[]={ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*00h-07h*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*08h-0fh*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*10h-17h*/ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /*18h-1fh*/ + 0x00, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x00, 0x00, /*20h-27h*/ + 0x10, 0x11, 0x17, 0x15, 0x1a, 0x16, 0x1b, 0x18, /*28h-2fh*/ + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, /*30h-37h*/ + 0x24, 0x25, 0x0e, 0x19, 0x13, 0x14, 0x12, 0x0f, /*38h-3fh*/ + 0x00, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, /*40h-47h*/ + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, /*48h-4fh*/ + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, /*50h-57h*/ + 0x3d, 0x3e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, /*58h-5fh*/ + 0x00, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, /*60h-67h*/ + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, /*68h-6fh*/ + 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, /*70h-77h*/ + 0x3d, 0x3e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, /*78h-7fh*/ +}; + +static void zx81_fill_file_name(const char* name) +{ + for (zx_file_name_length=0; (zx_file_name_length<128) && name[zx_file_name_length]; zx_file_name_length++) + zx_file_name[zx_file_name_length] = ((UINT8) name[zx_file_name_length]<0x80) ? zx81_chars[(UINT8) name[zx_file_name_length]] : 0x00; + zx_file_name[zx_file_name_length-1] |= 0x80; +} + +static int zx81_cassette_calculate_size_in_samples(const UINT8 *bytes, int length) +{ + unsigned int number_of_0_data = 0; + unsigned int number_of_1_data = 0; + unsigned int number_of_0_name = 0; + unsigned int number_of_1_name = 0; + + real_data_length = bytes[ZX81_DATA_LENGTH_OFFSET] + bytes[ZX81_DATA_LENGTH_OFFSET+1]*256 - ZX81_START_LOAD_ADDRESS; + + number_of_1_data = zx81_cassette_calculate_number_of_1(bytes, real_data_length); + number_of_0_data = length*8-number_of_1_data; + + number_of_1_name = zx81_cassette_calculate_number_of_1(zx_file_name, zx_file_name_length); + number_of_0_name = zx_file_name_length*8-number_of_1_name; + + return (number_of_0_data+number_of_0_name)*ZX81_LOW_BIT_LENGTH + (number_of_1_data+number_of_1_name)*ZX81_HIGH_BIT_LENGTH + ZX81_PILOT_LENGTH; +} + +static int zx81_cassette_fill_wave(INT16 *buffer, int length, UINT8 *bytes) +{ + INT16 * p = buffer; + int i; + + /* pilot */ + p = zx81_emit_level (p, ZX81_PILOT_LENGTH, WAVEENTRY_ZERO); + + /* name */ + for (i=0; i<zx_file_name_length; i++) + p = zx81_output_byte(p, zx_file_name[i]); + + /* data */ + for (i=0; i<real_data_length; i++) + p = zx81_output_byte(p, bytes[i]); + + return p - buffer; +} + +static const struct CassetteLegacyWaveFiller zx81_legacy_fill_wave = +{ + zx81_cassette_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + zx81_cassette_calculate_size_in_samples, /* chunk_sample_calc */ + ZX81_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t zx81_p_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &zx81_legacy_fill_wave); +} + +static casserr_t zx81_p_load(cassette_image *cassette) +{ + /* The filename of the file is used to create the wave stream for the emulated machine. Why is this information not + part of the image file itself? + Hardcoding this to "cassette". + */ + zx81_fill_file_name ("cassette" /*image_basename_noext(device_list_find_by_tag( Machine->config->m_devicelist, CASSETTE, "cassette" ))*/ ); + return cassette_legacy_construct(cassette, &zx81_legacy_fill_wave); +} + +static const struct CassetteFormat zx81_p_image_format = +{ + "p,81", + zx81_p_identify, + zx81_p_load, + NULL +}; + +CASSETTE_FORMATLIST_START(zx81_p_format) + CASSETTE_FORMAT(zx81_p_image_format) +CASSETTE_FORMATLIST_END + +/* ZX-80 functions */ + +static int zx80_cassette_calculate_size_in_samples(const UINT8 *bytes, int length) +{ + unsigned int number_of_0_data = 0; + unsigned int number_of_1_data = 0; + + real_data_length = bytes[ZX80_DATA_LENGTH_OFFSET] + bytes[ZX80_DATA_LENGTH_OFFSET+1]*256 - ZX80_START_LOAD_ADDRESS - 1; + + number_of_1_data = zx81_cassette_calculate_number_of_1(bytes, real_data_length); + number_of_0_data = length*8-number_of_1_data; + + return number_of_0_data*ZX81_LOW_BIT_LENGTH + number_of_1_data*ZX81_HIGH_BIT_LENGTH + ZX81_PILOT_LENGTH; +} + +static int zx80_cassette_fill_wave(INT16 *buffer, int length, UINT8 *bytes) +{ + INT16 * p = buffer; + int i; + + /* pilot */ + p = zx81_emit_level (p, ZX81_PILOT_LENGTH, WAVEENTRY_ZERO); + + /* data */ + for (i=0; i<real_data_length; i++) + p = zx81_output_byte(p, bytes[i]); + + return p - buffer; +} + +static const struct CassetteLegacyWaveFiller zx80_legacy_fill_wave = +{ + zx80_cassette_fill_wave, /* fill_wave */ + -1, /* chunk_size */ + 0, /* chunk_samples */ + zx80_cassette_calculate_size_in_samples, /* chunk_sample_calc */ + ZX81_WAV_FREQUENCY, /* sample_frequency */ + 0, /* header_samples */ + 0 /* trailer_samples */ +}; + +static casserr_t zx80_o_identify(cassette_image *cassette, struct CassetteOptions *opts) +{ + return cassette_legacy_identify(cassette, opts, &zx80_legacy_fill_wave); +} + +static casserr_t zx80_o_load(cassette_image *cassette) +{ + return cassette_legacy_construct(cassette, &zx80_legacy_fill_wave); +} + +static const struct CassetteFormat zx80_o_image_format = +{ + "o,80", + zx80_o_identify, + zx80_o_load, + NULL +}; + +CASSETTE_FORMATLIST_START(zx80_o_format) + CASSETTE_FORMAT(zx80_o_image_format) +CASSETTE_FORMATLIST_END diff --git a/src/lib/formats/zx81_p.h b/src/lib/formats/zx81_p.h new file mode 100644 index 00000000000..3b57517f0c0 --- /dev/null +++ b/src/lib/formats/zx81_p.h @@ -0,0 +1,17 @@ +/********************************************************************* + + zx81_p.h + + Format code for ZX-81 casette files + +*********************************************************************/ + +#ifndef ZX81_P_H +#define ZX81_P_H + +#include "cassimg.h" + +CASSETTE_FORMATLIST_EXTERN(zx81_p_format); +CASSETTE_FORMATLIST_EXTERN(zx80_o_format); + +#endif /* ZX81_P_H */ diff --git a/src/lib/lib.mak b/src/lib/lib.mak index 7f98d1728a8..bcc8e6f397b 100644 --- a/src/lib/lib.mak +++ b/src/lib/lib.mak @@ -16,6 +16,7 @@ LIBOBJ = $(OBJ)/lib OBJDIRS += \ $(LIBOBJ)/util \ $(LIBOBJ)/expat \ + $(LIBOBJ)/formats \ $(LIBOBJ)/zlib \ $(LIBOBJ)/softfloat \ @@ -37,7 +38,6 @@ UTILOBJS = \ $(LIBOBJ)/util/coreutil.o \ $(LIBOBJ)/util/harddisk.o \ $(LIBOBJ)/util/huffman.o \ - $(LIBOBJ)/util/imageutl.o \ $(LIBOBJ)/util/jedparse.o \ $(LIBOBJ)/util/md5.o \ $(LIBOBJ)/util/opresolv.o \ @@ -75,6 +75,85 @@ $(LIBOBJ)/expat/%.o: $(LIBSRC)/explat/%.c | $(OSPREBUILD) #------------------------------------------------- +# formats library objects +#------------------------------------------------- + +EXPATOBJS = \ + $(LIBOBJ)/formats/cassimg.o \ + $(LIBOBJ)/formats/flopimg.o \ + $(LIBOBJ)/formats/imageutl.o \ + $(LIBOBJ)/formats/ioprocs.o \ + $(LIBOBJ)/formats/basicdsk.o \ + $(LIBOBJ)/formats/a26_cas.o \ + $(LIBOBJ)/formats/ace_tap.o \ + $(LIBOBJ)/formats/ami_dsk.o \ + $(LIBOBJ)/formats/ap2_dsk.o \ + $(LIBOBJ)/formats/apf_apt.o \ + $(LIBOBJ)/formats/apridisk.o \ + $(LIBOBJ)/formats/ap_dsk35.o \ + $(LIBOBJ)/formats/atarist_dsk.o \ + $(LIBOBJ)/formats/atom_tap.o \ + $(LIBOBJ)/formats/cbm_tap.o \ + $(LIBOBJ)/formats/cgen_cas.o \ + $(LIBOBJ)/formats/coco_cas.o \ + $(LIBOBJ)/formats/coco_dsk.o \ + $(LIBOBJ)/formats/comx35_dsk.o \ + $(LIBOBJ)/formats/coupedsk.o \ + $(LIBOBJ)/formats/cpis_dsk.o \ + $(LIBOBJ)/formats/cqm_dsk.o \ + $(LIBOBJ)/formats/csw_cas.o \ + $(LIBOBJ)/formats/d64_dsk.o \ + $(LIBOBJ)/formats/d81_dsk.o \ + $(LIBOBJ)/formats/d88_dsk.o \ + $(LIBOBJ)/formats/dim_dsk.o \ + $(LIBOBJ)/formats/dsk_dsk.o \ + $(LIBOBJ)/formats/fdi_dsk.o \ + $(LIBOBJ)/formats/fm7_cas.o \ + $(LIBOBJ)/formats/fmsx_cas.o \ + $(LIBOBJ)/formats/g64_dsk.o \ + $(LIBOBJ)/formats/gtp_cas.o \ + $(LIBOBJ)/formats/hect_tap.o \ + $(LIBOBJ)/formats/imd_dsk.o \ + $(LIBOBJ)/formats/kim1_cas.o \ + $(LIBOBJ)/formats/lviv_lvt.o \ + $(LIBOBJ)/formats/msx_dsk.o \ + $(LIBOBJ)/formats/mz_cas.o \ + $(LIBOBJ)/formats/nes_dsk.o \ + $(LIBOBJ)/formats/orao_cas.o \ + $(LIBOBJ)/formats/oric_dsk.o \ + $(LIBOBJ)/formats/oric_tap.o \ + $(LIBOBJ)/formats/p6001_cas.o \ + $(LIBOBJ)/formats/pc_dsk.o \ + $(LIBOBJ)/formats/pmd_pmd.o \ + $(LIBOBJ)/formats/primoptp.o \ + $(LIBOBJ)/formats/rk_cas.o \ + $(LIBOBJ)/formats/smx_dsk.o \ + $(LIBOBJ)/formats/sorc_dsk.o \ + $(LIBOBJ)/formats/sord_cas.o \ + $(LIBOBJ)/formats/svi_cas.o \ + $(LIBOBJ)/formats/svi_dsk.o \ + $(LIBOBJ)/formats/td0_dsk.o \ + $(LIBOBJ)/formats/thom_cas.o \ + $(LIBOBJ)/formats/thom_dsk.o \ + $(LIBOBJ)/formats/ti99_dsk.o \ + $(LIBOBJ)/formats/trd_dsk.o \ + $(LIBOBJ)/formats/trs_cas.o \ + $(LIBOBJ)/formats/trs_dsk.o \ + $(LIBOBJ)/formats/tzx_cas.o \ + $(LIBOBJ)/formats/uef_cas.o \ + $(LIBOBJ)/formats/vg5k_cas.o \ + $(LIBOBJ)/formats/vt_cas.o \ + $(LIBOBJ)/formats/vt_dsk.o \ + $(LIBOBJ)/formats/wavfile.o \ + $(LIBOBJ)/formats/x1_tap.o \ + $(LIBOBJ)/formats/z80ne_dsk.o \ + $(LIBOBJ)/formats/zx81_p.o \ + + +$(OBJ)/libformats.a: $(EXPATOBJS) + + +#------------------------------------------------- # zlib library objects #------------------------------------------------- diff --git a/src/mame/includes/msx.h b/src/mame/includes/msx.h index 2ad77b23c0f..af376c087b5 100644 --- a/src/mame/includes/msx.h +++ b/src/mame/includes/msx.h @@ -8,7 +8,7 @@ #define __MSX_H__ #include "machine/wd17xx.h" -#include "imagedev/flopimg.h" +#include "formats/flopimg.h" #define MSX_MAX_CARTS (2) diff --git a/src/mame/machine/md_cart.c b/src/mame/machine/md_cart.c index 3b5df422ce4..25ea2af8618 100644 --- a/src/mame/machine/md_cart.c +++ b/src/mame/machine/md_cart.c @@ -45,7 +45,7 @@ #include "emu.h" -#include "imageutl.h" +#include "formats/imageutl.h" #include "cpu/m68000/m68000.h" #include "imagedev/cartslot.h" diff --git a/src/mame/mame.mak b/src/mame/mame.mak index 906efaf7222..e2b4b744eae 100644 --- a/src/mame/mame.mak +++ b/src/mame/mame.mak @@ -18,7 +18,6 @@ DRIVERS = $(MAMEOBJ)/drivers LAYOUT = $(MAMEOBJ)/layout MACHINE = $(MAMEOBJ)/machine VIDEO = $(MAMEOBJ)/video -FORMATS = $(MAMEOBJ)/formats IMAGEDEV= $(MAMEOBJ)/imagedev OBJDIRS += \ @@ -27,7 +26,6 @@ OBJDIRS += \ $(LAYOUT) \ $(MACHINE) \ $(VIDEO) \ - $(FORMATS) \ $(IMAGEDEV) \ @@ -340,7 +338,6 @@ DRVLIBS += \ $(MAMEOBJ)/misc.a \ $(MAMEOBJ)/pinball.a \ $(MAMEOBJ)/shared.a \ - $(MAMEOBJ)/formats.a \ @@ -359,23 +356,6 @@ $(MAMEOBJ)/shared.a: \ $(VIDEO)/avgdvg.o \ #------------------------------------------------- -# file formats, mostly used for MESS software -#------------------------------------------------- - -$(MAMEOBJ)/formats.a: \ - $(FORMATS)/basicdsk.o \ - $(FORMATS)/ami_dsk.o \ - $(FORMATS)/cqm_dsk.o \ - $(FORMATS)/d88_dsk.o \ - $(FORMATS)/dsk_dsk.o \ - $(FORMATS)/fdi_dsk.o \ - $(FORMATS)/fmsx_cas.o \ - $(FORMATS)/imd_dsk.o \ - $(FORMATS)/msx_dsk.o \ - $(FORMATS)/nes_dsk.o \ - $(FORMATS)/td0_dsk.o \ - -#------------------------------------------------- # manufacturer-specific groupings for drivers #------------------------------------------------- |