summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
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
commit62d841c2703286b34983eb8dae174ecbf2c82c5f (patch)
treed97a50172e47372637a155978f3096618da8c275
parent987909ff31107893c4b748d86d97619db48c4b47 (diff)
Moved format handling and all formats to separate library [Miodrag Milanovic]
-rw-r--r--.gitattributes158
-rw-r--r--makefile7
-rw-r--r--src/emu/emu.mak4
-rw-r--r--src/emu/image.c1
-rw-r--r--src/emu/image.h2
-rw-r--r--src/emu/imagedev/cassette.c3
-rw-r--r--src/emu/imagedev/cassette.h2
-rw-r--r--src/emu/imagedev/flopdrv.c2
-rw-r--r--src/emu/imagedev/flopdrv.h2
-rw-r--r--src/emu/machine/wd17xx.c3
-rw-r--r--src/lib/formats/a26_cas.c166
-rw-r--r--src/lib/formats/a26_cas.h14
-rw-r--r--src/lib/formats/ace_tap.c188
-rw-r--r--src/lib/formats/ace_tap.h29
-rw-r--r--src/lib/formats/ami_dsk.c (renamed from src/mame/formats/ami_dsk.c)0
-rw-r--r--src/lib/formats/ami_dsk.h (renamed from src/mame/formats/ami_dsk.h)2
-rw-r--r--src/lib/formats/ap2_dsk.c511
-rw-r--r--src/lib/formats/ap2_dsk.h33
-rw-r--r--src/lib/formats/ap_dsk35.c1187
-rw-r--r--src/lib/formats/ap_dsk35.h25
-rw-r--r--src/lib/formats/apf_apt.c157
-rw-r--r--src/lib/formats/apf_apt.h9
-rw-r--r--src/lib/formats/apridisk.c226
-rw-r--r--src/lib/formats/apridisk.h15
-rw-r--r--src/lib/formats/atarist_dsk.c82
-rw-r--r--src/lib/formats/atarist_dsk.h18
-rw-r--r--src/lib/formats/atom_tap.c138
-rw-r--r--src/lib/formats/atom_tap.h18
-rw-r--r--src/lib/formats/basicdsk.c (renamed from src/mame/formats/basicdsk.c)0
-rw-r--r--src/lib/formats/basicdsk.h (renamed from src/mame/formats/basicdsk.h)2
-rw-r--r--src/lib/formats/cassimg.c (renamed from src/emu/imagedev/cassimg.c)9
-rw-r--r--src/lib/formats/cassimg.h (renamed from src/emu/imagedev/cassimg.h)7
-rw-r--r--src/lib/formats/cbm_tap.c369
-rw-r--r--src/lib/formats/cbm_tap.h16
-rw-r--r--src/lib/formats/cgen_cas.c149
-rw-r--r--src/lib/formats/cgen_cas.h17
-rw-r--r--src/lib/formats/coco_cas.c313
-rw-r--r--src/lib/formats/coco_cas.h20
-rw-r--r--src/lib/formats/coco_dsk.c1199
-rw-r--r--src/lib/formats/coco_dsk.h22
-rw-r--r--src/lib/formats/comx35_dsk.c78
-rw-r--r--src/lib/formats/comx35_dsk.h18
-rw-r--r--src/lib/formats/coupedsk.c488
-rw-r--r--src/lib/formats/coupedsk.h23
-rw-r--r--src/lib/formats/cpis_dsk.c93
-rw-r--r--src/lib/formats/cpis_dsk.h19
-rw-r--r--src/lib/formats/cqm_dsk.c (renamed from src/mame/formats/cqm_dsk.c)4
-rw-r--r--src/lib/formats/csw_cas.c329
-rw-r--r--src/lib/formats/csw_cas.h15
-rw-r--r--src/lib/formats/d64_dsk.c845
-rw-r--r--src/lib/formats/d64_dsk.h26
-rw-r--r--src/lib/formats/d81_dsk.c131
-rw-r--r--src/lib/formats/d81_dsk.h21
-rw-r--r--src/lib/formats/d88_dsk.c (renamed from src/mame/formats/d88_dsk.c)4
-rw-r--r--src/lib/formats/dim_dsk.c101
-rw-r--r--src/lib/formats/dim_dsk.h19
-rw-r--r--src/lib/formats/dsk_dsk.c (renamed from src/mame/formats/dsk_dsk.c)4
-rw-r--r--src/lib/formats/fdi_dsk.c (renamed from src/mame/formats/fdi_dsk.c)4
-rw-r--r--src/lib/formats/flopimg.c (renamed from src/emu/imagedev/flopimg.c)3
-rw-r--r--src/lib/formats/flopimg.h (renamed from src/emu/imagedev/flopimg.h)12
-rw-r--r--src/lib/formats/fm7_cas.c108
-rw-r--r--src/lib/formats/fm7_cas.h12
-rw-r--r--src/lib/formats/fmsx_cas.c (renamed from src/mame/formats/fmsx_cas.c)0
-rw-r--r--src/lib/formats/fmsx_cas.h (renamed from src/mame/formats/fmsx_cas.h)2
-rw-r--r--src/lib/formats/g64_dsk.c295
-rw-r--r--src/lib/formats/g64_dsk.h45
-rw-r--r--src/lib/formats/gtp_cas.c200
-rw-r--r--src/lib/formats/gtp_cas.h15
-rw-r--r--src/lib/formats/hect_tap.c305
-rw-r--r--src/lib/formats/hect_tap.h17
-rw-r--r--src/lib/formats/imageutl.c (renamed from src/lib/util/imageutl.c)0
-rw-r--r--src/lib/formats/imageutl.h (renamed from src/lib/util/imageutl.h)0
-rw-r--r--src/lib/formats/imd_dsk.c (renamed from src/mame/formats/imd_dsk.c)4
-rw-r--r--src/lib/formats/ioprocs.c (renamed from src/emu/ioprocs.c)1
-rw-r--r--src/lib/formats/ioprocs.h (renamed from src/emu/ioprocs.h)1
-rw-r--r--src/lib/formats/kim1_cas.c194
-rw-r--r--src/lib/formats/kim1_cas.h17
-rw-r--r--src/lib/formats/lviv_lvt.c149
-rw-r--r--src/lib/formats/lviv_lvt.h16
-rw-r--r--src/lib/formats/msx_dsk.c (renamed from src/mame/formats/msx_dsk.c)0
-rw-r--r--src/lib/formats/msx_dsk.h (renamed from src/mame/formats/msx_dsk.h)2
-rw-r--r--src/lib/formats/mz_cas.c340
-rw-r--r--src/lib/formats/mz_cas.h16
-rw-r--r--src/lib/formats/nes_dsk.c (renamed from src/mame/formats/nes_dsk.c)0
-rw-r--r--src/lib/formats/nes_dsk.h (renamed from src/mame/formats/nes_dsk.h)2
-rw-r--r--src/lib/formats/orao_cas.c120
-rw-r--r--src/lib/formats/orao_cas.h15
-rw-r--r--src/lib/formats/oric_dsk.c316
-rw-r--r--src/lib/formats/oric_dsk.h18
-rw-r--r--src/lib/formats/oric_tap.c519
-rw-r--r--src/lib/formats/oric_tap.h16
-rw-r--r--src/lib/formats/p6001_cas.c93
-rw-r--r--src/lib/formats/p6001_cas.h12
-rw-r--r--src/lib/formats/pc_dsk.c137
-rw-r--r--src/lib/formats/pc_dsk.h19
-rw-r--r--src/lib/formats/pmd_pmd.c119
-rw-r--r--src/lib/formats/pmd_pmd.h16
-rw-r--r--src/lib/formats/primoptp.c247
-rw-r--r--src/lib/formats/primoptp.h16
-rw-r--r--src/lib/formats/rk_cas.c310
-rw-r--r--src/lib/formats/rk_cas.h24
-rw-r--r--src/lib/formats/smx_dsk.c159
-rw-r--r--src/lib/formats/smx_dsk.h18
-rw-r--r--src/lib/formats/sorc_dsk.c157
-rw-r--r--src/lib/formats/sorc_dsk.h18
-rw-r--r--src/lib/formats/sord_cas.c138
-rw-r--r--src/lib/formats/sord_cas.h16
-rw-r--r--src/lib/formats/svi_cas.c209
-rw-r--r--src/lib/formats/svi_cas.h17
-rw-r--r--src/lib/formats/svi_dsk.c171
-rw-r--r--src/lib/formats/svi_dsk.h18
-rw-r--r--src/lib/formats/td0_dsk.c (renamed from src/mame/formats/td0_dsk.c)10
-rw-r--r--src/lib/formats/thom_cas.c787
-rw-r--r--src/lib/formats/thom_cas.h22
-rw-r--r--src/lib/formats/thom_dsk.c391
-rw-r--r--src/lib/formats/thom_dsk.h14
-rw-r--r--src/lib/formats/ti99_dsk.c2037
-rw-r--r--src/lib/formats/ti99_dsk.h19
-rw-r--r--src/lib/formats/trd_dsk.c50
-rw-r--r--src/lib/formats/trd_dsk.h18
-rw-r--r--src/lib/formats/trs_cas.c134
-rw-r--r--src/lib/formats/trs_cas.h17
-rw-r--r--src/lib/formats/trs_dsk.c77
-rw-r--r--src/lib/formats/trs_dsk.h18
-rw-r--r--src/lib/formats/tzx_cas.c677
-rw-r--r--src/lib/formats/tzx_cas.h16
-rw-r--r--src/lib/formats/uef_cas.c333
-rw-r--r--src/lib/formats/uef_cas.h16
-rw-r--r--src/lib/formats/vg5k_cas.c234
-rw-r--r--src/lib/formats/vg5k_cas.h15
-rw-r--r--src/lib/formats/vt_cas.c222
-rw-r--r--src/lib/formats/vt_cas.h17
-rw-r--r--src/lib/formats/vt_dsk.c54
-rw-r--r--src/lib/formats/vt_dsk.h16
-rw-r--r--src/lib/formats/wavfile.c (renamed from src/emu/imagedev/wavfile.c)0
-rw-r--r--src/lib/formats/wavfile.h (renamed from src/emu/imagedev/wavfile.h)0
-rw-r--r--src/lib/formats/x1_tap.c127
-rw-r--r--src/lib/formats/x1_tap.h14
-rw-r--r--src/lib/formats/z80ne_dsk.c847
-rw-r--r--src/lib/formats/z80ne_dsk.h22
-rw-r--r--src/lib/formats/zx81_p.c293
-rw-r--r--src/lib/formats/zx81_p.h17
-rw-r--r--src/lib/lib.mak81
-rw-r--r--src/mame/includes/msx.h2
-rw-r--r--src/mame/machine/md_cart.c2
-rw-r--r--src/mame/mame.mak20
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
diff --git a/makefile b/makefile
index 634d71c9f5b..c394684bf90 100644
--- a/makefile
+++ b/makefile
@@ -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, &sector_header, pos, 16);
+ type = pick_integer_le(&sector_header, 0, 4);
+
+ while (type == APR_DELETED || type == APR_MAGIC || type == APR_COMMENT || type == APR_CREATOR)
+ {
+ UINT16 compression = pick_integer_le(&sector_header, 4, 2);
+ UINT16 header_size = pick_integer_le(&sector_header, 6, 2);
+ UINT32 data_size = pick_integer_le(&sector_header, 8, 4);
+
+ tag->sectors[cur_sector].head = pick_integer_le(&sector_header, 12, 1);
+ tag->sectors[cur_sector].sector = pick_integer_le(&sector_header, 13, 1);
+ tag->sectors[cur_sector].track = pick_integer_le(&sector_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, &sector_header, pos, 16);
+ type = pick_integer_le(&sector_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, &sector_data, &sector_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, &sector_data, &sector_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, &sectors, &sector_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, &sectors, &sector_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, &sector_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, &gtp_legacy_fill_wave );
+}
+
+
+
+static casserr_t gtp_cassette_load( cassette_image *cassette ) {
+ return cassette_legacy_construct( cassette, &gtp_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, &sector, 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(&sector0[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, &sector_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, &sector_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, &sector_data, &sector_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, &sector_data, &sector_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, &sectors, &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, &sectors, &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
#-------------------------------------------------