summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/formats
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/formats')
-rw-r--r--src/lib/formats/2d_dsk.cpp10
-rw-r--r--src/lib/formats/2d_dsk.h10
-rw-r--r--src/lib/formats/86f_dsk.cpp272
-rw-r--r--src/lib/formats/86f_dsk.h37
-rw-r--r--src/lib/formats/a26_cas.cpp16
-rw-r--r--src/lib/formats/a5105_dsk.cpp12
-rw-r--r--src/lib/formats/a5105_dsk.h8
-rw-r--r--src/lib/formats/abc1600_dsk.cpp79
-rw-r--r--src/lib/formats/abc1600_dsk.h35
-rw-r--r--src/lib/formats/abc800_dsk.cpp52
-rw-r--r--src/lib/formats/abc800_dsk.h10
-rw-r--r--src/lib/formats/abc800i_dsk.cpp122
-rw-r--r--src/lib/formats/abc800i_dsk.h37
-rw-r--r--src/lib/formats/abcfd2_dsk.cpp12
-rw-r--r--src/lib/formats/abcfd2_dsk.h8
-rw-r--r--src/lib/formats/ace_tap.cpp23
-rw-r--r--src/lib/formats/acorn_dsk.cpp525
-rw-r--r--src/lib/formats/acorn_dsk.h127
-rw-r--r--src/lib/formats/adam_cas.cpp28
-rw-r--r--src/lib/formats/adam_dsk.cpp12
-rw-r--r--src/lib/formats/adam_dsk.h8
-rw-r--r--src/lib/formats/afs_dsk.cpp19
-rw-r--r--src/lib/formats/afs_dsk.h16
-rw-r--r--src/lib/formats/agat840k_hle_dsk.cpp5
-rw-r--r--src/lib/formats/agat840k_hle_dsk.h2
-rw-r--r--src/lib/formats/aim_dsk.cpp68
-rw-r--r--src/lib/formats/aim_dsk.h21
-rw-r--r--src/lib/formats/all.cpp1542
-rw-r--r--src/lib/formats/all.h27
-rw-r--r--src/lib/formats/ami_dsk.cpp135
-rw-r--r--src/lib/formats/ami_dsk.h20
-rw-r--r--src/lib/formats/ap2_dsk.cpp1598
-rw-r--r--src/lib/formats/ap2_dsk.h132
-rw-r--r--src/lib/formats/ap_dsk35.cpp1532
-rw-r--r--src/lib/formats/ap_dsk35.h61
-rw-r--r--src/lib/formats/apd_dsk.cpp53
-rw-r--r--src/lib/formats/apd_dsk.h14
-rw-r--r--src/lib/formats/apf_apt.cpp26
-rw-r--r--src/lib/formats/apollo_dsk.cpp23
-rw-r--r--src/lib/formats/apollo_dsk.h9
-rw-r--r--src/lib/formats/applix_dsk.cpp12
-rw-r--r--src/lib/formats/applix_dsk.h8
-rw-r--r--src/lib/formats/apridisk.cpp72
-rw-r--r--src/lib/formats/apridisk.h17
-rw-r--r--src/lib/formats/aquarius_caq.cpp101
-rw-r--r--src/lib/formats/aquarius_caq.h19
-rw-r--r--src/lib/formats/as_dsk.cpp613
-rw-r--r--src/lib/formats/as_dsk.h76
-rw-r--r--src/lib/formats/asst128_dsk.cpp12
-rw-r--r--src/lib/formats/asst128_dsk.h8
-rw-r--r--src/lib/formats/atari_dsk.cpp2
-rw-r--r--src/lib/formats/atari_dsk.h2
-rw-r--r--src/lib/formats/atom_dsk.cpp8
-rw-r--r--src/lib/formats/atom_dsk.h8
-rw-r--r--src/lib/formats/atom_tap.cpp39
-rw-r--r--src/lib/formats/basicdsk.cpp12
-rw-r--r--src/lib/formats/basicdsk.h2
-rw-r--r--src/lib/formats/bk0010_dsk.cpp39
-rw-r--r--src/lib/formats/bk0010_dsk.h30
-rw-r--r--src/lib/formats/bw12_dsk.cpp12
-rw-r--r--src/lib/formats/bw12_dsk.h8
-rw-r--r--src/lib/formats/bw2_dsk.cpp12
-rw-r--r--src/lib/formats/bw2_dsk.h8
-rw-r--r--src/lib/formats/c3040_dsk.cpp16
-rw-r--r--src/lib/formats/c3040_dsk.h16
-rw-r--r--src/lib/formats/c4040_dsk.cpp16
-rw-r--r--src/lib/formats/c4040_dsk.h14
-rw-r--r--src/lib/formats/c8280_dsk.cpp10
-rw-r--r--src/lib/formats/c8280_dsk.h8
-rw-r--r--src/lib/formats/camplynx_cas.cpp37
-rw-r--r--src/lib/formats/camplynx_dsk.cpp12
-rw-r--r--src/lib/formats/camplynx_dsk.h8
-rw-r--r--src/lib/formats/cassimg.cpp910
-rw-r--r--src/lib/formats/cassimg.h337
-rw-r--r--src/lib/formats/cbm_crt.cpp61
-rw-r--r--src/lib/formats/cbm_crt.h6
-rw-r--r--src/lib/formats/cbm_tap.cpp25
-rw-r--r--src/lib/formats/ccvf_dsk.cpp43
-rw-r--r--src/lib/formats/ccvf_dsk.h16
-rw-r--r--src/lib/formats/cd90_640_dsk.cpp56
-rw-r--r--src/lib/formats/cd90_640_dsk.h32
-rw-r--r--src/lib/formats/cgen_cas.cpp16
-rw-r--r--src/lib/formats/cgenie_dsk.cpp14
-rw-r--r--src/lib/formats/cgenie_dsk.h14
-rw-r--r--src/lib/formats/coco_cas.cpp82
-rw-r--r--src/lib/formats/coco_rawdsk.cpp45
-rw-r--r--src/lib/formats/coco_rawdsk.h33
-rw-r--r--src/lib/formats/comx35_dsk.cpp12
-rw-r--r--src/lib/formats/comx35_dsk.h8
-rw-r--r--src/lib/formats/concept_dsk.cpp65
-rw-r--r--src/lib/formats/concept_dsk.h18
-rw-r--r--src/lib/formats/coupedsk.cpp42
-rw-r--r--src/lib/formats/coupedsk.h16
-rw-r--r--src/lib/formats/cp68_dsk.cpp296
-rw-r--r--src/lib/formats/cp68_dsk.h31
-rw-r--r--src/lib/formats/cpis_dsk.cpp10
-rw-r--r--src/lib/formats/cpis_dsk.h8
-rw-r--r--src/lib/formats/cqm_dsk.cpp78
-rw-r--r--src/lib/formats/cqm_dsk.h18
-rw-r--r--src/lib/formats/csw_cas.cpp408
-rw-r--r--src/lib/formats/csw_cas.h2
-rw-r--r--src/lib/formats/d64_dsk.cpp95
-rw-r--r--src/lib/formats/d64_dsk.h40
-rw-r--r--src/lib/formats/d71_dsk.cpp12
-rw-r--r--src/lib/formats/d71_dsk.h8
-rw-r--r--src/lib/formats/d80_dsk.cpp27
-rw-r--r--src/lib/formats/d80_dsk.h20
-rw-r--r--src/lib/formats/d81_dsk.cpp14
-rw-r--r--src/lib/formats/d81_dsk.h10
-rw-r--r--src/lib/formats/d82_dsk.cpp12
-rw-r--r--src/lib/formats/d82_dsk.h8
-rw-r--r--src/lib/formats/d88_dsk.cpp137
-rw-r--r--src/lib/formats/d88_dsk.h17
-rw-r--r--src/lib/formats/dcp_dsk.cpp42
-rw-r--r--src/lib/formats/dcp_dsk.h14
-rw-r--r--src/lib/formats/dfi_dsk.cpp115
-rw-r--r--src/lib/formats/dfi_dsk.h15
-rw-r--r--src/lib/formats/dim_dsk.cpp139
-rw-r--r--src/lib/formats/dim_dsk.h19
-rw-r--r--src/lib/formats/dip_dsk.cpp29
-rw-r--r--src/lib/formats/dip_dsk.h14
-rw-r--r--src/lib/formats/dmk_dsk.cpp337
-rw-r--r--src/lib/formats/dmk_dsk.h17
-rw-r--r--src/lib/formats/dmv_dsk.cpp12
-rw-r--r--src/lib/formats/dmv_dsk.h8
-rw-r--r--src/lib/formats/ds9_dsk.cpp66
-rw-r--r--src/lib/formats/ds9_dsk.h26
-rw-r--r--src/lib/formats/dsk_dsk.cpp93
-rw-r--r--src/lib/formats/dsk_dsk.h14
-rw-r--r--src/lib/formats/dvk_mx_dsk.cpp177
-rw-r--r--src/lib/formats/dvk_mx_dsk.h29
-rw-r--r--src/lib/formats/ep64_dsk.cpp12
-rw-r--r--src/lib/formats/ep64_dsk.h8
-rw-r--r--src/lib/formats/esq16_dsk.cpp54
-rw-r--r--src/lib/formats/esq16_dsk.h18
-rw-r--r--src/lib/formats/esq8_dsk.cpp90
-rw-r--r--src/lib/formats/esq8_dsk.h18
-rw-r--r--src/lib/formats/excali64_dsk.cpp12
-rw-r--r--src/lib/formats/excali64_dsk.h8
-rw-r--r--src/lib/formats/fc100_cas.cpp16
-rw-r--r--src/lib/formats/fdd_dsk.cpp39
-rw-r--r--src/lib/formats/fdd_dsk.h14
-rw-r--r--src/lib/formats/fdi_dsk.cpp7
-rw-r--r--src/lib/formats/fdos_dsk.cpp189
-rw-r--r--src/lib/formats/fdos_dsk.h30
-rw-r--r--src/lib/formats/fl1_dsk.cpp57
-rw-r--r--src/lib/formats/fl1_dsk.h32
-rw-r--r--src/lib/formats/flacfile.cpp152
-rw-r--r--src/lib/formats/flacfile.h10
-rw-r--r--src/lib/formats/flex_dsk.cpp1433
-rw-r--r--src/lib/formats/flex_dsk.h66
-rw-r--r--src/lib/formats/flopimg.cpp1961
-rw-r--r--src/lib/formats/flopimg.h544
-rw-r--r--src/lib/formats/flopimg_legacy.cpp877
-rw-r--r--src/lib/formats/flopimg_legacy.h214
-rw-r--r--src/lib/formats/fm7_cas.cpp25
-rw-r--r--src/lib/formats/fmsx_cas.cpp17
-rw-r--r--src/lib/formats/fmtowns_dsk.cpp12
-rw-r--r--src/lib/formats/fmtowns_dsk.h8
-rw-r--r--src/lib/formats/fs_cbmdos.cpp821
-rw-r--r--src/lib/formats/fs_cbmdos.h44
-rw-r--r--src/lib/formats/fs_coco_os9.cpp750
-rw-r--r--src/lib/formats/fs_coco_os9.h47
-rw-r--r--src/lib/formats/fs_coco_rsdos.cpp743
-rw-r--r--src/lib/formats/fs_coco_rsdos.h43
-rw-r--r--src/lib/formats/fs_fat.cpp1353
-rw-r--r--src/lib/formats/fs_fat.h60
-rw-r--r--src/lib/formats/fs_hp98x5.cpp1552
-rw-r--r--src/lib/formats/fs_hp98x5.h88
-rw-r--r--src/lib/formats/fs_hplif.cpp499
-rw-r--r--src/lib/formats/fs_hplif.h43
-rw-r--r--src/lib/formats/fs_isis.cpp1031
-rw-r--r--src/lib/formats/fs_isis.h44
-rw-r--r--src/lib/formats/fs_oric_jasmin.cpp661
-rw-r--r--src/lib/formats/fs_oric_jasmin.h38
-rw-r--r--src/lib/formats/fs_prodos.cpp522
-rw-r--r--src/lib/formats/fs_prodos.h40
-rw-r--r--src/lib/formats/fs_unformatted.cpp158
-rw-r--r--src/lib/formats/fs_unformatted.h67
-rw-r--r--src/lib/formats/fs_vtech.cpp432
-rw-r--r--src/lib/formats/fs_vtech.h38
-rw-r--r--src/lib/formats/fsblk.cpp254
-rw-r--r--src/lib/formats/fsblk.h181
-rw-r--r--src/lib/formats/fsblk_vec.cpp42
-rw-r--r--src/lib/formats/fsblk_vec.h36
-rw-r--r--src/lib/formats/fsd_dsk.cpp38
-rw-r--r--src/lib/formats/fsd_dsk.h16
-rw-r--r--src/lib/formats/fsmeta.cpp130
-rw-r--r--src/lib/formats/fsmeta.h124
-rw-r--r--src/lib/formats/fsmgr.cpp80
-rw-r--r--src/lib/formats/fsmgr.h91
-rw-r--r--src/lib/formats/g64_dsk.cpp130
-rw-r--r--src/lib/formats/g64_dsk.h19
-rw-r--r--src/lib/formats/gtp_cas.cpp15
-rw-r--r--src/lib/formats/guab_dsk.cpp10
-rw-r--r--src/lib/formats/guab_dsk.h10
-rw-r--r--src/lib/formats/h17disk.cpp253
-rw-r--r--src/lib/formats/h17disk.h38
-rw-r--r--src/lib/formats/h8_cas.cpp96
-rw-r--r--src/lib/formats/h8_cas.h19
-rw-r--r--src/lib/formats/hect_dsk.cpp6
-rw-r--r--src/lib/formats/hect_dsk.h2
-rw-r--r--src/lib/formats/hect_tap.cpp33
-rw-r--r--src/lib/formats/hector_minidisc.cpp10
-rw-r--r--src/lib/formats/hector_minidisc.h10
-rw-r--r--src/lib/formats/hp300_dsk.cpp48
-rw-r--r--src/lib/formats/hp300_dsk.h32
-rw-r--r--src/lib/formats/hp_ipc_dsk.cpp12
-rw-r--r--src/lib/formats/hp_ipc_dsk.h8
-rw-r--r--src/lib/formats/hpi_dsk.cpp89
-rw-r--r--src/lib/formats/hpi_dsk.h45
-rw-r--r--src/lib/formats/hti_tape.cpp192
-rw-r--r--src/lib/formats/hti_tape.h104
-rw-r--r--src/lib/formats/hxchfe_dsk.cpp471
-rw-r--r--src/lib/formats/hxchfe_dsk.h64
-rw-r--r--src/lib/formats/hxcmfm_dsk.cpp68
-rw-r--r--src/lib/formats/hxcmfm_dsk.h18
-rw-r--r--src/lib/formats/ibmxdf_dsk.cpp268
-rw-r--r--src/lib/formats/ibmxdf_dsk.h45
-rw-r--r--src/lib/formats/idpart_dsk.cpp53
-rw-r--r--src/lib/formats/idpart_dsk.h32
-rw-r--r--src/lib/formats/imageutl.cpp2
-rw-r--r--src/lib/formats/imageutl.h144
-rw-r--r--src/lib/formats/imd_dsk.cpp258
-rw-r--r--src/lib/formats/imd_dsk.h15
-rw-r--r--src/lib/formats/img_dsk.cpp438
-rw-r--r--src/lib/formats/img_dsk.h54
-rw-r--r--src/lib/formats/ioprocs.cpp215
-rw-r--r--src/lib/formats/ioprocs.h70
-rw-r--r--src/lib/formats/ipf_dsk.cpp369
-rw-r--r--src/lib/formats/ipf_dsk.h80
-rw-r--r--src/lib/formats/iq151_dsk.cpp12
-rw-r--r--src/lib/formats/iq151_dsk.h8
-rw-r--r--src/lib/formats/itt3030_dsk.cpp15
-rw-r--r--src/lib/formats/itt3030_dsk.h8
-rw-r--r--src/lib/formats/jfd_dsk.cpp53
-rw-r--r--src/lib/formats/jfd_dsk.h14
-rw-r--r--src/lib/formats/juku_dsk.cpp18
-rw-r--r--src/lib/formats/juku_dsk.h12
-rw-r--r--src/lib/formats/jvc_dsk.cpp117
-rw-r--r--src/lib/formats/jvc_dsk.h20
-rw-r--r--src/lib/formats/kaypro_dsk.cpp28
-rw-r--r--src/lib/formats/kaypro_dsk.h16
-rw-r--r--src/lib/formats/kc85_dsk.cpp12
-rw-r--r--src/lib/formats/kc85_dsk.h8
-rw-r--r--src/lib/formats/kc_cas.cpp48
-rw-r--r--src/lib/formats/kim1_cas.cpp24
-rw-r--r--src/lib/formats/lviv_lvt.cpp32
-rw-r--r--src/lib/formats/lw30_dsk.cpp213
-rw-r--r--src/lib/formats/lw30_dsk.h29
-rw-r--r--src/lib/formats/m20_dsk.cpp71
-rw-r--r--src/lib/formats/m20_dsk.h18
-rw-r--r--src/lib/formats/m5_dsk.cpp12
-rw-r--r--src/lib/formats/m5_dsk.h8
-rw-r--r--src/lib/formats/mbee_cas.cpp14
-rw-r--r--src/lib/formats/mdos_dsk.cpp300
-rw-r--r--src/lib/formats/mdos_dsk.h45
-rw-r--r--src/lib/formats/mfi_dsk.cpp222
-rw-r--r--src/lib/formats/mfi_dsk.h38
-rw-r--r--src/lib/formats/mfm_hd.cpp18
-rw-r--r--src/lib/formats/mfm_hd.h44
-rw-r--r--src/lib/formats/mm_dsk.cpp23
-rw-r--r--src/lib/formats/mm_dsk.h16
-rw-r--r--src/lib/formats/ms0515_dsk.cpp10
-rw-r--r--src/lib/formats/ms0515_dsk.h8
-rw-r--r--src/lib/formats/msx_dsk.cpp18
-rw-r--r--src/lib/formats/msx_dsk.h10
-rw-r--r--src/lib/formats/mtx_dsk.cpp8
-rw-r--r--src/lib/formats/mtx_dsk.h8
-rw-r--r--src/lib/formats/mz_cas.cpp16
-rw-r--r--src/lib/formats/nabupc_dsk.cpp228
-rw-r--r--src/lib/formats/nabupc_dsk.h53
-rw-r--r--src/lib/formats/nanos_dsk.cpp12
-rw-r--r--src/lib/formats/nanos_dsk.h8
-rw-r--r--src/lib/formats/nascom_dsk.cpp10
-rw-r--r--src/lib/formats/nascom_dsk.h10
-rw-r--r--src/lib/formats/naslite_dsk.cpp12
-rw-r--r--src/lib/formats/naslite_dsk.h8
-rw-r--r--src/lib/formats/nes_dsk.cpp6
-rw-r--r--src/lib/formats/nes_dsk.h2
-rw-r--r--src/lib/formats/nfd_dsk.cpp59
-rw-r--r--src/lib/formats/nfd_dsk.h14
-rw-r--r--src/lib/formats/opd_dsk.cpp17
-rw-r--r--src/lib/formats/opd_dsk.h12
-rw-r--r--src/lib/formats/orao_cas.cpp42
-rw-r--r--src/lib/formats/oric_dsk.cpp161
-rw-r--r--src/lib/formats/oric_dsk.h36
-rw-r--r--src/lib/formats/oric_tap.cpp204
-rw-r--r--src/lib/formats/os9_dsk.cpp562
-rw-r--r--src/lib/formats/os9_dsk.h16
-rw-r--r--src/lib/formats/p2000t_cas.cpp322
-rw-r--r--src/lib/formats/p2000t_cas.h19
-rw-r--r--src/lib/formats/p6001_cas.cpp19
-rw-r--r--src/lib/formats/pasti_dsk.cpp69
-rw-r--r--src/lib/formats/pasti_dsk.h40
-rw-r--r--src/lib/formats/pc98_dsk.cpp19
-rw-r--r--src/lib/formats/pc98_dsk.h8
-rw-r--r--src/lib/formats/pc98fdi_dsk.cpp78
-rw-r--r--src/lib/formats/pc98fdi_dsk.h14
-rw-r--r--src/lib/formats/pc_dsk.cpp156
-rw-r--r--src/lib/formats/pc_dsk.h15
-rw-r--r--src/lib/formats/phc25_cas.cpp16
-rw-r--r--src/lib/formats/pk8020_dsk.cpp10
-rw-r--r--src/lib/formats/pk8020_dsk.h10
-rw-r--r--src/lib/formats/pmd_cas.cpp22
-rw-r--r--src/lib/formats/poly_dsk.cpp45
-rw-r--r--src/lib/formats/poly_dsk.h14
-rw-r--r--src/lib/formats/ppg_dsk.cpp12
-rw-r--r--src/lib/formats/ppg_dsk.h8
-rw-r--r--src/lib/formats/primoptp.cpp22
-rw-r--r--src/lib/formats/pyldin_dsk.cpp11
-rw-r--r--src/lib/formats/pyldin_dsk.h8
-rw-r--r--src/lib/formats/ql_dsk.cpp12
-rw-r--r--src/lib/formats/ql_dsk.h8
-rw-r--r--src/lib/formats/rc759_dsk.cpp41
-rw-r--r--src/lib/formats/rc759_dsk.h33
-rw-r--r--src/lib/formats/rk_cas.cpp64
-rw-r--r--src/lib/formats/roland_dsk.cpp38
-rw-r--r--src/lib/formats/roland_dsk.h27
-rw-r--r--src/lib/formats/rpk.cpp474
-rw-r--r--src/lib/formats/rpk.h143
-rw-r--r--src/lib/formats/rx50_dsk.cpp45
-rw-r--r--src/lib/formats/rx50_dsk.h18
-rw-r--r--src/lib/formats/sap_dsk.cpp169
-rw-r--r--src/lib/formats/sap_dsk.h30
-rw-r--r--src/lib/formats/sc3000_bit.cpp29
-rw-r--r--src/lib/formats/sdd_dsk.cpp64
-rw-r--r--src/lib/formats/sdd_dsk.h32
-rw-r--r--src/lib/formats/sdf_dsk.cpp52
-rw-r--r--src/lib/formats/sdf_dsk.h15
-rw-r--r--src/lib/formats/sf7000_dsk.cpp12
-rw-r--r--src/lib/formats/sf7000_dsk.h8
-rw-r--r--src/lib/formats/smx_dsk.cpp12
-rw-r--r--src/lib/formats/smx_dsk.h8
-rw-r--r--src/lib/formats/sol_cas.cpp55
-rw-r--r--src/lib/formats/sorc_cas.cpp15
-rw-r--r--src/lib/formats/sorc_dsk.cpp13
-rw-r--r--src/lib/formats/sorc_dsk.h2
-rw-r--r--src/lib/formats/sord_cas.cpp43
-rw-r--r--src/lib/formats/spc1000_cas.cpp29
-rw-r--r--src/lib/formats/st_dsk.cpp120
-rw-r--r--src/lib/formats/st_dsk.h40
-rw-r--r--src/lib/formats/svi_cas.cpp18
-rw-r--r--src/lib/formats/svi_dsk.cpp62
-rw-r--r--src/lib/formats/svi_dsk.h18
-rw-r--r--src/lib/formats/swd_dsk.cpp50
-rw-r--r--src/lib/formats/swd_dsk.h32
-rw-r--r--src/lib/formats/tandy2k_dsk.cpp12
-rw-r--r--src/lib/formats/tandy2k_dsk.h8
-rw-r--r--src/lib/formats/td0_dsk.cpp210
-rw-r--r--src/lib/formats/td0_dsk.h17
-rw-r--r--src/lib/formats/thom_cas.cpp146
-rw-r--r--src/lib/formats/thom_dsk.cpp469
-rw-r--r--src/lib/formats/thom_dsk.h42
-rw-r--r--src/lib/formats/ti99_dsk.cpp208
-rw-r--r--src/lib/formats/ti99_dsk.h73
-rw-r--r--src/lib/formats/tibdd001_dsk.cpp40
-rw-r--r--src/lib/formats/tibdd001_dsk.h33
-rw-r--r--src/lib/formats/tiki100_dsk.cpp12
-rw-r--r--src/lib/formats/tiki100_dsk.h8
-rw-r--r--src/lib/formats/tim011_dsk.cpp45
-rw-r--r--src/lib/formats/tim011_dsk.h32
-rw-r--r--src/lib/formats/trd_dsk.cpp95
-rw-r--r--src/lib/formats/trd_dsk.h9
-rw-r--r--src/lib/formats/trs80_dsk.cpp430
-rw-r--r--src/lib/formats/trs80_dsk.h32
-rw-r--r--src/lib/formats/trs_cas.cpp219
-rw-r--r--src/lib/formats/trs_cas.h1
-rw-r--r--src/lib/formats/tvc_cas.cpp54
-rw-r--r--src/lib/formats/tvc_dsk.cpp12
-rw-r--r--src/lib/formats/tvc_dsk.h8
-rw-r--r--src/lib/formats/tzx_cas.cpp247
-rw-r--r--src/lib/formats/tzx_cas.h2
-rw-r--r--src/lib/formats/uef_cas.cpp65
-rw-r--r--src/lib/formats/uef_cas.h2
-rw-r--r--src/lib/formats/uniflex_dsk.cpp53
-rw-r--r--src/lib/formats/uniflex_dsk.h12
-rw-r--r--src/lib/formats/upd765_dsk.cpp117
-rw-r--r--src/lib/formats/upd765_dsk.h21
-rw-r--r--src/lib/formats/vdk_dsk.cpp66
-rw-r--r--src/lib/formats/vdk_dsk.h18
-rw-r--r--src/lib/formats/vector06_dsk.cpp10
-rw-r--r--src/lib/formats/vector06_dsk.h10
-rw-r--r--src/lib/formats/vg5k_cas.cpp20
-rw-r--r--src/lib/formats/vgi_dsk.cpp146
-rw-r--r--src/lib/formats/vgi_dsk.h34
-rw-r--r--src/lib/formats/victor9k_dsk.cpp208
-rw-r--r--src/lib/formats/victor9k_dsk.h36
-rw-r--r--src/lib/formats/vt_cas.cpp42
-rw-r--r--src/lib/formats/vt_dsk.cpp420
-rw-r--r--src/lib/formats/vt_dsk.h58
-rw-r--r--src/lib/formats/wavfile.cpp76
-rw-r--r--src/lib/formats/wavfile.h2
-rw-r--r--src/lib/formats/wd177x_dsk.cpp316
-rw-r--r--src/lib/formats/wd177x_dsk.h27
-rw-r--r--src/lib/formats/x07_cas.cpp16
-rw-r--r--src/lib/formats/x1_tap.cpp25
-rw-r--r--src/lib/formats/xdf_dsk.cpp12
-rw-r--r--src/lib/formats/xdf_dsk.h8
-rw-r--r--src/lib/formats/zx81_p.cpp32
400 files changed, 30702 insertions, 11553 deletions
diff --git a/src/lib/formats/2d_dsk.cpp b/src/lib/formats/2d_dsk.cpp
index 7f304b4eaa0..3e091413264 100644
--- a/src/lib/formats/2d_dsk.cpp
+++ b/src/lib/formats/2d_dsk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -14,17 +14,17 @@ _2d_format::_2d_format() : wd177x_format(formats)
{
}
-const char *_2d_format::name() const
+const char *_2d_format::name() const noexcept
{
return "2d";
}
-const char *_2d_format::description() const
+const char *_2d_format::description() const noexcept
{
return "2D disk image";
}
-const char *_2d_format::extensions() const
+const char *_2d_format::extensions() const noexcept
{
return "2d";
}
@@ -38,4 +38,4 @@ const _2d_format::format _2d_format::formats[] =
{}
};
-const floppy_format_type FLOPPY_2D_FORMAT = &floppy_image_format_creator<_2d_format>;
+const _2d_format FLOPPY_2D_FORMAT;
diff --git a/src/lib/formats/2d_dsk.h b/src/lib/formats/2d_dsk.h
index b5c1a61c256..e64cd191a26 100644
--- a/src/lib/formats/2d_dsk.h
+++ b/src/lib/formats/2d_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -19,14 +19,14 @@ class _2d_format : public wd177x_format
public:
_2d_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_2D_FORMAT;
+extern const _2d_format FLOPPY_2D_FORMAT;
#endif // MAME_FORMATS_2D_DSK_H
diff --git a/src/lib/formats/86f_dsk.cpp b/src/lib/formats/86f_dsk.cpp
new file mode 100644
index 00000000000..654e847d8db
--- /dev/null
+++ b/src/lib/formats/86f_dsk.cpp
@@ -0,0 +1,272 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert, Carl
+
+#include "86f_dsk.h"
+
+#include "ioprocs.h"
+
+#include <cstring>
+#include <tuple>
+
+
+namespace {
+
+#define _86F_FORMAT_HEADER "86BF"
+
+#pragma pack(1)
+
+struct _86FIMG
+{
+ uint8_t headername[4];
+ uint8_t minor_version;
+ uint8_t major_version;
+ uint16_t flags;
+ uint32_t firsttrackoffs;
+};
+
+#pragma pack()
+
+enum
+{
+ SURFACE_DESC = 1,
+ TYPE_MASK = 6,
+ TYPE_DD = 0,
+ TYPE_HD = 2,
+ TYPE_ED = 4,
+ TYPE_ED2000 = 6,
+ TWO_SIDES = 8,
+ WRITE_PROTECT = 0x10,
+ RPM_MASK = 0x60,
+ RPM_0 = 0,
+ RPM_1 = 0x20,
+ RPM_15 = 0x40,
+ RPM_2 = 0x60,
+ EXTRA_BC = 0x80,
+ ZONED_RPM = 0x100,
+ ZONE_PREA2_1 = 0,
+ ZONE_PREA2_2 = 0x200,
+ ZONE_A2 = 0x400,
+ ZONE_C64 = 0x600,
+ ENDIAN_BIG = 0x800,
+ RPM_FAST = 0x1000,
+ TOTAL_BC = 0x1000
+};
+
+} // anonymous namespace
+
+
+_86f_format::_86f_format() : floppy_image_format_t()
+{
+}
+
+const char *_86f_format::name() const noexcept
+{
+ return "86f";
+}
+
+const char *_86f_format::description() const noexcept
+{
+ return "86f floppy image";
+}
+
+const char *_86f_format::extensions() const noexcept
+{
+ return "86f";
+}
+
+bool _86f_format::supports_save() const noexcept
+{
+ return false;
+}
+
+int _86f_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint8_t header[4];
+ auto const [err, actual] = read_at(io, 0, &header, sizeof(header));
+ if (err) {
+ return 0;
+ }
+ if (!memcmp(header, _86F_FORMAT_HEADER, 4)) {
+ return FIFID_SIGN;
+ }
+ return 0;
+}
+
+void _86f_format::generate_track_from_bitstream_with_weak(int track, int head, const uint8_t *trackbuf, const uint8_t *weak, int index_cell, int track_size, floppy_image &image) const
+{
+ int j = 0;
+ std::vector<uint32_t> &dest = image.get_buffer(track, head);
+ dest.clear();
+
+ for(int i=index_cell; i != track_size; i++, j++) {
+ int databit = trackbuf[i >> 3] & (0x80 >> (i & 7));
+ int weakbit = weak ? weak[i >> 3] & (0x80 >> (i & 7)) : 0;
+ if(weakbit && databit)
+ dest.push_back(floppy_image::MG_D | (j*2+1));
+ else if(weakbit && !databit)
+ dest.push_back(floppy_image::MG_N | (j*2+1));
+ else if(databit)
+ dest.push_back(floppy_image::MG_F | (j*2+1));
+ }
+ for(int i=0; i != index_cell; i++, j++) {
+ int databit = trackbuf[i >> 3] & (0x80 >> (i & 7));
+ int weakbit = weak ? weak[i >> 3] & (0x80 >> (i & 7)) : 0;
+ if(weakbit && databit)
+ dest.push_back(floppy_image::MG_D | (j*2+1));
+ else if(weakbit && !databit)
+ dest.push_back(floppy_image::MG_N | (j*2+1));
+ else if(databit)
+ dest.push_back(floppy_image::MG_F | (j*2+1));
+ }
+
+ normalize_times(dest, track_size*2);
+ image.set_write_splice_position(track, head, 0, 0);
+}
+
+bool _86f_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ std::error_condition err;
+ size_t actual;
+ _86FIMG header;
+
+ // read header
+ std::tie(err, actual) = read_at(io, 0, &header, sizeof(header));
+ if(err || (actual != sizeof(header)))
+ return false;
+
+ int drivecyl, driveheads;
+ image.get_maximal_geometry(drivecyl, driveheads);
+ bool skip_odd = drivecyl < 50;
+ int imagesides = header.flags & TWO_SIDES ? 2 : 1;
+ int sides = (imagesides == 2) && (driveheads == 2) ? 2 : 1;
+
+ std::vector<uint32_t> tracklist;
+ int tracklistsize = header.firsttrackoffs - 8;
+ tracklist.resize(tracklistsize / 4);
+ std::tie(err, actual) = read_at(io, 8, &tracklist[0], tracklistsize);
+ if(err || (actual != tracklistsize))
+ return false;
+
+ uint32_t tracklen = 0;
+ if((header.flags & (TOTAL_BC | EXTRA_BC | RPM_MASK)) != (TOTAL_BC | EXTRA_BC)) {
+ switch(header.flags & (RPM_MASK | RPM_FAST | TYPE_MASK)) {
+ case TYPE_DD | RPM_2:
+ case TYPE_HD | RPM_2:
+ tracklen = 12750;
+ break;
+ case TYPE_DD | RPM_15:
+ case TYPE_HD | RPM_15:
+ tracklen = 12687;
+ break;
+ case TYPE_DD | RPM_1:
+ case TYPE_HD | RPM_1:
+ tracklen = 12625;
+ break;
+ case TYPE_DD | RPM_0:
+ case TYPE_HD | RPM_0:
+ tracklen = 12500;
+ break;
+ case TYPE_DD | RPM_1 | RPM_FAST:
+ case TYPE_HD | RPM_1 | RPM_FAST:
+ tracklen = 12376;
+ break;
+ case TYPE_DD | RPM_15 | RPM_FAST:
+ case TYPE_HD | RPM_15 | RPM_FAST:
+ tracklen = 12315;
+ break;
+ case TYPE_DD | RPM_2 | RPM_FAST:
+ case TYPE_HD | RPM_2 | RPM_FAST:
+ tracklen = 12254;
+ break;
+
+ case TYPE_ED | RPM_2:
+ tracklen = 25500;
+ break;
+ case TYPE_ED | RPM_15:
+ tracklen = 25375;
+ break;
+ case TYPE_ED | RPM_1:
+ tracklen = 25250;
+ break;
+ case TYPE_ED | RPM_0:
+ tracklen = 25000;
+ break;
+ case TYPE_ED | RPM_1 | RPM_FAST:
+ tracklen = 25752;
+ break;
+ case TYPE_ED | RPM_15 | RPM_FAST:
+ tracklen = 24630;
+ break;
+ case TYPE_ED | RPM_2 | RPM_FAST:
+ tracklen = 24509;
+ break;
+ }
+ }
+ uint32_t trackoff;
+ int trackinfolen = header.flags & EXTRA_BC ? 10 : 6;
+ if(skip_odd)
+ drivecyl *= 2;
+ std::vector<uint8_t> trackbuf;
+ std::vector<uint8_t> weakbuf;
+ int track;
+ for(track=0; (track < (tracklistsize / 4)) && (track < drivecyl); track++) {
+ for(int side=0; side < sides; side++) {
+ trackoff = tracklist[(track * imagesides) + side];
+ if(!trackoff) break;
+ if(!skip_odd || track%2 == 0) {
+ char trackinfo[10];
+ std::tie(err, actual) = read_at(io, trackoff, &trackinfo, trackinfolen); // FIXME: check for errors and premature EOF
+ if(err || (actual != trackinfolen))
+ return false;
+ uint32_t bitcells = tracklen << 4;
+ uint32_t index_cell;
+ if(header.flags & EXTRA_BC)
+ {
+ uint32_t extra = *(uint32_t *)(trackinfo + 2);
+ index_cell = *(uint32_t *)(trackinfo + 6);
+ if((header.flags & TOTAL_BC) && !tracklen)
+ bitcells = extra;
+ else
+ bitcells += (int32_t)extra;
+ }
+ else
+ index_cell = *(uint32_t *)(trackinfo + 2);
+ uint32_t fulltracklen = (bitcells >> 3) + (bitcells & 7 ? 1 : 0);
+ uint8_t *weak = nullptr;
+ trackbuf.resize(fulltracklen);
+ std::tie(err, actual) = read_at(io, trackoff + trackinfolen, &trackbuf[0], fulltracklen);
+ if(err || (actual != fulltracklen))
+ return false;
+ if(header.flags & SURFACE_DESC)
+ {
+ weakbuf.resize(fulltracklen);
+ std::tie(err, actual) = read_at(io, trackoff + trackinfolen + fulltracklen, &weakbuf[0], fulltracklen);
+ if(err || (actual != fulltracklen))
+ return false;
+ weak = &weakbuf[0];
+ }
+ if(skip_odd) {
+ generate_track_from_bitstream_with_weak(track/2, side, &trackbuf[0], weak, index_cell, bitcells, image);
+ }
+ else {
+ generate_track_from_bitstream_with_weak(track, side, &trackbuf[0], weak, index_cell, bitcells, image);
+ }
+ }
+ }
+ if(!trackoff) break;
+ }
+
+ if(imagesides == 2)
+ image.set_variant(track > 50 ? floppy_image::DSHD : floppy_image::DSDD);
+ else
+ image.set_variant(floppy_image::SSDD);
+ return true;
+}
+
+/*
+bool _86f_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ return true;
+}
+*/
+const _86f_format FLOPPY_86F_FORMAT;
diff --git a/src/lib/formats/86f_dsk.h b/src/lib/formats/86f_dsk.h
new file mode 100644
index 00000000000..0afbe4d65ee
--- /dev/null
+++ b/src/lib/formats/86f_dsk.h
@@ -0,0 +1,37 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert, Carl
+/*********************************************************************
+
+ formats/86f_dsk.h
+
+ 86f disk images
+
+*********************************************************************/
+#ifndef MAME_FORMATS_86F_DSK_H
+#define MAME_FORMATS_86F_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+
+class _86f_format : public floppy_image_format_t
+{
+public:
+ _86f_format();
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ //virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
+
+private:
+ void generate_track_from_bitstream_with_weak(int track, int head, const uint8_t *trackbuf, const uint8_t *weak, int index_cell, int track_size, floppy_image &image) const;
+};
+
+extern const _86f_format FLOPPY_86F_FORMAT;
+
+#endif // MAME_FORMATS_86F_DSK_H
diff --git a/src/lib/formats/a26_cas.cpp b/src/lib/formats/a26_cas.cpp
index 5030c2bfe0f..c609f922348 100644
--- a/src/lib/formats/a26_cas.cpp
+++ b/src/lib/formats/a26_cas.cpp
@@ -6,8 +6,6 @@ Atari 2600 SuperCharger support
*/
-#include <assert.h>
-
#include "formats/a26_cas.h"
@@ -122,7 +120,7 @@ static int a26_cas_do_work( int16_t **buffer, const uint8_t *bytes ) {
return size;
}
-static int a26_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
+static int a26_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) {
int16_t *p = buffer;
return a26_cas_do_work( &p, (const uint8_t *)bytes );
@@ -132,7 +130,7 @@ static int a26_cas_to_wav_size( const uint8_t *casdata, int caslen ) {
return a26_cas_do_work( nullptr, casdata );
}
-static const struct CassetteLegacyWaveFiller a26_legacy_fill_wave = {
+static const cassette_image::LegacyWaveFiller a26_legacy_fill_wave = {
a26_cas_fill_wave,
-1,
0,
@@ -142,21 +140,21 @@ static const struct CassetteLegacyWaveFiller a26_legacy_fill_wave = {
0
};
-static cassette_image::error a26_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) {
+static cassette_image::error a26_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) {
uint64_t size;
- size = cassette_image_size( cassette );
+ size = cassette->image_size( );
if ( size == A26_CAS_SIZE ) {
- return cassette_legacy_identify( cassette, opts, &a26_legacy_fill_wave );
+ return cassette->legacy_identify( opts, &a26_legacy_fill_wave );
}
return cassette_image::error::INVALID_IMAGE;
}
static cassette_image::error a26_cassette_load( cassette_image *cassette ) {
- return cassette_legacy_construct( cassette, &a26_legacy_fill_wave );
+ return cassette->legacy_construct( &a26_legacy_fill_wave );
}
-static const struct CassetteFormat a26_cassette_format = {
+static const cassette_image::Format a26_cassette_format = {
"a26",
a26_cassette_identify,
a26_cassette_load,
diff --git a/src/lib/formats/a5105_dsk.cpp b/src/lib/formats/a5105_dsk.cpp
index 33d7f15e1fd..81c25945ebf 100644
--- a/src/lib/formats/a5105_dsk.cpp
+++ b/src/lib/formats/a5105_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/a5105_dsk.c
+ formats/a5105_dsk.cpp
a5105 format
*********************************************************************/
-#include <assert.h>
-
#include "formats/a5105_dsk.h"
a5105_format::a5105_format() : upd765_format(formats)
{
}
-const char *a5105_format::name() const
+const char *a5105_format::name() const noexcept
{
return "a5105";
}
-const char *a5105_format::description() const
+const char *a5105_format::description() const noexcept
{
return "A5105 disk image";
}
-const char *a5105_format::extensions() const
+const char *a5105_format::extensions() const noexcept
{
return "img";
}
@@ -44,4 +42,4 @@ const a5105_format::format a5105_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_A5105_FORMAT = &floppy_image_format_creator<a5105_format>;
+const a5105_format FLOPPY_A5105_FORMAT;
diff --git a/src/lib/formats/a5105_dsk.h b/src/lib/formats/a5105_dsk.h
index a2e8532ec56..c1c597378de 100644
--- a/src/lib/formats/a5105_dsk.h
+++ b/src/lib/formats/a5105_dsk.h
@@ -19,14 +19,14 @@ class a5105_format : public upd765_format
public:
a5105_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_A5105_FORMAT;
+extern const a5105_format FLOPPY_A5105_FORMAT;
#endif // MAME_FORMATS_A5105_DSK_H
diff --git a/src/lib/formats/abc1600_dsk.cpp b/src/lib/formats/abc1600_dsk.cpp
new file mode 100644
index 00000000000..41fbf895f39
--- /dev/null
+++ b/src/lib/formats/abc1600_dsk.cpp
@@ -0,0 +1,79 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/*********************************************************************
+
+ formats/abc1600_dsk.cpp
+
+ Luxor ABC 1600 disk image formats
+
+*********************************************************************/
+
+#include "formats/abc1600_dsk.h"
+
+abc1600_format::abc1600_format() : wd177x_format(formats)
+{
+}
+
+const char *abc1600_format::name() const noexcept
+{
+ return "abc1600";
+}
+
+const char *abc1600_format::description() const noexcept
+{
+ return "Luxor ABC 1600 disk image";
+}
+
+const char *abc1600_format::extensions() const noexcept
+{
+ return "img";
+}
+
+const abc1600_format::format abc1600_format::formats[] = {
+ // track description
+ // 55x4e 12x00 3xf5 fe 2x00 01 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 02 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 03 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 04 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 05 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 06 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 07 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 08 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 09 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0a 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0b 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0c 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0d 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0e 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0f 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 10 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 298x4e
+
+ { // 640K 5 1/4 inch quad density
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 16, 80, 2, 256, {}, 1, {}, 55, 22, 54
+ },
+
+ {}
+};
+
+const abc1600_format FLOPPY_ABC1600_FORMAT;
+
+int abc1600_format::get_image_offset(const format &f, int head, int track) const
+{
+ int offset = 0;
+
+ if(head) {
+ for(int trk=0; trk < f.track_count; trk++) {
+ const format &tf = get_track_format(f, 0, trk);
+ offset += compute_track_size(tf);
+ }
+ }
+
+ for(int trk=0; trk < track; trk++) {
+ const format &tf = get_track_format(f, head, trk);
+ offset += compute_track_size(tf);
+ }
+
+ return offset;
+}
diff --git a/src/lib/formats/abc1600_dsk.h b/src/lib/formats/abc1600_dsk.h
new file mode 100644
index 00000000000..02ac331345d
--- /dev/null
+++ b/src/lib/formats/abc1600_dsk.h
@@ -0,0 +1,35 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/*********************************************************************
+
+ formats/abc1600_dsk.h
+
+ Luxor ABC 1600 disk image formats
+
+*********************************************************************/
+#ifndef MAME_FORMATS_ABC1600_DSK_H
+#define MAME_FORMATS_ABC1600_DSK_H
+
+#pragma once
+
+#include "wd177x_dsk.h"
+
+class abc1600_format : public wd177x_format
+{
+public:
+ abc1600_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+protected:
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+
+private:
+ static const format formats[];
+};
+
+extern const abc1600_format FLOPPY_ABC1600_FORMAT;
+
+#endif // MAME_FORMATS_ABC1600_DSK_H
diff --git a/src/lib/formats/abc800_dsk.cpp b/src/lib/formats/abc800_dsk.cpp
index e61a0a1d1ff..d7b47b502e8 100644
--- a/src/lib/formats/abc800_dsk.cpp
+++ b/src/lib/formats/abc800_dsk.cpp
@@ -2,33 +2,31 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/abc800_dsk.c
+ formats/abc800_dsk.cpp
Luxor ABC 830/832/834/838 disk image formats
*********************************************************************/
-#include <assert.h>
-
-#include "formats/abc800_dsk.h"
+#include "abc800_dsk.h"
abc800_format::abc800_format() : wd177x_format(formats)
{
}
-const char *abc800_format::name() const
+const char *abc800_format::name() const noexcept
{
return "abc800";
}
-const char *abc800_format::description() const
+const char *abc800_format::description() const noexcept
{
return "Luxor ABC 830/832/834/838 disk image";
}
-const char *abc800_format::extensions() const
+const char *abc800_format::extensions() const noexcept
{
- return "dsk";
+ return "img";
}
const abc800_format::format abc800_format::formats[] = {
@@ -53,7 +51,7 @@ const abc800_format::format abc800_format::formats[] = {
{ // 80K 5 1/4 inch single density single sided
floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
- 4000, 16, 40, 1, 128, {}, -1, { 1,2,11,12,5,6,15,16,9,10,3,4,13,14,7,8 }, 28, 11, 27
+ 4000, 16, 40, 1, 128, {}, 1, {}, 28, 11, 27
},
// track description
@@ -77,7 +75,7 @@ const abc800_format::format abc800_format::formats[] = {
{ // 160K 5 1/4 inch double density single sided
floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
- 2000, 16, 40, 1, 256, {}, -1, { 1,8,15,6,13,4,11,2,9,16,7,14,5,12,3,10 }, 55, 22, 54
+ 2000, 16, 40, 1, 256, {}, 1, {}, 55, 22, 54
},
// track description
@@ -141,27 +139,23 @@ const abc800_format::format abc800_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_ABC800_FORMAT = &floppy_image_format_creator<abc800_format>;
+const abc800_format FLOPPY_ABC800_FORMAT;
-void abc800_format::build_sector_description(const format &f, uint8_t *sectdata, desc_s *sectors, int track, int head) const
+int abc800_format::get_image_offset(const format &f, int head, int track) const
{
- if(f.sector_base_id == -1) {
- for(int i=0; i<f.sector_count; i++) {
- int cur_offset = 0;
- for(int j=0; j<f.sector_count; j++)
- if(f.per_sector_id[j] < f.per_sector_id[i])
- cur_offset += f.sector_base_size ? f.sector_base_size : f.per_sector_size[j];
- sectors[i].data = sectdata + cur_offset;
- sectors[i].size = f.sector_base_size ? f.sector_base_size : f.per_sector_size[i];
- sectors[i].sector_id = i + f.per_sector_id[0];
- }
- } else {
- int cur_offset = 0;
- for(int i=0; i<f.sector_count; i++) {
- sectors[i].data = sectdata + cur_offset;
- sectors[i].size = f.sector_base_size ? f.sector_base_size : f.per_sector_size[i];
- cur_offset += sectors[i].size;
- sectors[i].sector_id = i + f.sector_base_id;
+ int offset = 0;
+
+ if(head) {
+ for(int trk=0; trk < f.track_count; trk++) {
+ const format &tf = get_track_format(f, 0, trk);
+ offset += compute_track_size(tf);
}
}
+
+ for(int trk=0; trk < track; trk++) {
+ const format &tf = get_track_format(f, head, trk);
+ offset += compute_track_size(tf);
+ }
+
+ return offset;
}
diff --git a/src/lib/formats/abc800_dsk.h b/src/lib/formats/abc800_dsk.h
index 90ad8627e8f..f2d95c7c579 100644
--- a/src/lib/formats/abc800_dsk.h
+++ b/src/lib/formats/abc800_dsk.h
@@ -19,17 +19,17 @@ class abc800_format : public wd177x_format
public:
abc800_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
protected:
- virtual void build_sector_description(const format &d, uint8_t *sectdata, desc_s *sectors, int track, int head) const override;
+ virtual int get_image_offset(const format &f, int head, int track) const override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_ABC800_FORMAT;
+extern const abc800_format FLOPPY_ABC800_FORMAT;
#endif // MAME_FORMATS_ABC800_DSK_H
diff --git a/src/lib/formats/abc800i_dsk.cpp b/src/lib/formats/abc800i_dsk.cpp
new file mode 100644
index 00000000000..9a5a798c886
--- /dev/null
+++ b/src/lib/formats/abc800i_dsk.cpp
@@ -0,0 +1,122 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/*********************************************************************
+
+ formats/abc800i_dsk.cpp
+
+ Luxor ABC 830 interleaved disk image formats
+
+*********************************************************************/
+
+#include "abc800i_dsk.h"
+
+#include "ioprocs.h"
+
+
+abc800i_format::abc800i_format() : wd177x_format(formats)
+{
+}
+
+const char *abc800i_format::name() const noexcept
+{
+ return "abc800i";
+}
+
+const char *abc800i_format::description() const noexcept
+{
+ return "Luxor ABC 830 interleaved disk image";
+}
+
+const char *abc800i_format::extensions() const noexcept
+{
+ return "dsk";
+}
+
+const abc800i_format::format abc800i_format::formats[] = {
+ // track description
+ // 28xff 6x00 fe 2x00 01 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 02 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 03 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 04 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 05 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 06 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 07 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 08 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 09 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 0a 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 0b 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 0c 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 0d 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 0e 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 0f 00 f7 11xff 6x00 fb 128xe5 f7
+ // 27xff 6x00 fe 2x00 10 00 f7 11xff 6x00 fb 128xe5 f7
+ // 117xff
+
+ { // 80K 5 1/4 inch single density single sided
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 16, 40, 1, 128, {}, -1, { 1,2,11,12,5,6,15,16,9,10,3,4,13,14,7,8 }, 28, 11, 27
+ },
+
+ // track description
+ // 55x4e 12x00 3xf5 fe 2x00 01 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 02 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 03 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 04 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 05 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 06 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 07 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 08 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 09 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0a 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0b 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0c 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0d 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0e 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 0f 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 54x4e 12x00 3xf5 fe 2x00 10 01 f7 22x4e 12x00 3xf5 fb 256xe5 f7
+ // 298x4e
+
+ { // 160K 5 1/4 inch double density single sided
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 16, 40, 1, 256, {}, -1, { 1,8,15,6,13,4,11,2,9,16,7,14,5,12,3,10 }, 55, 22, 54
+ },
+
+ {}
+};
+
+const abc800i_format FLOPPY_ABC800I_FORMAT;
+
+int abc800i_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint8_t h[1];
+ auto const [err, actual] = read_at(io, 0x810, h, 1);
+
+ // start of directory
+ if (!err && (actual == 1) && (h[0] == 0x03))
+ return FIFID_SIGN;
+
+ return 0;
+}
+
+void abc800i_format::build_sector_description(const format &f, uint8_t *sectdata, desc_s *sectors, int track, int head) const
+{
+ if(f.sector_base_id == -1) {
+ for(int i=0; i<f.sector_count; i++) {
+ int cur_offset = 0;
+ for(int j=0; j<f.sector_count; j++)
+ if(f.per_sector_id[j] < f.per_sector_id[i])
+ cur_offset += f.sector_base_size ? f.sector_base_size : f.per_sector_size[j];
+ sectors[i].data = sectdata + cur_offset;
+ sectors[i].size = f.sector_base_size ? f.sector_base_size : f.per_sector_size[i];
+ sectors[i].sector_id = i + f.per_sector_id[0];
+ }
+ } else {
+ int cur_offset = 0;
+ for(int i=0; i<f.sector_count; i++) {
+ sectors[i].data = sectdata + cur_offset;
+ sectors[i].size = f.sector_base_size ? f.sector_base_size : f.per_sector_size[i];
+ cur_offset += sectors[i].size;
+ sectors[i].sector_id = i + f.sector_base_id;
+ }
+ }
+}
diff --git a/src/lib/formats/abc800i_dsk.h b/src/lib/formats/abc800i_dsk.h
new file mode 100644
index 00000000000..7248a5c038e
--- /dev/null
+++ b/src/lib/formats/abc800i_dsk.h
@@ -0,0 +1,37 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/*********************************************************************
+
+ formats/abc800i_dsk.h
+
+ Luxor ABC 830 interleaved disk image formats
+
+*********************************************************************/
+#ifndef MAME_FORMATS_ABC800I_DSK_H
+#define MAME_FORMATS_ABC800I_DSK_H
+
+#pragma once
+
+#include "wd177x_dsk.h"
+
+class abc800i_format : public wd177x_format
+{
+public:
+ abc800i_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+
+protected:
+ virtual void build_sector_description(const format &d, uint8_t *sectdata, desc_s *sectors, int track, int head) const override;
+
+private:
+ static const format formats[];
+};
+
+extern const abc800i_format FLOPPY_ABC800I_FORMAT;
+
+#endif // MAME_FORMATS_ABC800I_DSK_H
diff --git a/src/lib/formats/abcfd2_dsk.cpp b/src/lib/formats/abcfd2_dsk.cpp
index 8cc87ae20cd..180d111430c 100644
--- a/src/lib/formats/abcfd2_dsk.cpp
+++ b/src/lib/formats/abcfd2_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/abcfd2_dsk.c
+ formats/abcfd2_dsk.cpp
Scandia Metric ABC FD2 disk image formats
*********************************************************************/
-#include <assert.h>
-
#include "formats/abcfd2_dsk.h"
abc_fd2_format::abc_fd2_format() : wd177x_format(formats)
{
}
-const char *abc_fd2_format::name() const
+const char *abc_fd2_format::name() const noexcept
{
return "abc_fd2";
}
-const char *abc_fd2_format::description() const
+const char *abc_fd2_format::description() const noexcept
{
return "Scandia Metric ABC FD2 disk image";
}
-const char *abc_fd2_format::extensions() const
+const char *abc_fd2_format::extensions() const noexcept
{
return "dsk";
}
@@ -59,4 +57,4 @@ const abc_fd2_format::format abc_fd2_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_ABC_FD2_FORMAT = &floppy_image_format_creator<abc_fd2_format>;
+const abc_fd2_format FLOPPY_ABC_FD2_FORMAT;
diff --git a/src/lib/formats/abcfd2_dsk.h b/src/lib/formats/abcfd2_dsk.h
index 174e0c29286..41f1924c392 100644
--- a/src/lib/formats/abcfd2_dsk.h
+++ b/src/lib/formats/abcfd2_dsk.h
@@ -19,14 +19,14 @@ class abc_fd2_format : public wd177x_format
public:
abc_fd2_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_ABC_FD2_FORMAT;
+extern const abc_fd2_format FLOPPY_ABC_FD2_FORMAT;
#endif // MAME_FORMATS_ABCFD2_DSK_H
diff --git a/src/lib/formats/ace_tap.cpp b/src/lib/formats/ace_tap.cpp
index 56b4bd877c8..9613450e59e 100644
--- a/src/lib/formats/ace_tap.cpp
+++ b/src/lib/formats/ace_tap.cpp
@@ -10,10 +10,10 @@ For more information see:
********************************************************************/
-#include <assert.h>
-
#include "ace_tap.h"
+#include "multibyte.h"
+
#define SMPLO -32768
#define SILENCE 0
@@ -97,11 +97,8 @@ static int ace_handle_tap(int16_t *buffer, const uint8_t *casdata)
while( data_pos < cas_size )
{
- uint16_t block_size;
- int i;
-
/* Handle a block of tape data */
- block_size = casdata[data_pos] + ( casdata[data_pos + 1] << 8 );
+ uint16_t block_size = get_u16le( &casdata[data_pos] );
data_pos += 2;
/* Make sure there are enough bytes left */
@@ -112,7 +109,7 @@ static int ace_handle_tap(int16_t *buffer, const uint8_t *casdata)
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-- )
+ for( int i = ( block_size == 0x001A ) ? 4096 : 512; i; i-- )
sample_count += ace_tap_cycle( buffer, sample_count, 27, 27 );
/* Sync samples */
@@ -138,7 +135,7 @@ static int ace_handle_tap(int16_t *buffer, const uint8_t *casdata)
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int ace_tap_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int ace_tap_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return ace_handle_tap( buffer, bytes );
}
@@ -155,7 +152,7 @@ static int ace_tap_to_wav_size(const uint8_t *casdata, int caslen)
}
-static const struct CassetteLegacyWaveFiller ace_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller ace_legacy_fill_wave =
{
ace_tap_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -167,19 +164,19 @@ static const struct CassetteLegacyWaveFiller ace_legacy_fill_wave =
};
-static cassette_image::error ace_tap_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error ace_tap_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &ace_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &ace_legacy_fill_wave);
}
static cassette_image::error ace_tap_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &ace_legacy_fill_wave);
+ return cassette->legacy_construct(&ace_legacy_fill_wave);
}
-static const struct CassetteFormat ace_tap_format =
+static const cassette_image::Format ace_tap_format =
{
"tap",
ace_tap_identify,
diff --git a/src/lib/formats/acorn_dsk.cpp b/src/lib/formats/acorn_dsk.cpp
index d8992b9ad5a..682b6424556 100644
--- a/src/lib/formats/acorn_dsk.cpp
+++ b/src/lib/formats/acorn_dsk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best, Nigel Barnes
/***************************************************************************
@@ -9,57 +9,88 @@
***************************************************************************/
#include "acorn_dsk.h"
+#include "imageutl.h"
+
+#include "coretmpl.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <cstring>
+#include <tuple>
+
acorn_ssd_format::acorn_ssd_format() : wd177x_format(formats)
{
}
-const char *acorn_ssd_format::name() const
+const char *acorn_ssd_format::name() const noexcept
{
return "ssd";
}
-const char *acorn_ssd_format::description() const
+const char *acorn_ssd_format::description() const noexcept
{
return "Acorn SSD disk image";
}
-const char *acorn_ssd_format::extensions() const
+const char *acorn_ssd_format::extensions() const noexcept
{
return "ssd,bbc,img";
}
-int acorn_ssd_format::find_size(io_generic *io, uint32_t form_factor)
+int acorn_ssd_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t cat[8];
uint32_t sectors0, sectors2;
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return -1;
- for(int i=0; formats[i].form_factor; i++) {
+ for (int i = 0; formats[i].form_factor; i++)
+ {
const format &f = formats[i];
- if(form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
+ if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
continue;
// test for Torch CPN - test pattern at sector &0018
- io_generic_read(io, cat, 0x32800, 8);
- if (memcmp(cat, "\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd", 4) == 0 && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
+ read_at(io, 0x32800, cat, 8); // FIXME: check for errors and premature EOF
+ if (memcmp(cat, "\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd", 8) == 0 && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
+ return i;
+
+ // test for HADFS - test pattern at sector 70
+ read_at(io, 0x04610, cat, 8); // FIXME: check for errors and premature EOF
+ if (memcmp(cat, "\x00\x28\x43\x29\x4a\x47\x48\x00", 8) == 0 && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
return i;
+ // test for Kenda SD - offset &0962 = 0 SD/1 DD, offset &0963 = disk size blocks / 4 (block size = 1K, ie. 0x400 bytes), reserved tracks = 3, ie. 0x1e00 bytes, soft stagger = 2 sectors, ie. 0x200 bytes
+ read_at(io, 0x0960, cat, 8); // FIXME: check for errors and premature EOF
+ if (cat[2] == 0 && ((uint64_t)cat[3] * 4 * 0x400 + 0x2000) == size && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
+ {
+ // valid blocks for single sided
+ if (f.head_count == 1 && (cat[3] == 0x17 || cat[3] == 0x30))
+ return i;
+ // valid blocks for double sided
+ if (f.head_count == 2 && (cat[3] == 0x2f || cat[3] == 0x62))
+ return i;
+ }
+
// read sector count from side 0 catalogue
- io_generic_read(io, cat, 0x100, 8);
- sectors0 = ((cat[6] & 3) << 8) + cat[7];
+ read_at(io, 0x100, cat, 8); // FIXME: check for errors and premature EOF
+ sectors0 = get_u16be(&cat[6]) & 0x3ff;
LOG_FORMATS("ssd: sector count 0: %d %s\n", sectors0, sectors0 % 10 != 0 ? "invalid" : "");
- if ((size <= (uint64_t)compute_track_size(f) * f.track_count * f.head_count) && (sectors0 <= f.track_count * f.sector_count)) {
+ if ((size <= (uint64_t)compute_track_size(f) * f.track_count * f.head_count) && (sectors0 <= f.track_count * f.sector_count))
+ {
if (f.head_count == 2)
{
// read sector count from side 2 catalogue
- io_generic_read(io, cat, compute_track_size(f) * f.track_count + 0x100, 8); // sequential
- sectors2 = ((cat[6] & 3) << 8) + cat[7];
+ read_at(io, (uint64_t)compute_track_size(f) * f.track_count + 0x100, cat, 8); // sequential
+ sectors2 = get_u16be(&cat[6]) & 0x3ff;
// exception case for Acorn CP/M System Disc 1
- io_generic_read(io, cat, 0x367ec, 8);
- if (memcmp(cat, "/M ", 4) == 0) sectors2 = ((cat[6] & 3) << 8) + cat[7];
+ read_at(io, 0x367ec, cat, 8); // FIXME: check for errors and premature EOF
+ if (memcmp(cat, "/M ", 4) == 0)
+ sectors2 = get_u16be(&cat[6]) & 0x3ff;
LOG_FORMATS("ssd: sector count 2: %d %s\n", sectors2, sectors2 % 10 != 0 ? "invalid" : "");
}
@@ -68,29 +99,26 @@ int acorn_ssd_format::find_size(io_generic *io, uint32_t form_factor)
sectors2 = sectors0;
}
- if (sectors0 > 0 && sectors0 % 10 == 0 && sectors2 > 0 && sectors2 % 10 == 0 && size <= (sectors0 + sectors2) * 256)
+ if (sectors0 > 0 && sectors2 > 0 && sectors0 % 10 == 0 && sectors2 % 10 == 0 && size <= (sectors0 + sectors2) * 256)
return i;
}
}
- LOG_FORMATS("ssd: no match\n");
return -1;
}
-int acorn_ssd_format::identify(io_generic *io, uint32_t form_factor)
+int acorn_ssd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int type = find_size(io, form_factor, variants);
+
+ if (type != -1)
+ return FIFID_SIZE | FIFID_STRUCT;
- if(type != -1)
- return 90;
return 0;
}
-int acorn_ssd_format::get_image_offset(const format &f, int head, int track)
+int acorn_ssd_format::get_image_offset(const format &f, int head, int track) const
{
- if (f.sector_base_id == -1)
- return (track * f.head_count + head) * compute_track_size(f);
- else
- return (f.track_count * head + track) * compute_track_size(f);
+ return (f.track_count * head + track) * compute_track_size(f);
}
const acorn_ssd_format::format acorn_ssd_format::formats[] =
@@ -113,23 +141,23 @@ const acorn_ssd_format::format acorn_ssd_format::formats[] =
},
{ // 100k 40 track single sided single density
floppy_image::FF_35, floppy_image::SSSD, floppy_image::FM,
- 4000, 10, 40, 1, 256,{}, 0,{}, 40, 10, 10
+ 4000, 10, 40, 1, 256, {}, 0, {}, 40, 10, 10
},
{ // 200k 80 track single sided single density
floppy_image::FF_35, floppy_image::SSDD, floppy_image::FM,
- 4000, 10, 80, 1, 256,{}, 0,{}, 40, 10, 10
+ 4000, 10, 80, 1, 256, {}, 0, {}, 40, 10, 10
},
{ // 200k 40 track double sided single density
floppy_image::FF_35, floppy_image::DSSD, floppy_image::FM,
- 4000, 10, 40, 2, 256,{}, 0,{}, 40, 10, 10
+ 4000, 10, 40, 2, 256, {}, 0, {}, 40, 10, 10
},
{ // 400k 80 track double sided single density
floppy_image::FF_35, floppy_image::DSDD, floppy_image::FM,
- 4000, 10, 80, 2, 256,{}, 0,{}, 40, 10, 10
+ 4000, 10, 80, 2, 256, {}, 0, {}, 40, 10, 10
},
{ // 100k 40 track single sided single density
floppy_image::FF_3, floppy_image::SSSD, floppy_image::FM,
- 4000, 10, 40, 1, 256,{}, 0,{}, 40, 10, 10
+ 4000, 10, 40, 1, 256, {}, 0, {}, 40, 10, 10
},
{}
};
@@ -139,76 +167,88 @@ acorn_dsd_format::acorn_dsd_format() : wd177x_format(formats)
{
}
-const char *acorn_dsd_format::name() const
+const char *acorn_dsd_format::name() const noexcept
{
return "dsd";
}
-const char *acorn_dsd_format::description() const
+const char *acorn_dsd_format::description() const noexcept
{
return "Acorn DSD disk image";
}
-const char *acorn_dsd_format::extensions() const
+const char *acorn_dsd_format::extensions() const noexcept
{
return "dsd";
}
-int acorn_dsd_format::find_size(io_generic *io, uint32_t form_factor)
+int acorn_dsd_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t cat[8];
uint32_t sectors0, sectors2;
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return -1;
- for (int i = 0; formats[i].form_factor; i++) {
+ for (int i = 0; formats[i].form_factor; i++)
+ {
const format &f = formats[i];
if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
continue;
// test for Torch CPN - test pattern at sector &0018
- io_generic_read(io, cat, 0x1200, 8);
- if (memcmp(cat, "\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd", 4) == 0 && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
+ read_at(io, 0x1200, cat, 8); // FIXME: check for errors and premature EOF
+ if (memcmp(cat, "\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd", 8) == 0 && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
+ return i;
+
+ // test for HADFS - test pattern at sector 70
+ read_at(io, 0x08c10, cat, 8); // FIXME: check for errors and premature EOF
+ if (memcmp(cat, "\x00\x28\x43\x29\x4a\x47\x48\x00", 8) == 0 && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
+ return i;
+
+ // test for FLEX - from System Information Record
+ read_at(io, 0x0226, cat, 2); // FIXME: check for errors and premature EOF
+ if ((memcmp(cat, "\x4f\x14", 2) == 0 || memcmp(cat, "\x4f\x0a", 2) == 0) && size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
return i;
// read sector count from side 0 catalogue
- io_generic_read(io, cat, 0x100, 8);
- sectors0 = ((cat[6] & 3) << 8) + cat[7];
+ read_at(io, 0x100, cat, 8); // FIXME: check for errors and premature EOF
+ sectors0 = get_u16be(&cat[6]) & 0x3ff;
LOG_FORMATS("dsd: sector count 0: %d %s\n", sectors0, sectors0 % 10 != 0 ? "invalid" : "");
- if ((size <= (uint64_t)compute_track_size(f) * f.track_count * f.head_count) && (sectors0 <= f.track_count * f.sector_count)) {
+ if ((size <= (uint64_t)compute_track_size(f) * f.track_count * f.head_count) && (sectors0 <= f.track_count * f.sector_count))
+ {
// read sector count from side 2 catalogue
- io_generic_read(io, cat, 0xb00, 8); // interleaved
- sectors2 = ((cat[6] & 3) << 8) + cat[7];
+ read_at(io, 0xb00, cat, 8); // interleaved
+ sectors2 = get_u16be(&cat[6]) & 0x3ff;
// exception case for Acorn CP/M System Disc 1
- io_generic_read(io, cat, 0x97ec, 8);
- if (memcmp(cat, "/M ", 4) == 0) sectors2 = ((cat[6] & 3) << 8) + cat[7];
+ read_at(io, 0x97ec, cat, 8); // FIXME: check for errors and premature EOF
+ if (memcmp(cat, "/M ", 4) == 0)
+ sectors2 = get_u16be(&cat[6]) & 0x3ff;
LOG_FORMATS("dsd: sector count 2: %d %s\n", sectors2, sectors2 % 10 != 0 ? "invalid" : "");
- if (sectors0 > 0 && sectors0 % 10 == 0 && sectors2 > 0 && sectors2 % 10 == 0 && size <= (sectors0 + sectors2) * 256)
+ if (sectors0 > 0 && sectors2 > 0 && size <= (sectors0 + sectors2) * 256)
return i;
}
}
- LOG_FORMATS("dsd: no match\n");
return -1;
}
-int acorn_dsd_format::identify(io_generic *io, uint32_t form_factor)
+int acorn_dsd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int type = find_size(io, form_factor, variants);
if (type != -1)
- return 90;
+ return FIFID_STRUCT | FIFID_SIZE;
+
return 0;
}
-int acorn_dsd_format::get_image_offset(const format &f, int head, int track)
+int acorn_dsd_format::get_image_offset(const format &f, int head, int track) const
{
- if (f.sector_base_id == -1)
- return (track * f.head_count + head) * compute_track_size(f);
- else
- return (f.track_count * head + track) * compute_track_size(f);
+ return (track * f.head_count + head) * compute_track_size(f);
}
const acorn_dsd_format::format acorn_dsd_format::formats[] =
@@ -223,11 +263,11 @@ const acorn_dsd_format::format acorn_dsd_format::formats[] =
},
{ // 400k 80 track double sided single density (interleaved)
floppy_image::FF_35, floppy_image::DSDD, floppy_image::FM,
- 4000, 10, 80, 2, 256,{}, -1,{ 0,1,2,3,4,5,6,7,8,9 }, 40, 10, 10
+ 4000, 10, 80, 2, 256, {}, -1, { 0,1,2,3,4,5,6,7,8,9 }, 40, 10, 10
},
{ // 200k 40 track double sided single density (interleaved)
floppy_image::FF_35, floppy_image::DSSD, floppy_image::FM,
- 4000, 10, 40, 2, 256,{}, -1,{ 0,1,2,3,4,5,6,7,8,9 }, 40, 10, 10
+ 4000, 10, 40, 2, 256, {}, -1, { 0,1,2,3,4,5,6,7,8,9 }, 40, 10, 10
},
{}
};
@@ -237,43 +277,48 @@ opus_ddos_format::opus_ddos_format() : wd177x_format(formats)
{
}
-const char *opus_ddos_format::name() const
+const char *opus_ddos_format::name() const noexcept
{
return "ddos";
}
-const char *opus_ddos_format::description() const
+const char *opus_ddos_format::description() const noexcept
{
return "Opus DDOS disk image";
}
-const char *opus_ddos_format::extensions() const
+const char *opus_ddos_format::extensions() const noexcept
{
return "dds";
}
-int opus_ddos_format::find_size(io_generic *io, uint32_t form_factor)
+int opus_ddos_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t cat[8];
uint32_t sectors0, sectors2;
// read sector count from side 0 catalogue
- io_generic_read(io, cat, 0x1000, 8);
- sectors0 = (cat[1] << 8) + cat[2];
+ read_at(io, 0x1000, cat, 8); // FIXME: check for errors and premature EOF
+ sectors0 = get_u16be(&cat[1]);
LOG_FORMATS("ddos: sector count 0: %d %s\n", sectors0, sectors0 % 18 != 0 ? "invalid" : "");
- uint64_t size = io_generic_size(io);
- for (int i = 0; formats[i].form_factor; i++) {
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ for (int i = 0; formats[i].form_factor; i++)
+ {
const format &f = formats[i];
if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
continue;
- if ((size <= (uint64_t)compute_track_size(f) * f.track_count * f.head_count) && (sectors0 <= f.track_count * f.sector_count)) {
+ if ((size <= (uint64_t)compute_track_size(f) * f.track_count * f.head_count) && (sectors0 <= f.track_count * f.sector_count))
+ {
if (f.head_count == 2)
{
// read sector count from side 2 catalogue
- io_generic_read(io, cat, compute_track_size(f) * f.track_count + 0x1000, 8); // sequential
- sectors2 = (cat[1] << 8) + cat[2];
+ read_at(io, (uint64_t)compute_track_size(f) * f.track_count + 0x1000, cat, 8); // sequential
+ sectors2 = get_u16be(&cat[1]);
LOG_FORMATS("ddos: sector count 2: %d %s\n", sectors2, sectors2 % 18 != 0 ? "invalid" : "");
}
else
@@ -285,44 +330,41 @@ int opus_ddos_format::find_size(io_generic *io, uint32_t form_factor)
return i;
}
}
- LOG_FORMATS("ddos: no match\n");
return -1;
}
-int opus_ddos_format::identify(io_generic *io, uint32_t form_factor)
+int opus_ddos_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int type = find_size(io, form_factor, variants);
if (type != -1)
- return 90;
+ return FIFID_STRUCT | FIFID_SIZE;
+
return 0;
}
-int opus_ddos_format::get_image_offset(const format &f, int head, int track)
+int opus_ddos_format::get_image_offset(const format &f, int head, int track) const
{
- if (f.sector_base_id == -1)
- return (track * f.head_count + head) * compute_track_size(f);
- else
- return (f.track_count * head + track) * compute_track_size(f);
+ return (f.track_count * head + track) * compute_track_size(f);
}
const opus_ddos_format::format opus_ddos_format::formats[] =
{
{ // 180k 40 track single sided double density - gaps unverified
floppy_image::FF_525, floppy_image::SSSD, floppy_image::MFM,
- 4000, 18, 40, 1, 256, {}, 0, {}, 36, 22, 27
+ 2000, 18, 40, 1, 256, {}, 0, {}, 36, 22, 27
},
{ // 360k 80 track single sided double density - gaps unverified
floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
- 4000, 18, 80, 1, 256, {}, 0, {}, 36, 22, 27
+ 2000, 18, 80, 1, 256, {}, 0, {}, 36, 22, 27
},
{ // 360k 40 track double sided double density - gaps unverified
floppy_image::FF_525, floppy_image::DSSD, floppy_image::MFM,
- 4000, 18, 40, 2, 256, {}, 0, {}, 36, 22, 27
+ 2000, 18, 40, 2, 256, {}, 0, {}, 36, 22, 27
},
{ // 720k 80 track double sided double density - gaps unverified
floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
- 4000, 18, 80, 2, 256, {}, 0, {}, 36, 22, 27
+ 2000, 18, 80, 2, 256, {}, 0, {}, 36, 22, 27
},
{}
};
@@ -332,40 +374,50 @@ acorn_adfs_old_format::acorn_adfs_old_format() : wd177x_format(formats)
{
}
-const char *acorn_adfs_old_format::name() const
+const char *acorn_adfs_old_format::name() const noexcept
{
return "adfs_o";
}
-const char *acorn_adfs_old_format::description() const
+const char *acorn_adfs_old_format::description() const noexcept
{
return "Acorn ADFS (OldMap) disk image";
}
-const char *acorn_adfs_old_format::extensions() const
+const char *acorn_adfs_old_format::extensions() const noexcept
{
return "adf,ads,adm,adl";
}
-int acorn_adfs_old_format::find_size(io_generic *io, uint32_t form_factor)
+int acorn_adfs_old_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
+ std::error_condition err;
+ size_t actual;
uint8_t map[3];
uint32_t sectors;
uint8_t oldmap[4];
// read sector count from free space map
- io_generic_read(io, map, 0xfc, 3);
- sectors = map[0] + (map[1] << 8) + (map[2] << 16);
+ std::tie(err, actual) = read_at(io, 0xfc, map, 3);
+ if (err || (3 != actual))
+ return -1;
+ sectors = get_u24le(map);
LOG_FORMATS("adfs_o: sector count %d %s\n", sectors, sectors % 16 != 0 ? "invalid" : "");
// read map identifier
- io_generic_read(io, oldmap, 0x201, 4);
+ std::tie(err, actual) = read_at(io, 0x201, oldmap, 4);
+ if (err || (4 != actual))
+ return -1;
LOG_FORMATS("adfs_o: map identifier %s %s\n", oldmap, memcmp(oldmap, "Hugo", 4) != 0 ? "invalid" : "");
- uint64_t size = io_generic_size(io);
- for(int i=0; formats[i].form_factor; i++) {
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ for (int i = 0; formats[i].form_factor; i++)
+ {
const format &f = formats[i];
- if(form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
+ if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
continue;
// valid images will have map identifier 'Hugo' and sector counts adfs-s = 0x280; adfs-m = 0x500; adfs-l = 0xa00; adfs-dos = 0xaa0; though many adfs-s images are incorrect
@@ -373,25 +425,22 @@ int acorn_adfs_old_format::find_size(io_generic *io, uint32_t form_factor)
return i;
}
}
- LOG_FORMATS("adfs_o: no match\n");
return -1;
}
-int acorn_adfs_old_format::identify(io_generic *io, uint32_t form_factor)
+int acorn_adfs_old_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int type = find_size(io, form_factor, variants);
+
+ if (type != -1)
+ return FIFID_STRUCT | FIFID_SIZE;
- if(type != -1)
- return 100;
return 0;
}
-int acorn_adfs_old_format::get_image_offset(const format &f, int head, int track)
+int acorn_adfs_old_format::get_image_offset(const format &f, int head, int track) const
{
- if (f.sector_base_id == -1)
- return (track * f.head_count + head) * compute_track_size(f);
- else
- return (f.track_count * head + track) * compute_track_size(f);
+ return (track * f.head_count + head) * compute_track_size(f);
}
const acorn_adfs_old_format::format acorn_adfs_old_format::formats[] =
@@ -428,67 +477,72 @@ acorn_adfs_new_format::acorn_adfs_new_format() : wd177x_format(formats)
{
}
-const char *acorn_adfs_new_format::name() const
+const char *acorn_adfs_new_format::name() const noexcept
{
return "adfs_n";
}
-const char *acorn_adfs_new_format::description() const
+const char *acorn_adfs_new_format::description() const noexcept
{
return "Acorn ADFS (NewMap) disk image";
}
-const char *acorn_adfs_new_format::extensions() const
+const char *acorn_adfs_new_format::extensions() const noexcept
{
return "adf";
}
-int acorn_adfs_new_format::find_size(io_generic *io, uint32_t form_factor)
+int acorn_adfs_new_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
+ std::error_condition err;
+ size_t actual;
uint8_t dform[4];
uint8_t eform[4];
// read map identifiers for D and E formats
- io_generic_read(io, dform, 0x401, 4);
+ std::tie(err, actual) = read_at(io, 0x401, dform, 4);
+ if (err || (4 != actual))
+ return -1;
LOG_FORMATS("adfs_n: map identifier (D format) %s %s\n", dform, (memcmp(dform, "Hugo", 4) != 0 && memcmp(dform, "Nick", 4) != 0) ? "invalid" : "");
- io_generic_read(io, eform, 0x801, 4);
+ std::tie(err, actual) = read_at(io, 0x801, eform, 4);
+ if (err || (4 != actual))
+ return -1;
LOG_FORMATS("adfs_n: map identifier (E format) %s %s\n", eform, memcmp(eform, "Nick", 4) != 0 ? "invalid" : "");
- uint64_t size = io_generic_size(io);
- for (int i = 0; formats[i].form_factor; i++) {
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ for (int i = 0; formats[i].form_factor; i++)
+ {
const format &f = formats[i];
if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
continue;
// no further checks for 1600K images
- if ((size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count) && size == 0x190000) {
+ if ((size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count) && size == 0x190000)
return i;
- }
// valid 800K images will have map identifier Nick, Arthur D format still use Hugo
- if ((size <= (uint64_t)compute_track_size(f) * f.track_count * f.head_count) && (memcmp(dform, "Hugo", 4) == 0 || memcmp(dform, "Nick", 4) == 0 || memcmp(eform, "Nick", 4) == 0)) {
+ if ((size <= (uint64_t)compute_track_size(f) * f.track_count * f.head_count) && (memcmp(dform, "Hugo", 4) == 0 || memcmp(dform, "Nick", 4) == 0 || memcmp(eform, "Nick", 4) == 0))
return i;
- }
}
- LOG_FORMATS("adfs_n: no match\n");
return -1;
}
-int acorn_adfs_new_format::identify(io_generic *io, uint32_t form_factor)
+int acorn_adfs_new_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int type = find_size(io, form_factor, variants);
if (type != -1)
- return 100;
+ return FIFID_STRUCT | FIFID_SIZE;
+
return 0;
}
-int acorn_adfs_new_format::get_image_offset(const format &f, int head, int track)
+int acorn_adfs_new_format::get_image_offset(const format &f, int head, int track) const
{
- if (f.sector_base_id == -1)
- return (track * f.head_count + head) * compute_track_size(f);
- else
- return (f.track_count * head + track) * compute_track_size(f);
+ return (track * f.head_count + head) * compute_track_size(f);
}
const acorn_adfs_new_format::format acorn_adfs_new_format::formats[] =
@@ -497,9 +551,9 @@ const acorn_adfs_new_format::format acorn_adfs_new_format::formats[] =
floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM,
2000, 5, 80, 2, 1024, {}, -1, { 0,1,2,3,4 }, 32, 22, 90
},
- { // F - 1600K 3 1/2 inch 80 track double sided quad density
+ { // F - 1600K 3 1/2 inch 80 track double sided high density
floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
- 2000, 10, 80, 2, 1024, {}, -1, { 0,1,2,3,4,5,6,7,8,9 }, 50, 22, 90
+ 1000, 10, 80, 2, 1024, {}, -1, { 0,1,2,3,4,5,6,7,8,9 }, 50, 22, 90
},
{}
};
@@ -509,57 +563,62 @@ acorn_dos_format::acorn_dos_format() : wd177x_format(formats)
{
}
-const char *acorn_dos_format::name() const
+const char *acorn_dos_format::name() const noexcept
{
return "dos";
}
-const char *acorn_dos_format::description() const
+const char *acorn_dos_format::description() const noexcept
{
return "Acorn DOS disk image";
}
-const char *acorn_dos_format::extensions() const
+const char *acorn_dos_format::extensions() const noexcept
{
return "img";
}
-int acorn_dos_format::find_size(io_generic *io, uint32_t form_factor)
+int acorn_dos_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint8_t type;
+ uint64_t size;
+ if (io.length(size))
+ return -1;
- uint64_t size = io_generic_size(io);
- for(int i=0; formats[i].form_factor; i++) {
+ for (int i=0; formats[i].form_factor; i++)
+ {
const format &f = formats[i];
- if(form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
+ if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
continue;
- if (size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count) {
+ if (size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
+ {
// read media type ID from FAT - Acorn DOS = 0xfd
- io_generic_read(io, &type, 0, 1);
- LOG_FORMATS("dos: 800k media type id %02X %s\n", type, type != 0xfd ? "invalid" : "");
- if (type == 0xfd) return i;
+ uint8_t type;
+ auto const [err, actual] = read_at(io, 0, &type, 1);
+ if (!err && (1 == actual))
+ {
+ LOG_FORMATS("dos: 800k media type id %02X %s\n", type, type != 0xfd ? "invalid" : "");
+ if (type == 0xfd)
+ return i;
+ }
}
}
- LOG_FORMATS("dos: no match\n");
return -1;
}
-int acorn_dos_format::identify(io_generic *io, uint32_t form_factor)
+int acorn_dos_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int type = find_size(io, form_factor, variants);
+
+ if (type != -1)
+ return FIFID_STRUCT | FIFID_SIZE;
- if(type != -1)
- return 90;
return 0;
}
-int acorn_dos_format::get_image_offset(const format &f, int head, int track)
+int acorn_dos_format::get_image_offset(const format &f, int head, int track) const
{
- if (f.sector_base_id == -1)
- return (track * f.head_count + head) * compute_track_size(f);
- else
- return (f.track_count * head + track) * compute_track_size(f);
+ return (track * f.head_count + head) * compute_track_size(f);
}
const acorn_dos_format::format acorn_dos_format::formats[] =
@@ -576,65 +635,71 @@ opus_ddcpm_format::opus_ddcpm_format()
{
}
-const char *opus_ddcpm_format::name() const
+const char *opus_ddcpm_format::name() const noexcept
{
return "ddcpm";
}
-const char *opus_ddcpm_format::description() const
+const char *opus_ddcpm_format::description() const noexcept
{
return "Opus DD CP/M disk image";
}
-const char *opus_ddcpm_format::extensions() const
+const char *opus_ddcpm_format::extensions() const noexcept
{
return "ssd";
}
-bool opus_ddcpm_format::supports_save() const
+bool opus_ddcpm_format::supports_save() const noexcept
{
return false;
}
-int opus_ddcpm_format::identify(io_generic *io, uint32_t form_factor)
+int opus_ddcpm_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t h[8];
+ auto const [err, actual] = read_at(io, 0, h, 8);
+ if (err || (8 != actual))
+ return 0;
- io_generic_read(io, h, 0, 8);
+ uint64_t size;
+ if (io.length(size))
+ return 0;
+
+ if (size == 819200 && memcmp(h, "Slogger ", 8) == 0)
+ return FIFID_SIGN | FIFID_SIZE;
- if (io_generic_size(io) == 819200 && memcmp(h, "Slogger ", 8) == 0)
- return 100;
- LOG_FORMATS("ddcpm: no match\n");
return 0;
}
-bool opus_ddcpm_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
-{
-// Double density discs formatted with DDCPM :
-//
-// Tracks 0 - 2 formatted Single Density
-// Tracks 3 - 159 formatted Double Density
-//
-// Single density tracks are 10 x 256 byte sectors per track
-// Sector skew of 2
-//
-// Double Density tracks are 10 x 512 byte sectors per track
-// Sector skew of 1
-// Sector interleave of 2
-//
- int spt, bps;
-
- for (int head = 0; head < 2; head++) {
- for (int track = 0; track < 80; track++) {
- bool mfm = track > 2 || head;
- bps = mfm ? 512 : 256;
- spt = 10;
+bool opus_ddcpm_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ // Double density discs formatted with DDCPM :
+ //
+ // Tracks 0 - 2 formatted Single Density
+ // Tracks 3 - 159 formatted Double Density
+ //
+ // Single density tracks are 10 x 256 byte sectors per track
+ // Sector skew of 2
+ //
+ // Double Density tracks are 10 x 512 byte sectors per track
+ // Sector skew of 1
+ // Sector interleave of 2
+ //
+ for (int head = 0; head < 2; head++)
+ {
+ for (int track = 0; track < 80; track++)
+ {
+ bool const mfm = track > 2 || head;
+ int const bps = mfm ? 512 : 256;
+ int const spt = 10;
desc_pc_sector sects[10];
uint8_t sectdata[10*512];
- io_generic_read(io, sectdata, head * 80 * spt * 512 + track * spt * 512, spt * 512);
+ /*auto const [err, actual] =*/ read_at(io, head * 80 * spt * 512 + track * spt * 512, sectdata, spt * 512); // FIXME: check for errors and premature EOF
- for (int i = 0; i < spt; i++) {
+ for (int i = 0; i < spt; i++)
+ {
sects[i].track = track;
sects[i].head = head;
sects[i].sector = i;
@@ -655,16 +720,98 @@ bool opus_ddcpm_format::load(io_generic *io, uint32_t form_factor, floppy_image
return true;
}
-bool opus_ddcpm_format::save(io_generic *io, floppy_image *image)
+bool opus_ddcpm_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
return false;
}
-const floppy_format_type FLOPPY_ACORN_SSD_FORMAT = &floppy_image_format_creator<acorn_ssd_format>;
-const floppy_format_type FLOPPY_ACORN_DSD_FORMAT = &floppy_image_format_creator<acorn_dsd_format>;
-const floppy_format_type FLOPPY_ACORN_DOS_FORMAT = &floppy_image_format_creator<acorn_dos_format>;
-const floppy_format_type FLOPPY_ACORN_ADFS_OLD_FORMAT = &floppy_image_format_creator<acorn_adfs_old_format>;
-const floppy_format_type FLOPPY_ACORN_ADFS_NEW_FORMAT = &floppy_image_format_creator<acorn_adfs_new_format>;
-const floppy_format_type FLOPPY_OPUS_DDOS_FORMAT = &floppy_image_format_creator<opus_ddos_format>;
-const floppy_format_type FLOPPY_OPUS_DDCPM_FORMAT = &floppy_image_format_creator<opus_ddcpm_format>;
+cumana_dfs_format::cumana_dfs_format() : wd177x_format(formats)
+{
+}
+
+const char *cumana_dfs_format::name() const noexcept
+{
+ return "cdfs";
+}
+
+const char *cumana_dfs_format::description() const noexcept
+{
+ return "Cumana DFS disk image";
+}
+
+const char *cumana_dfs_format::extensions() const noexcept
+{
+ return "img";
+}
+
+int cumana_dfs_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ for (int i = 0; formats[i].form_factor; i++)
+ {
+ const format &f = formats[i];
+ if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
+ continue;
+
+ if (size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
+ {
+ uint8_t info;
+ auto const [err, actual] = read_at(io, 14, &info, 1);
+ if (err || (actual != 1))
+ return -1;
+ if (f.head_count == (util::BIT(info, 6) ? 2 : 1) && f.track_count == (util::BIT(info, 7) ? 80 : 40))
+ return i;
+ }
+ }
+ return -1;
+}
+
+int cumana_dfs_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ int type = find_size(io, form_factor, variants);
+
+ if (type != -1)
+ return FIFID_STRUCT | FIFID_SIZE;
+
+ return 0;
+}
+
+int cumana_dfs_format::get_image_offset(const format &f, int head, int track) const
+{
+ return (track * f.head_count + head) * compute_track_size(f);
+}
+
+const cumana_dfs_format::format cumana_dfs_format::formats[] =
+{
+ { // 180K 5 1/4 inch 40 track single sided double density - gaps unverified
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 9, 40, 1, 512, {}, -1, { 0,3,6,1,4,7,2,5,8 }, 32, 22, 90
+ },
+ { // 360K 5 1/4 inch 40 track double sided double density - gaps unverified
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 9, 40, 2, 512, {}, -1, { 0,3,6,1,4,7,2,5,8 }, 32, 22, 90
+ },
+ { // 360K 5 1/4 inch 80 track single sided double density - gaps unverified
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 9, 80, 1, 512, {}, -1, { 0,3,6,1,4,7,2,5,8 }, 32, 22, 90
+ },
+ { // 720K 5 1/4 inch 80 track single double double density - gaps unverified
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 9, 80, 2, 512, {}, -1, { 0,3,6,1,4,7,2,5,8 }, 32, 22, 90
+ },
+ {}
+};
+
+
+const acorn_ssd_format FLOPPY_ACORN_SSD_FORMAT;
+const acorn_dsd_format FLOPPY_ACORN_DSD_FORMAT;
+const acorn_dos_format FLOPPY_ACORN_DOS_FORMAT;
+const acorn_adfs_old_format FLOPPY_ACORN_ADFS_OLD_FORMAT;
+const acorn_adfs_new_format FLOPPY_ACORN_ADFS_NEW_FORMAT;
+const opus_ddos_format FLOPPY_OPUS_DDOS_FORMAT;
+const opus_ddcpm_format FLOPPY_OPUS_DDCPM_FORMAT;
+const cumana_dfs_format FLOPPY_CUMANA_DFS_FORMAT;
diff --git a/src/lib/formats/acorn_dsk.h b/src/lib/formats/acorn_dsk.h
index 352ae3625d3..5dc870a914f 100644
--- a/src/lib/formats/acorn_dsk.h
+++ b/src/lib/formats/acorn_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best, Nigel Barnes
/***************************************************************************
@@ -15,124 +15,149 @@
#include "flopimg.h"
#include "wd177x_dsk.h"
+
class acorn_ssd_format : public wd177x_format
{
public:
acorn_ssd_format();
- virtual int find_size(io_generic *io, uint32_t form_factor) override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual int get_image_offset(const format &f, int head, int track) override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
+
class acorn_dsd_format : public wd177x_format
{
public:
acorn_dsd_format();
- virtual int find_size(io_generic *io, uint32_t form_factor) override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual int get_image_offset(const format &f, int head, int track) override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
+
class opus_ddos_format : public wd177x_format
{
public:
opus_ddos_format();
- virtual int find_size(io_generic *io, uint32_t form_factor) override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual int get_image_offset(const format &f, int head, int track) override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
+
class acorn_adfs_old_format : public wd177x_format
{
public:
acorn_adfs_old_format();
- virtual int find_size(io_generic *io, uint32_t form_factor) override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual int get_image_offset(const format &f, int head, int track) override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
+
class acorn_adfs_new_format : public wd177x_format
{
public:
acorn_adfs_new_format();
- virtual int find_size(io_generic *io, uint32_t form_factor) override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual int get_image_offset(const format &f, int head, int track) override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
+
class acorn_dos_format : public wd177x_format
{
public:
acorn_dos_format();
- virtual int find_size(io_generic *io, uint32_t form_factor) override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual int get_image_offset(const format &f, int head, int track) override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
+
class opus_ddcpm_format : public floppy_image_format_t
{
public:
opus_ddcpm_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
+};
+
+
+class cumana_dfs_format : public wd177x_format
+{
+public:
+ cumana_dfs_format();
+
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+private:
+ static const format formats[];
};
-extern const floppy_format_type FLOPPY_ACORN_SSD_FORMAT;
-extern const floppy_format_type FLOPPY_ACORN_DSD_FORMAT;
-extern const floppy_format_type FLOPPY_ACORN_DOS_FORMAT;
-extern const floppy_format_type FLOPPY_ACORN_ADFS_OLD_FORMAT;
-extern const floppy_format_type FLOPPY_ACORN_ADFS_NEW_FORMAT;
-extern const floppy_format_type FLOPPY_OPUS_DDOS_FORMAT;
-extern const floppy_format_type FLOPPY_OPUS_DDCPM_FORMAT;
+extern const acorn_ssd_format FLOPPY_ACORN_SSD_FORMAT;
+extern const acorn_dsd_format FLOPPY_ACORN_DSD_FORMAT;
+extern const acorn_dos_format FLOPPY_ACORN_DOS_FORMAT;
+extern const acorn_adfs_old_format FLOPPY_ACORN_ADFS_OLD_FORMAT;
+extern const acorn_adfs_new_format FLOPPY_ACORN_ADFS_NEW_FORMAT;
+extern const opus_ddos_format FLOPPY_OPUS_DDOS_FORMAT;
+extern const opus_ddcpm_format FLOPPY_OPUS_DDCPM_FORMAT;
+extern const cumana_dfs_format FLOPPY_CUMANA_DFS_FORMAT;
#endif // MAME_FORMATS_ACORN_DSK_H
diff --git a/src/lib/formats/adam_cas.cpp b/src/lib/formats/adam_cas.cpp
index 3de9413cb90..b0abdb9be2d 100644
--- a/src/lib/formats/adam_cas.cpp
+++ b/src/lib/formats/adam_cas.cpp
@@ -1,11 +1,9 @@
// license:BSD-3-Clause
// copyright-holders:Curt Coder
-#include <assert.h>
-
-#include "cassimg.h"
#include "adam_cas.h"
+
// This code will reproduce the timing of an adam tape played back on a standard tape deck with a 1 7/8ips speed.
#define CASS_AMP 0x1fffffff
#define SPEED_MULTIPLIER 20.0/1.875
@@ -50,7 +48,7 @@ Data is phase encoded, with 70us bit cells with a transition at 31us for a 1 and
****************************************************************/
-static cassette_image::error coladam_ddp_identify ( cassette_image *cass, struct CassetteOptions *opts )
+static cassette_image::error coladam_ddp_identify ( cassette_image *cass, cassette_image::Options *opts )
{
opts -> bits_per_sample = 16;
opts -> channels = 2;
@@ -67,14 +65,14 @@ cassette_image::error coladam_put_byte(cassette_image *cass, int channel, double
{
if(byte & 0x80)
{
- err = cassette_put_sample( cass, channel, *time_index, COL_ADAM_PERIOD1, -CASS_AMP*(*prev_sign) );
+ err = cass->put_sample( channel, *time_index, COL_ADAM_PERIOD1, -CASS_AMP*(*prev_sign) );
(*time_index) += COL_ADAM_PERIOD1;
- err = cassette_put_sample( cass, channel, *time_index, COL_ADAM_PERIOD2, CASS_AMP*(*prev_sign) );
+ err = cass->put_sample( channel, *time_index, COL_ADAM_PERIOD2, CASS_AMP*(*prev_sign) );
(*time_index) += COL_ADAM_PERIOD2;
}
else
{
- err = cassette_put_sample( cass, channel, *time_index, COL_ADAM_PERIOD, -CASS_AMP*(*prev_sign) );
+ err = cass->put_sample( channel, *time_index, COL_ADAM_PERIOD, -CASS_AMP*(*prev_sign) );
(*prev_sign) *=-1;
(*time_index) += COL_ADAM_PERIOD;
}
@@ -146,12 +144,12 @@ static cassette_image::error coladam_ddp_load( cassette_image *cass )
for (block = 0; block < 128; block++)
{
- cassette_image_read( cass, buffer, 0x20000+0x400*block, 0x400 );
+ cass->image_read( buffer, 0x20000+0x400*block, 0x400 );
err = coladam_put_block(cass, 0, &time, &prev_sign, block, buffer, layout_type);
}
for (block = 128; block < 131; block++)
{
- cassette_image_read( cass, buffer, 0x3f400+0x400*(block-128), 0x400 );
+ cass->image_read( buffer, 0x3f400+0x400*(block-128), 0x400 );
err = coladam_put_block(cass, 0, &time, &prev_sign, block, buffer, layout_type);
}
@@ -165,17 +163,17 @@ static cassette_image::error coladam_ddp_load( cassette_image *cass )
{
for (block = 0; block < 64; block++)
{
- cassette_image_read( cass, buffer, 0x10000+0x400*block, 0x400 );
+ cass->image_read( buffer, 0x10000+0x400*block, 0x400 );
err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type);
}
for (block = 64; block < 128; block++)
{
- cassette_image_read( cass, buffer, 0x00000+0x400*(block-64), 0x400 );
+ cass->image_read( buffer, 0x00000+0x400*(block-64), 0x400 );
err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type);
}
for (block = 128; block < 131; block++)
{
- cassette_image_read( cass, buffer, 0x0f400+0x400*(block-128), 0x400 );
+ cass->image_read( buffer, 0x0f400+0x400*(block-128), 0x400 );
err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type);
}
}
@@ -188,12 +186,12 @@ static cassette_image::error coladam_ddp_load( cassette_image *cass )
}
for (block = 0; block < 128; block++)
{
- cassette_image_read( cass, buffer, 0x400*block, 0x400 );
+ cass->image_read( buffer, 0x400*block, 0x400 );
err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type);
}
for (block = 128; block < 131; block++)
{
- cassette_image_read( cass, buffer, 0x1f400+0x400*(block-128), 0x400 );
+ cass->image_read( buffer, 0x1f400+0x400*(block-128), 0x400 );
err = coladam_put_block(cass, 1, &time, &prev_sign, block, buffer, layout_type);
}
}
@@ -203,7 +201,7 @@ static cassette_image::error coladam_ddp_load( cassette_image *cass )
-static const struct CassetteFormat coladam_ddp =
+static const cassette_image::Format coladam_ddp =
{ "ddp", coladam_ddp_identify, coladam_ddp_load, nullptr /* no save */ };
CASSETTE_FORMATLIST_START(coleco_adam_cassette_formats)
diff --git a/src/lib/formats/adam_dsk.cpp b/src/lib/formats/adam_dsk.cpp
index e78e94a4763..04848acd07c 100644
--- a/src/lib/formats/adam_dsk.cpp
+++ b/src/lib/formats/adam_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/adam_dsk.c
+ formats/adam_dsk.cpp
Coleco Adam disk image format
*********************************************************************/
-#include <assert.h>
-
#include "formats/adam_dsk.h"
adam_format::adam_format() : wd177x_format(formats)
{
}
-const char *adam_format::name() const
+const char *adam_format::name() const noexcept
{
return "adam";
}
-const char *adam_format::description() const
+const char *adam_format::description() const noexcept
{
return "Coleco Adam disk image";
}
-const char *adam_format::extensions() const
+const char *adam_format::extensions() const noexcept
{
return "dsk";
}
@@ -67,4 +65,4 @@ const adam_format::format adam_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_ADAM_FORMAT = &floppy_image_format_creator<adam_format>;
+const adam_format FLOPPY_ADAM_FORMAT;
diff --git a/src/lib/formats/adam_dsk.h b/src/lib/formats/adam_dsk.h
index 0ac28d0522f..98aaf24216f 100644
--- a/src/lib/formats/adam_dsk.h
+++ b/src/lib/formats/adam_dsk.h
@@ -19,14 +19,14 @@ class adam_format : public wd177x_format
public:
adam_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_ADAM_FORMAT;
+extern const adam_format FLOPPY_ADAM_FORMAT;
#endif // MAME_FORMATS_ADAM_DSK_H
diff --git a/src/lib/formats/afs_dsk.cpp b/src/lib/formats/afs_dsk.cpp
index c6a24067fa7..5a1d9a63526 100644
--- a/src/lib/formats/afs_dsk.cpp
+++ b/src/lib/formats/afs_dsk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/***************************************************************************
@@ -15,31 +15,32 @@ afs_format::afs_format() : wd177x_format(formats)
{
}
-const char *afs_format::name() const
+const char *afs_format::name() const noexcept
{
return "afs";
}
-const char *afs_format::description() const
+const char *afs_format::description() const noexcept
{
return "Acorn FileStore disk image";
}
-const char *afs_format::extensions() const
+const char *afs_format::extensions() const noexcept
{
return "adl,img";
}
-int afs_format::identify(io_generic *io, uint32_t form_factor)
+int afs_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int type = find_size(io, form_factor, variants);
if (type != -1)
- return 50;
+ return FIFID_SIZE;
+
return 0;
}
-int afs_format::get_image_offset(const format &f, int head, int track)
+int afs_format::get_image_offset(const format &f, int head, int track) const
{
if (f.sector_base_id == -1)
return (track * f.head_count + head) * compute_track_size(f);
@@ -57,4 +58,4 @@ const afs_format::format afs_format::formats[] =
};
-const floppy_format_type FLOPPY_AFS_FORMAT = &floppy_image_format_creator<afs_format>;
+const afs_format FLOPPY_AFS_FORMAT;
diff --git a/src/lib/formats/afs_dsk.h b/src/lib/formats/afs_dsk.h
index f1859615ade..c816ed2b806 100644
--- a/src/lib/formats/afs_dsk.h
+++ b/src/lib/formats/afs_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Nigel Barnes
/***************************************************************************
@@ -12,8 +12,6 @@
#pragma once
-#pragma once
-
#include "wd177x_dsk.h"
@@ -22,17 +20,17 @@ class afs_format : public wd177x_format
public:
afs_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual int get_image_offset(const format &f, int head, int track) override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_AFS_FORMAT;
+extern const afs_format FLOPPY_AFS_FORMAT;
#endif // MAME_FORMATS_AFS_DSK_H
diff --git a/src/lib/formats/agat840k_hle_dsk.cpp b/src/lib/formats/agat840k_hle_dsk.cpp
index 52cdd4668be..e873a8563c8 100644
--- a/src/lib/formats/agat840k_hle_dsk.cpp
+++ b/src/lib/formats/agat840k_hle_dsk.cpp
@@ -12,9 +12,10 @@
************************************************************************/
-#include <assert.h>
+#include "agat840k_hle_dsk.h"
+#include "imageutl.h"
-#include "formats/agat840k_hle_dsk.h"
+#include <cstring>
static FLOPPY_IDENTIFY(agat840k_hle_dsk_identify)
diff --git a/src/lib/formats/agat840k_hle_dsk.h b/src/lib/formats/agat840k_hle_dsk.h
index 6e8ad4036ee..32c4bd03808 100644
--- a/src/lib/formats/agat840k_hle_dsk.h
+++ b/src/lib/formats/agat840k_hle_dsk.h
@@ -10,7 +10,7 @@
#pragma once
-#include "flopimg.h"
+#include "flopimg_legacy.h"
#include "formats/basicdsk.h"
LEGACY_FLOPPY_OPTIONS_EXTERN(agat840k_hle);
diff --git a/src/lib/formats/aim_dsk.cpp b/src/lib/formats/aim_dsk.cpp
index 3bc2c2b5b5f..c33cab8fb12 100644
--- a/src/lib/formats/aim_dsk.cpp
+++ b/src/lib/formats/aim_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Sergey Svishchev
/*********************************************************************
- formats/aim_dsk.h
+ formats/aim_dsk.cpp
AIM disk images
@@ -12,48 +12,50 @@
*********************************************************************/
-#include <assert.h>
-
#include "aim_dsk.h"
+#include "ioprocs.h"
+
aim_format::aim_format()
{
}
-const char *aim_format::name() const
+const char *aim_format::name() const noexcept
{
return "aim";
}
-const char *aim_format::description() const
+const char *aim_format::description() const noexcept
{
return "AIM disk image";
}
-const char *aim_format::extensions() const
+const char *aim_format::extensions() const noexcept
{
return "aim";
}
-int aim_format::identify(io_generic *io, uint32_t form_factor)
+int aim_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- if (io_generic_size(io) == 2068480)
- {
- return 100;
- }
+ uint64_t size;
+ if (io.length(size))
+ return 0;
+
+ if (size == 2068480)
+ return FIFID_SIZE;
return 0;
}
-bool aim_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool aim_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- image->set_variant(floppy_image::DSQD);
+ image.set_variant(floppy_image::DSQD);
const int tracks = 80;
const int track_size = 6464 * 2;
@@ -66,23 +68,42 @@ bool aim_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
std::vector<uint8_t> track_data(track_size);
std::vector<uint32_t> raw_track_data;
int data_count = 0;
+ int splice_pos = -1;
bool header = false;
// Read track
- io_generic_read(io, &track_data[0], ( heads * track + head ) * track_size, track_size);
+ /*auto const [err, actual] =*/ read_at(io, (heads * track + head) * track_size, &track_data[0], track_size);
+ // FIXME: check for error and premature EOF
+ // Find first sector header or index mark
for (int offset = 0; offset < track_size; offset += 2)
{
- switch (track_data[offset + 1] & 1)
+ if (track_data[offset + 1] == 1 && splice_pos < 0)
+ {
+ splice_pos = offset - (20 * 2);
+ }
+ if (track_data[offset + 1] == 3)
+ {
+ splice_pos = offset;
+ }
+ }
+ if (splice_pos < 0)
+ {
+ splice_pos = 0;
+ }
+
+ for (int offset = splice_pos; offset < track_size + splice_pos; offset += 2)
+ {
+ switch (track_data[(offset + 1) % track_size])
{
- case 0:
+ case 0: // regular data
if (data_count == 0)
- header = (track_data[offset] == 0x95) ? true : false;
+ header = (track_data[offset % track_size] == 0x95) ? true : false;
data_count++;
- mfm_w(raw_track_data, 8, track_data[offset]);
+ mfm_w(raw_track_data, 8, track_data[offset % track_size]);
break;
- case 1:
+ case 1: // sync mark
if (header && data_count < 11) // XXX hack
{
for (; data_count < 12; data_count++)
@@ -94,6 +115,13 @@ bool aim_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
raw_w(raw_track_data, 16, 0x5555);
data_count = 0;
break;
+
+ // TELETEXT.AIM and others
+ case 0xff:
+ break;
+
+ default:
+ break;
}
}
@@ -105,4 +133,4 @@ bool aim_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
}
-const floppy_format_type FLOPPY_AIM_FORMAT = &floppy_image_format_creator<aim_format>;
+const aim_format FLOPPY_AIM_FORMAT;
diff --git a/src/lib/formats/aim_dsk.h b/src/lib/formats/aim_dsk.h
index 75229875c69..73f3cd2843a 100644
--- a/src/lib/formats/aim_dsk.h
+++ b/src/lib/formats/aim_dsk.h
@@ -7,9 +7,10 @@
AIM disk images
*********************************************************************/
+#ifndef MAME_FORMATS_AIM_DSK_H
+#define MAME_FORMATS_AIM_DSK_H
-#ifndef AIM_DSK_H
-#define AIM_DSK_H
+#pragma once
#include "flopimg.h"
@@ -20,15 +21,15 @@ class aim_format : public floppy_image_format_t
public:
aim_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override { return false; }
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override { return false; }
};
-extern const floppy_format_type FLOPPY_AIM_FORMAT;
+extern const aim_format FLOPPY_AIM_FORMAT;
-#endif /* AIM_DSK_H */
+#endif // MAME_FORMATS_AIM_DSK_H
diff --git a/src/lib/formats/all.cpp b/src/lib/formats/all.cpp
new file mode 100644
index 00000000000..bab86919cb7
--- /dev/null
+++ b/src/lib/formats/all.cpp
@@ -0,0 +1,1542 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Enumerate all the compiled-in formats
+
+#include "all.h"
+
+#include "has_formats.h" // Generated by genie
+
+#include "cqm_dsk.h"
+#include "dsk_dsk.h"
+#include "td0_dsk.h"
+#include "hxchfe_dsk.h"
+#include "hxcmfm_dsk.h"
+#include "mfi_dsk.h"
+#include "imd_dsk.h"
+#include "upd765_dsk.h"
+#include "pc_dsk.h"
+#include "d88_dsk.h"
+#include "dfi_dsk.h"
+#include "fs_unformatted.h"
+
+#ifdef HAS_FORMATS_2D_DSK
+#include "2d_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_A26_CAS
+#include "a26_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_A5105_DSK
+#include "a5105_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ABC800_DSK
+#include "abc800_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ABC800I_DSK
+#include "abc800i_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ABCFD2_DSK
+#include "abcfd2_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ACE_TAP
+#include "ace_tap.h"
+#endif
+
+#ifdef HAS_FORMATS_ACORN_DSK
+#include "acorn_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ADAM_CAS
+#include "adam_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_ADAM_DSK
+#include "adam_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_AFS_DSK
+#include "afs_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_AIM_DSK
+#include "aim_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_AMI_DSK
+#include "ami_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_AP2_DSK
+#include "ap2_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_APD_DSK
+#include "apd_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_APF_APT
+#include "apf_apt.h"
+#endif
+
+#ifdef HAS_FORMATS_APOLLO_DSK
+#include "apollo_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_APPLIX_DSK
+#include "applix_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_APRIDISK
+#include "apridisk.h"
+#endif
+
+#ifdef HAS_FORMATS_AP_DSK35
+#include "ap_dsk35.h"
+#endif
+
+#ifdef HAS_FORMATS_AQUARIUS_CAQ
+#include "aquarius_caq.h"
+#endif
+
+#ifdef HAS_FORMATS_ASST128_DSK
+#include "asst128_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_AS_DSK
+#include "as_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ATARI_DSK
+#include "atari_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ATOM_DSK
+#include "atom_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ATOM_TAP
+#include "atom_tap.h"
+#endif
+
+#ifdef HAS_FORMATS_BW12_DSK
+#include "bw12_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_BW2_DSK
+#include "bw2_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_C3040_DSK
+#include "c3040_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_C4040_DSK
+#include "c4040_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_C8280_DSK
+#include "c8280_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_CAMPLYNX_CAS
+#include "camplynx_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_CAMPLYNX_DSK
+#include "camplynx_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_CBM_CRT
+#include "cbm_crt.h"
+#endif
+
+#ifdef HAS_FORMATS_CBM_TAP
+#include "cbm_tap.h"
+#endif
+
+#ifdef HAS_FORMATS_CCVF_DSK
+#include "ccvf_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_CGENIE_DSK
+#include "cgenie_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_CGEN_CAS
+#include "cgen_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_COCO_CAS
+#include "coco_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_COMX35_DSK
+#include "comx35_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_CONCEPT_DSK
+#include "concept_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_COUPEDSK
+#include "coupedsk.h"
+#endif
+
+#ifdef HAS_FORMATS_CPIS_DSK
+#include "cpis_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_CSW_CAS
+#include "csw_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_D64_DSK
+#include "d64_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_D71_DSK
+#include "d71_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_D80_DSK
+#include "d80_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_D81_DSK
+#include "d81_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_D82_DSK
+#include "d82_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_DCP_DSK
+#include "dcp_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_DIM_DSK
+#include "dim_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_DIP_DSK
+#include "dip_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_DMK_DSK
+#include "dmk_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_DS9_DSK
+#include "ds9_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_SDF_DSK
+#include "sdf_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_EP64_DSK
+#include "ep64_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_DMV_DSK
+#include "dmv_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_DVK_MX_DSK
+#include "dvk_mx_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ESQ16_DSK
+#include "esq16_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ESQ8_DSK
+#include "esq8_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_EXCALI64_DSK
+#include "excali64_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_FC100_CAS
+#include "fc100_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_FDD_DSK
+#include "fdd_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_FL1_DSK
+#include "fl1_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_CP68_DSK
+#include "cp68_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_FDOS_DSK
+#include "fdos_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_FLEX_DSK
+#include "flex_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_UNIFLEX_DSK
+#include "uniflex_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_FM7_CAS
+#include "fm7_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_FMSX_CAS
+#include "fmsx_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_FMTOWNS_DSK
+#include "fmtowns_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_FSD_DSK
+#include "fsd_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_G64_DSK
+#include "g64_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_GTP_CAS
+#include "gtp_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_GUAB_DSK
+#include "guab_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_H17D_DSK
+#include "h17disk.h"
+#endif
+
+#ifdef HAS_FORMATS_H8_CAS
+#include "h8_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_HECTOR_MINIDISC
+#include "hector_minidisc.h"
+#endif
+
+#ifdef HAS_FORMATS_HECT_DSK
+#include "hect_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_HECT_TAP
+#include "hect_tap.h"
+#endif
+
+#ifdef HAS_FORMATS_HTI_TAP
+#include "hti_tape.h"
+#endif
+
+#ifdef HAS_FORMATS_HP300_DSK
+#include "hp300_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_HPI_DSK
+#include "hpi_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_HP_IPC_DSK
+#include "hp_ipc_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_IMG_DSK
+#include "img_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_IQ151_DSK
+#include "iq151_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ITT3030_DSK
+#include "itt3030_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_JUKU_DSK
+#include "juku_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_JVC_DSK
+#include "jvc_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_OS9_DSK
+#include "os9_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_COCO_RAWDSK
+#include "coco_rawdsk.h"
+#endif
+
+#ifdef HAS_FORMATS_FS_COCO_RSDOS
+#include "fs_coco_rsdos.h"
+#endif
+
+#ifdef HAS_FORMATS_FS_COCO_OS9
+#include "fs_coco_os9.h"
+#endif
+
+#ifdef HAS_FORMATS_JFD_DSK
+#include "jfd_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_KAYPRO_DSK
+#include "kaypro_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_KC85_DSK
+#include "kc85_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_KC_CAS
+#include "kc_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_KIM1_CAS
+#include "kim1_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_LVIV_LVT
+#include "lviv_lvt.h"
+#endif
+
+#ifdef HAS_FORMATS_M20_DSK
+#include "m20_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_M5_DSK
+#include "m5_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_MBEE_CAS
+#include "mbee_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_MDOS_DSK
+#include "mdos_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_MFM_HD
+#include "mfm_hd.h"
+#endif
+
+#ifdef HAS_FORMATS_MM_DSK
+#include "mm_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_MS0515_DSK
+#include "ms0515_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_MSX_DSK
+#include "msx_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_MTX_DSK
+#include "mtx_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_MZ_CAS
+#include "mz_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_NABUPC_DSK
+#include "nabupc_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_NANOS_DSK
+#include "nanos_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_NASCOM_DSK
+#include "nascom_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_NASLITE_DSK
+#include "naslite_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_NES_DSK
+#include "nes_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_NFD_DSK
+#include "nfd_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_OPD_DSK
+#include "opd_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ORAO_CAS
+#include "orao_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_ORIC_DSK
+#include "oric_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ORIC_TAP
+#include "oric_tap.h"
+#endif
+
+#ifdef HAS_FORMATS_FS_ORIC_JASMIN
+#include "fs_oric_jasmin.h"
+#endif
+
+#ifdef HAS_FORMATS_FS_CBMDOS
+#include "fs_cbmdos.h"
+#endif
+
+#ifdef HAS_FORMATS_IBMXDF_DSK
+#include "ibmxdf_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_P2000T_CAS
+#include "p2000t_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_P6001_CAS
+#include "p6001_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_PASTI_DSK
+#include "pasti_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_PC98FDI_DSK
+#include "pc98fdi_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_PC98_DSK
+#include "pc98_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_IPF_DSK
+#include "ipf_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_PHC25_CAS
+#include "phc25_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_PK8020_DSK
+#include "pk8020_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_PMD_CAS
+#include "pmd_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_POLY_DSK
+#include "poly_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_PPG_DSK
+#include "ppg_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_PRIMOPTP
+#include "primoptp.h"
+#endif
+
+#ifdef HAS_FORMATS_PYLDIN_DSK
+#include "pyldin_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_QL_DSK
+#include "ql_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_RC759_DSK
+#include "rc759_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_RK_CAS
+#include "rk_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_ROLAND_DSK
+#include "roland_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_RX50_DSK
+#include "rx50_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_SAP_DSK
+#include "sap_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_SC3000_BIT
+#include "sc3000_bit.h"
+#endif
+
+#ifdef HAS_FORMATS_SDD_DSK
+#include "sdd_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_SF7000_DSK
+#include "sf7000_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_SMX_DSK
+#include "smx_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_SOL_CAS
+#include "sol_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_SORC_CAS
+#include "sorc_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_SORC_DSK
+#include "sorc_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_SORD_CAS
+#include "sord_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_SPC1000_CAS
+#include "spc1000_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_ST_DSK
+#include "st_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_SVI_CAS
+#include "svi_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_SVI_DSK
+#include "svi_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_SWD_DSK
+#include "swd_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_TANDY2K_DSK
+#include "tandy2k_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_THOM_CAS
+#include "thom_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_THOM_DSK
+#include "thom_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_TI99_DSK
+#include "ti99_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_TIKI100_DSK
+#include "tiki100_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_TIM011_DSK
+#include "tim011_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_TRD_DSK
+#include "trd_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_TRS80_DSK
+#include "trs80_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_TRS_CAS
+#include "trs_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_TVC_CAS
+#include "tvc_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_TVC_DSK
+#include "tvc_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_TZX_CAS
+#include "tzx_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_UEF_CAS
+#include "uef_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_VDK_DSK
+#include "vdk_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_VECTOR06_DSK
+#include "vector06_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_VG5K_CAS
+#include "vg5k_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_VGI_DSK
+#include "vgi_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_VICTOR9K_DSK
+#include "victor9k_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_VT_CAS
+#include "vt_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_VT_DSK
+#include "vt_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_FS_VTECH
+#include "fs_vtech.h"
+#endif
+
+#ifdef HAS_FORMATS_WD177X_DSK
+#include "wd177x_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_X07_CAS
+#include "x07_cas.h"
+#endif
+
+#ifdef HAS_FORMATS_X1_TAP
+#include "x1_tap.h"
+#endif
+
+#ifdef HAS_FORMATS_XDF_DSK
+#include "xdf_dsk.h"
+#endif
+
+#ifdef HAS_FORMATS_ZX81_P
+#include "zx81_p.h"
+#endif
+
+#ifdef HAS_FORMATS_FS_PRODOS
+#include "fs_prodos.h"
+#endif
+
+#ifdef HAS_FORMATS_FS_FAT
+#include "fs_fat.h"
+#endif
+
+#ifdef HAS_FORMATS_FS_HPLIF
+#include "fs_hplif.h"
+#endif
+
+#ifdef HAS_FORMATS_FS_ISIS
+#include "fs_isis.h"
+#endif
+
+#ifdef HAS_FORMATS_FS_HP98X5
+#include "fs_hp98x5.h"
+#endif
+
+void mame_formats_full_list(mame_formats_enumerator &en)
+{
+ en.category("Generic");
+ en.add(fs::UNFORMATTED);
+ en.add(cassette_default_formats);
+#ifdef HAS_FORMATS_UEF_CAS
+ en.add(uef_cassette_formats); // uef_cas.h
+#endif
+ en.add(FLOPPY_MFI_FORMAT); // mfi_dsk.h
+ en.add(FLOPPY_DFI_FORMAT); // dfi_dsk.h
+#ifdef HAS_FORMATS_FS_FAT
+ en.add(fs::PC_FAT);
+#endif
+
+ en.category("Container FM/MFM");
+ en.add(FLOPPY_HFE_FORMAT); // hxchfe_dsk.h
+ en.add(FLOPPY_MFM_FORMAT); // hxcmfm_dsk.h
+ en.add(FLOPPY_TD0_FORMAT); // td0_dsk.h
+ en.add(FLOPPY_IMD_FORMAT); // imd_dsk.h
+
+ en.category("Container MFM");
+ en.add(FLOPPY_D88_FORMAT); // d88_dsk.h
+ en.add(FLOPPY_CQM_FORMAT); // cqm_dsk.h
+ en.add(FLOPPY_DSK_FORMAT); // dsk_dsk.h
+#ifdef HAS_FORMATS_DMK_DSK
+ en.add(FLOPPY_DMK_FORMAT); // dmk_dsk.h
+#endif
+
+ en.category("Apple");
+#ifdef HAS_FORMATS_FS_PRODOS
+ en.add(fs::PRODOS);
+#endif
+#ifdef HAS_FORMATS_AP2_DSK
+ en.add(FLOPPY_A213S_FORMAT); // ap2_dsk.h
+ en.add(FLOPPY_A216S_DOS_FORMAT); // ap2_dsk.h
+ en.add(FLOPPY_A216S_PRODOS_FORMAT); // ap2_dsk.h
+ en.add(FLOPPY_RWTS18_FORMAT); // ap2_dsk.h
+ en.add(FLOPPY_EDD_FORMAT); // ap2_dsk.h
+ en.add(FLOPPY_NIB_FORMAT); // ap2_dsk.h
+#endif
+#ifdef HAS_FORMATS_AS_DSK
+ en.add(FLOPPY_WOZ_FORMAT); // as_dsk.h
+ en.add(FLOPPY_MOOF_FORMAT); // as_dsk.h
+#endif
+#ifdef HAS_FORMATS_AIM_DSK
+ en.add(FLOPPY_AIM_FORMAT); // aim_dsk.h
+#endif
+#ifdef HAS_FORMATS_DS9_DSK
+ en.add(FLOPPY_DS9_FORMAT); // ds9_dsk.h
+#endif
+#ifdef HAS_FORMATS_AP_DSK35
+ en.add(FLOPPY_DC42_FORMAT); // ap_dsk35.h
+ en.add(FLOPPY_APPLE_GCR_FORMAT); // ap_dsk35.h
+ en.add(FLOPPY_APPLE_2MG_FORMAT); // ap_dsk35.h
+#endif
+
+ en.category("PC");
+ en.add(FLOPPY_PC_FORMAT); // pc_dsk.h
+#ifdef HAS_FORMATS_IBMXDF_DSK
+ en.add(FLOPPY_IBMXDF_FORMAT); // ibmxdf_dsk.h
+#endif
+#ifdef HAS_FORMATS_NASLITE_DSK
+ en.add(FLOPPY_NASLITE_FORMAT); // naslite_dsk.h
+#endif
+
+ en.category("Sharp");
+#ifdef HAS_FORMATS_MZ_CAS
+ en.add(mz700_cassette_formats); // mz_cas.h
+#endif
+#ifdef HAS_FORMATS_X1_TAP
+ en.add(x1_cassette_formats); // x1_tap.h
+#endif
+#ifdef HAS_FORMATS_2D_DSK
+ en.add(FLOPPY_2D_FORMAT); // 2d_dsk.h
+#endif
+
+ en.category("Robotron");
+#ifdef HAS_FORMATS_A5105_DSK
+ en.add(FLOPPY_A5105_FORMAT); // a5105_dsk.h
+#endif
+
+ en.category("Luxor ABC");
+#ifdef HAS_FORMATS_ABC800_DSK
+ en.add(FLOPPY_ABC800_FORMAT); // abc800_dsk.h
+#endif
+#ifdef HAS_FORMATS_ABC800I_DSK
+ en.add(FLOPPY_ABC800I_FORMAT); // abc800i_dsk.h
+#endif
+#ifdef HAS_FORMATS_ABCFD2_DSK
+ en.add(FLOPPY_ABC_FD2_FORMAT); // abcfd2_dsk.h
+#endif
+
+ en.category("Acorn");
+#ifdef HAS_FORMATS_ATOM_TAP
+ en.add(atom_cassette_formats); // atom_tap.h
+#endif
+#ifdef HAS_FORMATS_CSW_CAS
+ en.add(csw_cassette_formats); // csw_cas.h
+ en.add(bbc_cassette_formats); // csw_cas.h
+#endif
+#ifdef HAS_FORMATS_ACORN_DSK
+ en.add(FLOPPY_ACORN_SSD_FORMAT); // acorn_dsk.h
+ en.add(FLOPPY_ACORN_DSD_FORMAT); // acorn_dsk.h
+ en.add(FLOPPY_ACORN_DOS_FORMAT); // acorn_dsk.h
+ en.add(FLOPPY_ACORN_ADFS_OLD_FORMAT); // acorn_dsk.h
+ en.add(FLOPPY_ACORN_ADFS_NEW_FORMAT); // acorn_dsk.h
+ en.add(FLOPPY_OPUS_DDOS_FORMAT); // acorn_dsk.h
+ en.add(FLOPPY_OPUS_DDCPM_FORMAT); // acorn_dsk.h
+#endif
+#ifdef HAS_FORMATS_FSD_DSK
+ en.add(FLOPPY_FSD_FORMAT); // fsd_dsk.h
+#endif
+#ifdef HAS_FORMATS_AFS_DSK
+ en.add(FLOPPY_AFS_FORMAT); // afs_dsk.h
+#endif
+#ifdef HAS_FORMATS_ATOM_DSK
+ en.add(FLOPPY_ATOM_FORMAT); // atom_dsk.h
+#endif
+#ifdef HAS_FORMATS_JFD_DSK
+ en.add(FLOPPY_JFD_FORMAT); // jfd_dsk.h
+#endif
+
+ en.category("Coleco");
+#ifdef HAS_FORMATS_ADAM_CAS
+ en.add(coleco_adam_cassette_formats); // adam_cas.h
+#endif
+#ifdef HAS_FORMATS_ADAM_DSK
+ en.add(FLOPPY_ADAM_FORMAT); // adam_dsk.h
+#endif
+
+ en.category("Amiga");
+#ifdef HAS_FORMATS_AMI_DSK
+ en.add(FLOPPY_ADF_FORMAT); // ami_dsk.h
+#endif
+#ifdef HAS_FORMATS_IPF_DSK
+ en.add(FLOPPY_IPF_FORMAT); // ipf_dsk.h
+#endif
+
+ en.category("Archimedes");
+#ifdef HAS_FORMATS_APD_DSK
+ en.add(FLOPPY_APD_FORMAT); // apd_dsk.h
+#endif
+
+ en.category("HP");
+#ifdef HAS_FORMATS_APOLLO_DSK
+ en.add(FLOPPY_APOLLO_FORMAT); // apollo_dsk.h
+#endif
+#ifdef HAS_FORMATS_HP300_DSK
+ en.add(FLOPPY_HP300_FORMAT); // hp300_dsk.h
+#endif
+#ifdef HAS_FORMATS_HP_IPC_DSK
+ en.add(FLOPPY_HP_IPC_FORMAT); // hp_ipc_dsk.h
+#endif
+#ifdef HAS_FORMATS_HPI_DSK
+ en.add(FLOPPY_HPI_FORMAT); // hpi_dsk.h
+#endif
+#ifdef HAS_FORMATS_FS_HPLIF
+ en.add(fs::HPLIF); // fs_lif.h
+#endif
+#ifdef HAS_FORMATS_FS_HP98X5
+ en.add(fs::HP9825);
+ en.add(fs::HP9831);
+ en.add(fs::HP9845);
+#endif
+
+ en.category("Applix");
+#ifdef HAS_FORMATS_APPLIX_DSK
+ en.add(FLOPPY_APPLIX_FORMAT); // applix_dsk.h
+#endif
+
+ en.category("Apricot");
+#ifdef HAS_FORMATS_APRIDISK
+ en.add(FLOPPY_APRIDISK_FORMAT); // apridisk.h
+#endif
+
+ en.category("Assistent");
+#ifdef HAS_FORMATS_ASST128_DSK
+ en.add(FLOPPY_ASST128_FORMAT); // asst128_dsk.h
+#endif
+
+ en.category("Bondwell");
+#ifdef HAS_FORMATS_BW12_DSK
+ en.add(FLOPPY_BW12_FORMAT); // bw12_dsk.h
+#endif
+#ifdef HAS_FORMATS_BW2_DSK
+ en.add(FLOPPY_BW2_FORMAT); // bw2_dsk.h
+#endif
+
+ en.category("Commodore");
+#ifdef HAS_FORMATS_CBM_TAP
+ en.add(cbm_cassette_formats); // cbm_tap.h
+#endif
+#ifdef HAS_FORMATS_C3040_DSK
+ en.add(FLOPPY_C3040_FORMAT); // c3040_dsk.h
+#endif
+#ifdef HAS_FORMATS_C4040_DSK
+ en.add(FLOPPY_C4040_FORMAT); // c4040_dsk.h
+#endif
+#ifdef HAS_FORMATS_C8280_DSK
+ en.add(FLOPPY_C8280_FORMAT); // c8280_dsk.h
+#endif
+#ifdef HAS_FORMATS_D64_DSK
+ en.add(FLOPPY_D64_FORMAT); // d64_dsk.h
+#endif
+#ifdef HAS_FORMATS_D71_DSK
+ en.add(FLOPPY_D71_FORMAT); // d71_dsk.h
+#endif
+#ifdef HAS_FORMATS_D80_DSK
+ en.add(FLOPPY_D80_FORMAT); // d80_dsk.h
+#endif
+#ifdef HAS_FORMATS_D81_DSK
+ en.add(FLOPPY_D81_FORMAT); // d81_dsk.h
+#endif
+#ifdef HAS_FORMATS_D82_DSK
+ en.add(FLOPPY_D82_FORMAT); // d82_dsk.h
+#endif
+#ifdef HAS_FORMATS_G64_DSK
+ en.add(FLOPPY_G64_FORMAT); // g64_dsk.h
+#endif
+#ifdef HAS_FORMATS_FS_CBMDOS
+ en.add(fs::CBMDOS); // fs_cbmdos.h
+#endif
+
+ en.category("Camputers");
+#ifdef HAS_FORMATS_CAMPLYNX_CAS
+ en.add(lynx48k_cassette_formats); // camplynx_cas.h
+ en.add(lynx128k_cassette_formats); // camplynx_cas.h
+#endif
+#ifdef HAS_FORMATS_CAMPLYNX_DSK
+ en.add(FLOPPY_CAMPLYNX_FORMAT); // camplynx_dsk.h
+#endif
+
+ en.category("Compucolor");
+#ifdef HAS_FORMATS_CCVF_DSK
+ en.add(FLOPPY_CCVF_FORMAT); // ccvf_dsk.h
+#endif
+
+ en.category("Colour Genie");
+#ifdef HAS_FORMATS_CGEN_CAS
+ en.add(cgenie_cassette_formats); // cgen_cas.h
+#endif
+#ifdef HAS_FORMATS_CGENIE_DSK
+ en.add(FLOPPY_CGENIE_FORMAT); // cgenie_dsk.h
+#endif
+
+ en.category("Comx");
+#ifdef HAS_FORMATS_COMX35_DSK
+ en.add(FLOPPY_COMX35_FORMAT); // comx35_dsk.h
+#endif
+
+ en.category("Concept");
+#ifdef HAS_FORMATS_CONCEPT_DSK
+ en.add(FLOPPY_CONCEPT_525DSDD_FORMAT); // concept_dsk.h
+#endif
+
+ en.category("Compis");
+#ifdef HAS_FORMATS_CPIS_DSK
+ en.add(FLOPPY_CPIS_FORMAT); // cpis_dsk.h
+#endif
+
+ en.category("PC98");
+#ifdef HAS_FORMATS_DCP_DSK
+ en.add(FLOPPY_DCP_FORMAT); // dcp_dsk.h
+#endif
+#ifdef HAS_FORMATS_DIP_DSK
+ en.add(FLOPPY_DIP_FORMAT); // dip_dsk.h
+#endif
+#ifdef HAS_FORMATS_FDD_DSK
+ en.add(FLOPPY_FDD_FORMAT); // fdd_dsk.h
+#endif
+#ifdef HAS_FORMATS_NFD_DSK
+ en.add(FLOPPY_NFD_FORMAT); // nfd_dsk.h
+#endif
+#ifdef HAS_FORMATS_PC98_DSK
+ en.add(FLOPPY_PC98_FORMAT); // pc98_dsk.h
+#endif
+#ifdef HAS_FORMATS_PC98FDI_DSK
+ en.add(FLOPPY_PC98FDI_FORMAT); // pc98fdi_dsk.h
+#endif
+
+ en.category("X68k");
+#ifdef HAS_FORMATS_DIM_DSK
+ en.add(FLOPPY_DIM_FORMAT); // dim_dsk.h
+#endif
+#ifdef HAS_FORMATS_XDF_DSK
+ en.add(FLOPPY_XDF_FORMAT); // xdf_dsk.h
+#endif
+
+ en.category("NCR Decision Mate V");
+#ifdef HAS_FORMATS_DMV_DSK
+ en.add(FLOPPY_DMV_FORMAT); // dmv_dsk.h
+#endif
+
+ en.category("DVK MX");
+#ifdef HAS_FORMATS_DVK_MX_DSK
+ en.add(FLOPPY_DVK_MX_FORMAT); // dvk_mx_dsk.h
+#endif
+
+ en.category("Enterprise");
+#ifdef HAS_FORMATS_EP64_DSK
+ en.add(FLOPPY_EP64_FORMAT); // ep64_dsk.h
+#endif
+
+ en.category("Ensoniq");
+#ifdef HAS_FORMATS_ESQ16_DSK
+ en.add(FLOPPY_ESQIMG_FORMAT); // esq16_dsk.h
+#endif
+#ifdef HAS_FORMATS_ESQ8_DSK
+ en.add(FLOPPY_ESQ8IMG_FORMAT); // esq8_dsk.h
+#endif
+
+ en.category("Excalibur 64");
+#ifdef HAS_FORMATS_EXCALI64_DSK
+ en.add(FLOPPY_EXCALI64_FORMAT); // excali64_dsk.h
+#endif
+
+ en.category("Sinclair");
+#ifdef HAS_FORMATS_ZX81_P
+ en.add(zx81_p_format); // zx81_p.h
+ en.add(zx81_cassette_formats); // zx81_p.h
+ en.add(zx80_o_format); // zx81_p.h
+#endif
+#ifdef HAS_FORMATS_TZX_CAS
+ en.add(tzx_cassette_formats); // tzx_cas.h
+ en.add(cdt_cassette_formats); // tzx_cas.h
+#endif
+#ifdef HAS_FORMATS_FL1_DSK
+ en.add(FLOPPY_FL1_FORMAT); // fl1_dsk.h
+#endif
+#ifdef HAS_FORMATS_TRD_DSK
+ en.add(FLOPPY_TRD_FORMAT); // trd_dsk.h
+#endif
+#ifdef HAS_FORMATS_COUPEDSK
+ en.add(FLOPPY_MGT_FORMAT); // coupedsk.h
+#endif
+#ifdef HAS_FORMATS_OPD_DSK
+ en.add(FLOPPY_OPD_FORMAT); // opd_dsk.h
+#endif
+#ifdef HAS_FORMATS_SWD_DSK
+ en.add(FLOPPY_SWD_FORMAT); // swd_dsk.h
+#endif
+#ifdef HAS_FORMATS_SDD_DSK
+ en.add(FLOPPY_SDD_FORMAT); // sdd_dsk.h
+#endif
+#ifdef HAS_FORMATS_QL_DSK
+ en.add(FLOPPY_QL_FORMAT); // ql_dsk.h
+#endif
+
+ en.category("Flex");
+#ifdef HAS_FORMATS_FLEX_DSK
+ en.add(FLOPPY_FLEX_FORMAT); // flex_dsk.h
+#endif
+
+ en.category("FM Towns");
+#ifdef HAS_FORMATS_FMTOWNS_DSK
+ en.add(FLOPPY_FMTOWNS_FORMAT); // fmtowns_dsk.h
+#endif
+
+ en.category("JPM");
+#ifdef HAS_FORMATS_GUAB_DSK
+ en.add(FLOPPY_GUAB_FORMAT); // guab_dsk.h
+#endif
+
+ en.category("Hector");
+#ifdef HAS_FORMATS_HECT_TAP
+ en.add(hector_cassette_formats); // hect_tap.h
+#endif
+#ifdef HAS_FORMATS_HECTOR_MINIDISC
+ en.add(FLOPPY_HMD_FORMAT); // hector_minidisc.h
+#endif
+
+ en.category("Intel");
+#ifdef HAS_FORMATS_IMG_DSK
+ en.add(FLOPPY_IMG_FORMAT); // img_dsk.h
+#endif
+#ifdef HAS_FORMATS_FS_ISIS
+ en.add(fs::ISIS);
+#endif
+
+ en.category("IQ151");
+#ifdef HAS_FORMATS_IQ151_DSK
+ en.add(FLOPPY_IQ151_FORMAT); // iq151_dsk.h
+#endif
+
+ en.category("ITT3030");
+#ifdef HAS_FORMATS_ITT3030_DSK
+ en.add(FLOPPY_ITT3030_FORMAT); // itt3030_dsk.h
+#endif
+
+ en.category("Juku");
+#ifdef HAS_FORMATS_JUKU_DSK
+ en.add(FLOPPY_JUKU_FORMAT); // juku_dsk.h
+#endif
+
+ en.category("CoCo");
+#ifdef HAS_FORMATS_COCO_CAS
+ en.add(coco_cassette_formats); // coco_cas.h
+ en.add(alice32_cassette_formats); // coco_cas.h
+#endif
+#ifdef HAS_FORMATS_JVC_DSK
+ en.add(FLOPPY_JVC_FORMAT); // jvc_dsk.h
+#endif
+#ifdef HAS_FORMATS_OS9_DSK
+ en.add(FLOPPY_OS9_FORMAT); // os9_dsk.h
+#endif
+#ifdef HAS_FORMATS_VDK_DSK
+ en.add(FLOPPY_VDK_FORMAT); // vdk_dsk.h
+#endif
+#ifdef HAS_FORMATS_SDF_DSK
+ en.add(FLOPPY_SDF_FORMAT); // sdf_dsk.h
+#endif
+#ifdef HAS_FORMATS_TRS80_DSK
+ en.add(FLOPPY_JV1_FORMAT); // trs80_dsk.h
+ en.add(FLOPPY_JV3_FORMAT); // trs80_dsk.h
+#endif
+#ifdef HAS_FORMATS_COCO_RAWDSK
+ en.add(FLOPPY_COCO_RAWDSK_FORMAT); // coco_rawdsk.h
+#endif
+#ifdef HAS_FORMATS_FS_COCO_RSDOS
+ en.add(fs::COCO_RSDOS); // fs_coco_rsdos.h
+#endif
+#ifdef HAS_FORMATS_FS_COCO_OS9
+ en.add(fs::COCO_OS9); // fs_coco_os9.h
+#endif
+
+ en.category("Kaypro");
+#ifdef HAS_FORMATS_KAYPRO_DSK
+ en.add(FLOPPY_KAYPROII_FORMAT); // kaypro_dsk.h
+ en.add(FLOPPY_KAYPRO2X_FORMAT); // kaypro_dsk.h
+#endif
+
+ en.category("KC85");
+#ifdef HAS_FORMATS_KC85_DSK
+ en.add(FLOPPY_KC85_FORMAT); // kc85_dsk.h
+#endif
+
+ en.category("Olivetti");
+#ifdef HAS_FORMATS_M20_DSK
+ en.add(FLOPPY_M20_FORMAT); // m20_dsk.h
+#endif
+
+ en.category("Sord M5");
+#ifdef HAS_FORMATS_SORD_CAS
+ en.add(sordm5_cassette_formats); // sord_cas.h
+#endif
+#ifdef HAS_FORMATS_M5_DSK
+ en.add(FLOPPY_M5_FORMAT); // m5_dsk.h
+#endif
+
+ en.category("Motorola MDOS");
+#ifdef HAS_FORMATS_MDOS_DSK
+ en.add(FLOPPY_MDOS_FORMAT); // mdos_dsk.h
+#endif
+
+ en.category("MikroMikko");
+#ifdef HAS_FORMATS_MM_DSK
+ en.add(FLOPPY_MM1_FORMAT); // mm_dsk.h
+ en.add(FLOPPY_MM2_FORMAT); // mm_dsk.h
+#endif
+
+ en.category("Elektronika");
+#ifdef HAS_FORMATS_MS0515_DSK
+ en.add(FLOPPY_MS0515_FORMAT); // ms0515_dsk.h
+#endif
+
+ en.category("MSX");
+#ifdef HAS_FORMATS_FMSX_CAS
+ en.add(fmsx_cassette_formats); // fmsx_cas.h
+#endif
+#ifdef HAS_FORMATS_MSX_DSK
+ en.add(FLOPPY_MSX_FORMAT); // msx_dsk.h
+#endif
+
+ en.category("MTX");
+#ifdef HAS_FORMATS_MTX_DSK
+ en.add(FLOPPY_MTX_FORMAT); // mtx_dsk.h
+#endif
+
+ en.category("NABU PC");
+#ifdef HAS_FORMATS_NABUPC_DSK
+ en.add(FLOPPY_NABUPC_FORMAT); // nabupc_dsk.h
+#endif
+
+ en.category("Nanos");
+#ifdef HAS_FORMATS_NANOS_DSK
+ en.add(FLOPPY_NANOS_FORMAT); // nanos_dsk.h
+#endif
+
+ en.category("Nascom");
+#ifdef HAS_FORMATS_NASCOM_DSK
+ en.add(FLOPPY_NASCOM_FORMAT); // nascom_dsk.h
+#endif
+
+ en.category("Oric");
+#ifdef HAS_FORMATS_ORIC_TAP
+ en.add(oric_cassette_formats); // oric_tap.h
+#endif
+#ifdef HAS_FORMATS_ORIC_DSK
+ en.add(FLOPPY_ORIC_DSK_FORMAT); // oric_dsk.h
+ en.add(FLOPPY_ORIC_JASMIN_FORMAT); // oric_dsk.h
+#endif
+#ifdef HAS_FORMATS_FS_ORIC_JASMIN
+ en.add(fs::ORIC_JASMIN); // fs_oric_jasmin.h
+#endif
+
+ en.category("Atari");
+#ifdef HAS_FORMATS_A26_CAS
+ en.add(a26_cassette_formats); // a26_cas.h
+#endif
+#ifdef HAS_FORMATS_ST_DSK
+ en.add(FLOPPY_ST_FORMAT); // st_dsk.h
+ en.add(FLOPPY_MSA_FORMAT); // st_dsk.h
+#endif
+#ifdef HAS_FORMATS_PASTI_DSK
+ en.add(FLOPPY_PASTI_FORMAT); // pasti_dsk.h
+#endif
+
+ en.category("PK 8020");
+#ifdef HAS_FORMATS_PK8020_DSK
+ en.add(FLOPPY_PK8020_FORMAT); // pk8020_dsk.h
+#endif
+
+ en.category("Poly");
+#ifdef HAS_FORMATS_POLY_DSK
+ en.add(FLOPPY_POLY_CPM_FORMAT); // poly_dsk.h
+#endif
+
+ en.category("Eltek Eurocom");
+#ifdef HAS_FORMATS_PPG_DSK
+ en.add(FLOPPY_PPG_FORMAT); // ppg_dsk.h
+#endif
+
+ en.category("Pyldin");
+#ifdef HAS_FORMATS_PYLDIN_DSK
+ en.add(FLOPPY_PYLDIN_FORMAT); // pyldin_dsk.h
+#endif
+
+ en.category("Regnecentralen");
+#ifdef HAS_FORMATS_RC759_DSK
+ en.add(FLOPPY_RC759_FORMAT); // rc759_dsk.h
+#endif
+
+ en.category("Roland");
+#ifdef HAS_FORMATS_ROLAND_DSK
+ en.add(FLOPPY_ROLAND_SDISK_FORMAT); // roland_dsk.h
+#endif
+
+ en.category("DEC");
+#ifdef HAS_FORMATS_RX50_DSK
+ en.add(FLOPPY_RX50IMG_FORMAT); // rx50_dsk.h
+#endif
+
+ en.category("Sega");
+#ifdef HAS_FORMATS_SF7000_DSK
+ en.add(FLOPPY_SF7000_FORMAT); // sf7000_dsk.h
+#endif
+
+ en.category("Specialist");
+#ifdef HAS_FORMATS_SMX_DSK
+ en.add(FLOPPY_SMX_FORMAT); // smx_dsk.h
+#endif
+
+ en.category("Spectravideo");
+#ifdef HAS_FORMATS_SVI_CAS
+ en.add(svi_cassette_formats); // svi_cas.h
+#endif
+#ifdef HAS_FORMATS_SVI_DSK
+ en.add(FLOPPY_SVI_FORMAT); // svi_dsk.h
+#endif
+
+ en.category("Tandy");
+#ifdef HAS_FORMATS_TRS_CAS
+ en.add(trs80l1_cassette_formats); // trs_cas.h
+ en.add(trs80l2_cassette_formats); // trs_cas.h
+#endif
+#ifdef HAS_FORMATS_TANDY2K_DSK
+ en.add(FLOPPY_TANDY_2000_FORMAT); // tandy2k_dsk.h
+#endif
+
+ en.category("Thomson");
+#ifdef HAS_FORMATS_THOM_CAS
+ en.add(to7_cassette_formats); // thom_cas.h
+ en.add(mo5_cassette_formats); // thom_cas.h
+#endif
+#ifdef HAS_FORMATS_THOM_DSK
+ en.add(FLOPPY_THOMSON_525_FORMAT); // thom_dsk.h
+ en.add(FLOPPY_THOMSON_35_FORMAT); // thom_dsk.h
+#endif
+#ifdef HAS_FORMATS_SAP_DSK
+ en.add(FLOPPY_SAP_FORMAT);
+#endif
+
+ en.category("Texas Instruments");
+#ifdef HAS_FORMATS_TI99_DSK
+ en.add(FLOPPY_TI99_SDF_FORMAT); // ti99_dsk.h
+ en.add(FLOPPY_TI99_TDF_FORMAT); // ti99_dsk.h
+#endif
+
+ en.category("Tiki 100");
+#ifdef HAS_FORMATS_TIKI100_DSK
+ en.add(FLOPPY_TIKI100_FORMAT); // tiki100_dsk.h
+#endif
+
+ en.category("TIM 011");
+#ifdef HAS_FORMATS_TIM011_DSK
+ en.add(FLOPPY_TIM011_FORMAT); // tim011_dsk.h
+#endif
+
+ en.category("Videoton");
+#ifdef HAS_FORMATS_TVC_CAS
+ en.add(tvc64_cassette_formats); // tvc_cas.h
+#endif
+#ifdef HAS_FORMATS_TVC_DSK
+ en.add(FLOPPY_TVC_FORMAT); // tvc_dsk.h
+#endif
+
+ en.category("SWTPC");
+#ifdef HAS_FORMATS_UNIFLEX_DSK
+ en.add(FLOPPY_UNIFLEX_FORMAT); // uniflex_dsk.h
+#endif
+
+ en.category("Vector 06");
+#ifdef HAS_FORMATS_VECTOR06_DSK
+ en.add(FLOPPY_VECTOR06_FORMAT); // vector06_dsk.h
+#endif
+
+ en.category("Victor");
+#ifdef HAS_FORMATS_VICTOR9K_DSK
+ en.add(FLOPPY_VICTOR_9000_FORMAT); // victor9k_dsk.h
+#endif
+
+ en.category("Jupiter ACE");
+#ifdef HAS_FORMATS_ACE_TAP
+ en.add(ace_cassette_formats); // ace_tap.h
+#endif
+
+ en.category("APF");
+#ifdef HAS_FORMATS_APF_APT
+ en.add(apf_cassette_formats); // apf_apt.h
+#endif
+
+ en.category("Mattel");
+#ifdef HAS_FORMATS_AQUARIUS_CAQ
+ en.add(aquarius_cassette_formats); // aquarius_caq.h
+#endif
+
+ en.category("Goldstar");
+#ifdef HAS_FORMATS_FC100_CAS
+ en.add(fc100_cassette_formats); // fc100_cas.h
+#endif
+
+ en.category("Fujitsu");
+#ifdef HAS_FORMATS_FM7_CAS
+ en.add(fm7_cassette_formats); // fm7_cas.h
+#endif
+
+ en.category("Galaksija");
+#ifdef HAS_FORMATS_GTP_CAS
+ en.add(gtp_cassette_formats); // gtp_cas.h
+#endif
+
+ en.category("Heathkit");
+#ifdef HAS_FORMATS_H8_CAS
+ en.add(h8_cassette_formats); // h8_cas.h
+#endif
+
+ en.category("KC 85");
+#ifdef HAS_FORMATS_KC_CAS
+ en.add(kc_cassette_formats); // kc_cas.h
+#endif
+
+ en.category("KIM 1");
+#ifdef HAS_FORMATS_KIM1_CAS
+ en.add(kim1_cassette_formats); // kim1_cas.h
+#endif
+
+ en.category("Lviv");
+#ifdef HAS_FORMATS_LVIV_LVT
+ en.add(lviv_lvt_format); // lviv_lvt.h
+#endif
+
+ en.category("Microbee");
+#ifdef HAS_FORMATS_MBEE_CAS
+ en.add(mbee_cassette_formats); // mbee_cas.h
+#endif
+
+ en.category("Micropolis");
+#ifdef HAS_FORMATS_VGI_DSK
+ en.add(FLOPPY_VGI_FORMAT); // vgi_dsk.h
+#endif
+
+ en.category("Orao");
+#ifdef HAS_FORMATS_ORAO_CAS
+ en.add(orao_cassette_formats); // orao_cas.h
+#endif
+
+ en.category("Philips");
+#ifdef HAS_FORMATS_VG5K_CAS
+ en.add(vg5k_cassette_formats); // vg5k_cas.h
+#endif
+#ifdef HAS_FORMATS_P2000T_CAS
+ en.add(p2000t_cassette_formats); // p2000t_cas.h
+#endif
+
+ en.category("NEC");
+#ifdef HAS_FORMATS_P6001_CAS
+ en.add(pc6001_cassette_formats); // p6001_cas.h
+#endif
+
+ en.category("Sanyo");
+#ifdef HAS_FORMATS_PHC25_CAS
+ en.add(phc25_cassette_formats); // phc25_cas.h
+#endif
+
+ en.category("Tesla");
+#ifdef HAS_FORMATS_PMD_CAS
+ en.add(pmd85_cassette_formats); // pmd_cas.h
+#endif
+
+ en.category("Microkey");
+#ifdef HAS_FORMATS_PRIMOPTP
+ en.add(primo_ptp_format); // primoptp.h
+#endif
+
+ en.category("UT 88");
+#ifdef HAS_FORMATS_RK_CAS
+ en.add(rku_cassette_formats); // rk_cas.h
+ en.add(rk8_cassette_formats); // rk_cas.h
+ en.add(rks_cassette_formats); // rk_cas.h
+ en.add(rko_cassette_formats); // rk_cas.h
+ en.add(rkr_cassette_formats); // rk_cas.h
+ en.add(rka_cassette_formats); // rk_cas.h
+ en.add(rkm_cassette_formats); // rk_cas.h
+ en.add(rkp_cassette_formats); // rk_cas.h
+ en.add(gam_cassette_formats); // rk_cas.h
+#endif
+
+ en.category("SG 1000");
+#ifdef HAS_FORMATS_SC3000_BIT
+ en.add(sc3000_cassette_formats); // sc3000_bit.h
+#endif
+
+ en.category("Sol 20");
+#ifdef HAS_FORMATS_SOL_CAS
+ en.add(sol20_cassette_formats); // sol_cas.h
+#endif
+
+ en.category("Exidy");
+#ifdef HAS_FORMATS_SORC_CAS
+ en.add(sorcerer_cassette_formats); // sorc_cas.h
+#endif
+
+ en.category("Samsung");
+#ifdef HAS_FORMATS_SPC1000_CAS
+ en.add(spc1000_cassette_formats); // spc1000_cas.h
+#endif
+
+ en.category("Video Technology");
+#ifdef HAS_FORMATS_VT_CAS
+ en.add(vtech1_cassette_formats); // vt_cas.h
+ en.add(vtech2_cassette_formats); // vt_cas.h
+#endif
+#ifdef HAS_FORMATS_VT_DSK
+ en.add(FLOPPY_VTECH_BIN_FORMAT); // vt_dsk.h
+ en.add(FLOPPY_VTECH_DSK_FORMAT); // vt_dsk.h
+#endif
+#ifdef HAS_FORMATS_FS_VTECH
+ en.add(fs::VTECH); // fs_vtech.h
+#endif
+
+ en.category("Canon");
+#ifdef HAS_FORMATS_X07_CAS
+ en.add(x07_cassette_formats); // x07_cas.h
+#endif
+
+ en.category("Heath");
+#ifdef HAS_FORMATS_H17D_DSK
+ en.add(FLOPPY_H17D_FORMAT); // h17disk.h
+#endif
+}
diff --git a/src/lib/formats/all.h b/src/lib/formats/all.h
new file mode 100644
index 00000000000..7c252a5bea4
--- /dev/null
+++ b/src/lib/formats/all.h
@@ -0,0 +1,27 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Enumerate all the compiled-in formats
+
+#ifndef MAME_FORMATS_ALL_H
+#define MAME_FORMATS_ALL_H
+
+#pragma once
+
+#include "cassimg.h"
+#include "flopimg.h"
+#include "fsmgr.h"
+
+struct mame_formats_enumerator {
+ virtual ~mame_formats_enumerator() = default;
+
+ virtual void category(const char *name) = 0;
+ virtual void add(const cassette_image::Format *const *formats) = 0;
+ virtual void add(const floppy_image_format_t &format) = 0;
+ virtual void add(const fs::manager_t &fs) = 0;
+};
+
+void mame_formats_full_list(mame_formats_enumerator &en);
+
+#endif
+
diff --git a/src/lib/formats/ami_dsk.cpp b/src/lib/formats/ami_dsk.cpp
index 8a757e6a4c9..d4c567050c8 100644
--- a/src/lib/formats/ami_dsk.cpp
+++ b/src/lib/formats/ami_dsk.cpp
@@ -2,93 +2,98 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/ami_dsk.c
+ formats/ami_dsk.cpp
Amiga disk images
*********************************************************************/
-#include <assert.h>
-
#include "formats/ami_dsk.h"
+#include "ioprocs.h"
+
+
adf_format::adf_format() : floppy_image_format_t()
{
}
-const char *adf_format::name() const
+const char *adf_format::name() const noexcept
{
return "adf";
}
-const char *adf_format::description() const
+const char *adf_format::description() const noexcept
{
return "Amiga ADF floppy disk image";
}
-const char *adf_format::extensions() const
+const char *adf_format::extensions() const noexcept
{
return "adf";
}
-bool adf_format::supports_save() const
+bool adf_format::supports_save() const noexcept
{
return true;
}
-int adf_format::identify(io_generic *io, uint32_t form_factor)
+int adf_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return 0;
+
if ((size == 901120) || (size == 912384) || (size == 1802240))
- {
- return 50;
- }
+ return FIFID_SIZE;
+
return 0;
}
-bool adf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool adf_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
desc_s sectors[22];
uint8_t sectdata[512*22];
bool is_hd = false;
int tracks = 80;
- for(int i=0; i<22; i++) {
+ for (int i=0; i<22; i++) {
sectors[i].data = sectdata + 512*i;
sectors[i].size = 512;
sectors[i].sector_id = i;
}
- uint64_t size = io_generic_size(io);
- if(size == 901120)
- {
+ uint64_t size;
+ if (io.length(size))
+ return false;
+
+ if (size == 901120) {
is_hd = false;
tracks = 80;
- }
- else if (size == 912384)
- {
+ } else if (size == 912384) {
is_hd = false;
tracks = 81;
- }
- else
- {
+ } else {
is_hd = true;
tracks = 80;
}
if (!is_hd) {
- image->set_variant(floppy_image::DSDD);
- for(int track=0; track < tracks; track++) {
- for(int side=0; side < 2; side++) {
- io_generic_read(io, sectdata, (track*2 + side)*512*11, 512*11);
+ image.set_variant(floppy_image::DSDD);
+ for (int track=0; track < tracks; track++) {
+ for (int side=0; side < 2; side++) {
+ auto const [err, actual] = read_at(io, (track*2 + side)*512*11, sectdata, 512*11);
+ if (err || (512*11 != actual))
+ return false;
generate_track(amiga_11, track, side, sectors, 11, 100000, image);
}
}
} else {
- image->set_variant(floppy_image::DSHD);
- for(int track=0; track < tracks; track++) {
- for(int side=0; side < 2; side++) {
- io_generic_read(io, sectdata, (track*2 + side)*512*22, 512*22);
+ image.set_variant(floppy_image::DSHD);
+ for (int track=0; track < tracks; track++) {
+ for (int side=0; side < 2; side++) {
+ auto const [err, actual] = read_at(io, (track*2 + side)*512*22, sectdata, 512*22);
+ if (err || (512*22 != actual))
+ return false;
generate_track(amiga_22, track, side, sectors, 22, 200000, image);
}
}
@@ -97,73 +102,63 @@ bool adf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-uint32_t adf_format::g32(const uint8_t *trackbuf, int track_size, int pos)
+uint32_t adf_format::g32(const std::vector<bool> &trackbuf, uint32_t pos)
{
- if(pos >= 0 && track_size-pos >= 40) {
- int pp = pos >> 3;
- int dp = pos & 7;
- return
- (trackbuf[pp] << (24+dp)) |
- (trackbuf[pp+1] << (16+dp)) |
- (trackbuf[pp+2] << (8+dp)) |
- (trackbuf[pp+3] << dp) |
- (trackbuf[pp+4] >> (8-dp));
- } else {
- uint32_t res = 0;
- for(int i=0; i<32; i++) {
- int pp = (pos+i) % track_size;
- if(trackbuf[pp>>3] & (0x80 >> (pp & 7)))
- res |= 0x80000000 >> i;
- }
- return res;
+ uint32_t res = 0;
+ for (int i=0; i != 32; i++) {
+ if (trackbuf[pos])
+ res |= 0x80000000 >> i;
+ pos++;
+ if (pos == trackbuf.size())
+ pos = 0;
}
+ return res;
}
-uint32_t adf_format::checksum(const uint8_t *trackbuf, int track_size, int pos, int long_count)
+uint32_t adf_format::checksum(const std::vector<bool> &trackbuf, uint32_t pos, int long_count)
{
uint32_t check = 0;
- for(int i=0; i<long_count; i++)
- check ^= g32(trackbuf, track_size, pos+32*i);
+ for (int i=0; i<long_count; i++)
+ check ^= g32(trackbuf, pos+32*i);
return check & 0x55555555;
}
-bool adf_format::save(io_generic *io, floppy_image *image)
+bool adf_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
uint8_t sectdata[512*22];
- uint8_t trackbuf[300000/8];
- bool hd = image->get_variant() == floppy_image::DSHD;
+ bool const hd = image.get_variant() == floppy_image::DSHD;
- int data_track_size = hd ? 512*22 : 512*11;
+ int const data_track_size = hd ? 512*22 : 512*11;
- for(int track=0; track < 80; track++) {
- for(int side=0; side < 2; side++) {
- int track_size;
- generate_bitstream_from_track(track, side, hd ? 1000 : 2000, trackbuf, track_size, image);
+ for (int track=0; track < 80; track++) {
+ for (int side=0; side < 2; side++) {
+ auto trackbuf = generate_bitstream_from_track(track, side, hd ? 1000 : 2000, image);
- for(int i=0; i<track_size; i++)
- if(g32(trackbuf, track_size, i) == 0x44894489 &&
- (g32(trackbuf, track_size, i+384) & 0x55555555) == checksum(trackbuf, track_size, i+32, 10) &&
- (g32(trackbuf, track_size, i+448) & 0x55555555) == checksum(trackbuf, track_size, i+480, 256)) {
- uint32_t head = ((g32(trackbuf, track_size, i+32) & 0x55555555) << 1) | (g32(trackbuf, track_size, i+64) & 0x55555555);
+ for (uint32_t i=0; i<trackbuf.size(); i++)
+ if (g32(trackbuf, i) == 0x44894489 &&
+ (g32(trackbuf, i+384) & 0x55555555) == checksum(trackbuf, i+32, 10) &&
+ (g32(trackbuf, i+448) & 0x55555555) == checksum(trackbuf, i+480, 256)) {
+ uint32_t head = ((g32(trackbuf, i+32) & 0x55555555) << 1) | (g32(trackbuf, i+64) & 0x55555555);
int sect = (head >> 8) & 0xff;
- if(sect > (hd ? 22 : 11))
+ if (sect > (hd ? 22 : 11))
continue;
uint8_t *dest = sectdata + 512*sect;
- for(int j=0; j<128; j++) {
- uint32_t val = ((g32(trackbuf, track_size, i+480+32*j) & 0x55555555) << 1) | (g32(trackbuf, track_size, i+4576+32*j) & 0x55555555);
+ for (int j=0; j<128; j++) {
+ uint32_t val = ((g32(trackbuf, i+480+32*j) & 0x55555555) << 1) | (g32(trackbuf, i+4576+32*j) & 0x55555555);
*dest++ = val >> 24;
*dest++ = val >> 16;
*dest++ = val >> 8;
*dest++ = val;
}
- io_generic_write(io, sectdata, (track*2 + side)*data_track_size, data_track_size);
+ // FIXME: check for errors
+ /*auto const [err, actual] =*/ write_at(io, (track*2 + side) * data_track_size, sectdata, data_track_size);
}
}
}
return true;
}
-const floppy_format_type FLOPPY_ADF_FORMAT = &floppy_image_format_creator<adf_format>;
+const adf_format FLOPPY_ADF_FORMAT;
diff --git a/src/lib/formats/ami_dsk.h b/src/lib/formats/ami_dsk.h
index 899e612415a..3284b08aaf7 100644
--- a/src/lib/formats/ami_dsk.h
+++ b/src/lib/formats/ami_dsk.h
@@ -19,20 +19,20 @@ class adf_format : public floppy_image_format_t
public:
adf_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
private:
- static uint32_t g32(const uint8_t *trackbuf, int track_size, int pos);
- static uint32_t checksum(const uint8_t *trackbuf, int track_size, int pos, int long_count);
+ static uint32_t g32(const std::vector<bool> &bitsteam, uint32_t pos);
+ static uint32_t checksum(const std::vector<bool> &bitsteam, uint32_t pos, int long_count);
};
-extern const floppy_format_type FLOPPY_ADF_FORMAT;
+extern const adf_format FLOPPY_ADF_FORMAT;
#endif // MAME_FORMATS_AMI_DSK_H
diff --git a/src/lib/formats/ap2_dsk.cpp b/src/lib/formats/ap2_dsk.cpp
index b9685d8e40f..1880ad7bd23 100644
--- a/src/lib/formats/ap2_dsk.cpp
+++ b/src/lib/formats/ap2_dsk.cpp
@@ -2,689 +2,323 @@
// copyright-holders:Olivier Galibert, R. Belmont
/*********************************************************************
- ap2_dsk.c
+ ap2_dsk.cpp
Apple II disk images
*********************************************************************/
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
#include "ap2_dsk.h"
#include "basicdsk.h"
+#include "ioprocs.h"
+#include "multibyte.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_t apple2_get_track_size(floppy_image_legacy *floppy, int head, int track);
-static int disk_decode_nib(uint8_t *data, const uint8_t *nibble, int *volume, int *track, int *sector);
-static void disk_encode_nib(uint8_t *nibble, const uint8_t *data, int volume, int track, int sector);
-
-/* used in DOS/ProDOS order images */
-static int apple2_do_translate_sector(floppy_image_legacy *floppy, int sector);
-static int apple2_po_translate_sector(floppy_image_legacy *floppy, int sector);
-static floperr_t apple2_dsk_read_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buflen);
-static floperr_t apple2_dsk_write_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, const void *buffer, size_t buflen);
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
-/* used in nibble order images */
-static floperr_t apple2_nib_read_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buflen);
-static floperr_t apple2_nib_write_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, const void *buffer, size_t buflen);
-static floperr_t apple2_nib_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen);
-static floperr_t apple2_nib_write_sector(floppy_image_legacy *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_legacy *floppy, int head, int track, int sector, uint32_t *sector_length);
-
-
-static const uint8_t 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
+static const uint8_t translate5[] = {
+ 0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba,
+ 0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb,
+ 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef,
+ 0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff,
};
-
-
-/* -----------------------------------------------------------------------
- * Utility code
- * ----------------------------------------------------------------------- */
-
-static const uint8_t *get_untranslate6_map(void)
+a2_13sect_format::a2_13sect_format() : floppy_image_format_t()
{
- static uint8_t map[256];
- static int map_inited = 0;
- uint8_t 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_legacy *floppy, int floppy_type)
+int a2_13sect_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- 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;
- }
+ uint64_t size;
+ if (io.length(size))
+ return 0;
- format->get_track_size = apple2_get_track_size;
+ if (size != APPLE2_STD_TRACK_COUNT * 13 * APPLE2_SECTOR_SIZE)
+ return 0;
- return FLOPPY_ERROR_SUCCESS;
+ return FIFID_SIZE;
}
-
-
-/* -----------------------------------------------------------------------
- * DOS order and ProDOS order code
- * ----------------------------------------------------------------------- */
-
-static FLOPPY_IDENTIFY(apple2_dsk_identify)
+bool a2_13sect_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint64_t size;
- uint64_t expected_size;
-
- size = floppy_image_size(floppy);
- expected_size = APPLE2_TRACK_COUNT * APPLE2_SECTOR_COUNT * APPLE2_SECTOR_SIZE;
-
- if ((size == expected_size) || (size == 35 * APPLE2_SECTOR_COUNT * APPLE2_SECTOR_SIZE))
- *vote = 100;
- else if ((size > expected_size) && ((size - expected_size) < 8))
- *vote = 90; /* tolerate images with up to eight fewer/extra bytes (bug #638) */
- else if ((size < expected_size) && ((expected_size - size) < 8))
- *vote = 90; /* tolerate images with up to eight fewer/extra bytes (bug #638) */
- else
- *vote = 0;
- return FLOPPY_ERROR_SUCCESS;
-}
+ if (io.length(size))
+ return false;
+ image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD);
+ int tracks = size / 13 / APPLE2_SECTOR_SIZE;
-static int apple2_do_translate_sector(floppy_image_legacy *floppy, int sector)
-{
- static const uint8_t skewing[] =
- {
- /* DOS order (*.do) */
- 0x00, 0x07, 0x0E, 0x06, 0x0D, 0x05, 0x0C, 0x04,
- 0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x0F
- };
- return skewing[sector];
-}
+ for(int track = 0; track < tracks; track++) {
+ std::vector<uint32_t> track_data;
+ uint8_t sector_data[APPLE2_SECTOR_SIZE * 13];
+ auto const [err, actual] = read_at(
+ io, track * sizeof sector_data, sector_data, sizeof sector_data);
+ if (err || actual != sizeof sector_data)
+ return false;
+ for(int i=0; i<13; i++) {
+ int sector = (i * 10) % 13;
-static int apple2_po_translate_sector(floppy_image_legacy *floppy, int sector)
-{
- static const uint8_t skewing[] =
- {
- /* ProDOS order (*.po) */
- 0x00, 0x08, 0x01, 0x09, 0x02, 0x0A, 0x03, 0x0B,
- 0x04, 0x0C, 0x05, 0x0D, 0x06, 0x0E, 0x07, 0x0F
- };
- return skewing[sector];
-}
+ // write inter-sector padding
+ for(int j=0; j<40; j++)
+ raw_w(track_data, 9, 0x1fe);
+ raw_w(track_data, 8, 0xff);
+ // write sector address
+ raw_w(track_data, 24, 0xd5aab5);
+ raw_w(track_data, 16, gcr4_encode(0xfe));
+ raw_w(track_data, 16, gcr4_encode(track));
+ raw_w(track_data, 16, gcr4_encode(sector));
+ raw_w(track_data, 16, gcr4_encode(0xfe ^ track ^ sector));
+ raw_w(track_data, 24, 0xdeaaeb);
+ // write intra-sector padding
+ for(int j=0; j<11; j++)
+ raw_w(track_data, 9, 0x1fe);
-static FLOPPY_CONSTRUCT(apple2_do_construct)
-{
- return apple2_general_construct(floppy, APPLE2_IMAGE_DO);
-}
+ // write sector data
+ raw_w(track_data, 24, 0xd5aaad);
+ uint8_t pval = 0x00;
+ auto write_data_byte = [&track_data, &pval](uint8_t nval) {
+ raw_w(track_data, 8, translate5[nval ^ pval]);
+ pval = nval;
+ };
+
+ const uint8_t *sdata = sector_data + APPLE2_SECTOR_SIZE * sector;
+
+ // write 154 bytes encoding bits 2-0
+ write_data_byte(sdata[255] & 7);
+ for (int k=2; k>-1; k--)
+ for (int j=0; j<51; j++)
+ write_data_byte(
+ (sdata[j*5+k] & 7) << 2
+ | ((sdata[j*5+3] >> (2-k)) & 1) << 1
+ | ((sdata[j*5+4] >> (2-k)) & 1));
+
+ // write 256 bytes encoding bits 7-3
+ for (int k=0; k<5; k++)
+ for (int j=50; j>-1; j--)
+ write_data_byte(sdata[j*5+k] >> 3);
+ write_data_byte(sdata[255] >> 3);
+
+ raw_w(track_data, 8, translate5[pval]);
+ raw_w(track_data, 24, 0xdeaaeb);
+ raw_w(track_data, 8, 0xff);
+ }
+ generate_track_from_levels(track, 0, track_data, 0, image);
+ }
-static FLOPPY_CONSTRUCT(apple2_po_construct)
-{
- return apple2_general_construct(floppy, APPLE2_IMAGE_PO);
+ return true;
}
-
-static floperr_t apple2_dsk_read_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buflen)
+const char *a2_13sect_format::name() const noexcept
{
- uint8_t sector_buffer[APPLE2_SECTOR_SIZE];
- int sector;
- uint8_t *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_t *)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;
+ return "a2_13sect";
}
-
-
-static floperr_t apple2_dsk_write_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, const void *buffer, size_t buflen)
+const char *a2_13sect_format::description() const noexcept
{
- int sector;
- uint8_t sector_buffer[APPLE2_SECTOR_SIZE];
- const uint8_t *nibble;
-
- if (offset != 0)
- return FLOPPY_ERROR_UNSUPPORTED;
-
- for (sector = 0; sector < APPLE2_SECTOR_COUNT; sector++)
- {
- nibble = (uint8_t *)buffer;
- nibble += sector * APPLE2_SMALL_NIBBLE_SIZE;
-
- disk_decode_nib(sector_buffer, nibble, nullptr, nullptr, nullptr);
- floppy_write_sector(floppy, head, track, sector, 0, sector_buffer, sizeof(sector_buffer), 0);
- }
-
- return FLOPPY_ERROR_SUCCESS;
+ return "Apple II 13-sector d13 image";
}
-
-
-/* -----------------------------------------------------------------------
- * Nibble order code
- * ----------------------------------------------------------------------- */
-
-static FLOPPY_IDENTIFY(apple2_nib_identify)
+const char *a2_13sect_format::extensions() const noexcept
{
- uint64_t size;
- size = floppy_image_size(floppy);
- *vote = ((size == APPLE2_TRACK_COUNT * APPLE2_SECTOR_COUNT * APPLE2_NIBBLE_SIZE) || (size == (APPLE2_TRACK_COUNT + 1) * APPLE2_SECTOR_COUNT * APPLE2_NIBBLE_SIZE)) ? 100 : 0;
- return FLOPPY_ERROR_SUCCESS;
+ return "d13";
}
-
-
-static FLOPPY_CONSTRUCT(apple2_nib_construct)
+bool a2_13sect_format::supports_save() const noexcept
{
- return apple2_general_construct(floppy, APPLE2_IMAGE_NIB);
+ return false;
}
+const a2_13sect_format FLOPPY_A213S_FORMAT;
-
-static floperr_t apple2_nib_read_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buflen)
+static const uint8_t translate6[0x40] =
{
- 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;
-}
-
-
+ 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
+};
-static floperr_t apple2_nib_write_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, const void *buffer, size_t buflen)
+static const uint8_t dos_skewing[] =
{
- 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
- * ----------------------------------------------------------------------- */
+ 0x00, 0x07, 0x0E, 0x06, 0x0D, 0x05, 0x0C, 0x04,
+ 0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x0F
+};
-static int decode_nibbyte(uint8_t *nibint, const uint8_t *nibdata)
+static const uint8_t prodos_skewing[] =
{
- 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;
-}
-
+ 0x00, 0x08, 0x01, 0x09, 0x02, 0x0A, 0x03, 0x0B,
+ 0x04, 0x0C, 0x05, 0x0D, 0x06, 0x0E, 0x07, 0x0F
+};
-static int disk_decode_nib(uint8_t *data, const uint8_t *nibble, int *volume, int *track, int *sector)
+a2_16sect_format::a2_16sect_format(bool prodos_order) : floppy_image_format_t(), m_prodos_order(prodos_order)
{
- uint8_t read_volume;
- uint8_t read_track;
- uint8_t read_sector;
- uint8_t read_checksum;
- int i;
- uint8_t b, xorvalue, newvalue;
-
- const uint8_t *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_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
+a2_16sect_dos_format::a2_16sect_dos_format() : a2_16sect_format(false)
{
- floperr_t err;
- const uint8_t *nibble;
- uint8_t *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, nullptr);
- if (err)
- return err;
- track_data = (uint8_t *) track_data_v;
-
- nibble = track_data + (sector * APPLE2_NIBBLE_SIZE);
-
- if (disk_decode_nib((uint8_t *)buffer, nibble, nullptr, nullptr, nullptr))
- return FLOPPY_ERROR_INVALIDIMAGE;
-
- return FLOPPY_ERROR_SUCCESS;
}
-
-
-static void disk_encode_nib(uint8_t *nibble, const uint8_t *data, int volume, int track, int sector)
+const char *a2_16sect_dos_format::name() const noexcept
{
- 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];
+ return "a2_16sect_dos";
}
-
-
-static floperr_t apple2_nib_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
+const char *a2_16sect_dos_format::description() const noexcept
{
- floperr_t err;
- uint8_t *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, nullptr);
- if (err)
- return err;
- track_data = (uint8_t *) track_data_v;
-
- disk_encode_nib(track_data + sector * APPLE2_NIBBLE_SIZE, (const uint8_t *)buffer, 254, track, sector);
- return FLOPPY_ERROR_SUCCESS;
+ return "Apple II 16-sector dsk image (DOS sector order)";
}
-
-
-static floperr_t apple2_nib_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length)
+const char *a2_16sect_dos_format::extensions() const noexcept
{
- *sector_length = APPLE2_SECTOR_SIZE;
- return FLOPPY_ERROR_SUCCESS;
+ return "dsk,do";
}
-
-
-static uint32_t apple2_get_track_size(floppy_image_legacy *floppy, int head, int track)
+a2_16sect_prodos_format::a2_16sect_prodos_format() : a2_16sect_format(true)
{
- return APPLE2_NIBBLE_SIZE * APPLE2_SECTOR_COUNT;
}
-
-
-/* ----------------------------------------------------------------------- */
-
-LEGACY_FLOPPY_OPTIONS_START( apple2 )
- LEGACY_FLOPPY_OPTION( apple2_do, "do,dsk,bin", "Apple ][ DOS order disk image", apple2_dsk_identify, apple2_do_construct, nullptr,
- HEADS([1])
- TRACKS([APPLE2_TRACK_COUNT])
- SECTORS([16])
- SECTOR_LENGTH([256])
- FIRST_SECTOR_ID([0]))
- LEGACY_FLOPPY_OPTION( apple2_po, "po,dsk,bin", "Apple ][ ProDOS order disk image", apple2_dsk_identify, apple2_po_construct, nullptr,
- HEADS([1])
- TRACKS([APPLE2_TRACK_COUNT])
- SECTORS([16])
- SECTOR_LENGTH([256])
- FIRST_SECTOR_ID([0]))
- LEGACY_FLOPPY_OPTION( apple2_nib, "dsk,nib", "Apple ][ Nibble order disk image", apple2_nib_identify, apple2_nib_construct, nullptr,
- HEADS([1])
- TRACKS([APPLE2_TRACK_COUNT])
- SECTORS([16])
- SECTOR_LENGTH([256])
- FIRST_SECTOR_ID([0]))
-LEGACY_FLOPPY_OPTIONS_END
-
-// license:BSD-3-Clause
-// copyright-holders:Olivier Galibert
-/***************************************************************************
-
- New implementation
-
-****************************************************************************/
-
-static const uint8_t dos_skewing[] =
-{
- 0x00, 0x07, 0x0E, 0x06, 0x0D, 0x05, 0x0C, 0x04,
- 0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x0F
-};
-
-static const uint8_t prodos_skewing[] =
-{
- 0x00, 0x08, 0x01, 0x09, 0x02, 0x0A, 0x03, 0x0B,
- 0x04, 0x0C, 0x05, 0x0D, 0x06, 0x0E, 0x07, 0x0F
-};
-
-
-a2_16sect_format::a2_16sect_format() : floppy_image_format_t(), m_prodos_order(false)
+const char *a2_16sect_prodos_format::name() const noexcept
{
+ return "a2_16sect_prodos";
}
-const char *a2_16sect_format::name() const
+const char *a2_16sect_prodos_format::description() const noexcept
{
- return "a2_16sect";
+ return "Apple II 16-sector dsk image (ProDos sector order)";
}
-const char *a2_16sect_format::description() const
+const char *a2_16sect_prodos_format::extensions() const noexcept
{
- return "Apple II 16-sector dsk image";
+ return "dsk,po";
}
-const char *a2_16sect_format::extensions() const
+bool a2_16sect_format::supports_save() const noexcept
{
- return "dsk,do,po";
+ return true;
}
-bool a2_16sect_format::supports_save() const
+int a2_16sect_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- return true;
-}
+ uint64_t size;
+ if (io.length(size))
+ return 0;
-int a2_16sect_format::identify(io_generic *io, uint32_t form_factor)
-{
- uint64_t size = io_generic_size(io);
- //uint32_t expected_size = 35 * 16 * 256;
- uint32_t expected_size = APPLE2_TRACK_COUNT * 16 * 256;
+ // check standard size plus some oddball sizes in our softlist
+ if (
+ size != APPLE2_TRACK_COUNT * 16 * APPLE2_SECTOR_SIZE
+ && size != APPLE2_STD_TRACK_COUNT * 16 * APPLE2_SECTOR_SIZE
+ && size != 143403 && size != 143363 && size != 143358 && size != 143195
+ )
+ {
+ return 0;
+ }
+
+ uint8_t sector_data[APPLE2_SECTOR_SIZE*2];
+ static const unsigned char pascal_block1[4] = { 0x08, 0xa5, 0x0f, 0x29 };
+ static const unsigned char pascal2_block1[4] = { 0xff, 0xa2, 0x00, 0x8e };
+ static const unsigned char dos33_block1[4] = { 0xa2, 0x02, 0x8e, 0x52 };
+ static const unsigned char sos_block1[4] = { 0xc9, 0x20, 0xf0, 0x3e };
+ static const unsigned char a3a2emul_block1[6] = { 0x8d, 0xd0, 0x03, 0x4c, 0xc7, 0xa4 };
+ static const unsigned char cpm22_block1[8] = { 0xa2, 0x55, 0xa9, 0x00, 0x9d, 0x00, 0x0d, 0xca };
+ static const unsigned char subnod_block1[8] = { 0x63, 0xaa, 0xf0, 0x76, 0x8d, 0x63, 0xaa, 0x8e };
+
+ auto const [err, actual] = read_at(io, 0, sector_data, sizeof sector_data);
+ if (err || actual != sizeof sector_data)
+ return 0;
- // check standard size plus some oddball sizes in our softlist
- if ((size == expected_size) || (size == 35 * 16 * 256) || (size == 143403) || (size == 143363) || (size == 143358))
+ bool prodos_order = false;
+ // check ProDOS boot block
+ if (!memcmp("PRODOS", &sector_data[0x103], 6))
+ {
+ prodos_order = true;
+ } // check for alternate version ProDOS boot block
+ if (!memcmp("PRODOS", &sector_data[0x121], 6))
+ {
+ prodos_order = true;
+ } // check for ProDOS order SOS disk
+ else if (!memcmp(sos_block1, &sector_data[0x100], 4))
+ {
+ prodos_order = true;
+ } // check for Apple III A2 emulator disk in ProDOS order
+ else if (!memcmp(a3a2emul_block1, &sector_data[0x100], 6))
+ {
+ prodos_order = true;
+ } // check for PCPI Applicard software in ProDOS order
+ else if (!memcmp("COPYRIGHT (C) 1979, DIGITAL RESEARCH", &sector_data[0x118], 36))
+ {
+ prodos_order = true;
+ } // check Apple II Pascal
+ else if (!memcmp("SYSTEM.APPLE", &sector_data[0xd7], 12))
+ {
+ // Pascal discs can still be DOS order.
+ // Check for the second half of the boot code at 0x100
+ // (which means ProDOS order)
+ if (!memcmp(pascal_block1, &sector_data[0x100], 4))
{
- return 50;
+ prodos_order = true;
}
+ } // check for DOS 3.3 disks in ProDOS order
+ else if (!memcmp(dos33_block1, &sector_data[0x100], 4))
+ {
+ prodos_order = true;
+ } // check for a later version of the Pascal boot block
+ else if (!memcmp(pascal2_block1, &sector_data[0x100], 4))
+ {
+ prodos_order = true;
+ } // check for CP/M disks in ProDOS order
+ else if (!memcmp(cpm22_block1, &sector_data[0x100], 8))
+ {
+ prodos_order = true;
+ } // check for subnodule disk
+ else if (!memcmp(subnod_block1, &sector_data[0x100], 8))
+ {
+ prodos_order = true;
+ } // check for ProDOS 2.5's new boot block
+ else if (!memcmp("PRODOS", &sector_data[0x3a], 6))
+ {
+ prodos_order = true;
+ }
- return 0;
+ return FIFID_SIZE | (m_prodos_order == prodos_order ? FIFID_HINT : 0);
}
-// following is placeholder, is completely wrong.
-const floppy_image_format_t::desc_e a2_16sect_format::mac_gcr[] = {
- { SECTOR_LOOP_START, 0, -1 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xd5aa96, 24 },
- { CRC_MACHEAD_START, 0 },
- { TRACK_ID_GCR6 },
- { SECTOR_ID_GCR6 },
- { TRACK_HEAD_ID_GCR6 },
- { SECTOR_INFO_GCR6 },
- { CRC_END, 0 },
- { CRC, 0 },
- { RAWBITS, 0xdeaaff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xd5aaad, 24 },
- { SECTOR_ID_GCR6 },
- { SECTOR_DATA_MAC, -1 },
- { RAWBITS, 0xdeaaff, 24 },
- { RAWBITS, 0xff, 8 },
- { SECTOR_LOOP_END },
- { END },
-};
-
-bool a2_16sect_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool a2_16sect_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return false;
- m_prodos_order = false;
- m_tracks = (size == (40 * 16 * 256)) ? 40 : 35;
+ image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD);
+
+ int tracks = (size == (APPLE2_TRACK_COUNT * 16 * APPLE2_SECTOR_SIZE)) ? APPLE2_TRACK_COUNT : APPLE2_STD_TRACK_COUNT;
int fpos = 0;
- for(int track=0; track < m_tracks; track++) {
+ for(int track=0; track < tracks; track++) {
std::vector<uint32_t> track_data;
- uint8_t sector_data[256*16];
- static const unsigned char pascal_block1[4] = { 0x08, 0xa5, 0x0f, 0x29 };
- static const unsigned char pascal2_block1[4] = { 0xff, 0xa2, 0x00, 0x8e };
- static const unsigned char dos33_block1[4] = { 0xa2, 0x02, 0x8e, 0x52 };
- static const unsigned char sos_block1[4] = { 0xc9, 0x20, 0xf0, 0x3e };
- static const unsigned char a3a2emul_block1[6] = { 0x8d, 0xd0, 0x03, 0x4c, 0xc7, 0xa4 };
- static const unsigned char cpm22_block1[8] = { 0xa2, 0x55, 0xa9, 0x00, 0x9d, 0x00, 0x0d, 0xca };
- static const unsigned char subnod_block1[8] = { 0x63, 0xaa, 0xf0, 0x76, 0x8d, 0x63, 0xaa, 0x8e };
-
- io_generic_read(io, sector_data, fpos, 256*16);
-
- if (track == 0 && fpos == 0)
- {
- // check ProDOS boot block
- if (!memcmp("PRODOS", &sector_data[0x103], 6))
- {
- m_prodos_order = true;
- } // check for alternate version ProDOS boot block
- if (!memcmp("PRODOS", &sector_data[0x121], 6))
- {
- m_prodos_order = true;
- } // check for ProDOS order SOS disk
- else if (!memcmp(sos_block1, &sector_data[0x100], 4))
- {
- m_prodos_order = true;
- } // check for Apple III A2 emulator disk in ProDOS order
- else if (!memcmp(a3a2emul_block1, &sector_data[0x100], 6))
- {
- m_prodos_order = true;
- } // check for PCPI Applicard software in ProDOS order
- else if (!memcmp("COPYRIGHT (C) 1979, DIGITAL RESEARCH", &sector_data[0x118], 36))
- {
- m_prodos_order = true;
- } // check Apple II Pascal
- else if (!memcmp("SYSTEM.APPLE", &sector_data[0xd7], 12))
- {
- // Pascal discs can still be DOS order.
- // Check for the second half of the boot code at 0x100
- // (which means ProDOS order)
- if (!memcmp(pascal_block1, &sector_data[0x100], 4))
- {
- m_prodos_order = true;
- }
- } // check for DOS 3.3 disks in ProDOS order
- else if (!memcmp(dos33_block1, &sector_data[0x100], 4))
- {
- m_prodos_order = true;
- } // check for a later version of the Pascal boot block
- else if (!memcmp(pascal2_block1, &sector_data[0x100], 4))
- {
- m_prodos_order = true;
- } // check for CP/M disks in ProDOS order
- else if (!memcmp(cpm22_block1, &sector_data[0x100], 8))
- {
- m_prodos_order = true;
- } // check for subnodule disk
- else if (!memcmp(subnod_block1, &sector_data[0x100], 8))
- {
- m_prodos_order = true;
- } // check for ProDOS 2.5's new boot block
- else if (!memcmp("PRODOS", &sector_data[0x3a], 6))
- {
- m_prodos_order = true;
- }
- else if (!memcmp("PRODOS", &sector_data[0x40], 6))
- {
- m_prodos_order = true;
- }
- }
+ uint8_t sector_data[APPLE2_SECTOR_SIZE*16];
- fpos += 256*16;
+ auto const [err, actual] = read_at(io, fpos, sector_data, sizeof sector_data);
+ // Some supported images have oddball sizes, where the last track is incomplete.
+ // Skip the `actual` check to avoid rejecting them.
+ if (err /* || actual != sizeof sector_data */)
+ return false;
+
+ fpos += APPLE2_SECTOR_SIZE*16;
for(int i=0; i<49; i++)
raw_w(track_data, 10, 0x3fc);
for(int i=0; i<16; i++) {
@@ -699,7 +333,7 @@ bool a2_16sect_format::load(io_generic *io, uint32_t form_factor, floppy_image *
sector = dos_skewing[i];
}
- const uint8_t *sdata = sector_data + 256 * sector;
+ const uint8_t *sdata = sector_data + APPLE2_SECTOR_SIZE * sector;
for(int j=0; j<20; j++)
raw_w(track_data, 10, 0x3fc);
raw_w(track_data, 8, 0xff);
@@ -747,14 +381,14 @@ bool a2_16sect_format::load(io_generic *io, uint32_t form_factor, floppy_image *
return true;
}
-uint8_t a2_16sect_format::gb(const uint8_t *buf, int ts, int &pos, int &wrap)
+uint8_t a2_16sect_format::gb(const std::vector<bool> &buf, int &pos, int &wrap)
{
uint8_t v = 0;
int w1 = wrap;
while(wrap != w1+2 && !(v & 0x80)) {
- v = v << 1 | ((buf[pos >> 3] >> (7-(pos & 7))) & 1);
+ v = (v << 1) | buf[pos];
pos++;
- if(pos == ts) {
+ if(pos == buf.size()) {
pos = 0;
wrap++;
}
@@ -762,239 +396,238 @@ uint8_t a2_16sect_format::gb(const uint8_t *buf, int ts, int &pos, int &wrap)
return v;
}
-void a2_16sect_format::update_chk(const uint8_t *data, int size, uint32_t &chk)
-{
-}
-
-//#define VERBOSE_SAVE
-
-bool a2_16sect_format::save(io_generic *io, floppy_image *image)
+bool a2_16sect_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
- int g_tracks, g_heads;
- int visualgrid[16][APPLE2_TRACK_COUNT]; // visualizer grid, cleared/initialized below
-
-// lenient addr check: if unset, only accept an addr mark if the checksum was good
-// if set, accept an addr mark if the track and sector values are both sane
-#undef LENIENT_ADDR_CHECK
-// if set, use the old, not as robust logic for choosing which copy of a decoded sector to write
-// to the resulting image if the sector has a bad checksum and/or postamble
-#undef USE_OLD_BEST_SECTOR_PRIORITY
-// nothing found
-#define NOTFOUND 0
-// address mark was found
-#define ADDRFOUND 1
-// address checksum is good
-#define ADDRGOOD 2
-// data mark was found (requires addrfound and sane values)
-#define DATAFOUND 4
-// data checksum is good
-#define DATAGOOD 8
-// data postamble is good
-#define DATAPOST 16
- for (auto & elem : visualgrid) {
- for (int j = 0; j < m_tracks; j++) {
- elem[j] = 0;
- }
+ int g_tracks, g_heads;
+ int visualgrid[16][APPLE2_TRACK_COUNT]; // visualizer grid, cleared/initialized below
+
+ constexpr bool VERBOSE_SAVE = false;
+
+ // if false, only accept an addr mark if the checksum was good
+ // if true, accept an addr mark if the track and sector values are both sane
+ constexpr bool LENIENT_ADDR_CHECK = false;
+
+ // if true, use the old, not as robust logic for choosing which copy of a decoded sector to write
+ // to the resulting image if the sector has a bad checksum and/or postamble
+ constexpr bool USE_OLD_BEST_SECTOR_PRIORITY = false;
+
+ // nothing found
+ constexpr int NOTFOUND = 0;
+ // address mark was found
+ constexpr int ADDRFOUND = 1;
+ // address checksum is good
+ constexpr int ADDRGOOD = 2;
+ // data mark was found (requires addrfound and sane values)
+ constexpr int DATAFOUND = 4;
+ // data checksum is good
+ constexpr int DATAGOOD = 8;
+ // data postamble is good
+ constexpr int DATAPOST = 16;
+
+ for (auto & elem : visualgrid) {
+ for (int j = 0; j < APPLE2_TRACK_COUNT; j++) {
+ elem[j] = NOTFOUND;
}
- image->get_actual_geometry(g_tracks, g_heads);
+ }
+ image.get_actual_geometry(g_tracks, g_heads);
- int head = 0;
+ int head = 0;
- int pos_data = 0;
+ int pos_data = 0;
- for(int track=0; track < m_tracks; track++) {
- uint8_t sectdata[(256)*16];
- memset(sectdata, 0, sizeof(sectdata));
- int nsect = 16;
- uint8_t buf[10000]; // normal is 51090 cells, e.g. 6387 bytes, add 50% and round up for denser than normal disks
- int ts;
- #ifdef VERBOSE_SAVE
- fprintf(stderr,"DEBUG: a2_16sect_format::save() about to generate bitstream from track %d...", track);
- #endif
- generate_bitstream_from_track(track, head, 3915, buf, ts, image);
- #ifdef VERBOSE_SAVE
- fprintf(stderr,"done.\n");
- #endif
- int pos = 0;
- int wrap = 0;
- int hb = 0;
- int dosver = 0; // apple dos version; 0 = >=3.3, 1 = <3.3
- for(;;) {
- uint8_t v = gb(buf, ts, pos, wrap);
- if(v == 0xff) {
+ for(int track=0; track < g_tracks; track++) {
+ uint8_t sectdata[APPLE2_SECTOR_SIZE*16];
+ memset(sectdata, 0, sizeof(sectdata));
+ int nsect = 16;
+ if(VERBOSE_SAVE) {
+ fprintf(stderr,"DEBUG: a2_16sect_format::save() about to generate bitstream from track %d...", track);
+ }
+ auto buf = generate_bitstream_from_track(track, head, 3915, image);
+ if(VERBOSE_SAVE) {
+ fprintf(stderr,"done.\n");
+ }
+ int pos = 0;
+ int wrap = 0;
+ int hb = 0;
+ int dosver = 0; // apple dos version; 0 = >=3.3, 1 = <3.3
+ for(;;) {
+ uint8_t v = gb(buf, pos, wrap);
+ if(v == 0xff) {
+ hb = 1;
+ }
+ else if(hb == 1 && v == 0xd5){
+ hb = 2;
+ }
+ else if(hb == 2 && v == 0xaa) {
+ hb = 3;
+ }
+ else if(hb == 3 && ((v == 0x96) || (v == 0xab))) { // 0x96 = dos 3.3/16sec, 0xab = dos 3.21 and below/13sec
+ hb = 4;
+ if (v == 0xab) dosver = 1;
+ }
+ else
+ hb = 0;
+
+ if(hb == 4) {
+ uint8_t h[11];
+ for(auto & elem : h)
+ elem = gb(buf, pos, wrap);
+ //uint8_t v2 = gcr6bw_tb[h[2]];
+ uint8_t vl = gcr4_decode(h[0],h[1]);
+ uint8_t tr = gcr4_decode(h[2],h[3]);
+ uint8_t se = gcr4_decode(h[4],h[5]);
+ uint8_t chk = gcr4_decode(h[6],h[7]);
+ if(VERBOSE_SAVE) {
+ uint32_t post = get_u24be(&h[8]);
+ printf("Address Mark:\tVolume %d, Track %d, Sector %2d, Checksum %02X: %s, Postamble %03X: %s\n",
+ vl, tr, se, chk, (chk ^ vl ^ tr ^ se)==0?"OK":"BAD", post, (post&0xFFFF00)==0xDEAA00?"OK":"BAD");
+ }
+ // sanity check
+ if (tr == track && se < nsect) {
+ visualgrid[se][track] |= ADDRFOUND;
+ visualgrid[se][track] |= ((chk ^ vl ^ tr ^ se)==0)?ADDRGOOD:0;
+
+ if (visualgrid[se][track] & (LENIENT_ADDR_CHECK ? ADDRFOUND : ADDRGOOD)) {
+ int opos = pos;
+ int owrap = wrap;
+ hb = 0;
+ for(int i=0; i<20 && hb != 4; i++) {
+ v = gb(buf, pos, wrap);
+ if(v == 0xff)
hb = 1;
- }
- else if(hb == 1 && v == 0xd5){
+ else if(hb == 1 && v == 0xd5)
hb = 2;
- }
- else if(hb == 2 && v == 0xaa) {
+ else if(hb == 2 && v == 0xaa)
hb = 3;
- }
- else if(hb == 3 && ((v == 0x96) || (v == 0xab))) { // 0x96 = dos 3.3/16sec, 0xab = dos 3.21 and below/13sec
+ else if(hb == 3 && v == 0xad)
hb = 4;
- if (v == 0xab) dosver = 1;
- }
else
hb = 0;
+ }
+ if((hb == 4)&&(dosver == 0)) {
+ visualgrid[se][track] |= DATAFOUND;
+ uint8_t *dest;
+ uint8_t data[0x157];
+ uint32_t dpost = 0;
+ uint8_t c = 0;
+
+ if (m_prodos_order)
+ {
+ dest = sectdata+APPLE2_SECTOR_SIZE*prodos_skewing[se];
+ }
+ else
+ {
+ dest = sectdata+APPLE2_SECTOR_SIZE*dos_skewing[se];
+ }
- if(hb == 4) {
- uint8_t h[11];
- for(auto & elem : h)
- elem = gb(buf, ts, pos, wrap);
- //uint8_t v2 = gcr6bw_tb[h[2]];
- uint8_t vl = gcr4_decode(h[0],h[1]);
- uint8_t tr = gcr4_decode(h[2],h[3]);
- uint8_t se = gcr4_decode(h[4],h[5]);
- uint8_t chk = gcr4_decode(h[6],h[7]);
- #ifdef VERBOSE_SAVE
- uint32_t post = (h[8]<<16)|(h[9]<<8)|h[10];
- printf("Address Mark:\tVolume %d, Track %d, Sector %2d, Checksum %02X: %s, Postamble %03X: %s\n", vl, tr, se, chk, (chk ^ vl ^ tr ^ se)==0?"OK":"BAD", post, (post&0xFFFF00)==0xDEAA00?"OK":"BAD");
- #endif
- // sanity check
- if (tr == track && se < nsect) {
- visualgrid[se][track] |= ADDRFOUND;
- visualgrid[se][track] |= ((chk ^ vl ^ tr ^ se)==0)?ADDRGOOD:0;
-#ifdef LENIENT_ADDR_CHECK
- if ((visualgrid[se][track] & ADDRFOUND) == ADDRFOUND) {
-#else
- if ((visualgrid[se][track] & ADDRGOOD) == ADDRGOOD) {
-#endif
- int opos = pos;
- int owrap = wrap;
- hb = 0;
- for(int i=0; i<20 && hb != 4; i++) {
- v = gb(buf, ts, pos, wrap);
- if(v == 0xff)
- hb = 1;
- else if(hb == 1 && v == 0xd5)
- hb = 2;
- else if(hb == 2 && v == 0xaa)
- hb = 3;
- else if(hb == 3 && v == 0xad)
- hb = 4;
- else
- hb = 0;
- }
- if((hb == 4)&&(dosver == 0)) {
- visualgrid[se][track] |= DATAFOUND;
- uint8_t *dest;
- uint8_t data[0x157];
- uint32_t dpost = 0;
- uint8_t c = 0;
-
- if (m_prodos_order)
- {
- dest = sectdata+(256)*prodos_skewing[se];
- }
- else
- {
- dest = sectdata+(256)*dos_skewing[se];
- }
-
- // first read in sector and decode to 6bit form
- for(int i=0; i<0x156; i++) {
- data[i] = gcr6bw_tb[gb(buf, ts, pos, wrap)] ^ c;
- c = data[i];
- // printf("%02x ", c);
- // if (((i&0xf)+1)==0x10) printf("\n");
- }
- // read the checksum byte
- data[0x156] = gcr6bw_tb[gb(buf,ts,pos,wrap)];
- // now read the postamble bytes
- for(int i=0; i<3; i++) {
- dpost <<= 8;
- dpost |= gb(buf, ts, pos, wrap);
- }
- // next combine in the upper 2 bits of each byte
- uint8_t bit_swap[4] = { 0, 2, 1, 3 };
- for(int i=0; i<0x56; i++)
- data[i+0x056] = data[i+0x056]<<2 | bit_swap[data[i]&3];
- for(int i=0; i<0x56; i++)
- data[i+0x0ac] = data[i+0x0ac]<<2 | bit_swap[(data[i]>>2)&3];
- for(int i=0; i<0x54; i++)
- data[i+0x102] = data[i+0x102]<<2 | bit_swap[(data[i]>>4)&3];
- // now decode it into 256 bytes
- // but only write it if the bitfield of the track shows datagood is NOT set.
- // if it is set we don't want to overwrite a guaranteed good read with a bad one
- // if past read had a bad checksum or bad postamble...
-#ifndef USE_OLD_BEST_SECTOR_PRIORITY
- if (((visualgrid[se][track]&DATAGOOD)==0)||((visualgrid[se][track]&DATAPOST)==0)) {
- // if the current read is good, and postamble is good, write it in, no matter what.
- // if the current read is good and the current postamble is bad, write it in unless the postamble was good before
- // if the current read is bad and the current postamble is good and the previous read had neither good, write it in
- // if the current read isn't good and neither is the postamble but nothing better
- // has been written before, write it anyway.
- if ( ((data[0x156] == c) && (dpost&0xFFFF00)==0xDEAA00) ||
- (((data[0x156] == c) && (dpost&0xFFFF00)!=0xDEAA00) && ((visualgrid[se][track]&DATAPOST)==0)) ||
- (((data[0x156] != c) && (dpost&0xFFFF00)==0xDEAA00) && (((visualgrid[se][track]&DATAGOOD)==0)&&(visualgrid[se][track]&DATAPOST)==0)) ||
- (((data[0x156] != c) && (dpost&0xFFFF00)!=0xDEAA00) && (((visualgrid[se][track]&DATAGOOD)==0)&&(visualgrid[se][track]&DATAPOST)==0))
- ) {
- for(int i=0x56; i<0x156; i++) {
- uint8_t dv = data[i];
- *dest++ = dv;
- }
- }
- }
-#else
- if ((visualgrid[se][track]&DATAGOOD)==0) {
- for(int i=0x56; i<0x156; i++) {
- uint8_t dv = data[i];
- *dest++ = dv;
- }
- }
-#endif
- // do some checking
- #ifdef VERBOSE_SAVE
- if ((data[0x156] != c) || (dpost&0xFFFF00)!=0xDEAA00)
- fprintf(stderr,"Data Mark:\tChecksum xpctd %d found %d: %s, Postamble %03X: %s\n", data[0x156], c, (data[0x156]==c)?"OK":"BAD", dpost, (dpost&0xFFFF00)==0xDEAA00?"OK":"BAD");
- #endif
- if (data[0x156] == c) visualgrid[se][track] |= DATAGOOD;
- if ((dpost&0xFFFF00)==0xDEAA00) visualgrid[se][track] |= DATAPOST;
- } else if ((hb == 4)&&(dosver == 1)) {
- fprintf(stderr,"ERROR: We don't handle dos sectors below 3.3 yet!\n");
- } else {
- pos = opos;
- wrap = owrap;
+ // first read in sector and decode to 6bit form
+ for(int i=0; i<0x156; i++) {
+ data[i] = gcr6bw_tb[gb(buf, pos, wrap)] ^ c;
+ c = data[i];
+ // printf("%02x ", c);
+ // if (((i&0xf)+1)==0x10) printf("\n");
+ }
+ // read the checksum byte
+ data[0x156] = gcr6bw_tb[gb(buf,pos,wrap)];
+ // now read the postamble bytes
+ for(int i=0; i<3; i++) {
+ dpost <<= 8;
+ dpost |= gb(buf, pos, wrap);
+ }
+ // next combine in the upper 2 bits of each byte
+ uint8_t bit_swap[4] = { 0, 2, 1, 3 };
+ for(int i=0; i<0x56; i++)
+ data[i+0x056] = data[i+0x056]<<2 | bit_swap[data[i]&3];
+ for(int i=0; i<0x56; i++)
+ data[i+0x0ac] = data[i+0x0ac]<<2 | bit_swap[(data[i]>>2)&3];
+ for(int i=0; i<0x54; i++)
+ data[i+0x102] = data[i+0x102]<<2 | bit_swap[(data[i]>>4)&3];
+ // now decode it into 256 bytes
+ // but only write it if the bitfield of the track shows datagood is NOT set.
+ // if it is set we don't want to overwrite a guaranteed good read with a bad one
+ // if past read had a bad checksum or bad postamble...
+ if(USE_OLD_BEST_SECTOR_PRIORITY) {
+ if ((visualgrid[se][track]&DATAGOOD)==0) {
+ for(int i=0x56; i<0x156; i++) {
+ uint8_t dv = data[i];
+ *dest++ = dv;
+ }
+ }
+ } else {
+ if (((visualgrid[se][track]&DATAGOOD)==0)||((visualgrid[se][track]&DATAPOST)==0)) {
+ // if the current read is good, and postamble is good, write it in, no matter what.
+ // if the current read is good and the current postamble is bad, write it in unless the postamble was good before
+ // if the current read is bad and the current postamble is good and the previous read had neither good, write it in
+ // if the current read isn't good and neither is the postamble but nothing better
+ // has been written before, write it anyway.
+ if ( ((data[0x156] == c) && (dpost&0xFFFF00)==0xDEAA00) ||
+ (((data[0x156] == c) && (dpost&0xFFFF00)!=0xDEAA00) && ((visualgrid[se][track]&DATAPOST)==0)) ||
+ (((data[0x156] != c) && (dpost&0xFFFF00)==0xDEAA00) && (((visualgrid[se][track]&DATAGOOD)==0)&&(visualgrid[se][track]&DATAPOST)==0)) ||
+ (((data[0x156] != c) && (dpost&0xFFFF00)!=0xDEAA00) && (((visualgrid[se][track]&DATAGOOD)==0)&&(visualgrid[se][track]&DATAPOST)==0))
+ ) {
+ for(int i=0x56; i<0x156; i++) {
+ uint8_t dv = data[i];
+ *dest++ = dv;
}
}
}
- hb = 0;
+ }
+ // do some checking
+ if(VERBOSE_SAVE) {
+ if ((data[0x156] != c) || (dpost&0xFFFF00)!=0xDEAA00)
+ fprintf(stderr,"Data Mark:\tChecksum xpctd %d found %d: %s, Postamble %03X: %s\n",
+ data[0x156], c, (data[0x156]==c)?"OK":"BAD", dpost, (dpost&0xFFFF00)==0xDEAA00?"OK":"BAD");
+ }
+ if (data[0x156] == c) visualgrid[se][track] |= DATAGOOD;
+ if ((dpost&0xFFFF00)==0xDEAA00) visualgrid[se][track] |= DATAPOST;
+ } else if ((hb == 4)&&(dosver == 1)) {
+ fprintf(stderr,"ERROR: We don't handle dos sectors below 3.3 yet!\n");
+ } else {
+ pos = opos;
+ wrap = owrap;
}
- if(wrap)
- break;
- }
- for(int i=0; i<nsect; i++) {
- //if(nsect>0) printf("t%d,", track);
- uint8_t *data = sectdata + (256)*i;
- io_generic_write(io, data, pos_data, 256);
- pos_data += 256;
+ }
}
- //printf("\n");
+ hb = 0;
+ }
+ if(wrap)
+ break;
}
- // display a little table of which sectors decoded ok
- #ifdef VERBOSE_SAVE
+ for(int i=0; i<nsect; i++) {
+ //if(nsect>0) printf("t%d,", track);
+ uint8_t const *const data = sectdata + APPLE2_SECTOR_SIZE*i;
+ auto const [err, actual] = write_at(io, pos_data, data, APPLE2_SECTOR_SIZE);
+ if (err || actual != APPLE2_SECTOR_SIZE)
+ return false;
+ pos_data += APPLE2_SECTOR_SIZE;
+ }
+ //printf("\n");
+ }
+ // display a little table of which sectors decoded ok
+ if(VERBOSE_SAVE) {
int total_good = 0;
for (int j = 0; j < APPLE2_TRACK_COUNT; j++) {
printf("T%2d: ",j);
for (int i = 0; i < 16; i++) {
if (visualgrid[i][j] == NOTFOUND) printf("-NF- ");
else {
- if (visualgrid[i][j] & ADDRFOUND) printf("a"); else printf(" ");
- if (visualgrid[i][j] & ADDRGOOD) printf("A"); else printf(" ");
- if (visualgrid[i][j] & DATAFOUND) printf("d"); else printf(" ");
- if (visualgrid[i][j] & DATAGOOD) { printf("D"); total_good++; } else printf(" ");
- if (visualgrid[i][j] & DATAPOST) printf("."); else printf(" ");
+ if (visualgrid[i][j] & ADDRFOUND) printf("a"); else printf(" ");
+ if (visualgrid[i][j] & ADDRGOOD) printf("A"); else printf(" ");
+ if (visualgrid[i][j] & DATAFOUND) printf("d"); else printf(" ");
+ if (visualgrid[i][j] & DATAGOOD) { printf("D"); total_good++; } else printf(" ");
+ if (visualgrid[i][j] & DATAPOST) printf("."); else printf(" ");
}
}
printf("\n");
}
printf("Total Good Sectors: %d\n", total_good);
- #endif
+ }
- return true;
+ return true;
}
-const floppy_format_type FLOPPY_A216S_FORMAT = &floppy_image_format_creator<a2_16sect_format>;
+const a2_16sect_dos_format FLOPPY_A216S_DOS_FORMAT;
+const a2_16sect_prodos_format FLOPPY_A216S_PRODOS_FORMAT;
/* RWTS18 format
* Developed by Roland Gustafsson (http://www.acts.org/roland/index.html)
for Br0derbund Software around 1986
@@ -1026,65 +659,36 @@ a2_rwts18_format::a2_rwts18_format() : floppy_image_format_t()
{
}
-const char *a2_rwts18_format::name() const
+const char *a2_rwts18_format::name() const noexcept
{
return "a2_rwts18";
}
-const char *a2_rwts18_format::description() const
+const char *a2_rwts18_format::description() const noexcept
{
return "Apple II RWTS18-type Image";
}
-const char *a2_rwts18_format::extensions() const
+const char *a2_rwts18_format::extensions() const noexcept
{
return "rti";
}
-bool a2_rwts18_format::supports_save() const
+bool a2_rwts18_format::supports_save() const noexcept
{
return true;
}
-int a2_rwts18_format::identify(io_generic *io, uint32_t form_factor)
+int a2_rwts18_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
- uint32_t expected_size = APPLE2_TRACK_COUNT * 16 * 256;
- return size == expected_size;
+ uint64_t size;
+ if(io.length(size))
+ return 0;
+ uint32_t const expected_size = APPLE2_TRACK_COUNT * 16 * 256;
+ return size == expected_size ? FIFID_SIZE : 0;
}
-// following is placeholder, is completely wrong.
-const floppy_image_format_t::desc_e a2_rwts18_format::mac_gcr[] = {
- { SECTOR_LOOP_START, 0, -1 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xd5aa96, 24 },
- { CRC_MACHEAD_START, 0 },
- { TRACK_ID_GCR6 },
- { SECTOR_ID_GCR6 },
- { TRACK_HEAD_ID_GCR6 },
- { SECTOR_INFO_GCR6 },
- { CRC_END, 0 },
- { CRC, 0 },
- { RAWBITS, 0xdeaaff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xd5aaad, 24 },
- { SECTOR_ID_GCR6 },
- { SECTOR_DATA_MAC, -1 },
- { RAWBITS, 0xdeaaff, 24 },
- { RAWBITS, 0xff, 8 },
- { SECTOR_LOOP_END },
- { END },
-};
-
-
-bool a2_rwts18_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool a2_rwts18_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
/* TODO: rewrite me properly
uint8_t sector_data[(256)*16];
@@ -1104,7 +708,8 @@ bool a2_rwts18_format::load(io_generic *io, uint32_t form_factor, floppy_image *
sectors[si].size = 256;
sectors[si].sector_id = si;
sectors[si].sector_info = format;
- io_generic_read(io, data, pos_data, 256);
+ size_t actual;
+ io.read_at(pos_data, data, 256, actual);
pos_data += 256;
}
generate_track(mac_gcr, track, head, sectors, 16, 3104*16, image);
@@ -1114,14 +719,14 @@ bool a2_rwts18_format::load(io_generic *io, uint32_t form_factor, floppy_image *
return false; // I hope that throws an error...
}
-uint8_t a2_rwts18_format::gb(const uint8_t *buf, int ts, int &pos, int &wrap)
+uint8_t a2_rwts18_format::gb(const std::vector<bool> &buf, int &pos, int &wrap)
{
uint8_t v = 0;
int w1 = wrap;
while(wrap != w1+2 && !(v & 0x80)) {
- v = v << 1 | ((buf[pos >> 3] >> (7-(pos & 7))) & 1);
+ v = (v << 1) | buf[pos];
pos++;
- if(pos == ts) {
+ if(pos == buf.size()) {
pos = 0;
wrap++;
}
@@ -1129,11 +734,7 @@ uint8_t a2_rwts18_format::gb(const uint8_t *buf, int ts, int &pos, int &wrap)
return v;
}
-void a2_rwts18_format::update_chk(const uint8_t *data, int size, uint32_t &chk)
-{
-}
-
-bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
+bool a2_rwts18_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
int g_tracks, g_heads;
int visualgrid[18][APPLE2_TRACK_COUNT]; // visualizer grid, cleared/initialized below
@@ -1162,7 +763,7 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
elem[j] = 0;
}
}
- image->get_actual_geometry(g_tracks, g_heads);
+ image.get_actual_geometry(g_tracks, g_heads);
int head = 0;
@@ -1172,18 +773,16 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
uint8_t sectdata[(768)*6];
memset(sectdata, 0, sizeof(sectdata));
int nsect = 18;
- uint8_t buf[130000]; // originally 13000, multiread dfi disks need larger
- int ts;
//fprintf(stderr,"DEBUG: a2_rwts18_format::save() about to generate bitstream from physical track %d (logical %d)...", track, track/2);
//~332 samples per cell, times 3+8+3 (14) for address mark, 24 for sync, 3+343+3 (349) for data mark, 24 for sync is around 743, near 776 expected
- generate_bitstream_from_track(0, head, 200000000/((3004*nsect*6)/2), buf, ts, image); // 3104 needs tweaking
+ auto buf = generate_bitstream_from_track(0, head, 200000000/((3004*nsect*6)/2), image); // 3104 needs tweaking
//fprintf(stderr,"done.\n");
int pos = 0;
int wrap = 0;
int hb = 0;
int dosver = 0; // apple dos version; 0 = >=3.3, 1 = <3.3
for(;;) {
- uint8_t v = gb(buf, ts, pos, wrap);
+ uint8_t v = gb(buf, pos, wrap);
if(v == 0xff)
hb = 1;
else if(hb == 1 && v == 0xd5)
@@ -1200,13 +799,13 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
if(hb == 4) {
uint8_t h[11];
for(auto & elem : h)
- elem = gb(buf, ts, pos, wrap);
+ elem = gb(buf, pos, wrap);
//uint8_t v2 = gcr6bw_tb[h[2]];
uint8_t vl = gcr4_decode(h[0],h[1]);
uint8_t tr = gcr4_decode(h[2],h[3]);
uint8_t se = gcr4_decode(h[4],h[5]);
uint8_t chk = gcr4_decode(h[6],h[7]);
- uint32_t post = (h[8]<<16)|(h[9]<<8)|h[10];
+ uint32_t post = get_u24be(&h[8]);
printf("Address Mark:\tVolume %d, Track %d, Sector %2d, Checksum %02X: %s, Postamble %03X: %s\n", vl, tr, se, chk, (chk ^ vl ^ tr ^ se)==0?"OK":"BAD", post, (post&0xFFFF00)==0xDEAA00?"OK":"BAD");
// sanity check
if (tr == 0 && se < nsect) {
@@ -1221,7 +820,7 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
int owrap = wrap;
hb = 0;
for(int i=0; i<20 && hb != 4; i++) {
- v = gb(buf, ts, pos, wrap);
+ v = gb(buf, pos, wrap);
if(v == 0xff)
hb = 1;
else if(hb == 1 && v == 0xd5)
@@ -1256,17 +855,17 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
uint8_t c = 0;
// first read in sector and decode to 6bit form
for(int i=0; i<0x156; i++) {
- data[i] = gcr6bw_tb[gb(buf, ts, pos, wrap)] ^ c;
+ data[i] = gcr6bw_tb[gb(buf, pos, wrap)] ^ c;
c = data[i];
// printf("%02x ", c);
// if (((i&0xf)+1)==0x10) printf("\n");
}
// read the checksum byte
- data[0x156] = gcr6bw_tb[gb(buf,ts,pos,wrap)];
+ data[0x156] = gcr6bw_tb[gb(buf,pos,wrap)];
// now read the postamble bytes
for(int i=0; i<3; i++) {
dpost <<= 8;
- dpost |= gb(buf, ts, pos, wrap);
+ dpost |= gb(buf, pos, wrap);
}
// next combine in the upper 2 bits of each byte
uint8_t bit_swap[4] = { 0, 2, 1, 3 };
@@ -1326,8 +925,8 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
}
for(int i=0; i<nsect; i++) {
//if(nsect>0) printf("t%d,", track);
- uint8_t *data = sectdata + (256)*i;
- io_generic_write(io, data, pos_data, 256);
+ uint8_t const *const data = sectdata + (256)*i;
+ /*auto const [err, actual] =*/ write_at(io, pos_data, data, 256); // FIXME: check for errors
pos_data += 256;
}
@@ -1336,18 +935,16 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
uint8_t sectdata[(768)*6];
memset(sectdata, 0, sizeof(sectdata));
int nsect = 18;
- uint8_t buf[130000]; // originally 13000, multiread dfi disks need larger
- int ts;
//fprintf(stderr,"DEBUG: a2_rwts18_format::save() about to generate bitstream from physical track %d (logical %d)...", track, track/2);
//~332 samples per cell, times 3+8+3 (14) for address mark, 24 for sync, 3+343+3 (349) for data mark, 24 for sync is around 743, near 776 expected
- generate_bitstream_from_track(track, head, 200000000/((3004*nsect*6)/2), buf, ts, image); // 3104 needs tweaking
+ auto buf = generate_bitstream_from_track(track, head, 200000000/((3004*nsect*6)/2), image); // 3104 needs tweaking
//fprintf(stderr,"done.\n");
int oldpos = 0; // DEBUG
int pos = 0;
int wrap = 0;
int hb = 0;
for(;;) {
- uint8_t v = gb(buf, ts, pos, wrap);
+ uint8_t v = gb(buf, pos, wrap);
if((v == 0xff) || (v == 0x9a)) // note 0x9a varies per title! this is an LFSR? generated value intended to throw off copiers, and only appears after the track splice (before sector 5)
hb = 1;
else if(hb == 1 && v == 0xd5)
@@ -1363,11 +960,11 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
uint8_t h[7];
// grab exactly 7 bytes: should be Track, Sector, Checksum, AA, FF and FF and the Br0derbund Title ID
for(auto & elem : h)
- elem = gb(buf, ts, pos, wrap);
+ elem = gb(buf, pos, wrap);
uint8_t tr = gcr6bw_tb[h[0]];
uint8_t se = gcr6bw_tb[h[1]];
uint8_t chk = gcr6bw_tb[h[2]];
- uint32_t post = (h[3]<<16)|(h[4]<<8)|h[5];
+ uint32_t post = get_u24be(&h[3]);
uint8_t bbundid = h[6];
printf("RWTS18 AM:\t Track %d, Sector %2d, Checksum %02X: %s, Postamble %03X: %s, BBUNDID %02x\n", tr, se, chk, (chk ^ tr ^ se)==0?"OK":"BAD", post, post==0xAAFFFF?"OK":"BAD", bbundid);
// sanity check
@@ -1390,7 +987,7 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
//dest = sectdata+(768)*se;
// now read in the sector and decode to 6bit form
for(int i=0; i<0x400; i++) {
- data[i] = gcr6bw_tb[gb(buf, ts, pos, wrap)] ;//^ c;
+ data[i] = gcr6bw_tb[gb(buf, pos, wrap)] ;//^ c;
c ^= data[i];
/*if (((i&0x3)+1)==0x04) {
printf("%c", ((((data[i-3]&0x30)<<2)|((data[i-2]&0x3F)>>0))&0x3F)+0x40);
@@ -1404,18 +1001,18 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
// if (((i&0xf)+1)==0x10) printf("\n");
}
// read the checksum byte (checksum is calced by xoring all data together)
- data[0x400] = gcr6bw_tb[gb(buf,ts,pos,wrap)];
+ data[0x400] = gcr6bw_tb[gb(buf,pos,wrap)];
// now read the postamble bytes
for(int i=0; i<4; i++) {
dpost <<= 8;
- dpost |= gb(buf, ts, pos, wrap);
+ dpost |= gb(buf, pos, wrap);
}
/*if (se == 0) // dump some debug data to help find the lfsr before sector 5
{
printf("Data Postamble was 0x%08x\n", dpost);
for(int i=0; i<0x400; i++) {
- data[i] = gcr6bw_tb[gb(buf, ts, pos, wrap)] ;//^ c;
+ data[i] = gcr6bw_tb[gb(buf, pos, wrap)] ;//^ c;
c ^= data[i];
printf("%02x ", data[i]);
if (((i&0xf)+1)==0x10) printf("\n");
@@ -1509,8 +1106,8 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
}
for(int i=0; i<nsect; i++) {
//if(nsect>0) printf("t%d,", track);
- uint8_t *data = sectdata + (256)*i;
- io_generic_write(io, data, pos_data, 256);
+ uint8_t const *const data = sectdata + (256)*i;
+ /*auto const [err, actual] =*/ write_at(io, pos_data, data, 256); // FIXME: check for errors
pos_data += 256;
}
//printf("\n");
@@ -1536,59 +1133,57 @@ bool a2_rwts18_format::save(io_generic *io, floppy_image *image)
return true;
}
-const floppy_format_type FLOPPY_RWTS18_FORMAT = &floppy_image_format_creator<a2_rwts18_format>;
+const a2_rwts18_format FLOPPY_RWTS18_FORMAT;
a2_edd_format::a2_edd_format() : floppy_image_format_t()
{
}
-const char *a2_edd_format::name() const
+const char *a2_edd_format::name() const noexcept
{
return "a2_edd";
}
-const char *a2_edd_format::description() const
+const char *a2_edd_format::description() const noexcept
{
return "Apple II EDD Image";
}
-const char *a2_edd_format::extensions() const
+const char *a2_edd_format::extensions() const noexcept
{
return "edd";
}
-bool a2_edd_format::supports_save() const
+bool a2_edd_format::supports_save() const noexcept
{
return false;
}
-int a2_edd_format::identify(io_generic *io, uint32_t form_factor)
+int a2_edd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- return ((io_generic_size(io) == 2244608) || (io_generic_size(io) == 2310144)) ? 50 : 0;
+ uint64_t size;
+ if (io.length(size))
+ return 0;
+ return ((size == 2244608) || (size == 2310144)) ? FIFID_SIZE : 0;
}
uint8_t a2_edd_format::pick(const uint8_t *data, int pos)
{
- return ((data[pos>>3] << 8) | data[(pos>>3)+1]) >> (8-(pos & 7));
+ return get_u16be(&data[pos>>3]) >> (8-(pos & 7));
}
-bool a2_edd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool a2_edd_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint8_t *img;
uint8_t nibble[16384], stream[16384];
int npos[16384];
+ static const size_t img_size = 2'244'608;
- img = (uint8_t *) malloc(2244608);
-
- if (!img)
- {
+ auto [err, img, actual] = read_at(io, 0, img_size);
+ if(err || actual != img_size)
return false;
- }
-
- io_generic_read(io, img, 0, 2244608);
for(int i=0; i<137; i++) {
- const uint8_t *trk = img + 16384*i;
+ uint8_t const *const trk = &img[16384*i];
int pos = 0;
int wpos = 0;
while(pos < 16383*8) {
@@ -1650,166 +1245,191 @@ bool a2_edd_format::load(io_generic *io, uint32_t form_factor, floppy_image *ima
stream[splice >> 3] ^= 0x80 >> (splice & 7);
generate_track_from_bitstream(i >> 2, 0, stream, len, image, i & 3);
- image->set_write_splice_position(i >> 2, 0, uint32_t(uint64_t(200'000'000)*splice/len), i & 3);
+ image.set_write_splice_position(i >> 2, 0, uint32_t(uint64_t(200'000'000)*splice/len), i & 3);
}
- free(img);
+ img.reset();
+
+ image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD);
+
return true;
}
-const floppy_format_type FLOPPY_EDD_FORMAT = &floppy_image_format_creator<a2_edd_format>;
+const a2_edd_format FLOPPY_EDD_FORMAT;
-a2_woz_format::a2_woz_format() : floppy_image_format_t()
+a2_nib_format::a2_nib_format() : floppy_image_format_t()
{
}
-const char *a2_woz_format::name() const
+const char *a2_nib_format::name() const noexcept
{
- return "a2_woz";
+ return "a2_nib";
}
-const char *a2_woz_format::description() const
+const char *a2_nib_format::description() const noexcept
{
- return "Apple II WOZ Image";
+ return "Apple II NIB Image";
}
-const char *a2_woz_format::extensions() const
+const char *a2_nib_format::extensions() const noexcept
{
- return "woz";
+ return "nib";
}
-bool a2_woz_format::supports_save() const
+bool a2_nib_format::supports_save() const noexcept
{
return false;
}
-const uint8_t a2_woz_format::signature[8] = { 0x57, 0x4f, 0x5a, 0x31, 0xff, 0x0a, 0x0d, 0x0a };
-const uint8_t a2_woz_format::signature2[8] = { 0x57, 0x4f, 0x5a, 0x32, 0xff, 0x0a, 0x0d, 0x0a };
-
-int a2_woz_format::identify(io_generic *io, uint32_t form_factor)
+int a2_nib_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint8_t header[8];
- io_generic_read(io, header, 0, 8);
- if (!memcmp(header, signature, 8)) return 100;
- if (!memcmp(header, signature2, 8)) return 100;
- return 0;
-}
+ uint64_t size;
+ if (io.length(size))
+ return 0;
-bool a2_woz_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
-{
- std::vector<uint8_t> img(io_generic_size(io));
- io_generic_read(io, &img[0], 0, img.size());
+ if (size == expected_size_35t || size == expected_size_40t)
+ return FIFID_SIZE;
- // Check signature
- if ((memcmp(&img[0], signature, 8)) && (memcmp(&img[0], signature2, 8)))
- return false;
+ return 0;
+}
- uint32_t woz_vers = 1;
- if (!memcmp(&img[0], signature2, 8)) woz_vers = 2;
- // Check integrity
- uint32_t crc = crc32r(&img[12], img.size() - 12);
- if(crc != r32(img, 8))
- return false;
+template<class It>
+static
+size_t count_leading_FFs(const It first, const It last)
+{
+ auto curr = first;
+ for (; curr != last; ++curr) {
+ if (*curr != 0xFF) {
+ break;
+ }
+ }
+ return curr - first;
+}
- uint32_t off_info = find_tag(img, 0x4f464e49);
- uint32_t off_tmap = find_tag(img, 0x50414d54);
- uint32_t off_trks = find_tag(img, 0x534b5254);
-// uint32_t off_writ = find_tag(img, 0x54495257);
+static
+size_t count_trailing_padding(const std::vector<uint8_t>& nibbles) {
+ const auto b = nibbles.rbegin();
+ const auto e = nibbles.rend();
+ auto i = b;
- if(!off_info || !off_tmap || !off_trks)
- return false;
+ // skip until the first valid nibble...
+ for (; i != e; ++i) {
+ if ((*i & 0x80) != 0) { // valid nibble
+ break;
+ }
+ }
+ return i - b;
+}
- uint32_t info_vers = r8(img, off_info + 0);
+std::vector<uint32_t> a2_nib_format::generate_levels_from_nibbles(const std::vector<uint8_t>& nibbles)
+{
+ std::vector<uint32_t> levels;
+ const auto append_FFs = [&] (size_t count) {
+ while (count-- > 0) {
+ raw_w(levels, 8, 0xFF);
+ }
+ };
+ const auto append_syncs = [&] (size_t count) {
+ while (count-- > 0) {
+ raw_w(levels, 10, 0x00FF << 2);
+ }
+ };
+ const auto append_byte = [&] (uint8_t byte) {
+ raw_w(levels, 8, byte);
+ };
- if ((info_vers != 1) && (info_vers != 2))
- return false;
- bool is_35 = r8(img, off_info + 1) == 2;
- if((form_factor == floppy_image::FF_35 && !is_35) || (form_factor == floppy_image::FF_525 && is_35))
- return false;
+ const auto leading_FF_count =
+ count_leading_FFs(nibbles.begin(), nibbles.end());
- unsigned int limit = is_35 ? 160 : 141;
+ if (leading_FF_count >= nibbles.size()) { // all are 0xFF !?!?
+ assert(leading_FF_count >= min_sync_bytes);
+ append_syncs(leading_FF_count);
+ return levels;
+ }
- if (woz_vers == 1) {
- for (unsigned int trkid = 0; trkid != limit; trkid++) {
- int head = is_35 && trkid >= 80 ? 1 : 0;
- int track = is_35 ? trkid % 80 : trkid / 4;
- int subtrack = is_35 ? 0 : trkid & 3;
+ const auto trailing_padding_size = count_trailing_padding(nibbles);
+ const auto trailing_FF_count =
+ count_leading_FFs(nibbles.rbegin() + trailing_padding_size,
+ nibbles.rend());
+ const auto wrapped_FF_count = leading_FF_count + trailing_FF_count;
+ const bool wrapped_FF_are_syncs = wrapped_FF_count >= min_sync_bytes;
+
+ if (wrapped_FF_are_syncs) {
+ append_syncs(leading_FF_count);
+ } else {
+ append_FFs(leading_FF_count);
+ }
- uint8_t idx = r8(img, off_tmap + trkid);
- if(idx != 0xff) {
- uint32_t boff = off_trks + 6656*idx;
- if (r16(img, boff + 6648) == 0)
- return false;
- generate_track_from_bitstream(track, head, &img[boff], r16(img, boff + 6648), image, subtrack, r16(img, boff + 6650));
+ {
+ size_t FF_count = 0;
+ const auto flush_FFs = [&] {
+ if (FF_count == 0) {
+ return;
}
- }
- } else if (woz_vers == 2) {
- for (unsigned int trkid = 0; trkid != limit; trkid++) {
- int head = is_35 && trkid & 1 ? 1 : 0;
- int track = is_35 ? trkid >> 1 : trkid / 4;
- int subtrack = is_35 ? 0 : trkid & 3;
- uint8_t idx = r8(img, off_tmap + trkid);
- if(idx != 0xff) {
- uint32_t trks_off = off_trks + (idx * 8);
-
- uint32_t boff = (uint32_t)r16(img, trks_off + 0) * 512;
-
- if (r16(img, trks_off + 4) == 0)
- return false;
+ if (FF_count >= a2_nib_format::min_sync_bytes) {
+ append_syncs(FF_count);
+ } else {
+ append_FFs(FF_count);
+ }
+ FF_count = 0;
+ };
+
+ const auto end = nibbles.end() - trailing_padding_size - trailing_FF_count;
+ for (auto i = nibbles.begin() + leading_FF_count; i != end; ++i) {
+ const auto nibble = *i;
+ if ((nibble & 0x80) == 0) {
+ continue;
+ }
- // TODO: when write capability is added, use the WRIT chunk data if it's present
- generate_track_from_bitstream(track, head, &img[boff], r16(img, trks_off + 4), image, subtrack, 0xffff);
+ if (nibble == 0xFF) {
+ ++FF_count;
+ continue;
}
+
+ flush_FFs();
+ append_byte(nibble);
}
+ flush_FFs();
}
- else return false;
- return true;
-}
+ if (wrapped_FF_are_syncs) {
+ append_syncs(trailing_FF_count);
+ } else {
+ append_FFs(trailing_FF_count);
+ }
-uint32_t a2_woz_format::find_tag(const std::vector<uint8_t> &data, uint32_t tag)
-{
- uint32_t offset = 12;
- do {
- if(r32(data, offset) == tag)
- return offset + 8;
- offset += r32(data, offset+4) + 8;
- } while(offset < data.size() - 8);
- return 0;
+ return levels;
}
-uint32_t a2_woz_format::r32(const std::vector<uint8_t> &data, uint32_t offset)
+bool a2_nib_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- return data[offset] | (data[offset+1] << 8) | (data[offset+2] << 16) | (data[offset+3] << 24);
-}
+ uint64_t size;
+ if (io.length(size))
+ return false;
+ if (size != expected_size_35t && size != expected_size_40t)
+ return false;
-uint16_t a2_woz_format::r16(const std::vector<uint8_t> &data, uint32_t offset)
-{
- return data[offset] | (data[offset+1] << 8);
-}
+ const auto nr_tracks = size / nibbles_per_track;
-uint8_t a2_woz_format::r8(const std::vector<uint8_t> &data, uint32_t offset)
-{
- return data[offset];
-}
+ std::vector<uint8_t> nibbles(nibbles_per_track);
+ for (unsigned track = 0; track < nr_tracks; ++track) {
+ auto const [err, actual] = read_at(io, track * nibbles_per_track, &nibbles[0], nibbles_per_track);
+ if (err || actual != nibbles_per_track)
+ return false;
-uint32_t a2_woz_format::crc32r(const uint8_t *data, uint32_t size)
-{
- // Reversed crc32
- uint32_t crc = 0xffffffff;
- for(uint32_t i=0; i != size; i++) {
- crc = crc ^ data[i];
- for(int j=0; j<8; j++)
- if(crc & 1)
- crc = (crc >> 1) ^ 0xedb88320;
- else
- crc = crc >> 1;
+ auto levels = generate_levels_from_nibbles(nibbles);
+ if (!levels.empty()) {
+ generate_track_from_levels(track, 0, levels, 0, image);
+ }
}
- return ~crc;
+
+ image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD);
+
+ return true;
}
-const floppy_format_type FLOPPY_WOZ_FORMAT = &floppy_image_format_creator<a2_woz_format>;
+const a2_nib_format FLOPPY_NIB_FORMAT;
diff --git a/src/lib/formats/ap2_dsk.h b/src/lib/formats/ap2_dsk.h
index b5b08db663b..33117ecbd84 100644
--- a/src/lib/formats/ap2_dsk.h
+++ b/src/lib/formats/ap2_dsk.h
@@ -21,70 +21,89 @@
***************************************************************************/
-#define APPLE2_NIBBLE_SIZE 416
-#define APPLE2_SMALL_NIBBLE_SIZE 374
-//#define APPLE2_TRACK_COUNT 35
-#define APPLE2_TRACK_COUNT 40
-#define APPLE2_SECTOR_COUNT 16
-#define APPLE2_SECTOR_SIZE 256
+constexpr int APPLE2_STD_TRACK_COUNT = 35;
+constexpr int APPLE2_TRACK_COUNT = 40;
+constexpr int APPLE2_SECTOR_SIZE = 256;
+/**************************************************************************/
+class a2_13sect_format : public floppy_image_format_t
+{
+public:
+ a2_13sect_format();
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
-/**************************************************************************/
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
-LEGACY_FLOPPY_OPTIONS_EXTERN(apple2);
+private:
+ static uint8_t gb(const std::vector<bool> &buf, int &pos, int &wrap);
+};
-// new style code
+extern const a2_13sect_format FLOPPY_A213S_FORMAT;
class a2_16sect_format : public floppy_image_format_t
{
public:
- a2_16sect_format();
+ a2_16sect_format(bool prodos_order);
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual bool supports_save() const noexcept override;
private:
- static const desc_e mac_gcr[];
+ const bool m_prodos_order;
- uint8_t gb(const uint8_t *buf, int ts, int &pos, int &wrap);
- void update_chk(const uint8_t *data, int size, uint32_t &chk);
+ static uint8_t gb(const std::vector<bool> &buf, int &pos, int &wrap);
+};
- bool m_prodos_order;
+class a2_16sect_dos_format : public a2_16sect_format
+{
+public:
+ a2_16sect_dos_format();
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+};
- int m_tracks;
+class a2_16sect_prodos_format : public a2_16sect_format
+{
+public:
+ a2_16sect_prodos_format();
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
};
-extern const floppy_format_type FLOPPY_A216S_FORMAT;
+extern const a2_16sect_dos_format FLOPPY_A216S_DOS_FORMAT;
+extern const a2_16sect_prodos_format FLOPPY_A216S_PRODOS_FORMAT;
class a2_rwts18_format : public floppy_image_format_t
{
public:
a2_rwts18_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
private:
static const desc_e mac_gcr[];
- uint8_t gb(const uint8_t *buf, int ts, int &pos, int &wrap);
- void update_chk(const uint8_t *data, int size, uint32_t &chk);
+ static uint8_t gb(const std::vector<bool> &buf, int &pos, int &wrap);
};
-extern const floppy_format_type FLOPPY_RWTS18_FORMAT;
+extern const a2_rwts18_format FLOPPY_RWTS18_FORMAT;
class a2_edd_format : public floppy_image_format_t
@@ -92,44 +111,43 @@ class a2_edd_format : public floppy_image_format_t
public:
a2_edd_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static uint8_t pick(const uint8_t *data, int pos);
};
-extern const floppy_format_type FLOPPY_EDD_FORMAT;
+extern const a2_edd_format FLOPPY_EDD_FORMAT;
-class a2_woz_format : public floppy_image_format_t
+class a2_nib_format : public floppy_image_format_t
{
public:
- a2_woz_format();
+ a2_nib_format();
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
private:
- static const uint8_t signature[8];
- static const uint8_t signature2[8];
-
- static uint32_t r32(const std::vector<uint8_t> &data, uint32_t offset);
- static uint16_t r16(const std::vector<uint8_t> &data, uint32_t offset);
- static uint8_t r8(const std::vector<uint8_t> &data, uint32_t offset);
- static uint32_t crc32r(const uint8_t *data, uint32_t size);
- static uint32_t find_tag(const std::vector<uint8_t> &data, uint32_t tag);
+ static constexpr size_t nibbles_per_track = 0x1a00;
+ static constexpr size_t min_sync_bytes = 4;
+ static constexpr auto expected_size_35t = APPLE2_STD_TRACK_COUNT * nibbles_per_track;
+ static constexpr auto expected_size_40t = APPLE2_TRACK_COUNT * nibbles_per_track;
+
+ static std::vector<uint32_t> generate_levels_from_nibbles(const std::vector<uint8_t>& nibbles);
};
-extern const floppy_format_type FLOPPY_WOZ_FORMAT;
+extern const a2_nib_format FLOPPY_NIB_FORMAT;
#endif // MAME_FORMATS_AP2_DSK_H
diff --git a/src/lib/formats/ap_dsk35.cpp b/src/lib/formats/ap_dsk35.cpp
index c18ea8c65d4..4096215c35f 100644
--- a/src/lib/formats/ap_dsk35.cpp
+++ b/src/lib/formats/ap_dsk35.cpp
@@ -1,8 +1,8 @@
// license:BSD-3-Clause
-// copyright-holders:Nathan Woods, R. Belmont
+// copyright-holders:O. Galibert, R. Belmont
/*********************************************************************
- ap_dsk35.c
+ ap_dsk35.cpp
Apple 3.5" disk images
@@ -98,1405 +98,435 @@
*********************************************************************/
-#include <stdio.h>
-#include <assert.h>
-
-#include "emu.h" // logerror
#include "ap_dsk35.h"
-struct apple35_tag
-{
- uint32_t data_offset;
- uint32_t data_size;
- uint8_t format_byte;
- uint8_t sides;
- unsigned int is_1440k : 1;
-
- /* stuff used in DiskCopy images */
- uint32_t tag_offset;
- uint32_t tag_size;
-};
+#include "ioprocs.h"
+#include "multibyte.h"
+#include "opresolv.h"
+#include "eminline.h"
+#include "osdcore.h" // osd_printf_error
+#include <cassert>
+#include <cstdio>
+#include <cstring>
-/* normal number of sector for each track */
-static const uint8_t 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_t 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_t 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_t 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_t blk2[] =
-{
- 0xDE, 0xAA, 0xFF, 0xFF, 0x3F, 0xCF, 0xF3, 0xFC, 0xFF, 0xD5, 0xAA, 0xAD
-};
-static const uint8_t blk3[] =
+dc42_format::dc42_format() : floppy_image_format_t()
{
- 0xDE, 0xAA, 0xFF
-};
-
-
+}
-static struct apple35_tag *get_apple35_tag(floppy_image_legacy *floppy)
+const char *dc42_format::name() const noexcept
{
- struct apple35_tag *tag;
- tag = (struct apple35_tag *) floppy_tag(floppy);
- return tag;
+ return "dc42";
}
-
-
-int apple35_sectors_per_track(floppy_image_legacy *image, int track)
+const char *dc42_format::description() const noexcept
{
- 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;
+ return "DiskCopy 4.2 image";
}
-
-
-/*
- 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_t *in, uint8_t *nib_ptr, uint8_t *csum)
+const char *dc42_format::extensions() const noexcept
{
- int i, j;
- uint32_t c1, c2, c3, c4;
- uint8_t val;
- uint8_t w1, w2, w3, w4;
- uint8_t 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;
+ return "dc42";
}
-
-
-/*
- does the reverse process of sony_nibblize35
-*/
-static void sony_denibblize35(uint8_t *out, const uint8_t *nib_ptr, uint8_t *checksum)
+bool dc42_format::supports_save() const noexcept
{
- int i, j;
- uint32_t c1,c2,c3,c4;
- uint8_t val;
- uint8_t w1,w2,w3=0,w4;
- uint8_t 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);
- }
+ return true;
+}
- /* Copy from the user's buffer to our buffer, while computing
- * the three-byte data checksum
- */
+int dc42_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if(io.length(size) || (size < 0x54))
+ return 0;
- 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;
+ uint8_t h[0x54];
+ auto const [err, actual] = read_at(io, 0, h, 0x54);
+ if (err || (0x54 != actual))
+ return 0;
- if (i == 524) break;
+ uint32_t const dsize = get_u32be(&h[0x40]);
+ uint32_t const tsize = get_u32be(&h[0x44]);
- 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;
+ uint8_t const encoding = h[0x50];
+ uint8_t const format = h[0x51];
+ // if it's a 1.44MB DC42 image, reject it so the generic PC MFM handler picks it up
+ if ((encoding == 0x03) && (format == 0x02))
+ return 0;
- checksum[0] = c1 & 0x3F;
- checksum[1] = c2 & 0x3F;
- checksum[2] = c3 & 0x3F;
- checksum[3] = c4 & 0x3F;
+ return (size == 0x54+tsize+dsize && h[0] < 64 && h[0x52] == 1 && h[0x53] == 0) ? FIFID_STRUCT : 0;
}
-
-
-void sony_filltrack(uint8_t *buffer, size_t buffer_len, size_t *pos, uint8_t data)
+bool dc42_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- buffer[*pos / 8] &= 0xFF << (8 - (*pos % 8));
- buffer[*pos / 8] |= data >> (*pos % 8);
+ uint8_t h[0x54];
+ read_at(io, 0, h, 0x54); // FIXME: check for errors and premature EOF
+ int dsize = get_u32be(&h[0x40]);
+ int tsize = get_u32be(&h[0x44]);
- *pos += 8;
- *pos %= buffer_len * 8;
+ uint8_t encoding = h[0x50];
+ uint8_t format = h[0x51];
- buffer[*pos / 8] &= 0xFF >> (*pos % 8);
- buffer[*pos / 8] |= data << (8 - (*pos % 8));
-}
+ if((encoding != 0x00 || format != 0x02) && ((encoding != 0x01 && encoding != 0x03) || (format != 0x22 && format != 0x24))) {
+ osd_printf_error("dc42: Unsupported encoding/format combination %02x/%02x\n", encoding, format);
+ return false;
+ }
+ switch(dsize) {
+ case 409600: // Mac 400K
+ image.set_form_variant(floppy_image::FF_35, floppy_image::SSDD);
+ break;
+ case 737280: // PC 720K
+ case 819200: // Mac/A2 800K
+ image.set_form_variant(floppy_image::FF_35, floppy_image::DSDD);
+ break;
-uint8_t sony_fetchtrack(const uint8_t *buffer, size_t buffer_len, size_t *pos)
-{
- uint8_t data;
+ case 1474560: // PC or Mac 1.44M
+ image.set_form_variant(floppy_image::FF_35, floppy_image::DSHD);
+ break;
- data = buffer[*pos / 8] << (*pos % 8);
- *pos += 8;
- *pos %= (buffer_len * 8);
- data |= buffer[*pos / 8] >> (8 - (*pos % 8));
+ case 871424: // Apple Twiggy 851KiB
+ image.set_form_variant(floppy_image::FF_525, floppy_image::DSHD);
+ break;
- 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);
+ default:
+ osd_printf_error("dc42: dsize is %d, unknown form factor\n");
+ break;
}
-// printf("sony_fetchtrack: pos %ld = %02x\n", *pos/8, data);
+ uint8_t sector_data[(512+12)*12];
+ memset(sector_data, 0, sizeof(sector_data));
- return data;
-}
+ desc_gcr_sector sectors[12];
+ int pos_data = 0x54;
+ int pos_tag = 0x54+dsize;
+ int head_count = encoding == 1 ? 2 : 1;
-static uint32_t apple35_get_offset(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *tag_offset)
-{
- int i;
- uint32_t 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;
- if (*tag_offset >= tag->tag_size)
- {
- *tag_offset = ~0;
- }
- else
- {
- *tag_offset += tag->tag_offset;
+ for(int track=0; track < 80; track++) {
+ for(int head=0; head < head_count; head++) {
+ int ns = 12 - (track/16);
+ int si = 0;
+ for(int i=0; i<ns; i++) {
+ uint8_t *data = sector_data + (512+12)*i;
+ sectors[si].track = track;
+ sectors[si].head = head;
+ sectors[si].sector = i;
+ sectors[si].info = format;
+ if(tsize) {
+ read_at(io, pos_tag, data, 12); // FIXME: check for errors and premature EOF
+ sectors[si].tag = data;
+ pos_tag += 12;
+ } else {
+ sectors[si].tag = nullptr;
+ }
+ sectors[si].data = data+12;
+ read_at(io, pos_data, data+12, 512); // FIXME: check for errors and premature EOF
+ pos_data += 512;
+ si = (si + 2) % ns;
+ if(si == 0)
+ si++;
+ }
+ build_mac_track_gcr(track, head, image, sectors);
}
}
- return sector_index * 0x200 + tag->data_offset;
+ return true;
}
-
-
-static floperr_t apple35_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
+void dc42_format::update_chk(const uint8_t *data, int size, uint32_t &chk)
{
- uint32_t data_offset;
- data_offset = apple35_get_offset(floppy, head, track, sector, nullptr);
- if (data_offset == ~0)
- {
- return FLOPPY_ERROR_SEEKERROR;
+ for(int i=0; i<size; i+=2) {
+ chk += get_u16be(&data[i]);
+ chk = rotr_32(chk, 1);
}
- floppy_image_read(floppy, buffer, data_offset, buflen);
- return FLOPPY_ERROR_SUCCESS;
}
-
-
-static floperr_t apple35_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
+bool dc42_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
- uint32_t data_offset;
-
- data_offset = apple35_get_offset(floppy, head, track, sector, nullptr);
- if (data_offset == ~0)
- return FLOPPY_ERROR_SEEKERROR;
- floppy_image_write(floppy, buffer, data_offset, buflen);
- return FLOPPY_ERROR_SUCCESS;
-}
-
+ int g_tracks, g_heads;
+ image.get_actual_geometry(g_tracks, g_heads);
+ if(g_heads == 0)
+ g_heads = 1;
-static floperr_t apple35_read_sector_td(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
-{
- floperr_t err;
- uint32_t tag_offset = 0;
+ uint8_t h[0x54];
+ memset(h, 0, 0x54);
+ strcpy((char *)h+1, "Unnamed");
+ h[0] = 7;
+ int nsect = 16*(12+11+10+9+8)*g_heads;
+ uint32_t dsize = nsect*512;
+ uint32_t tsize = nsect*12;
+ put_u32be(&h[0x40], dsize);
+ put_u32be(&h[0x44], tsize);
+ h[0x50] = g_heads == 2 ? 0x01 : 0x00;
+ h[0x51] = g_heads == 2 ? 0x22 : 0x02;
+ h[0x52] = 0x01;
+ h[0x53] = 0x00;
- assert(buflen == 524);
+ uint32_t dchk = 0;
+ uint32_t tchk = 0;
- /* first read the sector */
- err = apple35_read_sector(floppy, head, track, sector, ((uint8_t *) buffer) + 12, 512);
- if (err)
- {
- return err;
- }
+ int pos_data = 0x54;
+ int pos_tag = 0x54+dsize;
- /* 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);
+ for(int track=0; track < 80; track++) {
+ for(int head=0; head < g_heads; head++) {
+ auto sectors = extract_sectors_from_track_mac_gcr6(head, track, image);
+ for(unsigned int i=0; i < sectors.size(); i++) {
+ auto &sdata = sectors[i];
+ sdata.resize(512+12);
+ write_at(io, pos_tag, &sdata[0], 12); // FIXME: check for errors
+ write_at(io, pos_data, &sdata[12], 512); // FIXME: check for errors
+ pos_tag += 12;
+ pos_data += 512;
+ if(track || head || i)
+ update_chk(&sdata[0], 12, tchk);
+ update_chk(&sdata[12], 512, dchk);
+ }
+ }
}
- return FLOPPY_ERROR_SUCCESS;
-}
-
-
-
-static floperr_t apple35_write_sector_td(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
-{
- floperr_t err;
- uint32_t tag_offset = 0;
+ put_u32be(&h[0x48], dchk);
+ put_u32be(&h[0x4c], tchk);
- assert(buflen == 524);
-
- /* first write the sector */
- err = apple35_write_sector(floppy, head, track, sector, ((const uint8_t *) 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;
+ write_at(io, 0, h, 0x54); // FIXME: check for errors
+ return true;
}
+const dc42_format FLOPPY_DC42_FORMAT;
-static floperr_t apple35_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length)
+apple_gcr_format::apple_gcr_format() : floppy_image_format_t()
{
- *sector_length = 512;
- return FLOPPY_ERROR_SUCCESS;
}
-
-
-static int apple35_get_heads_per_disk(floppy_image_legacy *floppy)
+const char *apple_gcr_format::name() const noexcept
{
- return get_apple35_tag(floppy)->sides;
+ return "apple_gcr";
}
-
-
-static int apple35_get_tracks_per_disk(floppy_image_legacy *floppy)
+const char *apple_gcr_format::description() const noexcept
{
- return 80;
+ return "Apple GCR 400/800K raw sector image";
}
-
-
-static uint32_t apple35_get_track_size(floppy_image_legacy *floppy, int head, int track)
+const char *apple_gcr_format::extensions() const noexcept
{
- if ((track < 0) || (track >= 80))
- return 0;
- return apple35_sectors_per_track(floppy, track) * 800;
+ return "img";
}
-
-
-static uint8_t calculate_side(int head, int track)
+bool apple_gcr_format::supports_save() const noexcept
{
- uint8_t side;
- side = head ? 0x20 : 0x00;
- if (track & 0x40)
- side |= 0x01;
- return side;
+ return true;
}
-
-
-static floperr_t apple35_read_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buflen)
+int apple_gcr_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- floperr_t err;
- size_t pos = 0;
- int sector_count, sector, i;
- uint8_t sum, side;
- struct apple35_tag *tag;
- uint8_t sector_data[524];
- uint8_t nibble_data[699];
- uint8_t 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_t*)buffer, buflen, &pos, blk1[i]);
-
- sum = (track ^ sector ^ side ^ tag->format_byte) & 0x3F;
-
- sony_filltrack((uint8_t*)buffer, buflen, &pos, diskbytes[track & 0x3f]);
- sony_filltrack((uint8_t*)buffer, buflen, &pos, diskbytes[sector]);
- sony_filltrack((uint8_t*)buffer, buflen, &pos, diskbytes[side]);
- sony_filltrack((uint8_t*)buffer, buflen, &pos, diskbytes[tag->format_byte]);
- sony_filltrack((uint8_t*)buffer, buflen, &pos, diskbytes[sum]);
-
- for (i = 0; i < ARRAY_LENGTH(blk2); i++)
- sony_filltrack((uint8_t*)buffer, buflen, &pos, blk2[i]);
-
- sony_filltrack((uint8_t*)buffer, buflen, &pos, diskbytes[sector]);
-
- for (i = 0; i < ARRAY_LENGTH(nibble_data); i++)
- sony_filltrack((uint8_t*)buffer, buflen, &pos, diskbytes[nibble_data[i]]);
+ uint64_t size;
+ if(io.length(size))
+ return 0;
- for (i = 3; i >= 0; i--)
- sony_filltrack((uint8_t*)buffer, buflen, &pos, diskbytes[checksum[i]]);
+ if(size == 409600 || (size == 819200 && (variants.empty() || has_variant(variants, floppy_image::DSDD))))
+ return FIFID_SIZE;
- for (i = 0; i < ARRAY_LENGTH(blk3); i++)
- sony_filltrack((uint8_t*)buffer, buflen, &pos, blk3[i]);
- }
-
- return FLOPPY_ERROR_SUCCESS;
+ return 0;
}
-
-
-static floperr_t apple35_write_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, const void *buffer, size_t buflen)
+bool apple_gcr_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- floperr_t err;
- size_t pos = 0;
- int sector_count, sector, i, j;
- struct apple35_tag *tag;
- uint8_t sum, format_byte, val, side;
- uint32_t found_sectors = 0;
- uint8_t sector_data[524];
- uint8_t nibble_data[699];
- uint8_t 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_t*)buffer, buflen, &pos) != 0xD5)
- continue;
- j++;
-
- if (sony_fetchtrack((uint8_t*)buffer, buflen, &pos) != 0xAA)
- continue;
- j++;
-
- if (sony_fetchtrack((uint8_t*)buffer, buflen, &pos) != 0x96)
- continue;
- j++;
-
- if (rev_diskbytes[sony_fetchtrack((uint8_t*)buffer, buflen, &pos)] != (track & 0x3F))
- continue;
- j++;
-
- sector = rev_diskbytes[sony_fetchtrack((uint8_t*)buffer, buflen, &pos)];
- if ((sector < 0) || (sector >= sector_count))
- continue;
- j++;
-
- if (rev_diskbytes[sony_fetchtrack((uint8_t*)buffer, buflen, &pos)] != side)
- continue;
- j++;
-
- format_byte = rev_diskbytes[sony_fetchtrack((uint8_t*)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_t*)buffer, buflen, &pos)] != sum)
- continue;
- j++;
-
- if (sony_fetchtrack((uint8_t*)buffer, buflen, &pos) != 0xDE)
- continue;
- j++;
-
- if (sony_fetchtrack((uint8_t*)buffer, buflen, &pos) != 0xAA)
- continue;
- j++;
-
- while((val = sony_fetchtrack((uint8_t*)buffer, buflen, &pos)) == 0xFF)
- j++;
- if (val != 0xD5)
- continue; /* lost bit slip mark! */
- j++;
-
- if (sony_fetchtrack((uint8_t*)buffer, buflen, &pos) != 0xAA)
- continue;
- j++;
-
- if (sony_fetchtrack((uint8_t*)buffer, buflen, &pos) != 0xAD)
- continue;
- j++;
-
- /* should this be regarded as a critical error ??? */
- if (rev_diskbytes[sony_fetchtrack((uint8_t*)buffer, buflen, &pos)] != sector)
- continue;
- j++;
-
- for (i = 0; i < ARRAY_LENGTH(nibble_data); i++)
- {
- nibble_data[i] = rev_diskbytes[sony_fetchtrack((uint8_t*)buffer, buflen, &pos)];
- j++;
- }
+ desc_gcr_sector sectors[12];
+ uint8_t sdata[512*12];
- for (i = 3; i >= 0; i--)
- {
- /* should be checking checksum */
- sony_fetchtrack((uint8_t*)buffer, buflen, &pos);
- }
+ int pos_data = 0;
+
+ uint8_t header[64];
+ read_at(io, 0, header, 64); // FIXME: check for errors and premature EOF
- sony_fetchtrack((uint8_t*)buffer, buflen, &pos); /* should get 0xDE */
- sony_fetchtrack((uint8_t*)buffer, buflen, &pos); /* should get 0xAA */
- sony_fetchtrack((uint8_t*)buffer, buflen, &pos); /* should get 0xFF */
+ uint64_t size;
+ if(io.length(size))
+ return false;
+ int head_count = size == 409600 ? 1 : size == 819200 ? 2 : 0;
- /* did we already write this sector? */
- if ((found_sectors & (1 << sector)) == 0)
- {
- sony_denibblize35(sector_data, nibble_data, checksum);
+ image.set_form_variant(floppy_image::FF_35, head_count == 2 ? floppy_image::DSDD : floppy_image::SSDD);
- /* write the sector */
- err = apple35_write_sector_td(floppy, head, track, sector, sector_data, ARRAY_LENGTH(sector_data), 0);
- if (err)
- return err;
+ if(!head_count)
+ return false;
+ for(int track=0; track < 80; track++) {
+ for(int head=0; head < head_count; head++) {
+ int ns = 12 - (track/16);
+ read_at(io, pos_data, sdata, 512*ns); // FIXME: check for errors and premature EOF
+ pos_data += 512*ns;
- found_sectors |= 1 << sector;
+ int si = 0;
+ for(int i=0; i<ns; i++) {
+ sectors[si].track = track;
+ sectors[si].head = head;
+ sectors[si].sector = i;
+ sectors[si].info = head_count == 2 ? 0x22 : 0x02;
+ sectors[si].tag = nullptr;
+ sectors[si].data = sdata + 512*i;
+ si = (si + 2) % ns;
+ if(si == 0)
+ si++;
+ }
+ build_mac_track_gcr(track, head, image, sectors);
}
}
- return FLOPPY_ERROR_SUCCESS;
+ return true;
}
+bool apple_gcr_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ int g_tracks, g_heads;
+ image.get_actual_geometry(g_tracks, g_heads);
+ if(g_heads == 0)
+ g_heads = 1;
-static floperr_t apple35_construct(floppy_image_legacy *floppy, uint32_t data_offset, uint32_t data_size,
- uint32_t tag_offset, uint32_t tag_size, int16_t format_byte, uint8_t sides, bool is_1440k)
-{
- struct apple35_tag *tag;
- struct FloppyCallbacks *format;
+ int pos_data = 0;
- /* 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;
+ for(int track=0; track < 80; track++) {
+ for(int head=0; head < g_heads; head++) {
+ auto sectors = extract_sectors_from_track_mac_gcr6(head, track, image);
+ for(unsigned int i=0; i < sectors.size(); i++) {
+ auto &sdata = sectors[i];
+ sdata.resize(512+12);
+ /*auto const [err, actual] =*/ write_at(io, pos_data, &sdata[12], 512); // FIXME: check for errors
+ pos_data += 512;
+ }
}
}
- /* 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_t) 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;
+ return true;
}
+const apple_gcr_format FLOPPY_APPLE_GCR_FORMAT;
-
-/* ----------------------------------------------------------------------- */
-
-static FLOPPY_IDENTIFY(apple35_raw_identify)
+// .2MG format
+apple_2mg_format::apple_2mg_format() : floppy_image_format_t()
{
- uint64_t 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)
+const char *apple_2mg_format::name() const noexcept
{
- uint64_t size;
- uint8_t sides;
- bool is_1440k;
-
- if (params)
- {
- /* create */
- sides = params->lookup_int(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_t) size, 0, 0, -1, sides, is_1440k);
+ return "apple_2mg";
}
-
-
-/* ----------------------------------------------------------------------- */
-
-struct header_diskcopy
-{
- uint8_t disk_name[64]; /* name of the disk */
- uint32_t data_size; /* total size of data for all sectors (512*number_of_sectors) */
- uint32_t 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_t data_checksum; /* CRC32 checksum of all sector data */
- uint32_t tag_checksum; /* CRC32 checksum of all tag data */
- uint8_t disk_format; /* 0 = 400K, 1 = 800K, 2 = 720K, 3 = 1440K (other values reserved) */
- uint8_t 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_t magic; /* always $0100 (otherwise, the file may be in a different format. */
-};
-
-
-
-static floperr_t apple35_diskcopy_headerdecode(floppy_image_legacy *floppy, uint32_t *data_offset,
- uint32_t *data_size, uint32_t *tag_offset, uint32_t *tag_size, uint8_t *format_byte, uint8_t *sides)
+const char *apple_2mg_format::description() const noexcept
{
- uint64_t 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;
+ return "Apple II .2MG image";
}
-
-
-static FLOPPY_IDENTIFY(apple35_diskcopy_identify)
+const char *apple_2mg_format::extensions() const noexcept
{
- *vote = apple35_diskcopy_headerdecode(floppy, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr) ? 0 : 100;
- return FLOPPY_ERROR_SUCCESS;
+ return "2mg";
}
-
-
-
-static FLOPPY_CONSTRUCT(apple35_diskcopy_construct)
+bool apple_2mg_format::supports_save() const noexcept
{
- floperr_t err;
- uint8_t format_byte, sides;
- uint32_t data_offset, data_size;
- uint32_t tag_offset, tag_size;
- int16_t format_byte_param = -1;
- struct header_diskcopy header;
-
- if (params)
- {
- /* create */
- sides = params->lookup_int(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);
+ return true;
}
-
-
-/* ----------------------------------------------------------------------- */
-
-struct header_2img
+int apple_2mg_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- char magic[4]; /* '2IMG' */
- char creator[4]; /* signature; 'MESS' for MESS */
- uint16_t header_length;
- uint16_t version;
- uint32_t image_format;
- uint32_t flags;
- uint32_t block_count;
- uint32_t data_offset;
- uint32_t data_length;
- uint32_t comment_offset;
- uint32_t comment_length;
- uint32_t creator_offset;
- uint32_t creator_length;
- uint32_t 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_legacy *floppy, uint32_t *image_format,
- uint32_t *data_offset, uint32_t *data_length)
-{
- struct header_2img header;
- uint64_t 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))
+ uint8_t signature[4];
+ auto const [err, actual] = read_at(io, 0, signature, 4);
+ if (err || (4 != actual))
{
- return FLOPPY_ERROR_INVALIDIMAGE;
+ return 0;
}
- floppy_image_read(floppy, &header, 0, sizeof(header));
-
- if (memcmp(header.magic, "2IMG", 4))
+ if (!memcmp(signature, "2IMG", 4))
{
- return FLOPPY_ERROR_INVALIDIMAGE;
+ return FIFID_SIGN;
}
- 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)
+ // Bernie ][ The Rescue wrote 2MGs with the signature byte-flipped, other fields are valid
+ if (!memcmp(signature, "GMI2", 4))
{
- LOG_FORMATS("ap_dsk35: corrected bad-endian data length\n");
- header.data_length = 0x0c8000;
+ return FIFID_SIGN;
}
- if ((((uint64_t) header.data_offset) + header.data_length) > size)
- return FLOPPY_ERROR_INVALIDIMAGE;
- if ((((uint64_t) header.comment_offset) + header.comment_length) > size)
- return FLOPPY_ERROR_INVALIDIMAGE;
- if ((((uint64_t) 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, nullptr, nullptr, nullptr) ? 0 : 100;
- return FLOPPY_ERROR_SUCCESS;
+ return 0;
}
-
-
-static FLOPPY_CONSTRUCT(apple35_2img_construct)
+bool apple_2mg_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- floperr_t err;
- uint32_t image_format;
- uint32_t data_offset;
- uint32_t data_size;
- uint8_t sides = 2;
- struct header_2img header;
-
- if (params)
- {
- /* create */
- sides = params->lookup_int(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);
-}
-
-
-
-LEGACY_FLOPPY_OPTIONS_START( apple35_mac )
- LEGACY_FLOPPY_OPTION( apple35_raw, "dsk,img,image", "Apple raw 3.5\" disk image", apple35_raw_identify, apple35_raw_construct, nullptr,
- HEADS([1]-2)
- TRACKS([80])
- SECTOR_LENGTH([512])
- FIRST_SECTOR_ID([0]))
- LEGACY_FLOPPY_OPTION( apple35_dc, "dc,dc42,dsk,img,image", "Apple DiskCopy disk image", apple35_diskcopy_identify, apple35_diskcopy_construct, nullptr,
- HEADS([1]-2)
- TRACKS([80])
- SECTOR_LENGTH([512])
- FIRST_SECTOR_ID([0]))
-LEGACY_FLOPPY_OPTIONS_END
-
-LEGACY_FLOPPY_OPTIONS_START( apple35_iigs )
- LEGACY_FLOPPY_OPTION( apple35_raw, "dsk,img,image,po", "Apple raw 3.5\" disk image", apple35_raw_identify, apple35_raw_construct, nullptr,
- HEADS([1]-2)
- TRACKS([80])
- SECTOR_LENGTH([512])
- FIRST_SECTOR_ID([0]))
- LEGACY_FLOPPY_OPTION( apple35_dc, "dc,dsk,img,image", "Apple DiskCopy disk image", apple35_diskcopy_identify, apple35_diskcopy_construct, nullptr,
- HEADS([1]-2)
- TRACKS([80])
- SECTOR_LENGTH([512])
- FIRST_SECTOR_ID([0]))
- LEGACY_FLOPPY_OPTION( apple35_2img, "2img,2mg", "Apple ][gs 2IMG disk image", apple35_2img_identify, apple35_2img_construct, nullptr,
- HEADS([1]-2)
- TRACKS([80])
- SECTOR_LENGTH([512])
- FIRST_SECTOR_ID([0]))
-LEGACY_FLOPPY_OPTIONS_END
+ desc_gcr_sector sectors[12];
+ uint8_t sdata[512*12], header[64];
+ read_at(io, 0, header, 64); // FIXME: check for errors and premature EOF
+ uint32_t blocks = get_u32le(&header[0x14]);
+ uint32_t pos_data = get_u32le(&header[0x18]);
-
-// license:BSD-3-Clause
-// copyright-holders:Olivier Galibert
-dc42_format::dc42_format() : floppy_image_format_t()
-{
-}
-
-const char *dc42_format::name() const
-{
- return "dc42";
-}
-
-const char *dc42_format::description() const
-{
- return "DiskCopy 4.2 image";
-}
-
-const char *dc42_format::extensions() const
-{
- return "dc42";
-}
-
-bool dc42_format::supports_save() const
-{
- return true;
-}
-
-int dc42_format::identify(io_generic *io, uint32_t form_factor)
-{
- uint8_t h[0x54];
- uint64_t size = io_generic_size(io);
- if(size < 0x54)
- return 0;
-
- io_generic_read(io, h, 0, 0x54);
- uint32_t dsize = (h[0x40] << 24) | (h[0x41] << 16) | (h[0x42] << 8) | h[0x43];
- uint32_t tsize = (h[0x44] << 24) | (h[0x45] << 16) | (h[0x46] << 8) | h[0x47];
-
- return size == 0x54+tsize+dsize && h[0] < 64 && h[0x52] == 1 && h[0x53] == 0 ? 100 : 0;
-}
-
-const floppy_image_format_t::desc_e dc42_format::mac_gcr[] = {
- { SECTOR_LOOP_START, 0, -1 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xd5aa96, 24 },
- { CRC_MACHEAD_START, 0 },
- { TRACK_ID_GCR6 },
- { SECTOR_ID_GCR6 },
- { TRACK_HEAD_ID_GCR6 },
- { SECTOR_INFO_GCR6 },
- { CRC_END, 0 },
- { CRC, 0 },
- { RAWBITS, 0xdeaaff, 24 },
- { RAWBITS, 0xff3fcf, 24 }, { RAWBITS, 0xf3fcff, 24 },
- { RAWBITS, 0xd5aaad, 24 },
- { SECTOR_ID_GCR6 },
- { SECTOR_DATA_MAC, -1 },
- { RAWBITS, 0xdeaaff, 24 },
- { RAWBITS, 0xff, 8 },
- { SECTOR_LOOP_END },
- { END },
-};
-
-
-bool dc42_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
-{
- uint8_t h[0x54];
- io_generic_read(io, h, 0, 0x54);
- int dsize = (h[0x40] << 24) | (h[0x41] << 16) | (h[0x42] << 8) | h[0x43];
- int tsize = (h[0x44] << 24) | (h[0x45] << 16) | (h[0x46] << 8) | h[0x47];
-
- uint8_t encoding = h[0x50];
- uint8_t format = h[0x51];
-
- if((encoding != 0x00 || format != 0x02) && (encoding != 0x01 || format != 0x22)) {
- osd_printf_error("dc42: Unsupported encoding/format combination %02x/%02x\n", encoding, format);
+ if(blocks != 1600 && blocks != 16390)
return false;
- }
-
- uint8_t sector_data[(512+12)*12];
- memset(sector_data, 0, sizeof(sector_data));
-
- desc_s sectors[12];
- int pos_data = 0x54;
- int pos_tag = 0x54+dsize;
-
- int head_count = encoding == 1 ? 2 : 1;
+ image.set_form_variant(floppy_image::FF_35, (blocks > 800) ? floppy_image::DSDD : floppy_image::SSDD);
for(int track=0; track < 80; track++) {
- for(int head=0; head < head_count; head++) {
+ for(int head=0; head < 2; head++) {
int ns = 12 - (track/16);
+ read_at(io, pos_data, sdata, 512*ns); // FIXME: check for errors and premature EOF
+ pos_data += 512*ns;
+
int si = 0;
for(int i=0; i<ns; i++) {
- uint8_t *data = sector_data + (512+12)*i;
- sectors[si].data = data;
- sectors[si].size = 512+12;
- sectors[si].sector_id = i;
- sectors[si].sector_info = format;
- if(tsize) {
- io_generic_read(io, data, pos_tag, 12);
- pos_tag += 12;
- }
- io_generic_read(io, data+12, pos_data, 512);
- pos_data += 512;
+ sectors[si].track = track;
+ sectors[si].head = head;
+ sectors[si].sector = i;
+ sectors[si].info = 0x22;
+ sectors[si].tag = nullptr;
+ sectors[si].data = sdata + 512*i;
si = (si + 2) % ns;
if(si == 0)
si++;
}
- generate_track(mac_gcr, track, head, sectors, ns, 6208*ns, image);
+ build_mac_track_gcr(track, head, image, sectors);
}
}
return true;
}
-uint8_t dc42_format::gb(const uint8_t *buf, int ts, int &pos, int &wrap)
-{
- uint8_t v = 0;
- int w1 = wrap;
- while(wrap != w1+2 && !(v & 0x80)) {
- v = v << 1 | ((buf[pos >> 3] >> (7-(pos & 7))) & 1);
- pos++;
- if(pos == ts) {
- pos = 0;
- wrap++;
- }
- }
- return v;
-}
-
-void dc42_format::update_chk(const uint8_t *data, int size, uint32_t &chk)
-{
- for(int i=0; i<size; i+=2) {
- chk += (data[i] << 8) | data[i+1];
- chk = (chk >> 1) | (chk << 31);
- }
-}
-
-bool dc42_format::save(io_generic *io, floppy_image *image)
-{
- int g_tracks, g_heads;
- image->get_actual_geometry(g_tracks, g_heads);
-
- if(g_heads == 0)
- g_heads = 1;
-
- uint8_t h[0x54];
- memset(h, 0, 0x54);
- strcpy((char *)h+1, "Unnamed");
- h[0] = 7;
- int nsect = 16*(12+11+10+9+8)*g_heads;
- uint32_t dsize = nsect*512;
- uint32_t tsize = nsect*12;
- h[0x40] = dsize >> 24;
- h[0x41] = dsize >> 16;
- h[0x42] = dsize >> 8;
- h[0x43] = dsize;
- h[0x44] = tsize >> 24;
- h[0x45] = tsize >> 16;
- h[0x46] = tsize >> 8;
- h[0x47] = tsize;
- h[0x50] = g_heads == 2 ? 0x01 : 0x00;
- h[0x51] = g_heads == 2 ? 0x22 : 0x02;
- h[0x52] = 0x01;
- h[0x53] = 0x00;
-
- uint32_t dchk = 0;
- uint32_t tchk = 0;
-
- int pos_data = 0x54;
- int pos_tag = 0x54+dsize;
+bool apple_2mg_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ uint8_t header[0x40];
+ int pos_data = 0x40;
+
+ memset(header, 0, sizeof(header));
+ // file ID
+ header[0] = '2'; header[1] = 'I'; header[2] = 'M'; header[3] = 'G';
+ // creator program
+ header[4] = 'M'; header[5] = 'A'; header[6] = 'M'; header[7] = 'E';
+ // header size
+ header[8] = 0x40;
+ // version
+ header[0xa] = 1;
+ // flags
+ header[0xc] = 1; // ProDOS sector order
+ // number of ProDOS blocks
+ header[0x14] = 0x40; header[0x15] = 0x06; // 0x640 (1600)
+ // offset to sector data
+ header[0x18] = 0x40;
+ // bytes of disk data
+ header[0x1c] = 0x00; header[0x1d] = 0x80; header[0x1e] = 0x0c; // 0xC8000 (819200)
+ write_at(io, 0, header, 0x40); // FIXME: check for errors
for(int track=0; track < 80; track++) {
- for(int head=0; head < g_heads; head++) {
- uint8_t sectdata[(512+12)*12];
- memset(sectdata, 0, sizeof(sectdata));
- int nsect = 12-(track/16);
- uint8_t buf[13000];
- int ts;
- generate_bitstream_from_track(track, head, 200000000/(6208*nsect), buf, ts, image);
- int pos = 0;
- int wrap = 0;
- int hb = 0;
- for(;;) {
- uint8_t v = gb(buf, ts, pos, wrap);
- if(v == 0xff)
- hb = 1;
- else if(hb == 1 && v == 0xd5)
- hb = 2;
- else if(hb == 2 && v == 0xaa)
- hb = 3;
- else if(hb == 3 && v == 0x96)
- hb = 4;
- else
- hb = 0;
-
- if(hb == 4) {
- uint8_t h[7];
- for(auto & elem : h)
- elem = gb(buf, ts, pos, wrap);
- uint8_t v2 = gcr6bw_tb[h[2]];
- uint8_t v3 = gcr6bw_tb[h[3]];
- uint8_t tr = gcr6bw_tb[h[0]] | (v2 & 1 ? 0x40 : 0x00);
- uint8_t se = gcr6bw_tb[h[1]];
- uint8_t si = v2 & 0x20 ? 1 : 0;
- // uint8_t ds = v3 & 0x20 ? 1 : 0;
- // uint8_t fmt = v3 & 0x1f;
- uint8_t c1 = (tr^se^v2^v3) & 0x3f;
- uint8_t chk = gcr6bw_tb[h[4]];
- if(chk == c1 && tr == track && si == head && se < nsect) {
- int opos = pos;
- int owrap = wrap;
- hb = 0;
- for(int i=0; i<20 && hb != 4; i++) {
- v = gb(buf, ts, pos, wrap);
- if(v == 0xff)
- hb = 1;
- else if(hb == 1 && v == 0xd5)
- hb = 2;
- else if(hb == 2 && v == 0xaa)
- hb = 3;
- else if(hb == 3 && v == 0xad)
- hb = 4;
- else
- hb = 0;
- }
- if(hb == 4) {
- uint8_t *dest = sectdata+(512+12)*se;
- gb(buf, ts, pos, wrap); // Ignore the sector byte
- uint8_t ca = 0, cb = 0, cc = 0;
- for(int i=0; i<522/3+1; i++) {
- uint8_t e0 = gb(buf, ts, pos, wrap);
- uint8_t e1 = gb(buf, ts, pos, wrap);
- uint8_t e2 = gb(buf, ts, pos, wrap);
- uint8_t e3 = i == 522/3 ? 0x96 : gb(buf, ts, pos, wrap);
- uint8_t va, vb, vc;
- gcr6_decode(e0, e1, e2, e3, va, vb, vc);
- cc = (cc << 1) | (cc >> 7);
- va = va ^ cc;
- int suma = ca + va + (cc & 1);
- ca = suma;
- vb = vb ^ ca;
- int sumb = cb + vb + (suma >> 8);
- cb = sumb;
- vc = vc ^ cb;
- cc = cc + vc + (sumb >> 8);
- *dest++ = va;
- *dest++ = vb;
- if(i != 522/3)
- *dest++ = vc;
- }
- } else {
- pos = opos;
- wrap = owrap;
- }
- }
- hb = 0;
- }
- if(wrap)
- break;
- }
- for(int i=0; i<nsect; i++) {
- uint8_t *data = sectdata + (512+12)*i;
- io_generic_write(io, data, pos_tag, 12);
- io_generic_write(io, data+12, pos_data, 512);
- pos_tag += 12;
+ for(int head=0; head < 2; head++) {
+ auto sectors = extract_sectors_from_track_mac_gcr6(head, track, image);
+ for(unsigned int i=0; i < sectors.size(); i++) {
+ auto &sdata = sectors[i];
+ sdata.resize(512+12);
+ write_at(io, pos_data, &sdata[12], 512); // FIXME: check for errors
pos_data += 512;
- if(track || head || i)
- update_chk(data, 12, tchk);
- update_chk(data+12, 512, dchk);
}
}
}
- h[0x48] = dchk >> 24;
- h[0x49] = dchk >> 16;
- h[0x4a] = dchk >> 8;
- h[0x4b] = dchk;
- h[0x4c] = tchk >> 24;
- h[0x4d] = tchk >> 16;
- h[0x4e] = tchk >> 8;
- h[0x4f] = tchk;
-
- io_generic_write(io, h, 0, 0x54);
return true;
}
-const floppy_format_type FLOPPY_DC42_FORMAT = &floppy_image_format_creator<dc42_format>;
+const apple_2mg_format FLOPPY_APPLE_2MG_FORMAT;
diff --git a/src/lib/formats/ap_dsk35.h b/src/lib/formats/ap_dsk35.h
index 8b3a8bbc1d3..67874ecbcb6 100644
--- a/src/lib/formats/ap_dsk35.h
+++ b/src/lib/formats/ap_dsk35.h
@@ -14,37 +14,58 @@
#include "flopimg.h"
-void sony_filltrack(uint8_t *buffer, size_t buffer_len, size_t *pos, uint8_t data);
-uint8_t sony_fetchtrack(const uint8_t *buffer, size_t buffer_len, size_t *pos);
+class dc42_format : public floppy_image_format_t
+{
+public:
+ dc42_format();
-int apple35_sectors_per_track(floppy_image_legacy *image, int track);
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
-/**************************************************************************/
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
-LEGACY_FLOPPY_OPTIONS_EXTERN(apple35_mac);
-LEGACY_FLOPPY_OPTIONS_EXTERN(apple35_iigs);
+private:
+ static void update_chk(const uint8_t *data, int size, uint32_t &chk);
+};
-class dc42_format : public floppy_image_format_t
+extern const dc42_format FLOPPY_DC42_FORMAT;
+
+class apple_gcr_format : public floppy_image_format_t
{
public:
- dc42_format();
+ apple_gcr_format();
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
+};
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+extern const apple_gcr_format FLOPPY_APPLE_GCR_FORMAT;
-private:
- static const desc_e mac_gcr[];
+class apple_2mg_format : public floppy_image_format_t
+{
+public:
+ apple_2mg_format();
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- uint8_t gb(const uint8_t *buf, int ts, int &pos, int &wrap);
- void update_chk(const uint8_t *data, int size, uint32_t &chk);
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_DC42_FORMAT;
+extern const apple_2mg_format FLOPPY_APPLE_2MG_FORMAT;
#endif // MAME_FORMATS_AP_DSK35_H
diff --git a/src/lib/formats/apd_dsk.cpp b/src/lib/formats/apd_dsk.cpp
index 202c7e0d239..55d722bdf38 100644
--- a/src/lib/formats/apd_dsk.cpp
+++ b/src/lib/formats/apd_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Nigel Barnes
/*********************************************************************
- formats/apd_dsk.c
+ formats/apd_dsk.cpp
Archimedes Protected Disk Image format
@@ -60,9 +60,18 @@
*********************************************************************/
-#include <zlib.h>
#include "formats/apd_dsk.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include "osdcore.h" // osd_printf_*, little_endianize_int32
+
+#include <zlib.h>
+
+#include <cstring>
+
+
static const uint8_t APD_HEADER[8] = { 'A', 'P', 'D', 'X', '0', '0', '0', '1' };
static const uint8_t GZ_HEADER[2] = { 0x1f, 0x8b };
@@ -70,26 +79,31 @@ apd_format::apd_format()
{
}
-const char *apd_format::name() const
+const char *apd_format::name() const noexcept
{
return "apd";
}
-const char *apd_format::description() const
+const char *apd_format::description() const noexcept
{
return "Archimedes Protected Disk Image";
}
-const char *apd_format::extensions() const
+const char *apd_format::extensions() const noexcept
{
return "apd";
}
-int apd_format::identify(io_generic *io, uint32_t form_factor)
+int apd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size) || !size)
+ return 0;
+
std::vector<uint8_t> img(size);
- io_generic_read(io, &img[0], 0, size);
+ auto const [ioerr, actual] = read_at(io, 0, &img[0], size);
+ if (ioerr || (actual != size))
+ return 0;
int err;
std::vector<uint8_t> gz_ptr(8);
@@ -113,26 +127,31 @@ int apd_format::identify(io_generic *io, uint32_t form_factor)
err = inflateEnd(&d_stream);
if (err != Z_OK) return 0;
- img = gz_ptr;
+ img = std::move(gz_ptr);
}
if (!memcmp(&img[0], APD_HEADER, sizeof(APD_HEADER))) {
- return 100;
+ return FIFID_SIGN;
}
return 0;
}
-bool apd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool apd_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return false;
+
std::vector<uint8_t> img(size);
- io_generic_read(io, &img[0], 0, size);
+ auto const [ioerr, actual] = read_at(io, 0, &img[0], size);
+ if (ioerr || (actual != size))
+ return false;
int err;
std::vector<uint8_t> gz_ptr;
z_stream d_stream;
- int inflate_size = (img[size - 1] << 24) | (img[size - 2] << 16) | (img[size - 3] << 8) | img[size - 4];
+ int inflate_size = get_u32le(&img[size - 4]);
uint8_t *in_ptr = &img[0];
if (!memcmp(&img[0], GZ_HEADER, sizeof(GZ_HEADER))) {
@@ -184,14 +203,14 @@ bool apd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
data += (qdlen + 7) >> 3;
}
}
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
return true;
}
-bool apd_format::supports_save() const
+bool apd_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_APD_FORMAT = &floppy_image_format_creator<apd_format>;
+const apd_format FLOPPY_APD_FORMAT;
diff --git a/src/lib/formats/apd_dsk.h b/src/lib/formats/apd_dsk.h
index 343d45eb51d..3d7d161f83e 100644
--- a/src/lib/formats/apd_dsk.h
+++ b/src/lib/formats/apd_dsk.h
@@ -19,15 +19,15 @@ class apd_format : public floppy_image_format_t
public:
apd_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_APD_FORMAT;
+extern const apd_format FLOPPY_APD_FORMAT;
#endif // MAME_FORMATS_APD_DSK_H
diff --git a/src/lib/formats/apf_apt.cpp b/src/lib/formats/apf_apt.cpp
index de919b0bddd..9930d501652 100644
--- a/src/lib/formats/apf_apt.cpp
+++ b/src/lib/formats/apf_apt.cpp
@@ -34,8 +34,6 @@ e. A checksum byte (8-bit addition)
********************************************************************/
-#include <assert.h>
-
#include "formats/apf_apt.h"
#define WAVEENTRY_LOW -32768
@@ -154,12 +152,12 @@ static int apf_cpf_handle_cassette(int16_t *buffer, const uint8_t *bytes)
Generate samples for the tape image
********************************************************************/
-static int apf_apt_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int apf_apt_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return apf_apt_handle_cassette(buffer, bytes);
}
-static int apf_cpf_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int apf_cpf_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return apf_cpf_handle_cassette(buffer, bytes);
}
@@ -184,7 +182,7 @@ static int apf_cpf_calculate_size_in_samples(const uint8_t *bytes, int length)
//*********************************************************************************
-static const struct CassetteLegacyWaveFiller apf_cpf_fill_intf =
+static const cassette_image::LegacyWaveFiller apf_cpf_fill_intf =
{
apf_cpf_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -195,17 +193,17 @@ static const struct CassetteLegacyWaveFiller apf_cpf_fill_intf =
0 /* trailer_samples */
};
-static cassette_image::error apf_cpf_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error apf_cpf_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &apf_cpf_fill_intf);
+ return cassette->legacy_identify(opts, &apf_cpf_fill_intf);
}
static cassette_image::error apf_cpf_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &apf_cpf_fill_intf);
+ return cassette->legacy_construct(&apf_cpf_fill_intf);
}
-static const struct CassetteFormat apf_cpf_format =
+static const cassette_image::Format apf_cpf_format =
{
"cas,cpf",
apf_cpf_identify,
@@ -215,7 +213,7 @@ static const struct CassetteFormat apf_cpf_format =
//*********************************************************************************
-static const struct CassetteLegacyWaveFiller apf_apt_fill_intf =
+static const cassette_image::LegacyWaveFiller apf_apt_fill_intf =
{
apf_apt_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -226,17 +224,17 @@ static const struct CassetteLegacyWaveFiller apf_apt_fill_intf =
0 /* trailer_samples */
};
-static cassette_image::error apf_apt_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error apf_apt_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &apf_apt_fill_intf);
+ return cassette->legacy_identify(opts, &apf_apt_fill_intf);
}
static cassette_image::error apf_apt_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &apf_apt_fill_intf);
+ return cassette->legacy_construct(&apf_apt_fill_intf);
}
-static const struct CassetteFormat apf_apt_format =
+static const cassette_image::Format apf_apt_format =
{
"apt",
apf_apt_identify,
diff --git a/src/lib/formats/apollo_dsk.cpp b/src/lib/formats/apollo_dsk.cpp
index b2fef28be71..069f22c5db5 100644
--- a/src/lib/formats/apollo_dsk.cpp
+++ b/src/lib/formats/apollo_dsk.cpp
@@ -2,31 +2,32 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/apollo_dsk.c
+ formats/apollo_dsk.cpp
apollo format
*********************************************************************/
-#include <assert.h>
-
#include "formats/apollo_dsk.h"
+#include "ioprocs.h"
+
+
apollo_format::apollo_format() : upd765_format(formats)
{
}
-const char *apollo_format::name() const
+const char *apollo_format::name() const noexcept
{
return "apollo";
}
-const char *apollo_format::description() const
+const char *apollo_format::description() const noexcept
{
return "APOLLO disk image";
}
-const char *apollo_format::extensions() const
+const char *apollo_format::extensions() const noexcept
{
return "afd";
}
@@ -44,12 +45,4 @@ const apollo_format::format apollo_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_APOLLO_FORMAT = &floppy_image_format_creator<apollo_format>;
-
-int apollo_format::identify(io_generic *io, uint32_t form_factor)
-{
- uint64_t size = io_generic_size(io);
- uint32_t expected_size = 77*2*8*1024;
-
- return ((size == expected_size) || (size == 0)) ? 1 : 0;
-}
+const apollo_format FLOPPY_APOLLO_FORMAT;
diff --git a/src/lib/formats/apollo_dsk.h b/src/lib/formats/apollo_dsk.h
index 048a2438cc5..4026d5d6a10 100644
--- a/src/lib/formats/apollo_dsk.h
+++ b/src/lib/formats/apollo_dsk.h
@@ -19,15 +19,14 @@ class apollo_format : public upd765_format
public:
apollo_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_APOLLO_FORMAT;
+extern const apollo_format FLOPPY_APOLLO_FORMAT;
#endif // MAME_FORMATS_APOLLO_DSK_H
diff --git a/src/lib/formats/applix_dsk.cpp b/src/lib/formats/applix_dsk.cpp
index 59a0d4c06f5..378ffae346b 100644
--- a/src/lib/formats/applix_dsk.cpp
+++ b/src/lib/formats/applix_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Robbbert
/*********************************************************************
- formats/applix_dsk.c
+ formats/applix_dsk.cpp
Applix disk image format
*********************************************************************/
-#include <assert.h>
-
#include "formats/applix_dsk.h"
applix_format::applix_format() : wd177x_format(formats)
{
}
-const char *applix_format::name() const
+const char *applix_format::name() const noexcept
{
return "applix";
}
-const char *applix_format::description() const
+const char *applix_format::description() const noexcept
{
return "Applix disk image";
}
-const char *applix_format::extensions() const
+const char *applix_format::extensions() const noexcept
{
return "raw";
}
@@ -40,4 +38,4 @@ const applix_format::format applix_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_APPLIX_FORMAT = &floppy_image_format_creator<applix_format>;
+const applix_format FLOPPY_APPLIX_FORMAT;
diff --git a/src/lib/formats/applix_dsk.h b/src/lib/formats/applix_dsk.h
index 6086d55fe57..a2030e39245 100644
--- a/src/lib/formats/applix_dsk.h
+++ b/src/lib/formats/applix_dsk.h
@@ -19,14 +19,14 @@ class applix_format : public wd177x_format
public:
applix_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_APPLIX_FORMAT;
+extern const applix_format FLOPPY_APPLIX_FORMAT;
#endif // MAME_FORMATS_APPLIX_DSK_H
diff --git a/src/lib/formats/apridisk.cpp b/src/lib/formats/apridisk.cpp
index a42aa9fa4d4..36ad6018336 100644
--- a/src/lib/formats/apridisk.cpp
+++ b/src/lib/formats/apridisk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -8,33 +8,39 @@
***************************************************************************/
-#include "emu.h"
-#include "imageutl.h"
#include "apridisk.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include "osdcore.h" // osd_printf_error
+
+
apridisk_format::apridisk_format()
{
}
-const char *apridisk_format::name() const
+const char *apridisk_format::name() const noexcept
{
return "apridisk";
}
-const char *apridisk_format::description() const
+const char *apridisk_format::description() const noexcept
{
return "APRIDISK disk image";
}
-const char *apridisk_format::extensions() const
+const char *apridisk_format::extensions() const noexcept
{
return "dsk";
}
-int apridisk_format::identify(io_generic *io, uint32_t form_factor)
+int apridisk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t header[APR_HEADER_SIZE];
- io_generic_read(io, header, 0, APR_HEADER_SIZE);
+ auto const [err, actual] = read_at(io, 0, header, APR_HEADER_SIZE);
+ if (err || (APR_HEADER_SIZE != actual))
+ return 0;
const char magic[] = "ACT Apricot disk image\x1a\x04";
@@ -44,35 +50,38 @@ int apridisk_format::identify(io_generic *io, uint32_t form_factor)
return 0;
}
-bool apridisk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool apridisk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
desc_pc_sector sectors[80][2][18];
- uint8_t sector_data[MAX_SECTORS * SECTOR_SIZE];
- uint8_t *data_ptr = sector_data;
+ std::unique_ptr<uint8_t []> sector_data(new uint8_t [MAX_SECTORS * SECTOR_SIZE]);
+ uint8_t *data_ptr = sector_data.get();
int track_count = 0, head_count = 0, sector_count = 0;
- uint64_t file_size = io_generic_size(io);
+ uint64_t file_size;
+ if (io.length(file_size))
+ return false;
+
uint64_t file_offset = APR_HEADER_SIZE;
while (file_offset < file_size)
{
// read sector header
uint8_t sector_header[16];
- io_generic_read(io, sector_header, file_offset, 16);
+ read_at(io, file_offset, sector_header, 16); // FIXME: check for errors and premature EOF
- uint32_t type = pick_integer_le(&sector_header, 0, 4);
- uint16_t compression = pick_integer_le(&sector_header, 4, 2);
- uint16_t header_size = pick_integer_le(&sector_header, 6, 2);
- uint32_t data_size = pick_integer_le(&sector_header, 8, 4);
+ uint32_t type = get_u32le(&sector_header[0]);
+ uint16_t compression = get_u16le(&sector_header[4]);
+ uint16_t header_size = get_u16le(&sector_header[6]);
+ uint32_t data_size = get_u32le(&sector_header[8]);
file_offset += header_size;
switch (type)
{
case APR_SECTOR:
- uint8_t head = pick_integer_le(&sector_header, 12, 1);
- uint8_t sector = pick_integer_le(&sector_header, 13, 1);
- uint8_t track = (uint8_t) pick_integer_le(&sector_header, 14, 2);
+ uint8_t head = sector_header[12];
+ uint8_t sector = sector_header[13];
+ uint8_t track = (uint8_t) get_u16le(&sector_header[14]);
track_count = std::max(track_count, int(unsigned(track)));
head_count = std::max(head_count, int(unsigned(head)));
@@ -93,22 +102,26 @@ bool apridisk_format::load(io_generic *io, uint32_t form_factor, floppy_image *i
case APR_COMPRESSED:
{
uint8_t comp[3];
- io_generic_read(io, comp, file_offset, 3);
- uint16_t length = pick_integer_le(comp, 0, 2);
+ read_at(io, file_offset, comp, 3); // FIXME: check for errors and premature EOF
+ uint16_t length = get_u16le(&comp[0]);
if (length != SECTOR_SIZE)
- fatalerror("apridisk_format: Invalid compression length %04x\n", length);
+ {
+ osd_printf_error("apridisk_format: Invalid compression length %04x\n", length);
+ return false;
+ }
memset(data_ptr, comp[2], SECTOR_SIZE);
}
break;
case APR_UNCOMPRESSED:
- io_generic_read(io, data_ptr, file_offset, SECTOR_SIZE);
+ read_at(io, file_offset, data_ptr, SECTOR_SIZE); // FIXME: check for errors and premature EOF
break;
default:
- fatalerror("apridisk_format: Invalid compression %04x\n", compression);
+ osd_printf_error("apridisk_format: Invalid compression %04x\n", compression);
+ return false;
}
sectors[track][head][sector - 1].data = data_ptr;
@@ -135,14 +148,9 @@ bool apridisk_format::load(io_generic *io, uint32_t form_factor, floppy_image *i
return true;
}
-bool apridisk_format::save(io_generic *io, floppy_image *image)
-{
- return false;
-}
-
-bool apridisk_format::supports_save() const
+bool apridisk_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_APRIDISK_FORMAT = &floppy_image_format_creator<apridisk_format>;
+const apridisk_format FLOPPY_APRIDISK_FORMAT;
diff --git a/src/lib/formats/apridisk.h b/src/lib/formats/apridisk.h
index 125b71451a5..3759b1b59cd 100644
--- a/src/lib/formats/apridisk.h
+++ b/src/lib/formats/apridisk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -19,14 +19,13 @@ class apridisk_format : public floppy_image_format_t
public:
apridisk_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
private:
static const int APR_HEADER_SIZE = 128;
@@ -51,6 +50,6 @@ private:
static const int MAX_SECTORS = 2880; // enough for a hd disk image
};
-extern const floppy_format_type FLOPPY_APRIDISK_FORMAT;
+extern const apridisk_format FLOPPY_APRIDISK_FORMAT;
#endif // MAME_FORMATS_APRIDISK_H
diff --git a/src/lib/formats/aquarius_caq.cpp b/src/lib/formats/aquarius_caq.cpp
new file mode 100644
index 00000000000..b7b6cfec4f9
--- /dev/null
+++ b/src/lib/formats/aquarius_caq.cpp
@@ -0,0 +1,101 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/*********************************************************************
+
+ formats/aquarius_caq.cpp
+
+ Cassette code for Aquarius .CAQ files
+
+*********************************************************************/
+
+#include "aquarius_caq.h"
+
+#include "coretmpl.h" // BIT
+
+
+/*-------------------------------------------------
+ CassetteModulation aquarius_caq_modulation
+-------------------------------------------------*/
+
+static const cassette_image::Modulation aquarius_caq_modulation =
+{
+ cassette_image::MODULATION_SINEWAVE,
+ 900.0 - 300, 900.0, 900.0 + 300,
+ 1800.0 - 600, 1800.0, 1800.0 + 600
+
+
+};
+
+/*-------------------------------------------------
+ aquarius_caq_identify - identify cassette
+-------------------------------------------------*/
+
+static cassette_image::error aquarius_caq_identify(cassette_image *cassette, cassette_image::Options *opts)
+{
+ return cassette->modulation_identify(aquarius_caq_modulation, opts);
+}
+
+/*-------------------------------------------------
+ aquarius_caq_load - load cassette
+-------------------------------------------------*/
+
+#define MODULATE(_value) \
+ for (int i = 0; i < 2; i++) { \
+ err = cassette->put_modulated_data_bit(0, time_index, _value, aquarius_caq_modulation, &time_displacement); \
+ if (err != cassette_image::error::SUCCESS) return err; \
+ time_index += time_displacement; \
+ }
+
+static cassette_image::error aquarius_caq_load(cassette_image *cassette)
+{
+ cassette_image::error err;
+ uint64_t image_size = cassette->image_size();
+ double time_index = 0.0;
+ double time_displacement;
+
+ /* silence */
+ err = cassette->put_sample(0, time_index, 0.5, 0);
+ if (err != cassette_image::error::SUCCESS) return err;
+ time_index += 0.5;
+
+ for (uint64_t image_pos = 0; image_pos < image_size; image_pos++)
+ {
+ uint8_t data = cassette->image_read_byte(image_pos);
+
+ /* start bit */
+ MODULATE(0);
+
+ /* data bits */
+ for (int bit = 7; bit >= 0; bit--)
+ {
+ MODULATE(util::BIT(data, bit));
+ }
+
+ /* stop bits */
+ MODULATE(1);
+ MODULATE(1);
+ }
+
+ /* silence */
+ err = cassette->put_sample(0, time_index, 0.5, 0);
+ if (err != cassette_image::error::SUCCESS) return err;
+ time_index += 0.5;
+
+ return cassette_image::error::SUCCESS;
+}
+
+/*-------------------------------------------------
+ CassetteFormat aquarius_caq_cassette_format
+-------------------------------------------------*/
+
+const cassette_image::Format aquarius_caq_format =
+{
+ "caq",
+ aquarius_caq_identify,
+ aquarius_caq_load,
+ nullptr
+};
+
+CASSETTE_FORMATLIST_START( aquarius_cassette_formats )
+ CASSETTE_FORMAT(aquarius_caq_format)
+CASSETTE_FORMATLIST_END
diff --git a/src/lib/formats/aquarius_caq.h b/src/lib/formats/aquarius_caq.h
new file mode 100644
index 00000000000..158b1f12a5d
--- /dev/null
+++ b/src/lib/formats/aquarius_caq.h
@@ -0,0 +1,19 @@
+// license:BSD-3-Clause
+// copyright-holders:Nigel Barnes
+/*********************************************************************
+
+ formats/aquarius_caq.h
+
+ Cassette code for Aquarius .CAQ files
+
+*********************************************************************/
+#ifndef MAME_FORMATS_AQUARIUS_CAQ_H
+#define MAME_FORMATS_AQUARIUS_CAQ_H
+
+#pragma once
+
+#include "cassimg.h"
+
+CASSETTE_FORMATLIST_EXTERN(aquarius_cassette_formats);
+
+#endif // MAME_FORMATS_AQUARIUS_CAQ_H
diff --git a/src/lib/formats/as_dsk.cpp b/src/lib/formats/as_dsk.cpp
new file mode 100644
index 00000000000..0fad7217585
--- /dev/null
+++ b/src/lib/formats/as_dsk.cpp
@@ -0,0 +1,613 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Applesauce solved output formats
+
+#include "as_dsk.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <string.h>
+
+
+namespace {
+
+template <typename T>
+uint32_t crc32r(T &&data, uint32_t size)
+{
+ // Reversed crc32
+ uint32_t crc = 0xffffffff;
+ for(uint32_t i=0; i != size; i++) {
+ crc = crc ^ data[i];
+ for(int j=0; j<8; j++)
+ if(crc & 1)
+ crc = (crc >> 1) ^ 0xedb88320;
+ else
+ crc = crc >> 1;
+ }
+ return ~crc;
+}
+
+template <typename T>
+uint32_t find_tag(T &&data, size_t size, uint32_t tag)
+{
+ uint32_t offset = 12;
+ do {
+ if(get_u32le(&data[offset]) == tag)
+ return offset + 8;
+ offset += get_u32le(&data[offset+4]) + 8;
+ } while(offset < (size - 8));
+ return 0;
+}
+
+} // anonymous namespace
+
+
+as_format::as_format() : floppy_image_format_t()
+{
+}
+
+
+bool as_format::load_bitstream_track(const uint8_t *img, floppy_image &image, int head, int track, int subtrack, uint8_t idx, uint32_t off_trks, bool may_be_short, bool set_variant)
+{
+ uint32_t trks_off = off_trks + (idx * 8);
+
+ uint32_t track_size = get_u32le(&img[trks_off + 4]);
+ if (track_size == 0)
+ return false;
+
+ uint32_t boff = (uint32_t)get_u16le(&img[trks_off + 0]) * 512;
+
+ // With 5.25 floppies the end-of-track may be missing
+ // if unformatted. Accept track length down to 95% of
+ // 51090, otherwise pad it
+
+ bool short_track = may_be_short && track_size < 48535;
+
+ if(short_track) {
+ std::vector<uint8_t> buffer(6387, 0);
+ memcpy(buffer.data(), &img[boff], (track_size + 7) / 8);
+ generate_track_from_bitstream(track, head, buffer.data(), 51090, image, subtrack, 0xffff);
+
+ } else
+ generate_track_from_bitstream(track, head, &img[boff], track_size, image, subtrack, 0xffff);
+
+ if(set_variant)
+ image.set_variant(get_u32le(&img[trks_off + 4]) >= 90000 ? floppy_image::DSHD : floppy_image::DSDD);
+ return true;
+}
+
+void as_format::load_flux_track(const uint8_t *img, floppy_image &image, int head, int track, int subtrack, uint8_t fidx, uint32_t off_trks)
+{
+ uint32_t trks_off = off_trks + (fidx * 8);
+ uint32_t boff = (uint32_t)get_u16le(&img[trks_off + 0]) * 512;
+ uint32_t track_size = get_u32le(&img[trks_off + 4]);
+
+ uint32_t total_ticks = 0;
+ for(uint32_t i=0; i != track_size; i++)
+ total_ticks += img[boff+i];
+
+ // There is always a pulse at index, and it's
+ // the last one in the stream
+ std::vector<uint32_t> &buf = image.get_buffer(track, head, subtrack);
+ buf.push_back(floppy_image::MG_F | 0);
+ uint32_t cpos = 0;
+ for(uint32_t i=0; i != track_size; i++) {
+ uint8_t step = img[boff+i];
+ cpos += step;
+ if(step != 0xff && i != track_size-1)
+ buf.push_back(floppy_image::MG_F | uint64_t(cpos)*200000000/total_ticks);
+ }
+}
+
+
+as_format::tdata as_format::analyze_for_save(const floppy_image &image, int head, int track, int subtrack, int speed_zone)
+{
+ // 200000000 / 60.0 * 1.979e-6 ~= 6.5967
+ static const int cell_size_per_speed_zone[7] = {
+ 394 * 65967 / 10000,
+ 429 * 65967 / 10000,
+ 472 * 65967 / 10000,
+ 525 * 65967 / 10000,
+ 590 * 65967 / 10000,
+
+ 3915,
+ 1000
+ };
+
+ static const int ticks_per_speed_zone[7] = {
+ 60*8000000 / 394,
+ 60*8000000 / 429,
+ 60*8000000 / 472,
+ 60*8000000 / 525,
+ 60*8000000 / 590,
+
+ 1333333,
+ 1600000
+ };
+
+ tdata result;
+
+ if(!image.track_is_formatted(track, head, subtrack))
+ return result;
+
+ // Generate a bitstream to get the data and whether the phase is clean
+ int cell_size = cell_size_per_speed_zone[speed_zone];
+ int max_delta;
+ std::vector<bool> bitstream = generate_bitstream_from_track(track, head, cell_size, image, subtrack, &max_delta);
+
+ // Bitstreams encodable as non-flux have a max_delta as 10% or less, otherwise it's 40% or more. Use 20% as the limit
+ if(max_delta <= cell_size/5) {
+ result.track_size = bitstream.size();
+ result.data.resize((bitstream.size()+7)/8, 0);
+ for(unsigned j=0; j != bitstream.size(); j++)
+ if(bitstream[j])
+ result.data[j >> 3] |= 0x80 >> (j & 7);
+ return result;
+ }
+
+ result.flux = true;
+
+ const std::vector<uint32_t> &tbuf = image.get_buffer(track, head, subtrack);
+ uint32_t first_edge = 0, last_edge = 0;
+ for(uint32_t fp : tbuf)
+ if((fp & floppy_image::MG_MASK) == floppy_image::MG_F) {
+ first_edge = fp & floppy_image::TIME_MASK;
+ break;
+ }
+ for(auto i = tbuf.rbegin(); i != tbuf.rend(); ++i)
+ if((*i & floppy_image::MG_MASK) == floppy_image::MG_F) {
+ last_edge = *i & floppy_image::TIME_MASK;
+ break;
+ }
+
+ int dt = last_edge - 200000000;
+ if((-dt) < first_edge)
+ dt = first_edge;
+
+ if(dt < -10000 || dt > 10000)
+ dt = 0;
+
+ uint32_t cur_tick = 0;
+ uint64_t ticks = ticks_per_speed_zone[speed_zone];
+ for(uint32_t fp : tbuf)
+ if((fp & floppy_image::MG_MASK) == floppy_image::MG_F) {
+ uint32_t next_tick = ((fp & floppy_image::TIME_MASK) - dt) * ticks / 200000000;
+ uint32_t cdt = next_tick - cur_tick;
+ if(cdt) {
+ while(cdt >= 255) {
+ result.data.push_back(255);
+ cdt -= 255;
+ }
+ result.data.push_back(cdt);
+ }
+ cur_tick = next_tick;
+ }
+
+ uint32_t cdt = ticks - cur_tick;
+ if(cdt) {
+ while(cdt >= 255) {
+ result.data.push_back(255);
+ cdt -= 255;
+ }
+ result.data.push_back(cdt);
+ }
+
+ result.track_size = result.data.size();
+ return result;
+}
+
+std::pair<int, int> as_format::count_blocks(const std::vector<tdata> &tracks)
+{
+ int max_blocks = 0;
+ int total_blocks = 0;
+ for(const auto &t : tracks) {
+ int blocks = (t.data.size() + 511) / 512;
+ total_blocks += blocks;
+ if(max_blocks < blocks)
+ max_blocks = blocks;
+ }
+ return std::make_pair(total_blocks, max_blocks);
+}
+
+bool as_format::test_flux(const std::vector<tdata> &tracks)
+{
+ for(const auto &t : tracks)
+ if(t.flux)
+ return true;
+ return false;
+}
+
+void as_format::save_tracks(std::vector<uint8_t> &data, const std::vector<tdata> &tracks, uint32_t total_blocks, bool has_flux)
+{
+ put_u32le(&data[80], 0x50414d54); // TMAP
+ put_u32le(&data[84], 160); // size
+
+ uint32_t fstart = 1536 + total_blocks*512;
+ if(has_flux) {
+ put_u32le(&data[fstart], 0x58554c46);
+ put_u32le(&data[fstart+4], 160);
+ fstart += 8;
+ }
+
+ memset(data.data()+88, 0xff, 160);
+ if(has_flux)
+ memset(data.data()+fstart, 0xff, 160);
+
+ uint8_t tcount = 0;
+ for(int i=0; i != 160 ; i++) {
+ if(!tracks[i].data.empty()) {
+ if(!tracks[i].flux)
+ data[88+i] = tcount;
+ else
+ data[fstart+i] = tcount;
+ tcount++;
+ }
+ }
+
+ put_u32le(&data[248], 0x534b5254); // TRKS
+ put_u32le(&data[252], 1280 + total_blocks*512); // size
+
+ uint8_t tid = 0;
+ uint16_t tb = 3;
+ for(int i=0; i != 160 ; i++)
+ if(!tracks[i].data.empty()) {
+ int size = tracks[i].data.size();
+ int blocks = (size + 511) / 512;
+ memcpy(data.data() + tb*512, tracks[i].data.data(), size);
+ put_u16le(&data[256 + tid*8], tb);
+ put_u16le(&data[256 + tid*8 + 2], blocks);
+ put_u32le(&data[256 + tid*8 + 4], tracks[i].track_size);
+ tb += blocks;
+ tid ++;
+ }
+
+ put_u32le(&data[8], crc32r(&data[12], data.size() - 12));
+}
+
+
+
+woz_format::woz_format() : as_format()
+{
+}
+
+const char *woz_format::name() const noexcept
+{
+ return "woz";
+}
+
+const char *woz_format::description() const noexcept
+{
+ return "Apple II WOZ Image";
+}
+
+const char *woz_format::extensions() const noexcept
+{
+ return "woz";
+}
+
+bool woz_format::supports_save() const noexcept
+{
+ return true;
+}
+
+const uint8_t woz_format::signature[8] = { 0x57, 0x4f, 0x5a, 0x31, 0xff, 0x0a, 0x0d, 0x0a };
+const uint8_t woz_format::signature2[8] = { 0x57, 0x4f, 0x5a, 0x32, 0xff, 0x0a, 0x0d, 0x0a };
+
+int woz_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint8_t header[8];
+ auto const [err, actual] = read_at(io, 0, header, 8);
+ if(err || (8 != actual)) return 0;
+ if(!memcmp(header, signature, 8)) return FIFID_SIGN;
+ if(!memcmp(header, signature2, 8)) return FIFID_SIGN;
+ return 0;
+}
+
+bool woz_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ uint64_t image_size;
+ if(io.length(image_size))
+ return false;
+ auto const [err, img, actual] = read_at(io, 0, image_size);
+ if(err || (actual != image_size))
+ return false;
+
+ // Check signature
+ if((memcmp(&img[0], signature, 8)) && (memcmp(&img[0], signature2, 8)))
+ return false;
+
+ uint32_t woz_vers = 1;
+ if(!memcmp(&img[0], signature2, 8)) woz_vers = 2;
+
+ // Check integrity
+ uint32_t crc = crc32r(&img[12], image_size - 12);
+ if(crc != get_u32le(&img[8]))
+ return false;
+
+ uint32_t off_info = find_tag(img, image_size, 0x4f464e49);
+ uint32_t off_tmap = find_tag(img, image_size, 0x50414d54);
+ uint32_t off_trks = find_tag(img, image_size, 0x534b5254);
+// uint32_t off_writ = find_tag(img, image_size, 0x54495257);
+
+ if(!off_info || !off_tmap || !off_trks)
+ return false;
+
+ uint32_t info_vers = img[off_info + 0];
+ if(info_vers < 1 || info_vers > 3)
+ return false;
+
+ uint16_t off_flux = (info_vers < 3) ? 0 : get_u16le(&img[off_info + 46]);
+ uint16_t flux_size = (info_vers < 3) ? 0 : get_u16le(&img[off_info + 48]);
+
+ if(!flux_size)
+ off_flux = 0;
+
+ bool is_35 = img[off_info + 1] == 2;
+
+ if((form_factor == floppy_image::FF_35 && !is_35) || (form_factor == floppy_image::FF_525 && is_35))
+ return false;
+
+ unsigned int limit = is_35 ? 160 : 141;
+
+ if(is_35)
+ image.set_form_variant(floppy_image::FF_35, floppy_image::SSDD);
+ else
+ image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD);
+
+ if(woz_vers == 1) {
+ for (unsigned int trkid = 0; trkid != limit; trkid++) {
+ int head = is_35 && trkid >= 80 ? 1 : 0;
+ int track = is_35 ? trkid % 80 : trkid / 4;
+ int subtrack = is_35 ? 0 : trkid & 3;
+
+ uint8_t idx = img[off_tmap + trkid];
+ if(idx != 0xff) {
+ uint32_t boff = off_trks + 6656*idx;
+ if (get_u16le(&img[boff + 6648]) == 0)
+ return false;
+ generate_track_from_bitstream(track, head, &img[boff], get_u16le(&img[boff + 6648]), image, subtrack, get_u16le(&img[boff + 6650]));
+ if(is_35 && !track && head)
+ image.set_variant(floppy_image::DSDD);
+ }
+ }
+ } else if(woz_vers == 2) {
+ for (unsigned int trkid = 0; trkid != limit; trkid++) {
+ int head = is_35 && trkid & 1 ? 1 : 0;
+ int track = is_35 ? trkid >> 1 : trkid / 4;
+ int subtrack = is_35 ? 0 : trkid & 3;
+
+ uint8_t idx = img[off_tmap + trkid];
+ uint8_t fidx = off_flux ? img[off_flux*512 + 8 + trkid] : 0xff;
+
+ if(fidx != 0xff) {
+ load_flux_track(&img[0], image, head, track, subtrack, fidx, off_trks);
+
+ } else if(idx != 0xff) {
+ if(!load_bitstream_track(&img[0], image, head, track, subtrack, idx, off_trks, !is_35, is_35 && !track && head))
+ return false;
+ }
+ }
+ }
+ else return false;
+
+ return true;
+}
+
+
+bool woz_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ std::vector<tdata> tracks(160);
+ bool twosided = false;
+
+ if(image.get_form_factor() == floppy_image::FF_525) {
+ for(unsigned int i=0; i != 141; i++)
+ tracks[i] = analyze_for_save(image, 0, i >> 2, i & 3, 5);
+
+ } else if(image.get_variant() == floppy_image::DSHD) {
+ for(unsigned int i=0; i != 160; i++) {
+ tracks[i] = analyze_for_save(image, i & 1, i >> 1, 0, 6);
+ if((i & 1) && tracks[i].track_size)
+ twosided = true;
+ }
+
+ } else {
+ for(unsigned int i=0; i != 160; i++) {
+ tracks[i] = analyze_for_save(image, i & 1, i >> 1, 0, i / (2*16));
+ if((i & 1) && tracks[i].track_size)
+ twosided = true;
+ }
+ }
+
+ auto [total_blocks, max_blocks] = count_blocks(tracks);
+ bool has_flux = test_flux(tracks);
+
+ std::vector<uint8_t> data(1536 + total_blocks*512 + (has_flux ? 512 : 0), 0);
+
+ memcpy(&data[0], signature2, 8);
+
+ put_u32le(&data[12], 0x4f464e49); // INFO
+ put_u32le(&data[16], 60); // size
+ data[20] = 3; // chunk version
+ data[21] = image.get_form_factor() == floppy_image::FF_525 ? 1 : 2;
+ data[22] = 0; // not write protected
+ data[23] = 1; // synchronized, since our internal format is
+ data[24] = 1; // weak bits are generated, not stored
+ data[25] = 'M';
+ data[26] = 'A';
+ data[27] = 'M';
+ data[28] = 'E';
+ memset(&data[29], ' ', 32-4);
+ data[57] = twosided ? 2 : 1;
+ data[58] = 0; // boot sector unknown
+ data[59] = image.get_form_factor() == floppy_image::FF_525 ? 32 : image.get_variant() == floppy_image::DSHD ? 8 : 16;
+ put_u16le(&data[60], 0); // compatibility unknown
+ put_u16le(&data[62], 0); // needed RAM unknown
+ put_u16le(&data[64], max_blocks);
+ put_u16le(&data[66], has_flux ? total_blocks+3 : 0);
+ put_u16le(&data[68], max_blocks);
+
+ save_tracks(data, tracks, total_blocks, has_flux);
+
+ /*auto const [err, actual] =*/ write_at(io, 0, data.data(), data.size()); // FIXME: check for errors
+ return true;
+}
+
+const woz_format FLOPPY_WOZ_FORMAT;
+
+
+moof_format::moof_format() : as_format()
+{
+}
+
+const char *moof_format::name() const noexcept
+{
+ return "moof";
+}
+
+const char *moof_format::description() const noexcept
+{
+ return "Macintosh MOOF Image";
+}
+
+const char *moof_format::extensions() const noexcept
+{
+ return "moof";
+}
+
+bool moof_format::supports_save() const noexcept
+{
+ return true;
+}
+
+const uint8_t moof_format::signature[8] = { 0x4d, 0x4f, 0x4f, 0x46, 0xff, 0x0a, 0x0d, 0x0a };
+
+int moof_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint8_t header[8];
+ auto const [err, actual] = read_at(io, 0, header, 8);
+ if(err || (8 != actual)) return 0;
+ if(!memcmp(header, signature, 8)) return FIFID_SIGN;
+ return 0;
+}
+
+bool moof_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ uint64_t image_size;
+ if(io.length(image_size))
+ return false;
+ auto const [err, img, actual] = read_at(io, 0, image_size);
+ if(err || (actual != image_size))
+ return false;
+
+ // Check signature
+ if(memcmp(&img[0], signature, 8))
+ return false;
+
+ // Check integrity
+ uint32_t crc = crc32r(&img[12], image_size - 12);
+ if(crc != get_u32le(&img[8]))
+ return false;
+
+ uint32_t off_info = find_tag(img, image_size, 0x4f464e49);
+ uint32_t off_tmap = find_tag(img, image_size, 0x50414d54);
+ uint32_t off_trks = find_tag(img, image_size, 0x534b5254);
+
+ if(!off_info || !off_tmap || !off_trks)
+ return false;
+
+ uint32_t info_vers = img[off_info + 0];
+ if(info_vers != 1)
+ return false;
+
+ uint16_t off_flux = get_u16le(&img[off_info + 40]);
+ uint16_t flux_size = get_u16le(&img[off_info + 42]);
+
+ if(!flux_size)
+ off_flux = 0;
+
+ switch(img[off_info + 1]) {
+ case 1:
+ image.set_form_variant(floppy_image::FF_35, floppy_image::SSDD);
+ break;
+ case 2:
+ image.set_form_variant(floppy_image::FF_35, floppy_image::DSDD);
+ break;
+ case 3:
+ image.set_form_variant(floppy_image::FF_35, floppy_image::DSHD);
+ break;
+ default:
+ return false;
+ }
+
+ for (unsigned int trkid = 0; trkid != 160; trkid++) {
+ int head = trkid & 1;
+ int track = trkid >> 1;
+
+ uint8_t idx = img[off_tmap + trkid];
+ uint8_t fidx = off_flux ? img[off_flux*512 + 8 + trkid] : 0xff;
+
+ if(fidx != 0xff) {
+ load_flux_track(&img[0], image, head, track, 0, fidx, off_trks);
+
+ } else if(idx != 0xff) {
+ if(!load_bitstream_track(&img[0], image, head, track, 0, idx, off_trks, false, false))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool moof_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ std::vector<tdata> tracks(160);
+ bool twosided = false;
+ bool is_hd = false;
+
+ if(image.get_variant() == floppy_image::DSHD) {
+ twosided = true;
+ is_hd = true;
+ for(unsigned int i=0; i != 160; i++)
+ tracks[i] = analyze_for_save(image, i & 1, i >> 1, 0, 6);
+
+ } else {
+ for(unsigned int i=0; i != 160; i++) {
+ tracks[i] = analyze_for_save(image, i & 1, i >> 1, 0, i / (2*16));
+ if((i & 1) && tracks[i].track_size)
+ twosided = true;
+ }
+ }
+
+ auto [total_blocks, max_blocks] = count_blocks(tracks);
+ bool has_flux = test_flux(tracks);
+
+ std::vector<uint8_t> data(1536 + total_blocks*512 + (has_flux ? 512 : 0), 0);
+
+ memcpy(&data[0], signature, 8);
+
+ put_u32le(&data[12], 0x4f464e49); // INFO
+ put_u32le(&data[16], 60); // size
+ data[20] = 1; // chunk version
+ data[21] = is_hd ? 3 : twosided ? 2 : 1; // variant
+ data[22] = 0; // not write protected
+ data[23] = 1; // synchronized, since our internal format is
+ data[24] = is_hd ? 8 : 16; // optimal timing
+ data[25] = 'M';
+ data[26] = 'A';
+ data[27] = 'M';
+ data[28] = 'E';
+ memset(&data[29], ' ', 32-4);
+ data[57] = 0; // pad
+ put_u16le(&data[58], max_blocks);
+ put_u16le(&data[60], has_flux ? total_blocks+3 : 0);
+ put_u16le(&data[62], max_blocks);
+
+ save_tracks(data, tracks, total_blocks, has_flux);
+
+ /*auto const [err, actual] =*/ write_at(io, 0, data.data(), data.size()); // FIXME: check for errors
+ return true;
+}
+
+const moof_format FLOPPY_MOOF_FORMAT;
diff --git a/src/lib/formats/as_dsk.h b/src/lib/formats/as_dsk.h
new file mode 100644
index 00000000000..8871bc1ed11
--- /dev/null
+++ b/src/lib/formats/as_dsk.h
@@ -0,0 +1,76 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Applesauce solved output formats
+
+#ifndef MAME_FORMATS_AS_DSK_H
+#define MAME_FORMATS_AS_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+#include <utility>
+
+class as_format : public floppy_image_format_t
+{
+public:
+ as_format();
+
+protected:
+ struct tdata {
+ std::vector<uint8_t> data;
+ int track_size = 0;
+ bool flux = false;
+ };
+
+ static bool load_bitstream_track(const uint8_t *img, floppy_image &image, int head, int track, int subtrack, uint8_t idx, uint32_t off_trks, bool may_be_short, bool set_variant);
+ static void load_flux_track(const uint8_t *img, floppy_image &image, int head, int track, int subtrack, uint8_t fidx, uint32_t off_trks);
+
+ static tdata analyze_for_save(const floppy_image &image, int head, int track, int subtrack, int speed_zone);
+ static std::pair<int, int> count_blocks(const std::vector<tdata> &tracks);
+ static bool test_flux(const std::vector<tdata> &tracks);
+ static void save_tracks(std::vector<uint8_t> &data, const std::vector<tdata> &tracks, uint32_t total_blocks, bool has_flux);
+};
+
+class woz_format : public as_format
+{
+public:
+ woz_format();
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const uint8_t signature[8];
+ static const uint8_t signature2[8];
+};
+
+extern const woz_format FLOPPY_WOZ_FORMAT;
+
+class moof_format : public as_format
+{
+public:
+ moof_format();
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const uint8_t signature[8];
+};
+
+extern const moof_format FLOPPY_MOOF_FORMAT;
+
+#endif
diff --git a/src/lib/formats/asst128_dsk.cpp b/src/lib/formats/asst128_dsk.cpp
index fe64cc725ee..322475625f1 100644
--- a/src/lib/formats/asst128_dsk.cpp
+++ b/src/lib/formats/asst128_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/asst128_dsk.c
+ formats/asst128_dsk.cpp
asst128 format
*********************************************************************/
-#include <assert.h>
-
#include "formats/asst128_dsk.h"
asst128_format::asst128_format() : upd765_format(formats)
{
}
-const char *asst128_format::name() const
+const char *asst128_format::name() const noexcept
{
return "asst128";
}
-const char *asst128_format::description() const
+const char *asst128_format::description() const noexcept
{
return "ASST128 disk image";
}
-const char *asst128_format::extensions() const
+const char *asst128_format::extensions() const noexcept
{
return "img";
}
@@ -40,4 +38,4 @@ const asst128_format::format asst128_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_ASST128_FORMAT = &floppy_image_format_creator<asst128_format>;
+const asst128_format FLOPPY_ASST128_FORMAT;
diff --git a/src/lib/formats/asst128_dsk.h b/src/lib/formats/asst128_dsk.h
index 532bdb97c4b..77712df9cfa 100644
--- a/src/lib/formats/asst128_dsk.h
+++ b/src/lib/formats/asst128_dsk.h
@@ -18,14 +18,14 @@ class asst128_format : public upd765_format {
public:
asst128_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_ASST128_FORMAT;
+extern const asst128_format FLOPPY_ASST128_FORMAT;
#endif // MAME_FORMATS_ASST128_DSK_H
diff --git a/src/lib/formats/atari_dsk.cpp b/src/lib/formats/atari_dsk.cpp
index d73cbf8185f..317447986cb 100644
--- a/src/lib/formats/atari_dsk.cpp
+++ b/src/lib/formats/atari_dsk.cpp
@@ -8,8 +8,6 @@
*********************************************************************/
-#include <assert.h>
-
#include "formats/atari_dsk.h"
static FLOPPY_IDENTIFY( atari_dsk_identify )
diff --git a/src/lib/formats/atari_dsk.h b/src/lib/formats/atari_dsk.h
index 7f9a5bd194e..816ef6ffa75 100644
--- a/src/lib/formats/atari_dsk.h
+++ b/src/lib/formats/atari_dsk.h
@@ -12,7 +12,7 @@
#pragma once
-#include "flopimg.h"
+#include "flopimg_legacy.h"
LEGACY_FLOPPY_OPTIONS_EXTERN(atari_only);
diff --git a/src/lib/formats/atom_dsk.cpp b/src/lib/formats/atom_dsk.cpp
index 36e4eeab69a..9b133996013 100644
--- a/src/lib/formats/atom_dsk.cpp
+++ b/src/lib/formats/atom_dsk.cpp
@@ -6,17 +6,17 @@ atom_format::atom_format() : wd177x_format(formats)
{
}
-const char *atom_format::name() const
+const char *atom_format::name() const noexcept
{
return "atom";
}
-const char *atom_format::description() const
+const char *atom_format::description() const noexcept
{
return "Acorn Atom disk image";
}
-const char *atom_format::extensions() const
+const char *atom_format::extensions() const noexcept
{
return "40t,dsk";
}
@@ -30,4 +30,4 @@ const atom_format::format atom_format::formats[] =
{}
};
-const floppy_format_type FLOPPY_ATOM_FORMAT = &floppy_image_format_creator<atom_format>;
+const atom_format FLOPPY_ATOM_FORMAT;
diff --git a/src/lib/formats/atom_dsk.h b/src/lib/formats/atom_dsk.h
index ef9cfa28d85..c9db6aa1434 100644
--- a/src/lib/formats/atom_dsk.h
+++ b/src/lib/formats/atom_dsk.h
@@ -13,14 +13,14 @@ class atom_format : public wd177x_format
public:
atom_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_ATOM_FORMAT;
+extern const atom_format FLOPPY_ATOM_FORMAT;
#endif // MAME_FORMATS_ATOM_DSK_H
diff --git a/src/lib/formats/atom_tap.cpp b/src/lib/formats/atom_tap.cpp
index 881433ef1a8..db29798d51d 100644
--- a/src/lib/formats/atom_tap.cpp
+++ b/src/lib/formats/atom_tap.cpp
@@ -10,7 +10,7 @@
/*
- http://beebwiki.jonripley.com/Acorn_cassette_format#Atom
+ http://beebwiki.mdfs.net/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.
@@ -21,7 +21,7 @@
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.
+ 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.
@@ -40,11 +40,10 @@
*/
-#include <assert.h>
-
#include "atom_tap.h"
#include "csw_cas.h"
#include "uef_cas.h"
+
/***************************************************************************
PARAMETERS
***************************************************************************/
@@ -59,31 +58,20 @@
CassetteModulation atom_tap_modulation
-------------------------------------------------*/
-static const struct CassetteModulation atom_tap_modulation =
+static const cassette_image::Modulation atom_tap_modulation =
{
- CASSETTE_MODULATION_SINEWAVE,
+ cassette_image::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_t cassette_image_read_uint8(cassette_image *cassette, uint64_t offset)
-{
- uint8_t data;
- cassette_image_read(cassette, &data, offset, 1);
- return data;
-}
-
-/*-------------------------------------------------
atom_tap_identify - identify cassette
-------------------------------------------------*/
-static cassette_image::error atom_tap_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error atom_tap_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_modulation_identify( cassette, &atom_tap_modulation, opts);
+ return cassette->modulation_identify(atom_tap_modulation, opts);
}
/*-------------------------------------------------
@@ -92,7 +80,7 @@ static cassette_image::error atom_tap_identify(cassette_image *cassette, struct
#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);\
+ err = cassette->put_modulated_data_bit(0, time_index, _value, atom_tap_modulation, &time_displacement);\
if (err != cassette_image::error::SUCCESS) return err;\
time_index += time_displacement;\
}
@@ -102,14 +90,13 @@ static cassette_image::error atom_tap_identify(cassette_image *cassette, struct
static cassette_image::error atom_tap_load(cassette_image *cassette)
{
cassette_image::error err;
- uint64_t image_size = cassette_image_size(cassette);
- uint64_t image_pos = 0;
+ uint64_t image_size = cassette->image_size();
double time_index = 0.0;
double time_displacement;
- while (image_pos < image_size)
+ for (uint64_t image_pos = 0; image_pos < image_size; image_pos++)
{
- uint8_t data = cassette_image_read_uint8(cassette, image_pos);
+ uint8_t data = cassette->image_read_byte(image_pos);
/* start bit */
MODULATE(0);
@@ -123,8 +110,6 @@ static cassette_image::error atom_tap_load(cassette_image *cassette)
/* stop bits */
MODULATE(1);
MODULATE(1);
-
- image_pos++;
}
return cassette_image::error::SUCCESS;
@@ -134,7 +119,7 @@ static cassette_image::error atom_tap_load(cassette_image *cassette)
CassetteFormat atom_tap_cassette_format
-------------------------------------------------*/
-const struct CassetteFormat atom_tap_format =
+const cassette_image::Format atom_tap_format =
{
"tap",
atom_tap_identify,
diff --git a/src/lib/formats/basicdsk.cpp b/src/lib/formats/basicdsk.cpp
index 9de079232e0..1984f0bd7ca 100644
--- a/src/lib/formats/basicdsk.cpp
+++ b/src/lib/formats/basicdsk.cpp
@@ -8,12 +8,14 @@
*********************************************************************/
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
#include "basicdsk.h"
+#include "opresolv.h"
+
+#include <cstdlib>
+#include <cstring>
+#include <cassert>
+
static floperr_t basicdsk_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen);
static floperr_t basicdsk_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
static floperr_t basicdsk_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen);
@@ -288,7 +290,7 @@ static floperr_t basicdsk_get_indexed_sector_info(floppy_image_legacy *floppy, i
static void basicdsk_default_geometry(const struct FloppyFormat *format, struct basicdsk_geometry *geometry)
{
- int sector_length;
+ int sector_length = 0;
memset(geometry, 0, sizeof(*geometry));
auto err = util::option_resolution::get_default(format->param_guidelines, PARAM_HEADS, &geometry->heads);
diff --git a/src/lib/formats/basicdsk.h b/src/lib/formats/basicdsk.h
index dabe9afc278..8adfd9ed985 100644
--- a/src/lib/formats/basicdsk.h
+++ b/src/lib/formats/basicdsk.h
@@ -12,7 +12,7 @@
#pragma once
-#include "flopimg.h"
+#include "flopimg_legacy.h"
struct basicdsk_geometry
{
diff --git a/src/lib/formats/bk0010_dsk.cpp b/src/lib/formats/bk0010_dsk.cpp
new file mode 100644
index 00000000000..8922b144fcd
--- /dev/null
+++ b/src/lib/formats/bk0010_dsk.cpp
@@ -0,0 +1,39 @@
+// license:BSD-3-Clause
+// copyright-holders:Sergey Svishchev
+/*********************************************************************
+
+ formats/bk0010_dsk.cpp
+
+ Floppies used by BK (BY: device), DVK (MY: device) and UKNC
+ (MZ: device)
+
+*********************************************************************/
+
+#include "formats/bk0010_dsk.h"
+
+bk0010_format::bk0010_format() : wd177x_format(formats)
+{
+}
+
+// gap sizes taken from BKBTL emulator
+const bk0010_format::format bk0010_format::formats[] = {
+ {
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, // 2us, 300rpm
+ 10, 80, 1,
+ 512, {},
+ 1, {},
+ 42, 22, 36
+ },
+ {
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, // 2us, 300rpm
+ 10, 80, 2,
+ 512, {},
+ 1, {},
+ 42, 22, 36
+ },
+ {}
+};
+
+const bk0010_format FLOPPY_BK0010_FORMAT;
diff --git a/src/lib/formats/bk0010_dsk.h b/src/lib/formats/bk0010_dsk.h
new file mode 100644
index 00000000000..96b00ebede7
--- /dev/null
+++ b/src/lib/formats/bk0010_dsk.h
@@ -0,0 +1,30 @@
+// license:BSD-3-Clause
+// copyright-holders:Sergey Svishchev
+/*********************************************************************
+
+ formats/bk0010_dsk.h
+
+*********************************************************************/
+#ifndef MAME_FORMATS_BK0010_DSK_H
+#define MAME_FORMATS_BK0010_DSK_H
+
+#pragma once
+
+#include "wd177x_dsk.h"
+
+class bk0010_format : public wd177x_format
+{
+public:
+ bk0010_format();
+
+ virtual const char *name() const noexcept override { return "bk0010"; }
+ virtual const char *description() const noexcept override { return "BK-0010 disk image"; }
+ virtual const char *extensions() const noexcept override { return "img,bkd"; }
+
+private:
+ static const format formats[];
+};
+
+extern const bk0010_format FLOPPY_BK0010_FORMAT;
+
+#endif // MAME_FORMATS_BK0010_DSK_H
diff --git a/src/lib/formats/bw12_dsk.cpp b/src/lib/formats/bw12_dsk.cpp
index 06c8be72347..9dcd47f1a40 100644
--- a/src/lib/formats/bw12_dsk.cpp
+++ b/src/lib/formats/bw12_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/bw12_dsk.c
+ formats/bw12_dsk.cpp
bw12 format
*********************************************************************/
-#include <assert.h>
-
#include "formats/bw12_dsk.h"
bw12_format::bw12_format() : upd765_format(formats)
{
}
-const char *bw12_format::name() const
+const char *bw12_format::name() const noexcept
{
return "bw12";
}
-const char *bw12_format::description() const
+const char *bw12_format::description() const noexcept
{
return "Bondwell 12/14 disk image";
}
-const char *bw12_format::extensions() const
+const char *bw12_format::extensions() const noexcept
{
return "dsk";
}
@@ -57,4 +55,4 @@ const bw12_format::format bw12_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_BW12_FORMAT = &floppy_image_format_creator<bw12_format>;
+const bw12_format FLOPPY_BW12_FORMAT;
diff --git a/src/lib/formats/bw12_dsk.h b/src/lib/formats/bw12_dsk.h
index ac2e370aeb3..8d88ec91441 100644
--- a/src/lib/formats/bw12_dsk.h
+++ b/src/lib/formats/bw12_dsk.h
@@ -19,14 +19,14 @@ class bw12_format : public upd765_format
public:
bw12_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_BW12_FORMAT;
+extern const bw12_format FLOPPY_BW12_FORMAT;
#endif // MAME_FORMATS_BW12_DSK_H
diff --git a/src/lib/formats/bw2_dsk.cpp b/src/lib/formats/bw2_dsk.cpp
index 3dc46e9b225..de3e5763801 100644
--- a/src/lib/formats/bw2_dsk.cpp
+++ b/src/lib/formats/bw2_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/bw2_dsk.c
+ formats/bw2_dsk.cpp
bw2 format
*********************************************************************/
-#include <assert.h>
-
#include "formats/bw2_dsk.h"
bw2_format::bw2_format() : upd765_format(formats)
{
}
-const char *bw2_format::name() const
+const char *bw2_format::name() const noexcept
{
return "bw2";
}
-const char *bw2_format::description() const
+const char *bw2_format::description() const noexcept
{
return "Bondwell 2 disk image";
}
-const char *bw2_format::extensions() const
+const char *bw2_format::extensions() const noexcept
{
return "dsk";
}
@@ -43,4 +41,4 @@ const bw2_format::format bw2_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_BW2_FORMAT = &floppy_image_format_creator<bw2_format>;
+const bw2_format FLOPPY_BW2_FORMAT;
diff --git a/src/lib/formats/bw2_dsk.h b/src/lib/formats/bw2_dsk.h
index 8a84ef0dd27..3e49f718156 100644
--- a/src/lib/formats/bw2_dsk.h
+++ b/src/lib/formats/bw2_dsk.h
@@ -19,14 +19,14 @@ class bw2_format : public upd765_format
public:
bw2_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_BW2_FORMAT;
+extern const bw2_format FLOPPY_BW2_FORMAT;
#endif // MAME_FORMATS_BW2_DSK_H
diff --git a/src/lib/formats/c3040_dsk.cpp b/src/lib/formats/c3040_dsk.cpp
index 78786979953..d4c9a9b172c 100644
--- a/src/lib/formats/c3040_dsk.cpp
+++ b/src/lib/formats/c3040_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/c3040_dsk.c
+ formats/c3040_dsk.cpp
Commodore 3040 sector disk image format
*********************************************************************/
-#include <assert.h>
-
#include "formats/c3040_dsk.h"
c3040_format::c3040_format() : d64_format(file_formats)
{
}
-const char *c3040_format::name() const
+const char *c3040_format::name() const noexcept
{
return "c3040";
}
-const char *c3040_format::description() const
+const char *c3040_format::description() const noexcept
{
return "Commodore 3040 disk image";
}
-const char *c3040_format::extensions() const
+const char *c3040_format::extensions() const noexcept
{
return "d67";
}
@@ -56,7 +54,7 @@ const int c3040_format::c3040_gap2[] =
12, 12, 12, 12, 12 // 31-35
};
-floppy_image_format_t::desc_e* c3040_format::get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2)
+floppy_image_format_t::desc_e* c3040_format::get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2) const
{
static floppy_image_format_t::desc_e desc[] = {
/* 00 */ { SECTOR_LOOP_START, 0, -1 },
@@ -91,11 +89,11 @@ floppy_image_format_t::desc_e* c3040_format::get_sector_desc(const format &f, in
return desc;
}
-void c3040_format::fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size)
+void c3040_format::fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size) const
{
desc[19].p2 = remaining_size / 10;
desc[20].p2 = remaining_size % 10;
desc[20].p1 >>= remaining_size & 0x01;
}
-const floppy_format_type FLOPPY_C3040_FORMAT = &floppy_image_format_creator<c3040_format>;
+const c3040_format FLOPPY_C3040_FORMAT;
diff --git a/src/lib/formats/c3040_dsk.h b/src/lib/formats/c3040_dsk.h
index c5897a52f2b..cacceb62230 100644
--- a/src/lib/formats/c3040_dsk.h
+++ b/src/lib/formats/c3040_dsk.h
@@ -18,15 +18,15 @@ class c3040_format : public d64_format {
public:
c3040_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
protected:
- virtual int get_sectors_per_track(const format &f, int track) override { return c3040_sectors_per_track[track]; }
- virtual floppy_image_format_t::desc_e* get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2) override;
- virtual int get_gap2(const format &f, int head, int track) override { return c3040_gap2[track]; }
- virtual void fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size) override;
+ virtual int get_sectors_per_track(const format &f, int track) const override { return c3040_sectors_per_track[track]; }
+ virtual floppy_image_format_t::desc_e* get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2) const override;
+ virtual int get_gap2(const format &f, int head, int track) const override { return c3040_gap2[track]; }
+ virtual void fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size) const override;
static const format file_formats[];
@@ -34,6 +34,6 @@ protected:
static const int c3040_sectors_per_track[];
};
-extern const floppy_format_type FLOPPY_C3040_FORMAT;
+extern const c3040_format FLOPPY_C3040_FORMAT;
#endif // MAME_FORMATS_C3040_DSK_H
diff --git a/src/lib/formats/c4040_dsk.cpp b/src/lib/formats/c4040_dsk.cpp
index e6314f69261..ac5adf7e1c9 100644
--- a/src/lib/formats/c4040_dsk.cpp
+++ b/src/lib/formats/c4040_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/c4040_dsk.c
+ formats/c4040_dsk.cpp
Commodore 4040 sector disk image format
*********************************************************************/
-#include <assert.h>
-
#include "formats/c4040_dsk.h"
c4040_format::c4040_format() : d64_format(file_formats)
{
}
-const char *c4040_format::name() const
+const char *c4040_format::name() const noexcept
{
return "c4040";
}
-const char *c4040_format::description() const
+const char *c4040_format::description() const noexcept
{
return "Commodore 4040 disk image";
}
-const char *c4040_format::extensions() const
+const char *c4040_format::extensions() const noexcept
{
return "d64";
}
@@ -46,7 +44,7 @@ const int c4040_format::c4040_gap2[] =
12, 12, 12, 12, 12 // 31-35
};
-floppy_image_format_t::desc_e* c4040_format::get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2)
+floppy_image_format_t::desc_e* c4040_format::get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2) const
{
static floppy_image_format_t::desc_e desc[] = {
/* 00 */ { SECTOR_LOOP_START, 0, -1 },
@@ -81,11 +79,11 @@ floppy_image_format_t::desc_e* c4040_format::get_sector_desc(const format &f, in
return desc;
}
-void c4040_format::fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size)
+void c4040_format::fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size) const
{
desc[19].p2 = remaining_size / 10;
desc[20].p2 = remaining_size % 10;
desc[20].p1 >>= remaining_size & 0x01;
}
-const floppy_format_type FLOPPY_C4040_FORMAT = &floppy_image_format_creator<c4040_format>;
+const c4040_format FLOPPY_C4040_FORMAT;
diff --git a/src/lib/formats/c4040_dsk.h b/src/lib/formats/c4040_dsk.h
index 9fe29b399c2..e661b0d2af4 100644
--- a/src/lib/formats/c4040_dsk.h
+++ b/src/lib/formats/c4040_dsk.h
@@ -18,20 +18,20 @@ class c4040_format : public d64_format {
public:
c4040_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
protected:
- virtual floppy_image_format_t::desc_e* get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2) override;
- virtual int get_gap2(const format &f, int head, int track) override { return c4040_gap2[track]; }
- virtual void fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size) override;
+ virtual floppy_image_format_t::desc_e* get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2) const override;
+ virtual int get_gap2(const format &f, int head, int track) const override { return c4040_gap2[track]; }
+ virtual void fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size) const override;
static const format file_formats[];
static const int c4040_gap2[];
};
-extern const floppy_format_type FLOPPY_C4040_FORMAT;
+extern const c4040_format FLOPPY_C4040_FORMAT;
#endif // MAME_FORMATS_C4040_DSK_H
diff --git a/src/lib/formats/c8280_dsk.cpp b/src/lib/formats/c8280_dsk.cpp
index cb39b5c3ea5..d403b3c7c06 100644
--- a/src/lib/formats/c8280_dsk.cpp
+++ b/src/lib/formats/c8280_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/c8280_dsk.c
+ formats/c8280_dsk.cpp
Commodore 8280 disk image format
@@ -14,17 +14,17 @@ c8280_format::c8280_format() : wd177x_format(formats)
{
}
-const char *c8280_format::name() const
+const char *c8280_format::name() const noexcept
{
return "c8280";
}
-const char *c8280_format::description() const
+const char *c8280_format::description() const noexcept
{
return "Commodore 8280 disk image";
}
-const char *c8280_format::extensions() const
+const char *c8280_format::extensions() const noexcept
{
return "dsk";
}
@@ -66,4 +66,4 @@ const c8280_format::format c8280_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_C8280_FORMAT = &floppy_image_format_creator<c8280_format>;
+const c8280_format FLOPPY_C8280_FORMAT;
diff --git a/src/lib/formats/c8280_dsk.h b/src/lib/formats/c8280_dsk.h
index c7a9fc2b44e..3afdd275db1 100644
--- a/src/lib/formats/c8280_dsk.h
+++ b/src/lib/formats/c8280_dsk.h
@@ -19,14 +19,14 @@ class c8280_format : public wd177x_format
public:
c8280_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_C8280_FORMAT;
+extern const c8280_format FLOPPY_C8280_FORMAT;
#endif // MAME_FORMATS_C8280_DSK_H
diff --git a/src/lib/formats/camplynx_cas.cpp b/src/lib/formats/camplynx_cas.cpp
index bcf6c1caa6b..545814b954f 100644
--- a/src/lib/formats/camplynx_cas.cpp
+++ b/src/lib/formats/camplynx_cas.cpp
@@ -23,10 +23,13 @@ Each byte is 8 bits (MSB first) with no start or stop bits.
********************************************************************/
-#include "emu.h" // for popmessage and <string>
-
#include "camplynx_cas.h"
+#include "multibyte.h"
+
+#include "osdcore.h" // osd_printf_*
+
+
#define WAVEENTRY_LOW -32768
#define WAVEENTRY_HIGH 32767
@@ -128,7 +131,7 @@ static int camplynx_handle_cassette(int16_t *buffer, const uint8_t *bytes)
/* tell user how to load the tape */
if (buffer)
- osd_printf_info("%s\n", pgmname.c_str());
+ osd_printf_info("%s\n", pgmname);
/* second SYNC + A5 */
for (i = 0; i < 555; i++)
@@ -143,13 +146,13 @@ static int camplynx_handle_cassette(int16_t *buffer, const uint8_t *bytes)
switch (file_type)
{
case 'A':
- data_size = 5 + ((bytes[byte_count + 4]) << 8 | bytes[byte_count + 3]) + 12;
+ data_size = 5 + get_u16le(&bytes[byte_count + 3]) + 12;
break;
case 'B':
- data_size = 3 + ((bytes[byte_count + 2]) << 8 | bytes[byte_count + 1]) + 3;
+ data_size = 3 + get_u16le(&bytes[byte_count + 1]) + 3;
break;
case 'M':
- data_size = 3 + ((bytes[byte_count + 2]) << 8 | bytes[byte_count + 1]) + 7;
+ data_size = 3 + get_u16le(&bytes[byte_count + 1]) + 7;
break;
}
@@ -176,7 +179,7 @@ static int camplynx_handle_cassette(int16_t *buffer, const uint8_t *bytes)
Generate samples for the tape image
********************************************************************/
-static int camplynx_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int camplynx_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return camplynx_handle_cassette(buffer, bytes);
}
@@ -192,7 +195,7 @@ static int camplynx_cassette_calculate_size_in_samples(const uint8_t *bytes, int
return camplynx_handle_cassette(nullptr, bytes);
}
-static const struct CassetteLegacyWaveFiller lynx48k_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller lynx48k_legacy_fill_wave =
{
camplynx_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -203,7 +206,7 @@ static const struct CassetteLegacyWaveFiller lynx48k_legacy_fill_wave =
0 /* trailer_samples */
};
-static const struct CassetteLegacyWaveFiller lynx128k_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller lynx128k_legacy_fill_wave =
{
camplynx_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -214,27 +217,27 @@ static const struct CassetteLegacyWaveFiller lynx128k_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error lynx48k_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error lynx48k_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &lynx48k_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &lynx48k_legacy_fill_wave);
}
-static cassette_image::error lynx128k_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error lynx128k_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &lynx128k_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &lynx128k_legacy_fill_wave);
}
static cassette_image::error lynx48k_cassette_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &lynx48k_legacy_fill_wave);
+ return cassette->legacy_construct(&lynx48k_legacy_fill_wave);
}
static cassette_image::error lynx128k_cassette_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &lynx128k_legacy_fill_wave);
+ return cassette->legacy_construct(&lynx128k_legacy_fill_wave);
}
-static const struct CassetteFormat lynx48k_cassette_image_format =
+static const cassette_image::Format lynx48k_cassette_image_format =
{
"tap",
lynx48k_cassette_identify,
@@ -242,7 +245,7 @@ static const struct CassetteFormat lynx48k_cassette_image_format =
nullptr
};
-static const struct CassetteFormat lynx128k_cassette_image_format =
+static const cassette_image::Format lynx128k_cassette_image_format =
{
"tap",
lynx128k_cassette_identify,
diff --git a/src/lib/formats/camplynx_dsk.cpp b/src/lib/formats/camplynx_dsk.cpp
index c58b12b9423..d2493695e3a 100644
--- a/src/lib/formats/camplynx_dsk.cpp
+++ b/src/lib/formats/camplynx_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Robbbert
/*********************************************************************
- formats/camplynx_dsk.c
+ formats/camplynx_dsk.cpp
Camputers Lynx disk image format
@@ -19,25 +19,23 @@
*********************************************************************/
-#include <assert.h>
-
#include "formats/camplynx_dsk.h"
camplynx_format::camplynx_format() : wd177x_format(formats)
{
}
-const char *camplynx_format::name() const
+const char *camplynx_format::name() const noexcept
{
return "camplynx";
}
-const char *camplynx_format::description() const
+const char *camplynx_format::description() const noexcept
{
return "Camputers Lynx disk image";
}
-const char *camplynx_format::extensions() const
+const char *camplynx_format::extensions() const noexcept
{
return "ldf";
}
@@ -54,4 +52,4 @@ const camplynx_format::format camplynx_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_CAMPLYNX_FORMAT = &floppy_image_format_creator<camplynx_format>;
+const camplynx_format FLOPPY_CAMPLYNX_FORMAT;
diff --git a/src/lib/formats/camplynx_dsk.h b/src/lib/formats/camplynx_dsk.h
index f1ec996040a..be03e292b63 100644
--- a/src/lib/formats/camplynx_dsk.h
+++ b/src/lib/formats/camplynx_dsk.h
@@ -19,14 +19,14 @@ class camplynx_format : public wd177x_format
public:
camplynx_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_CAMPLYNX_FORMAT;
+extern const camplynx_format FLOPPY_CAMPLYNX_FORMAT;
#endif // MAME_FORMATS_CAMPLYNX_DSK_H
diff --git a/src/lib/formats/cassimg.cpp b/src/lib/formats/cassimg.cpp
index 816af2dccfd..cea484f2735 100644
--- a/src/lib/formats/cassimg.cpp
+++ b/src/lib/formats/cassimg.cpp
@@ -8,12 +8,14 @@
*********************************************************************/
-#include <string.h>
-#include <assert.h>
-
-#include "imageutl.h"
#include "cassimg.h"
+#include "imageutl.h"
+
+#include "corealloc.h" // util::make_unique_clear
+
#include <algorithm>
+#include <cassert>
+#include <cstring>
/* debugging parameters */
@@ -28,21 +30,22 @@ CASSETTE_FORMATLIST_START(cassette_default_formats)
CASSETTE_FORMATLIST_END
+namespace {
/*********************************************************************
helper code
*********************************************************************/
-static double map_double(double d, uint64_t low, uint64_t high, uint64_t value)
+constexpr double map_double(double d, uint64_t low, uint64_t high, uint64_t value)
{
return d * (value - low) / (high - low);
}
-static size_t waveform_bytes_per_sample(int waveform_flags)
+constexpr size_t waveform_bytes_per_sample(int waveform_flags)
{
- return (size_t) (1 << ((waveform_flags & 0x06) / 2));
+ return size_t(1 << ((waveform_flags & 0x06) / 2));
}
@@ -51,361 +54,344 @@ static size_t waveform_bytes_per_sample(int waveform_flags)
extrapolation and interpolation
*********************************************************************/
-static int32_t extrapolate8(int8_t value)
+constexpr int32_t extrapolate8(int8_t value)
{
- return ((int32_t) value) << 24;
+ return int32_t(value) << 24;
}
-static int32_t extrapolate16(int16_t value)
+constexpr int32_t extrapolate16(int16_t value)
{
- return ((int32_t) value) << 16;
+ return int32_t(value) << 16;
}
-static int8_t interpolate8(int32_t value)
+constexpr int8_t interpolate8(int32_t value)
{
- return (int8_t) (value >> 24);
+ return int8_t(value >> 24);
}
-static int16_t interpolate16(int32_t value)
+constexpr int16_t interpolate16(int32_t value)
{
- return (int16_t) (value >> 16);
+ return int16_t(value >> 16);
}
-/*********************************************************************
- initialization and termination
-*********************************************************************/
-
-static cassette_image *cassette_init(const struct CassetteFormat *format, void *file, const struct io_procs *procs, int flags)
+constexpr size_t my_round(double d)
{
- cassette_image *cassette;
-
- cassette = global_alloc_clear<cassette_image>();
- cassette->format = format;
- cassette->io.file = file;
- cassette->io.procs = procs;
- cassette->flags = flags;
- return cassette;
+ return size_t(d + 0.5);
}
-static void cassette_finishinit(cassette_image::error err, cassette_image *cassette, cassette_image **outcassette)
+/*********************************************************************
+ waveform accesses to/from the raw image
+*********************************************************************/
+
+const int8_t *choose_wave(const cassette_image::Modulation &modulation, size_t &wave_bytes_length)
{
- if (cassette && ((err != cassette_image::error::SUCCESS) || !outcassette))
+ static const int8_t square_wave[] = { -128, 127 };
+ static const int8_t sine_wave[] = { 0, 48, 89, 117, 127, 117, 89, 48, 0, -48, -89, -117, -127, -117, -89, -48 };
+
+ if (modulation.flags & cassette_image::MODULATION_SINEWAVE)
+ {
+ wave_bytes_length = std::size(sine_wave);
+ return sine_wave;
+ }
+ else
{
- cassette_close(cassette);
- cassette = nullptr;
+ wave_bytes_length = std::size(square_wave);
+ return square_wave;
}
- if (outcassette)
- *outcassette = cassette;
}
+} // anonymous namespace
+
-static cassette_image::error try_identify_format(const struct CassetteFormat &format, cassette_image *image, const std::string &extension, int flags, struct CassetteOptions &opts)
+/*********************************************************************
+ initialization and termination
+*********************************************************************/
+
+cassette_image::error cassette_image::try_identify_format(const Format &format, const std::string &extension, int flags, Options &opts)
{
// is this the right extension?
if (!extension.empty() && !image_find_extension(format.extensions, extension.c_str()))
- return cassette_image::error::INVALID_IMAGE;
+ return error::INVALID_IMAGE;
// invoke format->identify
- memset(&opts, 0, sizeof(opts));
- cassette_image::error err = format.identify(image, &opts);
- if (err != cassette_image::error::SUCCESS)
+ opts = Options();
+ error err = format.identify(this, &opts);
+ if (err != error::SUCCESS)
return err;
// is this a read only format, but the cassette was not opened read only?
- if (((flags & CASSETTE_FLAG_READONLY) == 0) && (format.save == nullptr))
- return cassette_image::error::READ_WRITE_UNSUPPORTED;
+ if (((flags & FLAG_READONLY) == 0) && (format.save == nullptr))
+ return error::READ_WRITE_UNSUPPORTED;
// success!
- return cassette_image::error::SUCCESS;
+ return error::SUCCESS;
+}
+
+
+cassette_image::error cassette_image::perform_save()
+{
+ Info info = get_info();
+ return m_format->save(this, &info);
}
-cassette_image::error cassette_open_choices(void *file, const struct io_procs *procs, const std::string &extension,
- const struct CassetteFormat *const *formats, int flags, cassette_image **outcassette)
+/*********************************************************************
+ waveform accesses
+*********************************************************************/
+
+struct cassette_image::manipulation_ranges
{
- cassette_image::error err;
- cassette_image *cassette;
- const struct CassetteFormat *format;
- struct CassetteOptions opts = {0, };
- int i;
+ int channel_first;
+ int channel_last;
+ size_t sample_first;
+ size_t sample_last;
+};
- /* if not specified, use the dummy arguments */
- if (!formats)
- formats = cassette_default_formats;
- /* create the cassette object */
- cassette = cassette_init(nullptr, file, procs, flags);
- if (!cassette)
- {
- err = cassette_image::error::OUT_OF_MEMORY;
- goto done;
- }
- /* identify the image */
- format = nullptr;
- for (i = 0; !format && formats[i]; i++)
+cassette_image::error cassette_image::compute_manipulation_ranges(int channel,
+ double time_index, double sample_period, manipulation_ranges &ranges) const
+{
+ if (channel < 0)
{
- // try this format
- err = try_identify_format(*formats[i], cassette, extension, flags, opts);
- if (err != cassette_image::error::SUCCESS && err != cassette_image::error::INVALID_IMAGE)
- goto done;
-
- // did we succeed?
- if (err == cassette_image::error::SUCCESS)
- format = formats[i];
+ ranges.channel_first = 0;
+ ranges.channel_last = m_channels - 1;
}
-
- /* have we found a proper format */
- if (!format)
+ else
{
- err = cassette_image::error::INVALID_IMAGE;
- goto done;
+ ranges.channel_first = channel;
+ ranges.channel_last = channel;
}
- cassette->format = format;
- /* read the options */
- cassette->channels = opts.channels;
- cassette->sample_frequency = opts.sample_frequency;
+ ranges.sample_first = my_round(time_index * m_sample_frequency);
+ ranges.sample_last = my_round((time_index + sample_period) * m_sample_frequency);
- /* load the image */
- err = format->load(cassette);
- if (err != cassette_image::error::SUCCESS)
- goto done;
+ if (ranges.sample_last > ranges.sample_first)
+ ranges.sample_last--;
- /* success */
- cassette->flags &= ~CASSETTE_FLAG_DIRTY;
- err = cassette_image::error::SUCCESS;
-
-done:
- cassette_finishinit(err, cassette, outcassette);
- return err;
+ return error::SUCCESS;
}
-cassette_image::error cassette_open(void *file, const struct io_procs *procs,
- const struct CassetteFormat *format, int flags, cassette_image **outcassette)
+cassette_image::error cassette_image::lookup_sample(int channel, size_t sample, int32_t *&ptr)
{
- const struct CassetteFormat *formats[2];
- formats[0] = format;
- formats[1] = nullptr;
- return cassette_open_choices(file, procs, nullptr, formats, flags, outcassette);
-}
-
+ ptr = nullptr;
+ size_t sample_blocknum = (sample / SAMPLES_PER_BLOCK) * m_channels + channel;
+ size_t sample_index = sample % SAMPLES_PER_BLOCK;
+ // is this block beyond the edge of our waveform?
+ if (sample_blocknum >= m_blocks.size())
+ m_blocks.resize(sample_blocknum + 1);
-cassette_image::error cassette_create(void *file, const struct io_procs *procs, const struct CassetteFormat *format,
- const struct CassetteOptions *opts, int flags, cassette_image **outcassette)
-{
- cassette_image::error err;
- cassette_image *cassette;
- static const struct CassetteOptions default_options = { 1, 16, 44100 };
+ if (!m_blocks[sample_blocknum])
+ m_blocks[sample_blocknum] = util::make_unique_clear<int32_t []>(SAMPLES_PER_BLOCK);
- /* cannot create to a read only image */
- if (flags & CASSETTE_FLAG_READONLY)
- return cassette_image::error::INVALID_IMAGE;
+ ptr = &m_blocks[sample_blocknum][sample_index];
+ return error::SUCCESS;
+}
- /* is this a good format? */
- if (format->save == nullptr)
- return cassette_image::error::INVALID_IMAGE;
- /* normalize arguments */
- if (!opts)
- opts = &default_options;
- /* create the cassette object */
- cassette = cassette_init(format, file, procs, flags);
- if (!cassette)
- {
- err = cassette_image::error::OUT_OF_MEMORY;
- goto done;
- }
+cassette_image::cassette_image(const Format *format, util::random_read_write::ptr &&io, int flags) :
+ m_format(format),
+ m_io(std::move(io)),
+ m_channels(0),
+ m_flags(flags),
+ m_sample_frequency(0),
+ m_blocks(),
+ m_sample_count(0)
+{
+}
- /* read the options */
- cassette->channels = opts->channels;
- cassette->sample_frequency = opts->sample_frequency;
- err = cassette_image::error::SUCCESS;
-done:
- cassette_finishinit(err, cassette, outcassette);
- return err;
+cassette_image::~cassette_image()
+{
+ if ((m_flags & CASSETTE_FLAG_DIRTY) && (m_flags & FLAG_SAVEONEXIT))
+ save();
}
-static cassette_image::error cassette_perform_save(cassette_image *cassette)
+cassette_image::error cassette_image::open(
+ util::random_read_write::ptr &&io,
+ const Format *format,
+ int flags,
+ ptr &outcassette)
{
- struct CassetteInfo info;
- cassette_get_info(cassette, &info);
- return cassette->format->save(cassette, &info);
+ const Format *const formats[2] = { format, nullptr };
+ return open_choices(std::move(io), { }, formats, flags, outcassette);
}
-cassette_image::error cassette_save(cassette_image *cassette)
+cassette_image::error cassette_image::open_choices(
+ util::random_read_write::ptr &&io,
+ const std::string &extension,
+ const Format *const *formats,
+ int flags,
+ ptr &outcassette)
{
- cassette_image::error err;
+ // if not specified, use the dummy arguments
+ if (!formats)
+ formats = cassette_default_formats;
+
+ // create the cassette object
+ ptr cassette;
+ try { cassette.reset(new cassette_image(nullptr, std::move(io), flags)); }
+ catch (std::bad_alloc const &) { return error::OUT_OF_MEMORY; }
+
+ // identify the image
+ const Format *format = nullptr;
+ Options opts;
+ for (int i = 0; !format && formats[i]; i++)
+ {
+ // try this format
+ error err = cassette->try_identify_format(*formats[i], extension, flags, opts);
+ if (err != error::SUCCESS && err != error::INVALID_IMAGE)
+ return err;
+
+ // did we succeed?
+ if (err == error::SUCCESS)
+ format = formats[i];
+ }
+
+ // have we found a proper format
+ if (!format)
+ return error::INVALID_IMAGE;
+ cassette->m_format = format;
- if (!cassette->format || !cassette->format->save)
- return cassette_image::error::UNSUPPORTED;
+ // read the options
+ cassette->m_channels = opts.channels;
+ cassette->m_sample_frequency = opts.sample_frequency;
- err = cassette_perform_save(cassette);
- if (err != cassette_image::error::SUCCESS)
+ // load the image
+ error err = format->load(cassette.get());
+ if (err != error::SUCCESS)
return err;
- cassette->flags &= ~CASSETTE_FLAG_DIRTY;
- return cassette_image::error::SUCCESS;
+ /* success */
+ cassette->m_flags &= ~CASSETTE_FLAG_DIRTY;
+ outcassette = std::move(cassette);
+ return error::SUCCESS;
}
-void cassette_get_info(cassette_image *cassette, struct CassetteInfo *info)
+cassette_image::error cassette_image::create(
+ util::random_read_write::ptr &&io,
+ const Format *format,
+ const Options *opts,
+ int flags,
+ ptr &outcassette)
{
- memset(info, 0, sizeof(*info));
- info->channels = cassette->channels;
- info->sample_count = cassette->sample_count;
- info->sample_frequency = cassette->sample_frequency;
- info->bits_per_sample = (int) waveform_bytes_per_sample(cassette->flags) * 8;
-}
+ static const Options default_options = { 1, 16, 44100 };
+ // cannot create to a read only image
+ if (flags & FLAG_READONLY)
+ return error::INVALID_IMAGE;
+ // is this a good format?
+ if (format->save == nullptr)
+ return error::INVALID_IMAGE;
-void cassette_close(cassette_image *cassette)
-{
- if (cassette)
- {
- if ((cassette->flags & CASSETTE_FLAG_DIRTY) && (cassette->flags & CASSETTE_FLAG_SAVEONEXIT))
- cassette_save(cassette);
- for (auto & elem : cassette->blocks)
- {
- global_free(elem);
- }
- global_free(cassette);
- }
-}
+ // normalize arguments
+ if (!opts)
+ opts = &default_options;
+ // create the cassette object
+ ptr cassette;
+ try { cassette.reset(new cassette_image(format, std::move(io), flags)); }
+ catch (std::bad_alloc const &) { return error::OUT_OF_MEMORY; }
+ // read the options
+ cassette->m_channels = opts->channels;
+ cassette->m_sample_frequency = opts->sample_frequency;
-void cassette_change(cassette_image *cassette, void *file, const struct io_procs *procs, const struct CassetteFormat *format, int flags)
-{
- if ((flags & CASSETTE_FLAG_READONLY) == 0)
- flags |= CASSETTE_FLAG_DIRTY;
- cassette->io.file = file;
- cassette->io.procs = procs;
- cassette->format = format;
- cassette->flags = flags;
+ outcassette = std::move(cassette);
+ return error::SUCCESS;
}
-/*********************************************************************
- calls for accessing the raw cassette image
-*********************************************************************/
-
-void cassette_image_read(cassette_image *cassette, void *buffer, uint64_t offset, size_t length)
+cassette_image::error cassette_image::save()
{
- io_generic_read(&cassette->io, buffer, offset, length);
+ if (!m_format || !m_format->save)
+ return error::UNSUPPORTED;
+
+ error err = perform_save();
+ if (err != error::SUCCESS)
+ return err;
+
+ m_flags &= ~CASSETTE_FLAG_DIRTY;
+ return error::SUCCESS;
}
-void cassette_image_write(cassette_image *cassette, const void *buffer, uint64_t offset, size_t length)
+cassette_image::Info cassette_image::get_info() const
{
- io_generic_write(&cassette->io, buffer, offset, length);
+ Info result;
+ result.channels = m_channels;
+ result.sample_count = m_sample_count;
+ result.sample_frequency = m_sample_frequency;
+ result.bits_per_sample = (int)waveform_bytes_per_sample(m_flags) * 8;
+ return result;
}
-uint64_t cassette_image_size(cassette_image *cassette)
+void cassette_image::change(util::random_read_write::ptr &&io, const Format *format, int flags)
{
- return io_generic_size(&cassette->io);
+ if (!(flags & FLAG_READONLY))
+ flags |= CASSETTE_FLAG_DIRTY;
+
+ m_format = format;
+ m_io = std::move(io);
+ m_flags = flags;
}
/*********************************************************************
- waveform accesses
+ calls for accessing the raw cassette image
*********************************************************************/
-struct manipulation_ranges
+void cassette_image::image_read(void *buffer, uint64_t offset, size_t length)
{
- int channel_first;
- int channel_last;
- size_t sample_first;
- size_t sample_last;
-};
+ /*auto const [err, actual] =*/ read_at(*m_io, offset, buffer, length); // FIXME: check for errors and premature EOF
+}
-static size_t my_round(double d)
+uint8_t cassette_image::image_read_byte(uint64_t offset)
{
- size_t result;
- d += 0.5;
- result = (size_t) d;
- return result;
+ uint8_t data;
+ image_read(&data, offset, 1);
+ return data;
}
-static cassette_image::error compute_manipulation_ranges(cassette_image *cassette, int channel,
- double time_index, double sample_period, struct manipulation_ranges *ranges)
+void cassette_image::image_write(const void *buffer, uint64_t offset, size_t length)
{
- if (channel < 0)
- {
- ranges->channel_first = 0;
- ranges->channel_last = cassette->channels - 1;
- }
- else
- {
- ranges->channel_first = channel;
- ranges->channel_last = channel;
- }
-
- ranges->sample_first = my_round(time_index * cassette->sample_frequency);
- ranges->sample_last = my_round((time_index + sample_period) * cassette->sample_frequency);
-
- if (ranges->sample_last > ranges->sample_first)
- ranges->sample_last--;
-
- return cassette_image::error::SUCCESS;
+ /*auto const [err, actual] =*/ write_at(*m_io, offset, buffer, length); // FIXME: check for errors
}
-static cassette_image::error lookup_sample(cassette_image *cassette, int channel, size_t sample, int32_t **ptr)
+uint64_t cassette_image::image_size()
{
- *ptr = nullptr;
- size_t sample_blocknum = (sample / SAMPLES_PER_BLOCK) * cassette->channels + channel;
- size_t sample_index = sample % SAMPLES_PER_BLOCK;
-
- /* is this block beyond the edge of our waveform? */
- if (sample_blocknum >= cassette->blocks.size()) {
- size_t osize = cassette->blocks.size();
- cassette->blocks.resize(sample_blocknum + 1);
- memset(&cassette->blocks[osize], 0, (cassette->blocks.size()-osize)*sizeof(cassette->blocks[0]));
- }
-
- if (cassette->blocks[sample_blocknum] == nullptr)
- cassette->blocks[sample_blocknum] = global_alloc(sample_block);
-
- sample_block &block = *cassette->blocks[sample_blocknum];
-
- /* is this sample access off the current block? */
- if (sample_index >= block.size()) {
- size_t osize = block.size();
- block.resize(SAMPLES_PER_BLOCK);
- memset(&block[osize], 0, (SAMPLES_PER_BLOCK-osize)*sizeof(block[0]));
- }
-
- *ptr = &block[sample_index];
- return cassette_image::error::SUCCESS;
+ uint64_t size = 0;
+ m_io->length(size);
+ return size;
}
@@ -417,38 +403,29 @@ static cassette_image::error lookup_sample(cassette_image *cassette, int channel
// Note: In normal use, the sample_spacing is the same as the sample size (in bytes)
// But it can be larger to help do an interleaved write to samples
// (see cassette_write_samples)
-cassette_image::error cassette_get_samples(cassette_image *cassette, int channel,
+cassette_image::error cassette_image::get_samples(int channel,
double time_index, double sample_period, size_t sample_count, size_t sample_spacing,
void *samples, int waveform_flags)
{
- cassette_image::error err;
- struct manipulation_ranges ranges;
- size_t sample_index;
- size_t cassette_sample_index;
- uint8_t *dest_ptr;
- const int32_t *source_ptr;
- double d;
- int16_t word;
- int32_t dword;
- int64_t sum;
-
- assert(cassette);
-
- err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges);
- if (err != cassette_image::error::SUCCESS)
+ error err;
+ manipulation_ranges ranges;
+ int32_t *source_ptr;
+
+ err = compute_manipulation_ranges(channel, time_index, sample_period, ranges);
+ if (err != error::SUCCESS)
return err;
- for (sample_index = 0; sample_index < sample_count; sample_index++)
+ for (size_t sample_index = 0; sample_index < sample_count; sample_index++)
{
- sum = 0;
+ int64_t sum = 0;
for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++)
{
/* find the sample that we are putting */
- d = map_double(ranges.sample_last + 1 - ranges.sample_first, 0, sample_count, sample_index) + ranges.sample_first;
- cassette_sample_index = (size_t) d;
- err = lookup_sample(cassette, channel, cassette_sample_index, (int32_t **) &source_ptr);
- if (err != cassette_image::error::SUCCESS)
+ double d = map_double(ranges.sample_last + 1 - ranges.sample_first, 0, sample_count, sample_index) + ranges.sample_first;
+ size_t cassette_sample_index = (size_t) d;
+ err = lookup_sample(channel, cassette_sample_index, source_ptr);
+ if (err != error::SUCCESS)
return err;
sum += *source_ptr;
@@ -458,61 +435,54 @@ cassette_image::error cassette_get_samples(cassette_image *cassette, int channel
sum /= (ranges.channel_last + 1 - ranges.channel_first);
/* and write out the result */
- dest_ptr = (uint8_t*)samples;
+ uint8_t *dest_ptr = (uint8_t*)samples;
dest_ptr += sample_index * sample_spacing;
- switch(waveform_bytes_per_sample(waveform_flags))
+ int16_t word;
+ int32_t dword;
+ switch (waveform_bytes_per_sample(waveform_flags))
{
- case 1:
- *((int8_t *) dest_ptr) = interpolate8(sum);
- break;
- case 2:
- word = interpolate16(sum);
- if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP)
- word = swapendian_int16(word);
- *((int16_t *) dest_ptr) = word;
- break;
- case 4:
- dword = sum;
- if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP)
- dword = swapendian_int32(dword);
- *((int32_t *) dest_ptr) = dword;
- break;
+ case 1:
+ *((int8_t *) dest_ptr) = interpolate8(sum);
+ break;
+ case 2:
+ word = interpolate16(sum);
+ if (waveform_flags & WAVEFORM_ENDIAN_FLIP)
+ word = swapendian_int16(word);
+ *((int16_t *) dest_ptr) = word;
+ break;
+ case 4:
+ dword = sum;
+ if (waveform_flags & WAVEFORM_ENDIAN_FLIP)
+ dword = swapendian_int32(dword);
+ *((int32_t *) dest_ptr) = dword;
+ break;
}
}
- return cassette_image::error::SUCCESS;
+ return error::SUCCESS;
}
// Note: In normal use, the sample_spacing is the same as the sample size (in bytes)
// But it can be larger to help do an interleaved read from samples
// (see cassette_read_samples)
-cassette_image::error cassette_put_samples(cassette_image *cassette, int channel,
+cassette_image::error cassette_image::put_samples(int channel,
double time_index, double sample_period, size_t sample_count, size_t sample_spacing,
const void *samples, int waveform_flags)
{
- cassette_image::error err;
- struct manipulation_ranges ranges;
- size_t sample_index;
+ error err;
+ manipulation_ranges ranges;
int32_t *dest_ptr;
- int32_t dest_value;
- int16_t word;
- int32_t dword;
- const uint8_t *source_ptr;
- double d;
-
- if (!cassette)
- return cassette_image::error::SUCCESS;
if (sample_period == 0)
- return cassette_image::error::SUCCESS;
+ return error::SUCCESS;
- err = compute_manipulation_ranges(cassette, channel, time_index, sample_period, &ranges);
- if (err != cassette_image::error::SUCCESS)
+ err = compute_manipulation_ranges(channel, time_index, sample_period, ranges);
+ if (err != error::SUCCESS)
return err;
- if (cassette->sample_count < ranges.sample_last+1)
- cassette->sample_count = ranges.sample_last + 1;
- cassette->flags |= CASSETTE_FLAG_DIRTY;
+ if (m_sample_count < ranges.sample_last+1)
+ m_sample_count = ranges.sample_last + 1;
+ m_flags |= CASSETTE_FLAG_DIRTY;
if (LOG_PUT_SAMPLES)
{
@@ -521,65 +491,68 @@ cassette_image::error cassette_put_samples(cassette_image *cassette, int channel
(int)ranges.sample_first, (int)ranges.sample_last);
}
- for (sample_index = ranges.sample_first; sample_index <= ranges.sample_last; sample_index++)
+ for (size_t sample_index = ranges.sample_first; sample_index <= ranges.sample_last; sample_index++)
{
/* figure out the source pointer */
- d = map_double(sample_count, ranges.sample_first, ranges.sample_last + 1, sample_index);
- source_ptr = (const uint8_t*)samples;
+ double d = map_double(sample_count, ranges.sample_first, ranges.sample_last + 1, sample_index);
+ const uint8_t *source_ptr = (const uint8_t*)samples;
source_ptr += ((size_t) d) * sample_spacing;
/* compute the value that we are writing */
+ int32_t dest_value;
+ int16_t word;
+ int32_t dword;
switch(waveform_bytes_per_sample(waveform_flags)) {
case 1:
- if (waveform_flags & CASSETTE_WAVEFORM_UNSIGNED)
+ if (waveform_flags & WAVEFORM_UNSIGNED)
dest_value = extrapolate8((int8_t)(*source_ptr - 128));
else
dest_value = extrapolate8(*((int8_t *) source_ptr));
break;
case 2:
word = *((int16_t *) source_ptr);
- if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP)
+ if (waveform_flags & WAVEFORM_ENDIAN_FLIP)
word = swapendian_int16(word);
dest_value = extrapolate16(word);
break;
case 4:
dword = *((int32_t *) source_ptr);
- if (waveform_flags & CASSETTE_WAVEFORM_ENDIAN_FLIP)
+ if (waveform_flags & WAVEFORM_ENDIAN_FLIP)
dword = swapendian_int32(dword);
dest_value = dword;
break;
default:
- return cassette_image::error::INTERNAL;
+ return error::INTERNAL;
}
for (channel = ranges.channel_first; channel <= ranges.channel_last; channel++)
{
/* find the sample that we are putting */
- err = lookup_sample(cassette, channel, sample_index, &dest_ptr);
- if (err != cassette_image::error::SUCCESS)
+ err = lookup_sample(channel, sample_index, dest_ptr);
+ if (err != error::SUCCESS)
return err;
*dest_ptr = dest_value;
}
}
- return cassette_image::error::SUCCESS;
+ return error::SUCCESS;
}
-cassette_image::error cassette_get_sample(cassette_image *cassette, int channel,
+cassette_image::error cassette_image::get_sample(int channel,
double time_index, double sample_period, int32_t *sample)
{
- return cassette_get_samples(cassette, channel, time_index,
- sample_period, 1, 0, sample, CASSETTE_WAVEFORM_32BIT);
+ return get_samples(channel, time_index,
+ sample_period, 1, 0, sample, WAVEFORM_32BIT);
}
-cassette_image::error cassette_put_sample(cassette_image *cassette, int channel,
+cassette_image::error cassette_image::put_sample(int channel,
double time_index, double sample_period, int32_t sample)
{
- return cassette_put_samples(cassette, channel, time_index,
- sample_period, 1, 0, &sample, CASSETTE_WAVEFORM_32BIT);
+ return put_samples(channel, time_index,
+ sample_period, 1, 0, &sample, WAVEFORM_32BIT);
}
@@ -588,153 +561,112 @@ cassette_image::error cassette_put_sample(cassette_image *cassette, int channel,
waveform accesses to/from the raw image
*********************************************************************/
-cassette_image::error cassette_read_samples(cassette_image *cassette, int channels, double time_index,
+cassette_image::error cassette_image::read_samples(int channels, double time_index,
double sample_period, size_t sample_count, uint64_t offset, int waveform_flags)
{
- cassette_image::error err;
- size_t chunk_sample_count;
- size_t bytes_per_sample;
- size_t sample_spacing;
+ error err;
size_t samples_loaded = 0;
- double chunk_time_index;
- double chunk_sample_period;
- int channel;
uint8_t buffer[8192];
- bytes_per_sample = waveform_bytes_per_sample(waveform_flags);
- sample_spacing = bytes_per_sample * channels;
+ size_t bytes_per_sample = waveform_bytes_per_sample(waveform_flags);
+ size_t sample_spacing = bytes_per_sample * channels;
- while(samples_loaded < sample_count)
+ while (samples_loaded < sample_count)
{
- chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (sample_count - samples_loaded));
- chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count);
- chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_loaded);
+ size_t chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (sample_count - samples_loaded));
+ double chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count);
+ double chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_loaded);
- cassette_image_read(cassette, buffer, offset, chunk_sample_count * sample_spacing);
+ image_read(buffer, offset, chunk_sample_count * sample_spacing);
- for (channel = 0; channel < channels; channel++)
+ for (int channel = 0; channel < channels; channel++)
{
- err = cassette_put_samples(cassette, channel, chunk_time_index, chunk_sample_period,
+ err = put_samples(channel, chunk_time_index, chunk_sample_period,
chunk_sample_count, sample_spacing, &buffer[channel * bytes_per_sample], waveform_flags);
- if (err != cassette_image::error::SUCCESS)
+ if (err != error::SUCCESS)
return err;
}
offset += chunk_sample_count * sample_spacing;
samples_loaded += chunk_sample_count;
}
- return cassette_image::error::SUCCESS;
+ return error::SUCCESS;
}
-cassette_image::error cassette_write_samples(cassette_image *cassette, int channels, double time_index,
+cassette_image::error cassette_image::write_samples(int channels, double time_index,
double sample_period, size_t sample_count, uint64_t offset, int waveform_flags)
{
- cassette_image::error err;
- size_t chunk_sample_count;
- size_t bytes_per_sample;
- size_t sample_spacing;
+ error err;
size_t samples_saved = 0;
- double chunk_time_index;
- double chunk_sample_period;
- int channel;
uint8_t buffer[8192];
- bytes_per_sample = waveform_bytes_per_sample(waveform_flags);
- sample_spacing = bytes_per_sample * channels;
+ size_t bytes_per_sample = waveform_bytes_per_sample(waveform_flags);
+ size_t sample_spacing = bytes_per_sample * channels;
- while(samples_saved < sample_count)
+ while (samples_saved < sample_count)
{
- chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (sample_count - samples_saved));
- chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count);
- chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_saved);
+ size_t chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (sample_count - samples_saved));
+ double chunk_sample_period = map_double(sample_period, 0, sample_count, chunk_sample_count);
+ double chunk_time_index = time_index + map_double(sample_period, 0, sample_count, samples_saved);
- for (channel = 0; channel < channels; channel++)
+ for (int channel = 0; channel < channels; channel++)
{
- err = cassette_get_samples(cassette, channel, chunk_time_index, chunk_sample_period,
+ err = get_samples(channel, chunk_time_index, chunk_sample_period,
chunk_sample_count, sample_spacing, &buffer[channel * bytes_per_sample], waveform_flags);
- if (err != cassette_image::error::SUCCESS)
+ if (err != error::SUCCESS)
return err;
}
- cassette_image_write(cassette, buffer, offset, chunk_sample_count * sample_spacing);
+ image_write(buffer, offset, chunk_sample_count * sample_spacing);
offset += chunk_sample_count * sample_spacing;
samples_saved += chunk_sample_count;
}
- return cassette_image::error::SUCCESS;
-}
-
-
-
-/*********************************************************************
- waveform accesses to/from the raw image
-*********************************************************************/
-
-static const int8_t *choose_wave(const struct CassetteModulation *modulation, size_t *wave_bytes_length)
-{
- static const int8_t square_wave[] = { -128, 127 };
- static const int8_t sine_wave[] = { 0, 48, 89, 117, 127, 117, 89, 48, 0, -48, -89, -117, -127, -117, -89, -48 };
-
- if (modulation->flags & CASSETTE_MODULATION_SINEWAVE)
- {
- *wave_bytes_length = ARRAY_LENGTH(sine_wave);
- return sine_wave;
- }
- else
- {
- *wave_bytes_length = ARRAY_LENGTH(square_wave);
- return square_wave;
- }
+ return error::SUCCESS;
}
-cassette_image::error cassette_modulation_identify(cassette_image *cassette, const struct CassetteModulation *modulation,
- struct CassetteOptions *opts)
+cassette_image::error cassette_image::modulation_identify(const Modulation &modulation, Options *opts)
{
size_t wave_bytes_length;
- choose_wave(modulation, &wave_bytes_length);
+ choose_wave(modulation, wave_bytes_length);
opts->bits_per_sample = 8;
opts->channels = 1;
- opts->sample_frequency = (uint32_t) (std::max(modulation->zero_frequency_high, modulation->one_frequency_high) * wave_bytes_length * 2);
- return cassette_image::error::SUCCESS;
+ opts->sample_frequency = uint32_t(std::max(modulation.zero_frequency_high, modulation.one_frequency_high) * wave_bytes_length * 2);
+ return error::SUCCESS;
}
-cassette_image::error cassette_put_modulated_data(cassette_image *cassette, int channel, double time_index,
- const void *data, size_t data_length, const struct CassetteModulation *modulation,
+cassette_image::error cassette_image::put_modulated_data(int channel, double time_index,
+ const void *data, size_t data_length, const Modulation &modulation,
double *time_displacement)
{
- cassette_image::error err;
+ error err;
const uint8_t *data_bytes = (const uint8_t *)data;
- const int8_t *wave_bytes;
size_t wave_bytes_length;
double total_displacement = 0.0;
- double pulse_period;
- double pulse_frequency;
- uint8_t b;
- int i;
- wave_bytes = choose_wave(modulation, &wave_bytes_length);
+ const int8_t *wave_bytes = choose_wave(modulation, wave_bytes_length);
- while(data_length--)
+ while (data_length--)
{
- b = *(data_bytes++);
- for (i = 0; i < 8; i++)
+ uint8_t b = *(data_bytes++);
+ for (int i = 0; i < 8; i++)
{
- pulse_frequency = (b & (1 << i)) ? modulation->one_frequency_canonical : modulation->zero_frequency_canonical;
- pulse_period = 1 / pulse_frequency;
- err = cassette_put_samples(cassette, 0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, CASSETTE_WAVEFORM_8BIT);
- if (err != cassette_image::error::SUCCESS)
+ double pulse_frequency = (b & (1 << i)) ? modulation.one_frequency_canonical : modulation.zero_frequency_canonical;
+ double pulse_period = 1 / pulse_frequency;
+ err = put_samples(0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, WAVEFORM_8BIT);
+ if (err != error::SUCCESS)
goto done;
time_index += pulse_period;
total_displacement += pulse_period;
}
}
- err = cassette_image::error::SUCCESS;
+ err = error::SUCCESS;
done:
if (time_displacement)
@@ -744,18 +676,18 @@ done:
-cassette_image::error cassette_put_modulated_filler(cassette_image *cassette, int channel, double time_index,
- uint8_t filler, size_t filler_length, const struct CassetteModulation *modulation,
+cassette_image::error cassette_image::put_modulated_filler(int channel, double time_index,
+ uint8_t filler, size_t filler_length, const Modulation &modulation,
double *time_displacement)
{
- cassette_image::error err;
+ error err;
double delta;
double total_displacement = 0.0;
- while(filler_length--)
+ while (filler_length--)
{
- err = cassette_put_modulated_data(cassette, channel, time_index, &filler, 1, modulation, &delta);
- if (err != cassette_image::error::SUCCESS)
+ err = put_modulated_data(channel, time_index, &filler, 1, modulation, &delta);
+ if (err != error::SUCCESS)
return err;
total_displacement += delta;
time_index += delta;
@@ -763,24 +695,19 @@ cassette_image::error cassette_put_modulated_filler(cassette_image *cassette, in
if (time_displacement)
*time_displacement = total_displacement;
- return cassette_image::error::SUCCESS;
+ return error::SUCCESS;
}
-cassette_image::error cassette_read_modulated_data(cassette_image *cassette, int channel, double time_index,
- uint64_t offset, uint64_t length, const struct CassetteModulation *modulation,
+cassette_image::error cassette_image::read_modulated_data(int channel, double time_index,
+ uint64_t offset, uint64_t length, const Modulation &modulation,
double *time_displacement)
{
- cassette_image::error err;
- uint8_t buffer_stack[1024];
uint8_t *buffer;
- uint8_t *alloc_buffer = nullptr;
- double delta;
- double total_displacement = 0.0;
- size_t this_length;
+ uint8_t buffer_stack[1024];
+ std::unique_ptr<uint8_t []> alloc_buffer;
size_t buffer_length;
-
if (length <= sizeof(buffer_stack))
{
buffer = buffer_stack;
@@ -789,23 +716,21 @@ cassette_image::error cassette_read_modulated_data(cassette_image *cassette, int
else
{
buffer_length = std::min<uint64_t>(length, 100000);
- alloc_buffer = (uint8_t*)malloc(buffer_length);
- if (!alloc_buffer)
- {
- err = cassette_image::error::OUT_OF_MEMORY;
- goto done;
- }
- buffer = alloc_buffer;
+ try { alloc_buffer = std::make_unique<uint8_t []>(buffer_length); }
+ catch (std::bad_alloc const &) { return error::OUT_OF_MEMORY; }
+ buffer = alloc_buffer.get();
}
- while(length > 0)
+ double delta;
+ double total_displacement = 0.0;
+ while (length > 0)
{
- this_length = (std::min<uint64_t>)(length, buffer_length);
- cassette_image_read(cassette, buffer, offset, this_length);
+ size_t this_length = std::min<uint64_t>(length, buffer_length);
+ image_read(buffer, offset, this_length);
- err = cassette_put_modulated_data(cassette, channel, time_index, buffer, this_length, modulation, &delta);
- if (err != cassette_image::error::SUCCESS)
- goto done;
+ error err = put_modulated_data(channel, time_index, buffer, this_length, modulation, &delta);
+ if (err != error::SUCCESS)
+ return err;
total_displacement += delta;
time_index += delta;
length -= this_length;
@@ -813,38 +738,30 @@ cassette_image::error cassette_read_modulated_data(cassette_image *cassette, int
if (time_displacement)
*time_displacement = total_displacement;
- err = cassette_image::error::SUCCESS;
-
-done:
- if (alloc_buffer)
- free(alloc_buffer);
- return err;
+ return error::SUCCESS;
}
-cassette_image::error cassette_put_modulated_data_bit(cassette_image *cassette, int channel, double time_index,
- uint8_t data, const struct CassetteModulation *modulation,
+cassette_image::error cassette_image::put_modulated_data_bit(int channel, double time_index,
+ uint8_t data, const Modulation &modulation,
double *time_displacement)
{
- cassette_image::error err;
- const int8_t *wave_bytes;
+ error err;
size_t wave_bytes_length;
double total_displacement = 0.0;
- double pulse_period;
- double pulse_frequency;
- wave_bytes = choose_wave(modulation, &wave_bytes_length);
+ const int8_t *wave_bytes = choose_wave(modulation, wave_bytes_length);
- pulse_frequency = (data) ? modulation->one_frequency_canonical : modulation->zero_frequency_canonical;
- pulse_period = 1 / pulse_frequency;
- err = cassette_put_samples(cassette, 0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, CASSETTE_WAVEFORM_8BIT);
- if (err != cassette_image::error::SUCCESS)
+ double pulse_frequency = (data) ? modulation.one_frequency_canonical : modulation.zero_frequency_canonical;
+ double pulse_period = 1 / pulse_frequency;
+ err = put_samples(0, time_index, pulse_period, wave_bytes_length, 1, wave_bytes, WAVEFORM_8BIT);
+ if (err != error::SUCCESS)
goto done;
time_index += pulse_period;
total_displacement += pulse_period;
- err = cassette_image::error::SUCCESS;
+ err = error::SUCCESS;
done:
if (time_displacement)
@@ -858,67 +775,66 @@ done:
waveform accesses to/from the raw image
*********************************************************************/
-cassette_image::error cassette_legacy_identify(cassette_image *cassette, struct CassetteOptions *opts,
- const struct CassetteLegacyWaveFiller *legacy_args)
+cassette_image::error cassette_image::legacy_identify(Options *opts,
+ const LegacyWaveFiller *legacy_args)
{
opts->channels = 1;
opts->bits_per_sample = 16;
opts->sample_frequency = legacy_args->sample_frequency;
- return cassette_image::error::SUCCESS;
+ return error::SUCCESS;
}
-cassette_image::error cassette_legacy_construct(cassette_image *cassette,
- const struct CassetteLegacyWaveFiller *legacy_args)
+cassette_image::error cassette_image::legacy_construct(const LegacyWaveFiller *legacy_args)
{
- cassette_image::error err;
+ error err;
int length;
int sample_count;
- std::vector<uint8_t> bytes;
- std::vector<uint8_t> chunk;
- std::vector<int16_t> samples;
+ std::unique_ptr<int16_t []> samples;
+ std::unique_ptr<uint8_t []> chunk;
int pos = 0;
uint64_t offset = 0;
- uint64_t size;
- struct CassetteLegacyWaveFiller args;
/* sanity check the args */
assert(legacy_args->header_samples >= -1);
assert(legacy_args->trailer_samples >= 0);
assert(legacy_args->fill_wave);
- size = cassette_image_size(cassette);
+ const uint64_t size = image_size();
/* normalize the args */
- args = *legacy_args;
+ LegacyWaveFiller args = *legacy_args;
if (args.chunk_size == 0)
args.chunk_size = 1;
else if (args.chunk_size < 0)
- args.chunk_size = cassette_image_size(cassette);
+ args.chunk_size = image_size();
if (args.sample_frequency == 0)
args.sample_frequency = 11025;
- /* allocate a buffer for the binary data */
- chunk.resize(args.chunk_size);
-
/* determine number of samples */
if (args.chunk_sample_calc != nullptr)
{
- if (size > 0x7FFFFFFF)
+ if (size > 0x7fffffff)
{
- err = cassette_image::error::OUT_OF_MEMORY;
+ err = error::OUT_OF_MEMORY;
goto done;
}
- bytes.resize(size);
- cassette_image_read(cassette, &bytes[0], 0, size);
- sample_count = args.chunk_sample_calc(&bytes[0], (int)size);
+ std::unique_ptr<uint8_t []> bytes(new (std::nothrow) uint8_t [size]);
+ if (!bytes)
+ {
+ err = error::OUT_OF_MEMORY;
+ goto done;
+ }
+
+ image_read(bytes.get(), 0, size);
+ sample_count = args.chunk_sample_calc(bytes.get(), (int)size);
// chunk_sample_calc functions report errors by returning negative numbers
if (sample_count < 0)
{
- err = cassette_image::error::INVALID_IMAGE;
+ err = error::INVALID_IMAGE;
goto done;
}
@@ -933,60 +849,73 @@ cassette_image::error cassette_legacy_construct(cassette_image *cassette,
sample_count += args.header_samples + args.trailer_samples;
/* allocate a buffer for the completed samples */
- samples.resize(sample_count);
+ samples.reset(new (std::nothrow) int16_t [sample_count]);
+ if (!samples)
+ {
+ err = error::OUT_OF_MEMORY;
+ goto done;
+ }
/* if there has to be a header */
if (args.header_samples > 0)
{
- length = args.fill_wave(&samples[pos], sample_count - pos, CODE_HEADER);
+ length = args.fill_wave(&samples[pos], sample_count - pos, CODE_HEADER, -1);
if (length < 0)
{
- err = cassette_image::error::INVALID_IMAGE;
+ err = error::INVALID_IMAGE;
goto done;
}
pos += length;
}
/* convert the file data to samples */
- while((pos < sample_count) && (offset < size))
+ chunk.reset(new (std::nothrow) uint8_t [args.chunk_size]);
+ if (!chunk)
+ {
+ err = error::OUT_OF_MEMORY;
+ goto done;
+ }
+ while ((pos < sample_count) && (offset < size))
{
- cassette_image_read(cassette, &chunk[0], offset, args.chunk_size);
- offset += args.chunk_size;
+ const int slice = std::min<int>(args.chunk_size, size - offset);
+ image_read(chunk.get(), offset, slice);
+ offset += slice;
- length = args.fill_wave(&samples[pos], sample_count - pos, &chunk[0]);
+ length = args.fill_wave(&samples[pos], sample_count - pos, chunk.get(), slice);
if (length < 0)
{
- err = cassette_image::error::INVALID_IMAGE;
+ err = error::INVALID_IMAGE;
goto done;
}
pos += length;
if (length == 0)
break;
}
+ chunk.reset();
/* if there has to be a trailer */
if (args.trailer_samples > 0)
{
- length = args.fill_wave(&samples[pos], sample_count - pos, CODE_TRAILER);
+ length = args.fill_wave(&samples[pos], sample_count - pos, CODE_TRAILER, -1);
if (length < 0)
{
- err = cassette_image::error::INVALID_IMAGE;
+ err = error::INVALID_IMAGE;
goto done;
}
pos += length;
}
/* specify the wave */
- err = cassette_put_samples(cassette, 0, 0.0, ((double) pos) / args.sample_frequency,
- pos, 2, &samples[0], CASSETTE_WAVEFORM_16BIT);
- if (err != cassette_image::error::SUCCESS)
+ err = put_samples(0, 0.0, ((double) pos) / args.sample_frequency,
+ pos, 2, &samples[0], WAVEFORM_16BIT);
+ if (err != error::SUCCESS)
goto done;
/* success! */
- err = cassette_image::error::SUCCESS;
+ err = error::SUCCESS;
#if DUMP_CASSETTES
- cassette_dump(cassette, "C:\\TEMP\\CASDUMP.WAV");
+ dump("C:\\TEMP\\CASDUMP.WAV");
#endif
done:
@@ -1001,26 +930,21 @@ done:
A debugging call to dump a cassette image to a disk based wave file
*********************************************************************/
-void cassette_dump(cassette_image *image, const char *filename)
+void cassette_image::dump(const char *filename)
{
- FILE *f;
- struct io_generic saved_io;
- const struct CassetteFormat *saved_format;
-
- f = fopen(filename, "wb");
- if (!f)
+ util::random_read_write::ptr saved_io = util::stdio_read_write(fopen(filename, "wb"), 0);
+ if (!saved_io)
return;
- memcpy(&saved_io, &image->io, sizeof(saved_io));
- saved_format = image->format;
+ Format const *const saved_format = m_format;
+
+ using std::swap;
- image->io.file = f;
- image->io.procs = &stdio_ioprocs_noclose;
- image->format = &wavfile_format;
- cassette_perform_save(image);
+ swap(m_io, saved_io);
+ m_format = &wavfile_format;
- memcpy(&image->io, &saved_io, sizeof(saved_io));
- image->format = saved_format;
+ perform_save();
- fclose(f);
+ swap(m_io, saved_io);
+ m_format = saved_format;
}
diff --git a/src/lib/formats/cassimg.h b/src/lib/formats/cassimg.h
index 5199c0e4a04..9867ecbdf4a 100644
--- a/src/lib/formats/cassimg.h
+++ b/src/lib/formats/cassimg.h
@@ -14,16 +14,10 @@
#include "ioprocs.h"
-#include "osdcore.h"
-#include "coretmpl.h"
-
+#include <memory>
#include <string>
#include <vector>
-#ifndef LOG_FORMATS
-#define LOG_FORMATS if (0) printf
-#endif
-
// hack to get around rogues that define this
#ifdef UNSUPPORTED
#undef UNSUPPORTED
@@ -31,65 +25,51 @@
/***************************************************************************
- Constants
+ Type definitions
***************************************************************************/
-#define CASSETTE_FLAG_READWRITE 0
-#define CASSETTE_FLAG_READONLY 1
-#define CASSETTE_FLAG_NOSAVEONEXIT 0
-#define CASSETTE_FLAG_SAVEONEXIT 2
-
-#define CASSETTE_WAVEFORM_8BIT 0
-#define CASSETTE_WAVEFORM_16BIT 2
-#define CASSETTE_WAVEFORM_16BIT_FLIP 3
-#define CASSETTE_WAVEFORM_32BIT 4
-#define CASSETTE_WAVEFORM_32BIT_FLIP 5
-#define CASSETTE_WAVEFORM_ENDIAN_FLIP 1
-#define CASSETTE_WAVEFORM_UNSIGNED 8
-
-#define CASSETTE_MODULATION_SQUAREWAVE 0
-#define CASSETTE_MODULATION_SINEWAVE 1
+class cassette_image
+{
+public:
+ enum : int
+ {
+ FLAG_READWRITE = 0,
+ FLAG_READONLY = 1,
+ FLAG_NOSAVEONEXIT = 0,
+ FLAG_SAVEONEXIT = 2
+ };
+ enum : int
+ {
+ WAVEFORM_8BIT = 0,
+ WAVEFORM_16BIT = 2,
+ WAVEFORM_16BIT_FLIP = 3,
+ WAVEFORM_32BIT = 4,
+ WAVEFORM_32BIT_FLIP = 5,
+ WAVEFORM_ENDIAN_FLIP = 1,
+ WAVEFORM_UNSIGNED = 8,
#ifdef LSB_FIRST
-#define CASSETTE_WAVEFORM_16BITBE CASSETTE_WAVEFORM_16BIT_FLIP
-#define CASSETTE_WAVEFORM_16BITLE CASSETTE_WAVEFORM_16BIT
-#define CASSETTE_WAVEFORM_32BITBE CASSETTE_WAVEFORM_32BIT_FLIP
-#define CASSETTE_WAVEFORM_32BITLE CASSETTE_WAVEFORM_32BIT
+ WAVEFORM_16BITBE = WAVEFORM_16BIT_FLIP,
+ WAVEFORM_16BITLE = WAVEFORM_16BIT,
+ WAVEFORM_32BITBE = WAVEFORM_32BIT_FLIP,
+ WAVEFORM_32BITLE = WAVEFORM_32BIT
#else
-#define CASSETTE_WAVEFORM_16BITBE CASSETTE_WAVEFORM_16BIT
-#define CASSETTE_WAVEFORM_16BITLE CASSETTE_WAVEFORM_16BIT_FLIP
-#define CASSETTE_WAVEFORM_32BITBE CASSETTE_WAVEFORM_32BIT
-#define CASSETTE_WAVEFORM_32BITLE CASSETTE_WAVEFORM_32BIT_FLIP
+ WAVEFORM_16BITBE = WAVEFORM_16BIT,
+ WAVEFORM_16BITLE = WAVEFORM_16BIT_FLIP,
+ WAVEFORM_32BITBE = WAVEFORM_32BIT,
+ WAVEFORM_32BITLE = WAVEFORM_32BIT_FLIP
#endif
+ };
+ enum : int
+ {
+ MODULATION_SQUAREWAVE = 0,
+ MODULATION_SINEWAVE = 1
+ };
-/***************************************************************************
-
- Type definitions
-
-***************************************************************************/
-
-typedef std::vector<int32_t> sample_block;
-
-struct CassetteOptions
-{
- int channels;
- int bits_per_sample;
- uint32_t sample_frequency;
-};
-
-struct CassetteInfo
-{
- int channels;
- int bits_per_sample;
- uint32_t sample_frequency;
- size_t sample_count;
-};
-struct cassette_image
-{
enum class error
{
SUCCESS, // no error
@@ -100,133 +80,160 @@ struct cassette_image
READ_WRITE_UNSUPPORTED // read/write is not supported by this image format
};
- const struct CassetteFormat *format;
- struct io_generic io;
+ using ptr = std::unique_ptr<cassette_image>;
+
+ struct Options
+ {
+ int channels = 0;
+ int bits_per_sample = 0;
+ uint32_t sample_frequency = 0;
+ };
- int channels;
- int flags;
- uint32_t sample_frequency;
+ struct Info
+ {
+ int channels = 0;
+ int bits_per_sample = 0;
+ uint32_t sample_frequency = 0;
+ size_t sample_count = 0;
+ };
- std::vector<sample_block *> blocks;
- size_t sample_count;
-};
+ struct Format
+ {
+ const char *extensions = nullptr;
+ error (*identify)(cassette_image *cassette, Options *opts) = nullptr;
+ error (*load)(cassette_image *cassette) = nullptr;
+ error (*save)(cassette_image *cassette, const Info *info) = nullptr;
+ };
-struct CassetteFormat
-{
- const char *extensions;
- cassette_image::error (*identify)(cassette_image *cassette, struct CassetteOptions *opts);
- cassette_image::error (*load)(cassette_image *cassette);
- cassette_image::error (*save)(cassette_image *cassette, const struct CassetteInfo *info);
-};
+ /* used for the core modulation code */
+ struct Modulation
+ {
+ int flags = 0;
+ double zero_frequency_low = 0;
+ double zero_frequency_canonical = 0;
+ double zero_frequency_high = 0;
+ double one_frequency_low = 0;
+ double one_frequency_canonical = 0;
+ double one_frequency_high = 0;
+ };
-/* used for the core modulation code */
-struct CassetteModulation
-{
- int flags;
- double zero_frequency_low;
- double zero_frequency_canonical;
- double zero_frequency_high;
- double one_frequency_low;
- double one_frequency_canonical;
- double one_frequency_high;
-};
+ /* code to adapt existing legacy fill_wave functions */
+ struct LegacyWaveFiller
+ {
+ int (*fill_wave)(int16_t *, int, const uint8_t *, int) = nullptr;
+ int chunk_size = 0;
+ int chunk_samples = 0;
+ int (*chunk_sample_calc)(const uint8_t *bytes, int length) = nullptr;
+ uint32_t sample_frequency = 0;
+ int header_samples = 0;
+ int trailer_samples = 0;
+ };
-/* code to adapt existing legacy fill_wave functions */
-struct CassetteLegacyWaveFiller
-{
- int (*fill_wave)(int16_t *, int, uint8_t *);
- int chunk_size;
- int chunk_samples;
- int (*chunk_sample_calc)(const uint8_t *bytes, int length);
- uint32_t sample_frequency;
- int header_samples;
- int trailer_samples;
+ ~cassette_image();
+
+ // calls for accessing the raw cassette image
+ void image_read(void *buffer, uint64_t offset, size_t length);
+ uint8_t image_read_byte(uint64_t offset);
+ void image_write(const void *buffer, uint64_t offset, size_t length);
+ uint64_t image_size();
+ util::random_read_write::ptr &get_raw_cassette_image() { return m_io; }
+
+ // waveform accesses
+ error get_samples(int channel,
+ double time_index, double sample_period, size_t sample_count, size_t sample_spacing,
+ void *samples, int waveform_flags);
+ error put_samples(int channel,
+ double time_index, double sample_period, size_t sample_count, size_t sample_spacing,
+ const void *samples, int waveform_flags);
+ error get_sample(int channel, double time_index, double sample_period, int32_t *sample);
+ error put_sample(int channel, double time_index, double sample_period, int32_t sample);
+
+ // waveform accesses to/from the raw image - these are only used by lib\formats\wavfile.cpp
+ error read_samples(int channels, double time_index,
+ double sample_period, size_t sample_count, uint64_t offset, int waveform_flags);
+ error write_samples(int channels, double time_index,
+ double sample_period, size_t sample_count, uint64_t offset, int waveform_flags);
+
+ // modulation support
+ error modulation_identify(const Modulation &modulation,
+ Options *opts);
+ error put_modulated_data(int channel, double time_index,
+ const void *data, size_t data_length, const Modulation &modulation,
+ double *time_displacement);
+ error put_modulated_filler(int channel, double time_index,
+ uint8_t filler, size_t filler_length, const Modulation &modulation,
+ double *time_displacement);
+ error read_modulated_data(int channel, double time_index,
+ uint64_t offset, uint64_t length, const Modulation &modulation,
+ double *time_displacement);
+ error put_modulated_data_bit(int channel, double time_index,
+ uint8_t data, const Modulation &modulation,
+ double *time_displacement);
+
+ /* legacy code support */
+#define CODE_HEADER ((uint8_t*)-1)
+#define CODE_TRAILER ((uint8_t*)-2)
+ error legacy_identify(Options *opts, const LegacyWaveFiller *legacy_args);
+ error legacy_construct(const LegacyWaveFiller *legacy_args);
+
+ /* debug calls */
+ void dump(const char *filename);
+
+ error save();
+ void change(util::random_read_write::ptr &&io, const Format *format, int flags);
+ Info get_info() const;
+
+ static error open(util::random_read_write::ptr &&io,
+ const Format *format, int flags, ptr &outcassette);
+ static error open_choices(util::random_read_write::ptr &&io, const std::string &extension,
+ const Format *const *formats, int flags, ptr &outcassette);
+ static error create(util::random_read_write::ptr &&io, const cassette_image::Format *format,
+ const cassette_image::Options *opts, int flags, ptr &outcassette);
+
+ // builtin formats
+ static const Format wavfile_format;
+ static const Format flacfile_format;
+
+private:
+ struct manipulation_ranges;
+
+ cassette_image(const Format *format, util::random_read_write::ptr &&io, int flags);
+ cassette_image(const cassette_image &) = delete;
+ cassette_image(cassette_image &&) = delete;
+ cassette_image &operator=(const cassette_image &) = delete;
+ cassette_image &operator=(cassette_image &&) = delete;
+
+ error try_identify_format(const Format &format, const std::string &extension, int flags, Options &opts);
+ error perform_save();
+ error compute_manipulation_ranges(int channel, double time_index, double sample_period, manipulation_ranges &ranges) const;
+ error lookup_sample(int channel, size_t sample, int32_t *&ptr);
+
+ const Format *m_format;
+ util::random_read_write::ptr m_io;
+
+ int m_channels;
+ int m_flags;
+ uint32_t m_sample_frequency;
+
+ std::vector<std::unique_ptr<int32_t []> > m_blocks;
+ size_t m_sample_count;
};
-/* builtin formats */
-extern const struct CassetteFormat wavfile_format;
-
/* macros for specifying format lists */
#define CASSETTE_FORMATLIST_EXTERN(name) \
- extern const struct CassetteFormat *const name[]
+ extern const cassette_image::Format *const name[]
#define CASSETTE_FORMATLIST_START(name) \
- const struct CassetteFormat *const name[] = \
+ const cassette_image::Format *const name[] = \
{ \
- &wavfile_format,
+ &cassette_image::wavfile_format, \
+ &cassette_image::flacfile_format,
#define CASSETTE_FORMAT(name) \
&(name),
#define CASSETTE_FORMATLIST_END \
- nullptr \
+ nullptr \
};
CASSETTE_FORMATLIST_EXTERN(cassette_default_formats);
-/***************************************************************************
-
- Prototypes
-
-***************************************************************************/
-
-cassette_image::error cassette_open(void *file, const struct io_procs *procs,
- const struct CassetteFormat *format, int flags, cassette_image **outcassette);
-cassette_image::error cassette_open_choices(void *file, const struct io_procs *procs, const std::string &extension,
- const struct CassetteFormat *const *formats, int flags, cassette_image **outcassette);
-cassette_image::error cassette_create(void *file, const struct io_procs *procs, const struct CassetteFormat *format,
- const struct CassetteOptions *opts, int flags, cassette_image **outcassette);
-cassette_image::error cassette_save(cassette_image *cassette);
-void cassette_close(cassette_image *cassette);
-void cassette_change(cassette_image *cassette, void *file, const struct io_procs *procs, const struct CassetteFormat *format, int flags);
-void cassette_get_info(cassette_image *cassette, struct CassetteInfo *info);
-
-/* calls for accessing the raw cassette image */
-void cassette_image_read(cassette_image *cassette, void *buffer, uint64_t offset, size_t length);
-void cassette_image_write(cassette_image *cassette, const void *buffer, uint64_t offset, size_t length);
-uint64_t cassette_image_size(cassette_image *cassette);
-
-/* waveform accesses */
-cassette_image::error cassette_get_samples(cassette_image *cassette, int channel,
- double time_index, double sample_period, size_t sample_count, size_t sample_spacing,
- void *samples, int waveform_flags);
-cassette_image::error cassette_put_samples(cassette_image *cassette, int channel,
- double time_index, double sample_period, size_t sample_count, size_t sample_spacing,
- const void *samples, int waveform_flags);
-cassette_image::error cassette_get_sample(cassette_image *cassette, int channel,
- double time_index, double sample_period, int32_t *sample);
-cassette_image::error cassette_put_sample(cassette_image *cassette, int channel,
- double time_index, double sample_period, int32_t sample);
-
-/* waveform accesses to/from the raw image - these are only used by lib\formats\wavfile.cpp */
-cassette_image::error cassette_read_samples(cassette_image *cassette, int channels, double time_index,
- double sample_period, size_t sample_count, uint64_t offset, int waveform_flags);
-cassette_image::error cassette_write_samples(cassette_image *cassette, int channels, double time_index,
- double sample_period, size_t sample_count, uint64_t offset, int waveform_flags);
-
-/* modulation support */
-cassette_image::error cassette_modulation_identify(cassette_image *cassette, const struct CassetteModulation *modulation,
- struct CassetteOptions *opts);
-cassette_image::error cassette_put_modulated_data(cassette_image *cassette, int channel, double time_index,
- const void *data, size_t data_length, const struct CassetteModulation *modulation,
- double *time_displacement);
-cassette_image::error cassette_put_modulated_filler(cassette_image *cassette, int channel, double time_index,
- uint8_t filler, size_t filler_length, const struct CassetteModulation *modulation,
- double *time_displacement);
-cassette_image::error cassette_read_modulated_data(cassette_image *cassette, int channel, double time_index,
- uint64_t offset, uint64_t length, const struct CassetteModulation *modulation,
- double *time_displacement);
-cassette_image::error cassette_put_modulated_data_bit(cassette_image *cassette, int channel, double time_index,
- uint8_t data, const struct CassetteModulation *modulation,
- double *time_displacement);
-
-/* debug calls */
-void cassette_dump(cassette_image *image, const char *filename);
-
-/* legacy code support */
-#define CODE_HEADER ((uint8_t*)-1)
-#define CODE_TRAILER ((uint8_t*)-2)
-cassette_image::error cassette_legacy_identify(cassette_image *cassette, struct CassetteOptions *opts,
- const struct CassetteLegacyWaveFiller *legacy_args);
-cassette_image::error cassette_legacy_construct(cassette_image *cassette,
- const struct CassetteLegacyWaveFiller *legacy_args);
-
#endif // MAME_FORMATS_CASSIMG_H
diff --git a/src/lib/formats/cbm_crt.cpp b/src/lib/formats/cbm_crt.cpp
index ae67b8c7f1b..6bc9e0f8d23 100644
--- a/src/lib/formats/cbm_crt.cpp
+++ b/src/lib/formats/cbm_crt.cpp
@@ -39,10 +39,14 @@
*********************************************************************/
-#include "emu.h" // fatalerror
#include "cbm_crt.h"
#include "corefile.h"
+#include "multibyte.h"
+
+#include "osdcore.h" // osd_printf_*
+
+#include <tuple>
//**************************************************************************
@@ -135,11 +139,11 @@ std::string cbm_crt_get_card(util::core_file &file)
{
// read the header
cbm_crt_header header;
- file.read(&header, CRT_HEADER_LENGTH);
+ auto const [err, actual] = read(file, &header, CRT_HEADER_LENGTH);
- if (memcmp(header.signature, CRT_SIGNATURE, 16) == 0)
+ if (!err && (CRT_HEADER_LENGTH == actual) && !memcmp(header.signature, CRT_SIGNATURE, 16))
{
- uint16_t hardware = pick_integer_be(header.hardware, 0, 2);
+ uint16_t hardware = get_u16be(header.hardware);
return std::string(CRT_C64_SLOT_NAMES[hardware]);
}
@@ -154,14 +158,19 @@ std::string cbm_crt_get_card(util::core_file &file)
bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_size, int *exrom, int *game)
{
+ std::error_condition err;
+ size_t actual;
+
// read the header
cbm_crt_header header;
- file.read(&header, CRT_HEADER_LENGTH);
+ std::tie(err, actual) = read(file, &header, CRT_HEADER_LENGTH);
+ if (err)
+ return false;
- if (memcmp(header.signature, CRT_SIGNATURE, 16) != 0)
+ if ((CRT_HEADER_LENGTH != actual) || (memcmp(header.signature, CRT_SIGNATURE, 16) != 0))
return false;
- uint16_t hardware = pick_integer_be(header.hardware, 0, 2);
+ uint16_t hardware = get_u16be(header.hardware);
*exrom = header.exrom;
*game = header.game;
@@ -178,11 +187,13 @@ bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_
while (!file.eof())
{
cbm_crt_chip chip;
- file.read(&chip, CRT_CHIP_LENGTH);
+ std::tie(err, actual) = read(file, &chip, CRT_CHIP_LENGTH);
+ if (err || (CRT_CHIP_LENGTH != actual))
+ return false;
- uint16_t address = pick_integer_be(chip.start_address, 0, 2);
- uint16_t size = pick_integer_be(chip.image_size, 0, 2);
- uint16_t type = pick_integer_be(chip.chip_type, 0, 2);
+ const uint16_t address = get_u16be(chip.start_address);
+ const uint16_t size = get_u16be(chip.image_size);
+ const uint16_t type = get_u16be(chip.chip_type);
if (LOG)
{
@@ -199,7 +210,8 @@ bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_
default: osd_printf_verbose("Invalid CHIP loading address!\n"); break;
}
- file.seek(size, SEEK_CUR);
+ if (file.seek(size, SEEK_CUR))
+ return false;
}
return true;
@@ -212,25 +224,34 @@ bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_
bool cbm_crt_read_data(util::core_file &file, uint8_t *roml, uint8_t *romh)
{
+ if (file.seek(CRT_HEADER_LENGTH, SEEK_SET))
+ return false;
+
uint32_t roml_offset = 0;
uint32_t romh_offset = 0;
- file.seek(CRT_HEADER_LENGTH, SEEK_SET);
-
while (!file.eof())
{
+ std::error_condition err;
+ size_t actual;
+
cbm_crt_chip chip;
- file.read(&chip, CRT_CHIP_LENGTH);
+ std::tie(err, actual) = read(file, &chip, CRT_CHIP_LENGTH);
+ if (err || (CRT_CHIP_LENGTH != actual))
+ return false;
- uint16_t address = pick_integer_be(chip.start_address, 0, 2);
- uint16_t size = pick_integer_be(chip.image_size, 0, 2);
+ const uint16_t address = get_u16be(chip.start_address);
+ const uint16_t size = get_u16be(chip.image_size);
switch (address)
{
- case 0x8000: file.read(roml + roml_offset, size); roml_offset += size; break;
- case 0xa000: file.read(romh + romh_offset, size); romh_offset += size; break;
- case 0xe000: file.read(romh + romh_offset, size); romh_offset += size; break;
+ case 0x8000: std::tie(err, actual) = read(file, roml + roml_offset, size); roml_offset += size; break;
+ case 0xa000: std::tie(err, actual) = read(file, romh + romh_offset, size); romh_offset += size; break;
+ case 0xe000: std::tie(err, actual) = read(file, romh + romh_offset, size); romh_offset += size; break;
+ // FIXME: surely one needs to report an error or skip over the data if the load address is not recognised?
}
+ if (err) // TODO: check size - all bets are off if the address isn't recognised anyway
+ return false;
}
return true;
diff --git a/src/lib/formats/cbm_crt.h b/src/lib/formats/cbm_crt.h
index c3fea3db799..5b447de226c 100644
--- a/src/lib/formats/cbm_crt.h
+++ b/src/lib/formats/cbm_crt.h
@@ -12,7 +12,11 @@
#pragma once
-#include "formats/imageutl.h"
+#include "utilfwd.h"
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
//**************************************************************************
diff --git a/src/lib/formats/cbm_tap.cpp b/src/lib/formats/cbm_tap.cpp
index 2699ab51ec0..65c265ac86f 100644
--- a/src/lib/formats/cbm_tap.cpp
+++ b/src/lib/formats/cbm_tap.cpp
@@ -60,11 +60,11 @@ pulse length is determined by the following formula:
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
+ The value of "clock cycles" 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
+compatibility problem converting European and NTSC tapes. I would stick to
using the PAL value just in case.
@@ -94,9 +94,10 @@ Unfortunately, I have no such a .tap file to test, so my implementation
below could be not working. FP ]
*/
-#include <assert.h>
-
#include "cbm_tap.h"
+#include "imageutl.h"
+
+#include "multibyte.h"
#define CBM_WAV_FREQUENCY 44100
@@ -137,7 +138,7 @@ static int len;
/* This in fact gives the number of samples for half of the pulse */
static 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 * (0.5 + (((double)CBM_WAV_FREQUENCY / frequency) * (double)data))); // MAME TZX formula
return (int) (0.5 * (((double)CBM_WAV_FREQUENCY / frequency) * (double)((data) + 0.5))); // tap2wav formula
}
@@ -282,7 +283,7 @@ static int cbm_tap_do_work( int16_t **buffer, int length, const uint8_t *data )
if (j >= 4)
{
- over_pulse_length = ((over_pulse_bytes[2] << 16) | (over_pulse_bytes[1] << 8) | over_pulse_bytes[0]) >> 3;
+ over_pulse_length = get_u24le(over_pulse_bytes) >> 3;
byte_samples = tap_data_to_samplecount(over_pulse_length, tap_frequency);
/* waveamp_high = WAVE_PAUSE; */
j = 0;
@@ -329,7 +330,7 @@ static int cbm_tap_to_wav_size( const uint8_t *tapdata, int taplen )
return size;
}
-static int cbm_tap_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
+static int cbm_tap_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int )
{
int16_t *p = buffer;
@@ -339,7 +340,7 @@ static int cbm_tap_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
-static const struct CassetteLegacyWaveFiller cbm_legacy_fill_wave = {
+static const cassette_image::LegacyWaveFiller cbm_legacy_fill_wave = {
cbm_tap_fill_wave, /* fill_wave */
-1, /* chunk_size */
0, /* chunk_samples */
@@ -350,19 +351,19 @@ static const struct CassetteLegacyWaveFiller cbm_legacy_fill_wave = {
};
-static cassette_image::error cbm_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts )
+static cassette_image::error cbm_cassette_identify( cassette_image *cassette, cassette_image::Options *opts )
{
- return cassette_legacy_identify( cassette, opts, &cbm_legacy_fill_wave );
+ return cassette->legacy_identify( opts, &cbm_legacy_fill_wave );
}
static cassette_image::error cbm_cassette_load( cassette_image *cassette )
{
- return cassette_legacy_construct( cassette, &cbm_legacy_fill_wave );
+ return cassette->legacy_construct( &cbm_legacy_fill_wave );
}
-static const struct CassetteFormat cbm_tap_cassette_format = {
+static const cassette_image::Format cbm_tap_cassette_format = {
"tap",
cbm_cassette_identify,
cbm_cassette_load,
diff --git a/src/lib/formats/ccvf_dsk.cpp b/src/lib/formats/ccvf_dsk.cpp
index febe78d45d2..d2c8da8e083 100644
--- a/src/lib/formats/ccvf_dsk.cpp
+++ b/src/lib/formats/ccvf_dsk.cpp
@@ -2,15 +2,18 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/ccvf_dsk.c
+ formats/ccvf_dsk.cpp
Compucolor Virtual Floppy Disk Image format
*********************************************************************/
-#include "emu.h" // BIT
#include "formats/ccvf_dsk.h"
+#include "coretmpl.h" // BIT
+#include "ioprocs.h"
+
+
ccvf_format::ccvf_format()
{
formats = file_formats;
@@ -21,17 +24,17 @@ ccvf_format::ccvf_format(const format *_formats)
formats = _formats;
}
-const char *ccvf_format::name() const
+const char *ccvf_format::name() const noexcept
{
return "ccvf";
}
-const char *ccvf_format::description() const
+const char *ccvf_format::description() const noexcept
{
return "Compucolor Virtual Floppy Disk Image";
}
-const char *ccvf_format::extensions() const
+const char *ccvf_format::extensions() const noexcept
{
return "ccvf";
}
@@ -44,13 +47,15 @@ const ccvf_format::format ccvf_format::file_formats[] = {
{}
};
-int ccvf_format::identify(io_generic *io, uint32_t form_factor)
+int ccvf_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
char h[36];
+ auto const [err, actual] = read_at(io, 0, h, 36);
+ if (err || (36 != actual))
+ return 0;
- io_generic_read(io, h, 0, 36);
- if(!memcmp(h, "Compucolor Virtual Floppy Disk Image", 36))
- return 100;
+ if (!memcmp(h, "Compucolor Virtual Floppy Disk Image", 36))
+ return FIFID_SIGN;
return 0;
}
@@ -85,13 +90,17 @@ floppy_image_format_t::desc_e* ccvf_format::get_desc_8n1(const format &f, int &c
return desc;
}
-bool ccvf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool ccvf_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
const format &f = formats[0];
- uint64_t size = io_generic_size(io);
- std::vector<uint8_t> img(size);
- io_generic_read(io, &img[0], 0, size);
+ uint64_t size;
+ if (io.length(size))
+ return false;
+
+ auto [err, img, actual] = read_at(io, 0, size);
+ if (err || (actual != size))
+ return false;
std::string ccvf = std::string((const char *)&img[0], size);
std::vector<uint8_t> bytes(78720);
@@ -124,7 +133,7 @@ bool ccvf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image
for (int i=0; i<1920 && pos<size; i++, pos++) {
for (int bit=0; bit<8; bit++) {
- bit_w(buffer, BIT(bytes[pos], bit), f.cell_size);
+ bit_w(buffer, util::BIT(bytes[pos], bit), f.cell_size);
}
}
@@ -139,14 +148,14 @@ bool ccvf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image
generate_track_from_levels(track, 0, buffer, 0, image);
}
- image->set_variant(f.variant);
+ image.set_variant(f.variant);
return true;
}
-bool ccvf_format::supports_save() const
+bool ccvf_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_CCVF_FORMAT = &floppy_image_format_creator<ccvf_format>;
+const ccvf_format FLOPPY_CCVF_FORMAT;
diff --git a/src/lib/formats/ccvf_dsk.h b/src/lib/formats/ccvf_dsk.h
index d101172b5b9..483b6a3cbb4 100644
--- a/src/lib/formats/ccvf_dsk.h
+++ b/src/lib/formats/ccvf_dsk.h
@@ -35,22 +35,22 @@ public:
ccvf_format();
ccvf_format(const format *formats);
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
protected:
const format *formats;
- floppy_image_format_t::desc_e* get_desc_8n1(const format &f, int &current_size);
+ static floppy_image_format_t::desc_e* get_desc_8n1(const format &f, int &current_size);
static const format file_formats[];
};
-extern const floppy_format_type FLOPPY_CCVF_FORMAT;
+extern const ccvf_format FLOPPY_CCVF_FORMAT;
#endif // MAME_FORMATS_CCVF_DSK_H
diff --git a/src/lib/formats/cd90_640_dsk.cpp b/src/lib/formats/cd90_640_dsk.cpp
deleted file mode 100644
index 1654d5d8c99..00000000000
--- a/src/lib/formats/cd90_640_dsk.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Dirk Best
-/***************************************************************************
-
- Thompson CD90-640 FDC
-
- Disk image format
-
- TODO:
- - Gap sizes unverified for FM
-
-***************************************************************************/
-
-#include "cd90_640_dsk.h"
-
-cd90_640_format::cd90_640_format() : wd177x_format(formats)
-{
-}
-
-const char *cd90_640_format::name() const
-{
- return "cd90_640";
-}
-
-const char *cd90_640_format::description() const
-{
- return "CD90-640 disk image";
-}
-
-const char *cd90_640_format::extensions() const
-{
- return "fd";
-}
-
-const cd90_640_format::format cd90_640_format::formats[] =
-{
- {
- floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
- 4000, 16, 40, 1, 128, {}, 1, {}, 14, 11, 12
- },
- {
- floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
- 2000, 16, 40, 1, 256, {}, 1, {}, 31, 22, 44
- },
- {
- floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
- 4000, 16, 40, 2, 128, {}, 1, {}, 14, 11, 12
- },
- {
- floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
- 2000, 16, 40, 2, 256, {}, 1, {}, 31, 22, 44
- },
- {}
-};
-
-const floppy_format_type FLOPPY_CD90_640_FORMAT = &floppy_image_format_creator<cd90_640_format>;
diff --git a/src/lib/formats/cd90_640_dsk.h b/src/lib/formats/cd90_640_dsk.h
deleted file mode 100644
index 5324698f498..00000000000
--- a/src/lib/formats/cd90_640_dsk.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// license:GPL-2.0+
-// copyright-holders:Dirk Best
-/***************************************************************************
-
- Thompson CD90-640 FDC
-
- Disk image format
-
-***************************************************************************/
-#ifndef MAME_FORMATS_CD90_640_DSK_H
-#define MAME_FORMATS_CD90_640_DSK_H
-
-#pragma once
-
-#include "wd177x_dsk.h"
-
-class cd90_640_format : public wd177x_format
-{
-public:
- cd90_640_format();
-
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
-
-private:
- static const format formats[];
-};
-
-extern const floppy_format_type FLOPPY_CD90_640_FORMAT;
-
-#endif // MAME_FORMATS_CD90_640_DSK_H
diff --git a/src/lib/formats/cgen_cas.cpp b/src/lib/formats/cgen_cas.cpp
index be4b9397f89..1ae1682cba9 100644
--- a/src/lib/formats/cgen_cas.cpp
+++ b/src/lib/formats/cgen_cas.cpp
@@ -18,10 +18,10 @@ NOTE: There exist multiples type of .cas files for Colour Genie
We now support these three types below...
********************************************************************/
+#include "cgen_cas.h"
-#include <assert.h>
+#include <cstring>
-#include "formats/cgen_cas.h"
#define TAPE_HEADER "Colour Genie - Virtual Tape File"
@@ -108,7 +108,7 @@ static int cgenie_handle_cas(int16_t *buffer, const uint8_t *casdata)
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int cgenie_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int cgenie_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return cgenie_handle_cas(buffer, bytes);
}
@@ -124,7 +124,7 @@ static int cgenie_cas_to_wav_size(const uint8_t *casdata, int caslen)
return cgenie_handle_cas(nullptr, casdata);
}
-static const struct CassetteLegacyWaveFiller cgenie_cas_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller cgenie_cas_legacy_fill_wave =
{
cgenie_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -136,19 +136,19 @@ static const struct CassetteLegacyWaveFiller cgenie_cas_legacy_fill_wave =
};
-static cassette_image::error cgenie_cas_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error cgenie_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &cgenie_cas_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &cgenie_cas_legacy_fill_wave);
}
static cassette_image::error cgenie_cas_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &cgenie_cas_legacy_fill_wave);
+ return cassette->legacy_construct(&cgenie_cas_legacy_fill_wave);
}
-static const struct CassetteFormat cgenie_cas_format =
+static const cassette_image::Format cgenie_cas_format =
{
"cas",
cgenie_cas_identify,
diff --git a/src/lib/formats/cgenie_dsk.cpp b/src/lib/formats/cgenie_dsk.cpp
index 827830682ec..55523714a3a 100644
--- a/src/lib/formats/cgenie_dsk.cpp
+++ b/src/lib/formats/cgenie_dsk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -14,27 +14,27 @@ cgenie_format::cgenie_format() : wd177x_format(formats)
{
}
-const char *cgenie_format::name() const
+const char *cgenie_format::name() const noexcept
{
return "cgenie";
}
-const char *cgenie_format::description() const
+const char *cgenie_format::description() const noexcept
{
return "Colour Genie disk image";
}
-const char *cgenie_format::extensions() const
+const char *cgenie_format::extensions() const noexcept
{
return "dsk";
}
-int cgenie_format::get_track_dam_fm(const format &f, int head, int track)
+int cgenie_format::get_track_dam_fm(const format &f, int head, int track) const
{
return (track == f.track_count/2) ? FM_DDAM : FM_DAM;
}
-int cgenie_format::get_track_dam_mfm(const format &f, int head, int track)
+int cgenie_format::get_track_dam_mfm(const format &f, int head, int track) const
{
return (track == f.track_count/2) ? MFM_DDAM : MFM_DAM;
}
@@ -76,4 +76,4 @@ const cgenie_format::format cgenie_format::formats[] =
{}
};
-const floppy_format_type FLOPPY_CGENIE_FORMAT = &floppy_image_format_creator<cgenie_format>;
+const cgenie_format FLOPPY_CGENIE_FORMAT;
diff --git a/src/lib/formats/cgenie_dsk.h b/src/lib/formats/cgenie_dsk.h
index 3bbbccf4762..c6e1319fd80 100644
--- a/src/lib/formats/cgenie_dsk.h
+++ b/src/lib/formats/cgenie_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -19,18 +19,18 @@ class cgenie_format : public wd177x_format
public:
cgenie_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
protected:
- virtual int get_track_dam_fm(const format &f, int head, int track) override;
- virtual int get_track_dam_mfm(const format &f, int head, int track) override;
+ virtual int get_track_dam_fm(const format &f, int head, int track) const override;
+ virtual int get_track_dam_mfm(const format &f, int head, int track) const override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_CGENIE_FORMAT;
+extern const cgenie_format FLOPPY_CGENIE_FORMAT;
#endif // MAME_FORMATS_CGENIE_DSK_H
diff --git a/src/lib/formats/coco_cas.cpp b/src/lib/formats/coco_cas.cpp
index 6579f47a60f..a7afc1eeacf 100644
--- a/src/lib/formats/coco_cas.cpp
+++ b/src/lib/formats/coco_cas.cpp
@@ -33,9 +33,11 @@
**************************************************************************/
-#include "emu.h" // PAIR
#include "coco_cas.h"
+#include <cassert>
+
+
#define COCO_WAVESAMPLES_HEADER (1.0)
#define COCO_WAVESAMPLES_TRAILER (1.0)
#define COCO_LONGSILENCE (5.0)
@@ -43,77 +45,72 @@
//some games load with only 5s, but most games need 15s
#define ALICE32_WAVESAMPLES_HEADER (15.0)
-static int synccount;
-
-const struct CassetteModulation coco_cas_modulation =
+const cassette_image::Modulation coco_cas_modulation =
{
- CASSETTE_MODULATION_SINEWAVE,
+ cassette_image::MODULATION_SINEWAVE,
600.0, 1200.0, 1500.0,
1500.0, 2400.0, 3000.0
};
-static cassette_image::error coco_cas_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error coco_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_modulation_identify(cassette, &coco_cas_modulation, opts);
+ return cassette->modulation_identify(coco_cas_modulation, opts);
}
-static bool get_cas_block(cassette_image *cassette, uint64_t *offset, uint8_t *block, int *block_len)
+static bool get_cas_block(cassette_image *cassette, uint64_t &offset, uint8_t *block, int &block_len, int &synccount)
{
uint8_t block_length = 0;
uint8_t block_checksum = 0;
- uint64_t current_offset;
- uint64_t 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;
+ uint16_t p = 0;
+ uint16_t image_size = cassette->image_size();
- while(current_offset < image_size)
+ for(uint64_t current_offset = offset; current_offset < image_size; )
{
- cassette_image_read(cassette, &p.b.h, current_offset, 1);
+ assert((p & 0xFF00) == 0);
+ p |= cassette->image_read_byte(current_offset) << 8;
current_offset++;
- for (i = 0; i < 8; i++)
+ for (int i = 0; i < 8; i++)
{
- p.w.l >>= 1;
+ p >>= 1;
if (state == 0)
{
/* searching for a block */
- if (p.b.l == 0x3C)
+ if ((p & 0xFF) == 0x3C)
{
/* found one! */
phase = i;
state++;
}
- else if (p.b.l == 0x55)
+ else if ((p & 0xFF) == 0x55)
{
synccount++;
}
}
else if (i == phase)
{
- *(block++) = p.b.l;
+ uint8_t b = p & 0xFF;
+ *(block++) = b;
switch(state) {
case 1:
/* found file type */
- block_checksum = p.b.l;
+ block_checksum = b;
state++;
break;
case 2:
/* found file size */
- block_length = p.b.l;
- *block_len = ((int) block_length) + 3;
- block_checksum += p.b.l;
+ block_length = b;
+ block_len = ((int) block_length) + 3;
+ block_checksum += b;
state++;
break;
@@ -122,12 +119,12 @@ static bool get_cas_block(cassette_image *cassette, uint64_t *offset, uint8_t *b
if (block_length)
{
block_length--;
- block_checksum += p.b.l;
+ block_checksum += b;
}
else
{
/* end of block */
- if (p.b.l != block_checksum)
+ if (b != block_checksum)
{
/* checksum failure */
return false;
@@ -135,7 +132,7 @@ static bool get_cas_block(cassette_image *cassette, uint64_t *offset, uint8_t *b
else
{
/* checksum success */
- *offset = current_offset;
+ offset = current_offset;
return true;
}
}
@@ -161,6 +158,7 @@ static cassette_image::error cas_load(cassette_image *cassette, uint8_t silence)
double time_index = 0.0;
double time_displacement;
static const uint8_t magic_bytes[2] = { 0x55, 0x3C };
+ int synccount;
#if 0
{
@@ -190,21 +188,21 @@ static cassette_image::error cas_load(cassette_image *cassette, uint8_t silence)
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);
+ 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);
+ err = cassette->put_sample(0, time_index, COCO_WAVESAMPLES_HEADER, 0);
if (err != cassette_image::error::SUCCESS)
return err;
time_index += COCO_WAVESAMPLES_HEADER;
offset = 0;
last_blocktype = 0;
- image_size = cassette_image_size(cassette);
+ image_size = cassette->image_size();
/* try to find a block that we can untangle */
- while(get_cas_block(cassette, &offset, block, &block_length))
+ while(get_cas_block(cassette, offset, block, block_length, synccount))
{
/* Forcing a silence before a filename block, improves the ability to load some */
/* copy protected Dragon games, e.g. Rommel's Revenge */
@@ -212,13 +210,13 @@ static cassette_image::error cas_load(cassette_image *cassette, uint8_t silence)
if ((last_blocktype == 0) || (last_blocktype == 0xFF) || (block[0] == 0))
{
/* silence */
- err = cassette_put_sample(cassette, 0, time_index, silence, 0);
+ err = cassette->put_sample(0, time_index, silence, 0);
if (err != cassette_image::error::SUCCESS)
return err;
time_index += silence;
/* sync data */
- err = cassette_put_modulated_filler(cassette, 0, time_index, 0x55, 128, &coco_cas_modulation, &time_displacement);
+ err = cassette->put_modulated_filler(0, time_index, 0x55, 128, coco_cas_modulation, &time_displacement);
if (err != cassette_image::error::SUCCESS)
return err;
time_index += time_displacement;
@@ -226,26 +224,26 @@ static cassette_image::error cas_load(cassette_image *cassette, uint8_t silence)
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);
+ err = cassette->put_modulated_filler(0, time_index, 0x55, synccount, coco_cas_modulation, &time_displacement);
if (err != cassette_image::error::SUCCESS)
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);
+ err = cassette->put_modulated_data(0, time_index, magic_bytes, sizeof(magic_bytes), coco_cas_modulation, &time_displacement);
if (err != cassette_image::error::SUCCESS)
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);
+ err = cassette->put_modulated_data(0, time_index, block, block_length, coco_cas_modulation, &time_displacement);
if (err != cassette_image::error::SUCCESS)
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);
+ err = cassette->put_modulated_filler(0, time_index, 0x55, 1, coco_cas_modulation, &time_displacement);
if (err != cassette_image::error::SUCCESS)
return err;
time_index += time_displacement;
@@ -254,7 +252,7 @@ static cassette_image::error cas_load(cassette_image *cassette, uint8_t silence)
}
/* 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);
+ err = cassette->read_modulated_data(0, time_index, offset, image_size - offset, coco_cas_modulation, &time_displacement);
if (err != cassette_image::error::SUCCESS)
return err;
time_index += time_displacement;
@@ -272,7 +270,7 @@ static cassette_image::error alice32_cas_load(cassette_image *cassette)
return cas_load(cassette, ALICE32_WAVESAMPLES_HEADER);
}
-const struct CassetteFormat coco_cas_format =
+const cassette_image::Format coco_cas_format =
{
"cas",
coco_cas_identify,
@@ -280,7 +278,7 @@ const struct CassetteFormat coco_cas_format =
nullptr
};
-const struct CassetteFormat alice32_cas_format =
+const cassette_image::Format alice32_cas_format =
{
"cas,c10,k7",
coco_cas_identify,
diff --git a/src/lib/formats/coco_rawdsk.cpp b/src/lib/formats/coco_rawdsk.cpp
new file mode 100644
index 00000000000..32300e7c398
--- /dev/null
+++ b/src/lib/formats/coco_rawdsk.cpp
@@ -0,0 +1,45 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ CoCo Raw Disk
+
+***************************************************************************/
+
+#include "coco_rawdsk.h"
+
+
+coco_rawdsk_format::coco_rawdsk_format() : wd177x_format(formats)
+{
+}
+
+const char *coco_rawdsk_format::name() const noexcept
+{
+ return "coco_rawdsk";
+}
+
+const char *coco_rawdsk_format::description() const noexcept
+{
+ return "CoCo Raw Disk";
+}
+
+const char *coco_rawdsk_format::extensions() const noexcept
+{
+ return "raw";
+}
+
+const coco_rawdsk_format::format coco_rawdsk_format::formats[] =
+{
+ {
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 35, 1, 256, {}, -1, { 1,12,5,16,9,2,13,6,17,10,3,14,7,18,11,4,15,8 }, 32, 22, 24
+ },
+ {
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 40, 1, 256, {}, -1, { 1,12,5,16,9,2,13,6,17,10,3,14,7,18,11,4,15,8 }, 32, 22, 24
+ },
+ {}
+};
+
+
+const coco_rawdsk_format FLOPPY_COCO_RAWDSK_FORMAT;
diff --git a/src/lib/formats/coco_rawdsk.h b/src/lib/formats/coco_rawdsk.h
new file mode 100644
index 00000000000..17eddbdfe10
--- /dev/null
+++ b/src/lib/formats/coco_rawdsk.h
@@ -0,0 +1,33 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ CoCo Raw Disk
+
+***************************************************************************/
+
+#ifndef MAME_FORMATS_COCO_RAWDSK_H
+#define MAME_FORMATS_COCO_RAWDSK_H
+
+#pragma once
+
+#include "wd177x_dsk.h"
+
+
+class coco_rawdsk_format : public wd177x_format
+{
+public:
+ coco_rawdsk_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const format formats[];
+};
+
+
+extern const coco_rawdsk_format FLOPPY_COCO_RAWDSK_FORMAT;
+
+#endif // MAME_FORMATS_COCO_RAWDSK_H
diff --git a/src/lib/formats/comx35_dsk.cpp b/src/lib/formats/comx35_dsk.cpp
index aa8603f3c1c..30b17d4622a 100644
--- a/src/lib/formats/comx35_dsk.cpp
+++ b/src/lib/formats/comx35_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/comx35_dsk.c
+ formats/comx35_dsk.cpp
COMX-35 disk image format
@@ -36,25 +36,23 @@
*/
-#include <assert.h>
-
#include "formats/comx35_dsk.h"
comx35_format::comx35_format() : wd177x_format(formats)
{
}
-const char *comx35_format::name() const
+const char *comx35_format::name() const noexcept
{
return "comx35";
}
-const char *comx35_format::description() const
+const char *comx35_format::description() const noexcept
{
return "COMX-35 disk image";
}
-const char *comx35_format::extensions() const
+const char *comx35_format::extensions() const noexcept
{
return "img";
}
@@ -76,4 +74,4 @@ const comx35_format::format comx35_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_COMX35_FORMAT = &floppy_image_format_creator<comx35_format>;
+const comx35_format FLOPPY_COMX35_FORMAT;
diff --git a/src/lib/formats/comx35_dsk.h b/src/lib/formats/comx35_dsk.h
index d5e2b961535..223fcd60917 100644
--- a/src/lib/formats/comx35_dsk.h
+++ b/src/lib/formats/comx35_dsk.h
@@ -19,14 +19,14 @@ class comx35_format : public wd177x_format
public:
comx35_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_COMX35_FORMAT;
+extern const comx35_format FLOPPY_COMX35_FORMAT;
#endif // MAME_FORMATS_COMX35_DSK_H
diff --git a/src/lib/formats/concept_dsk.cpp b/src/lib/formats/concept_dsk.cpp
index eea65064fc7..52950b3e57d 100644
--- a/src/lib/formats/concept_dsk.cpp
+++ b/src/lib/formats/concept_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Olivier Galibert, R. Belmont
/*********************************************************************
- formats/concept_dsk.c
+ formats/concept_dsk.cpp
Formats for Corvus Concept
@@ -10,11 +10,11 @@
*********************************************************************/
-#include <assert.h>
-
-#include "flopimg.h"
#include "formats/concept_dsk.h"
+#include "ioprocs.h"
+
+
/* 9 sectors / track, 512 bytes per sector */
const floppy_image_format_t::desc_e cc525dsdd_format::cc_9_desc[] = {
{ MFM, 0x4e, 80 },
@@ -52,105 +52,104 @@ cc525dsdd_format::cc525dsdd_format()
{
}
-const char *cc525dsdd_format::name() const
+const char *cc525dsdd_format::name() const noexcept
{
- return "img";
+ return "concept";
}
-const char *cc525dsdd_format::description() const
+const char *cc525dsdd_format::description() const noexcept
{
return "Corvus Concept 5.25\" DSDD floppy disk image";
}
-const char *cc525dsdd_format::extensions() const
+const char *cc525dsdd_format::extensions() const noexcept
{
return "img";
}
-bool cc525dsdd_format::supports_save() const
+bool cc525dsdd_format::supports_save() const noexcept
{
return true;
}
-void cc525dsdd_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
+void cc525dsdd_format::find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
{
- uint64_t size = io_generic_size(io);
-
track_count = 77;
head_count = 2;
sector_count = 9;
- uint32_t expected_size = 512 * track_count*head_count*sector_count;
- if (size == expected_size)
- {
+ uint32_t const expected_size = 512 * track_count*head_count*sector_count;
+
+ uint64_t size;
+ if (!io.length(size) && (size == expected_size))
return;
- }
track_count = head_count = sector_count = 0;
}
-int cc525dsdd_format::identify(io_generic *io, uint32_t form_factor)
+int cc525dsdd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
- if(track_count)
- return 50;
+ if (track_count)
+ return FIFID_SIZE;
+
return 0;
}
-bool cc525dsdd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool cc525dsdd_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
uint8_t sectdata[10*512];
desc_s sectors[10];
- for(int i=0; i<sector_count; i++) {
+ for (int i=0; i<sector_count; i++) {
sectors[i+1].data = sectdata + 512*i;
sectors[i+1].size = 512;
sectors[i+1].sector_id = i+1;
}
int track_size = sector_count*512;
- for(int track=0; track < track_count; track++) {
- for(int head=0; head < head_count; head++) {
- io_generic_read(io, sectdata, (track*head_count + head)*track_size, track_size);
+ for (int track=0; track < track_count; track++) {
+ for (int head=0; head < head_count; head++) {
+ /*auto const [err, acutal] =*/ read_at(io, (track*head_count + head) * track_size, sectdata, track_size); // FIXME: check for errors and premature EOF
generate_track(cc_9_desc, track, head, sectors, sector_count, 100000, image);
}
}
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
return true;
}
-bool cc525dsdd_format::save(io_generic *io, floppy_image *image)
+bool cc525dsdd_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
int track_count, head_count, sector_count;
get_geometry_mfm_pc(image, 2000, track_count, head_count, sector_count);
- if(track_count != 77)
+ if (track_count != 77)
track_count = 77;
// Happens for a fully unformatted floppy
- if(!head_count)
+ if (!head_count)
head_count = 2;
- if(sector_count != 9)
+ if (sector_count != 9)
sector_count = 9;
uint8_t sectdata[9*512];
int track_size = sector_count*512;
- for(int track=0; track < track_count; track++) {
- for(int head=0; head < head_count; head++) {
+ for (int track=0; track < track_count; track++) {
+ for (int head=0; head < head_count; head++) {
get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata);
- io_generic_write(io, sectdata, (track*head_count + head)*track_size, track_size);
+ /*auto const [err, actual] =*/ write_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors
}
}
return true;
}
-const floppy_format_type FLOPPY_CONCEPT_525DSDD_FORMAT = &floppy_image_format_creator<cc525dsdd_format>;
+const cc525dsdd_format FLOPPY_CONCEPT_525DSDD_FORMAT;
diff --git a/src/lib/formats/concept_dsk.h b/src/lib/formats/concept_dsk.h
index ad6e8d63514..90a2937a564 100644
--- a/src/lib/formats/concept_dsk.h
+++ b/src/lib/formats/concept_dsk.h
@@ -17,21 +17,21 @@ class cc525dsdd_format : public floppy_image_format_t
public:
cc525dsdd_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
static const desc_e cc_9_desc[];
private:
- void find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
+ static void find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
};
-extern const floppy_format_type FLOPPY_CONCEPT_525DSDD_FORMAT;
+extern const cc525dsdd_format FLOPPY_CONCEPT_525DSDD_FORMAT;
#endif // MAME_FORMATS_CONCEPT_DSK_H
diff --git a/src/lib/formats/coupedsk.cpp b/src/lib/formats/coupedsk.cpp
index 803f69bbbb7..a3edc5c7b40 100644
--- a/src/lib/formats/coupedsk.cpp
+++ b/src/lib/formats/coupedsk.cpp
@@ -2,16 +2,16 @@
// copyright-holders:Olivier Galibert
/*************************************************************************
- formats/coupedsk.c
+ formats/coupedsk.cpp
SAM Coupe disk image formats
**************************************************************************/
-#include <assert.h>
+#include "coupedsk.h"
+
+#include "ioprocs.h"
-#include "formats/coupedsk.h"
-#include "flopimg.h"
const floppy_image_format_t::desc_e mgt_format::desc_10[] = {
{ MFM, 0x4e, 60 },
@@ -44,40 +44,44 @@ mgt_format::mgt_format()
{
}
-const char *mgt_format::name() const
+const char *mgt_format::name() const noexcept
{
return "mgt";
}
-const char *mgt_format::description() const
+const char *mgt_format::description() const noexcept
{
return "Sam Coupe MGT image format";
}
-const char *mgt_format::extensions() const
+const char *mgt_format::extensions() const noexcept
{
return "mgt,dsk";
}
-bool mgt_format::supports_save() const
+bool mgt_format::supports_save() const noexcept
{
return true;
}
-int mgt_format::identify(io_generic *io, uint32_t form_factor)
+int mgt_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if(io.length(size))
+ return 0;
if(/*size == 737280 || */ size == 819200)
- return 50;
+ return FIFID_SIZE;
return 0;
}
-bool mgt_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool mgt_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint64_t size = io_generic_size(io);
- int sector_count = size == 737280 ? 9 : 10;
+ uint64_t size;
+ if(io.length(size))
+ return false;
+ int const sector_count = (size == 737280) ? 9 : 10;
uint8_t sectdata[10*512];
desc_s sectors[10];
@@ -90,16 +94,16 @@ bool mgt_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
int track_size = sector_count*512;
for(int head=0; head < 2; head++) {
for(int track=0; track < 80; track++) {
- io_generic_read(io, sectdata, (track*2+head)*track_size, track_size);
+ /*auto const [err, actual] =*/ read_at(io, (track*2+head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF
generate_track(desc_10, track, head, sectors, sector_count+1, 100000, image);
}
}
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
return true;
}
-bool mgt_format::save(io_generic *io, floppy_image *image)
+bool mgt_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
int track_count, head_count, sector_count;
get_geometry_mfm_pc(image, 2000, track_count, head_count, sector_count);
@@ -115,11 +119,11 @@ bool mgt_format::save(io_generic *io, floppy_image *image)
for(int head=0; head < 2; head++) {
for(int track=0; track < 80; track++) {
get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata);
- io_generic_write(io, sectdata, (head*80 + track)*track_size, track_size);
+ /*auto const [err, actual] =*/ write_at(io, (track*2+head)*track_size, sectdata, track_size); // FIXME: check for errors
}
}
return true;
}
-const floppy_format_type FLOPPY_MGT_FORMAT = &floppy_image_format_creator<mgt_format>;
+const mgt_format FLOPPY_MGT_FORMAT;
diff --git a/src/lib/formats/coupedsk.h b/src/lib/formats/coupedsk.h
index 190ec0b27b1..01cc65cf48e 100644
--- a/src/lib/formats/coupedsk.h
+++ b/src/lib/formats/coupedsk.h
@@ -19,18 +19,18 @@ class mgt_format : public floppy_image_format_t
public:
mgt_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
static const floppy_image_format_t::desc_e desc_10[];
};
-extern const floppy_format_type FLOPPY_MGT_FORMAT;
+extern const mgt_format FLOPPY_MGT_FORMAT;
#endif // MAME_FORMATS_COUPEDSK_H
diff --git a/src/lib/formats/cp68_dsk.cpp b/src/lib/formats/cp68_dsk.cpp
new file mode 100644
index 00000000000..5872da350b6
--- /dev/null
+++ b/src/lib/formats/cp68_dsk.cpp
@@ -0,0 +1,296 @@
+// license:BSD-3-Clause copyright-holders:Barry Rodewald, 68bit, Michael R. Furman
+/*
+ * cp68_dsk.cpp - CP68 compatible disk images
+ *
+ * Created on: 02/08/2022
+ *
+ * This CP68 floppy disk image support leverages the wd177x_format support with
+ * the few differences handled by format variations. This format is based off
+ * flex_dsk.cpp with the necessary changes for the CP/68 disk format.
+ *
+ * The CP68 Disk format is as follows:
+ *
+ * Geometry:: 18 Sectors * 35 Tracks * 128 Bytes * 1 Side
+ *
+ * Track 0:: Boot Sectors 0, 1, 2; Directory Sectors 4-18
+ * Tracks 1-34:: Sectors 1-18
+ *
+ * System Information Record:: Sector 2, Bytes $7A-$7F
+ * $7A - System Link Start Track
+ * $7B - System Link Start Sector
+ * $7C - System Link End Track
+ * $7D - System Link End Sector
+ * $7E - Free Chain Start Track
+ * $7F - Free Chain Start Sector
+ *
+ * The formats below include interleaved sectors to improve performance. The
+ * interleave has been chosen to perform well on slower hardware and software
+ * combinations while still offering some performance improvement. Tighter
+ * interleaving may well be possible but it depends on the systems and
+ * software.
+ *
+ * Note about Gap Bytes::
+ * The gap bytes used here for MAME are not standard. The reason is
+ * with 4,000 time quanta for each cell only allows for 50,000 Cells
+ * total on the disk. The original Gap Bytes as shown in the manual
+ * https://archive.org/details/CP-68_An_M6800_Operating_System_1979/page/80/mode/2up?q=GAP
+ * wind up with too many cells to fit on the disk. This has been
+ * adjusted to the minimum allowable according to the WD1771 data
+ * sheet of 8, 11, 11
+ *
+ * Note: This code is compatible with the Disk images from deramp.com (Mike
+ * Douglas) at the following location. It is _NOT_ compatible with the SimH
+ * disk images:
+ *
+ * https://deramp.com/downloads/swtpc/software/CP68/
+ */
+
+#include "cp68_dsk.h"
+
+#include "imageutl.h"
+
+#include "ioprocs.h"
+
+#include <tuple>
+
+
+namespace
+{
+ class cp68_formats : public wd177x_format
+ {
+ public:
+ struct sysinfo_sector_cp68
+ {
+ uint8_t unused1[122]{};
+ uint8_t link_start_track = 0;
+ uint8_t link_start_sector = 0;
+ uint8_t link_end_track = 0;
+ uint8_t link_end_sector = 0;
+ uint8_t fc_start_track = 0;
+ uint8_t fc_start_sector = 0;
+ };
+
+ static const format formats[];
+ static const format formats_head1[];
+ static const format formats_track0[];
+ static const format formats_head1_track0[];
+ };
+}
+
+cp68_format::cp68_format() : wd177x_format(cp68_formats::formats)
+{
+}
+
+const char *cp68_format::name() const noexcept
+{
+ return "cp68";
+}
+
+const char *cp68_format::description() const noexcept
+{
+ return "CP/68 compatible disk image";
+}
+
+const char *cp68_format::extensions() const noexcept
+{
+ return "dsk";
+}
+
+int cp68_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ int type = find_size(io, form_factor, variants);
+
+ if (type != -1)
+ return FIFID_SIZE;
+ return 0;
+}
+
+
+int cp68_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ uint8_t boot0[128];
+ cp68_formats::sysinfo_sector_cp68 info;
+ size_t actual;
+ std::error_condition ec;
+
+ // Look at the boot sector.
+ std::tie(ec, actual) = read_at(io, 128 * 0, &boot0, sizeof(boot0));
+ if (ec || actual == 0) // FIXME: what's the actual minimum size boot sector to probe?
+ return -1;
+ uint8_t boot0_sector_id = 1;
+ // uint8_t boot1_sector_id = 2;
+
+ // This floppy format uses a strategy of looking for 6800 boot code to
+ // set the numbering of the first two sectors. If this is shown to not
+ // be practical in some common cases then a separate format variant
+ // might be needed.
+ if (boot0[0] == 0xbd && boot0[3] == 0x86) {
+ // Found a 6800 jsr and ldaa, looks like a CP68 6800 boot sector.
+ boot0_sector_id = 0;
+ }
+
+ for (int i=0; cp68_formats::formats[i].form_factor; i++) {
+ const format &f = cp68_formats::formats[i];
+
+ // Look at the system information sector.
+ std::tie(ec, actual) = read_at(io, f.sector_base_size * 2, &info, sizeof(struct cp68_formats::sysinfo_sector_cp68));
+ if (ec || actual == 0) // FIXME: what's the actual minimum sector size?
+ continue;
+
+ LOG_FORMATS("CP68 floppy dsk: size %d bytes, %d total sectors, %d remaining bytes, expected form factor %x\n", (uint32_t)size, (uint32_t)size / f.sector_base_size, (uint32_t)size % f.sector_base_size, form_factor);
+
+ // Consistency checks.
+ // Free Chain
+ if (info.fc_start_track < 1 || info.fc_start_track >= f.track_count)
+ continue;
+ else if (info.fc_start_sector < 1 || info.fc_start_sector > f.sector_count)
+ continue;
+
+ // Init Linkage
+ if (info.link_start_track < 1 || info.link_start_track >= f.track_count)
+ continue;
+ else if (info.link_end_track < 1 || info.link_end_track >= f.track_count)
+ continue;
+
+ if (info.link_start_sector < 1 || info.link_start_sector > f.sector_count)
+ continue;
+ else if (info.link_end_sector < 1 || info.link_end_sector > f.sector_count)
+ continue;
+
+
+ unsigned int format_size = 0;
+ for (int track=0; track < f.track_count; track++) {
+ for (int head=0; head < f.head_count; head++) {
+ const format &tf = get_track_format(f, head, track);
+ format_size += compute_track_size(tf);
+ }
+ }
+
+ if (format_size != size)
+ continue;
+
+ // Check that the boot sector ID matches.
+ const format &ft0 = cp68_formats::formats_track0[i];
+ if (ft0.form_factor) {
+ // There is a specialized track 0 format.
+ if (ft0.sector_base_id == -1) {
+ if (ft0.per_sector_id[0] != boot0_sector_id)
+ continue;
+ } else {
+ if (ft0.sector_base_id != boot0_sector_id)
+ continue;
+ }
+ } else {
+ // Otherwise check the default track format.
+ if (f.sector_base_id == -1) {
+ if (f.per_sector_id[0] != boot0_sector_id)
+ continue;
+ } else {
+ if (f.sector_base_id != boot0_sector_id)
+ continue;
+ }
+ }
+
+ LOG_FORMATS("CP68 matching format index %d\n", i);
+ return i;
+ }
+ return -1;
+}
+
+const wd177x_format::format &cp68_format::get_track_format(const format &f, int head, int track) const
+{
+ int n = -1;
+
+ for (int i = 0; cp68_formats::formats[i].form_factor; i++) {
+ if (&cp68_formats::formats[i] == &f) {
+ n = i;
+ break;
+ }
+ }
+
+ if (n < 0) {
+ LOG_FORMATS("Error format not found\n");
+ return f;
+ }
+
+ if (head >= f.head_count) {
+ LOG_FORMATS("Error invalid head %d\n", head);
+ return f;
+ }
+
+ if (track >= f.track_count) {
+ LOG_FORMATS("Error invalid track %d\n", track);
+ return f;
+ }
+
+ if (track > 0) {
+ if (head == 1) {
+ const format &fh1 = cp68_formats::formats_head1[n];
+ if (!fh1.form_factor) {
+ LOG_FORMATS("Error expected a head 1 format\n");
+ return f;
+ }
+ return fh1;
+ }
+ return f;
+ }
+
+ // Track 0
+
+ if (head == 1) {
+ const format &fh1t0 = cp68_formats::formats_head1_track0[n];
+ if (fh1t0.form_factor) {
+ return fh1t0;
+ }
+ const format &fh1 = cp68_formats::formats_head1[n];
+ if (fh1.form_factor) {
+ return fh1;
+ }
+ LOG_FORMATS("Error expected a head 1 format\n");
+ return f;
+ }
+
+ // Head 0
+
+ const format &ft0 = cp68_formats::formats_track0[n];
+ if (ft0.form_factor) {
+ return ft0;
+ }
+
+ return f;
+}
+
+
+const cp68_formats::format cp68_formats::formats[] = {
+ { // 0 87.5K 5 1/4 inch single density cp68 1.0 format three boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 18, 35, 1, 128, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 8, 11, 11
+ },
+ {}
+};
+
+const cp68_formats::format cp68_formats::formats_head1[] = {
+ { // 0 87.5K 5 1/4 inch single density cp68 1.0 format three boot sectors
+ },
+ {}
+};
+
+const cp68_formats::format cp68_formats::formats_track0[] = {
+ { // 0 87.5K 5 1/4 inch single density cp68 1.0 format three boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 18, 35, 1, 128, {}, -1, {0, 6, 11, 16, 2, 8, 13, 18, 5, 10, 15, 1, 7, 12, 17, 4, 9, 14}, 8, 11, 11
+ },
+ {}
+};
+
+const cp68_formats::format cp68_formats::formats_head1_track0[] = {
+ { // 0 87.5K 5 1/4 inch single density cp68 1.0 format three boot sectors
+ },
+ {}
+};
+
+const cp68_format FLOPPY_CP68_FORMAT;
diff --git a/src/lib/formats/cp68_dsk.h b/src/lib/formats/cp68_dsk.h
new file mode 100644
index 00000000000..30343cdc6cf
--- /dev/null
+++ b/src/lib/formats/cp68_dsk.h
@@ -0,0 +1,31 @@
+// license:BSD-3-Clause
+// copyright-holders:Barry Rodewald, Michael R. Furman
+/*
+ * cp68_dsk.h
+ *
+ * Created on: 02/08/2022
+ */
+#ifndef MAME_FORMATS_CP68_DSK_H
+#define MAME_FORMATS_CP68_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+#include "wd177x_dsk.h"
+
+class cp68_format : public wd177x_format
+{
+public:
+ cp68_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual const wd177x_format::format &get_track_format(const format &f, int head, int track) const override;
+};
+
+extern const cp68_format FLOPPY_CP68_FORMAT;
+
+#endif // MAME_FORMATS_CP68_DSK_H
diff --git a/src/lib/formats/cpis_dsk.cpp b/src/lib/formats/cpis_dsk.cpp
index 8246db7e82e..e98bdb74944 100644
--- a/src/lib/formats/cpis_dsk.cpp
+++ b/src/lib/formats/cpis_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/cpis_dsk.c
+ formats/cpis_dsk.cpp
Telenova Compis disk images
@@ -14,17 +14,17 @@ cpis_format::cpis_format() : upd765_format(formats)
{
}
-const char *cpis_format::name() const
+const char *cpis_format::name() const noexcept
{
return "cpis";
}
-const char *cpis_format::description() const
+const char *cpis_format::description() const noexcept
{
return "COMPIS disk image";
}
-const char *cpis_format::extensions() const
+const char *cpis_format::extensions() const noexcept
{
return "dsk,img";
}
@@ -54,4 +54,4 @@ const cpis_format::format cpis_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_CPIS_FORMAT = &floppy_image_format_creator<cpis_format>;
+const cpis_format FLOPPY_CPIS_FORMAT;
diff --git a/src/lib/formats/cpis_dsk.h b/src/lib/formats/cpis_dsk.h
index 5328f5f3ca6..4eedabed68a 100644
--- a/src/lib/formats/cpis_dsk.h
+++ b/src/lib/formats/cpis_dsk.h
@@ -18,14 +18,14 @@ class cpis_format : public upd765_format {
public:
cpis_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_CPIS_FORMAT;
+extern const cpis_format FLOPPY_CPIS_FORMAT;
#endif // MAME_FORMATS_CPIS_DSK_H
diff --git a/src/lib/formats/cqm_dsk.cpp b/src/lib/formats/cqm_dsk.cpp
index e2bde8fcf8c..850d223da7a 100644
--- a/src/lib/formats/cqm_dsk.cpp
+++ b/src/lib/formats/cqm_dsk.cpp
@@ -2,15 +2,20 @@
// copyright-holders:Miodrag Milanovic
/*********************************************************************
- formats/cqm_dsk.c
+ formats/cqm_dsk.cpp
CopyQM disk images
*********************************************************************/
-#include <string.h>
-#include <assert.h>
-#include "flopimg.h"
+#include "cqm_dsk.h"
+#include "flopimg_legacy.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <cstring>
+
#define CQM_HEADER_SIZE 133
@@ -181,7 +186,7 @@ FLOPPY_CONSTRUCT( cqm_dsk_construct )
floppy_image_read(floppy, header, 0, CQM_HEADER_SIZE);
- tag->sector_size = (header[0x04] << 8) + header[0x03];
+ tag->sector_size = get_u16le(&header[0x03]);
tag->sector_per_track = header[0x10];
tag->heads = header[0x12];
tag->tracks = header[0x5b];
@@ -231,61 +236,52 @@ FLOPPY_CONSTRUCT( cqm_dsk_construct )
-
-/*********************************************************************
-
- formats/cqm_dsk.c
-
- CopyQM disk images
-
-*********************************************************************/
-
-#include "cqm_dsk.h"
-
cqm_format::cqm_format()
{
}
-const char *cqm_format::name() const
+const char *cqm_format::name() const noexcept
{
return "cqm";
}
-const char *cqm_format::description() const
+const char *cqm_format::description() const noexcept
{
return "CopyQM disk image";
}
-const char *cqm_format::extensions() const
+const char *cqm_format::extensions() const noexcept
{
return "cqm,cqi,dsk";
}
-int cqm_format::identify(io_generic *io, uint32_t form_factor)
+int cqm_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t h[3];
- io_generic_read(io, h, 0, 3);
+ auto const [err, actual] = read_at(io, 0, h, 3);
+ if (err || (3 != actual))
+ return 0;
if (h[0] == 'C' && h[1] == 'Q' && h[2] == 0x14)
- return 100;
+ return FIFID_SIGN;
return 0;
}
-bool cqm_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool cqm_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
const int max_size = 4*1024*1024; // 4MB ought to be large enough for any floppy
std::vector<uint8_t> imagebuf(max_size);
uint8_t header[CQM_HEADER_SIZE];
- io_generic_read(io, header, 0, CQM_HEADER_SIZE);
+ read_at(io, 0, header, CQM_HEADER_SIZE); // FIXME: check for errors and premature EOF
- int sector_size = (header[0x04] << 8) | header[0x03];
- int sector_per_track = (header[0x11] << 8) | header[0x10];
- int heads = (header[0x13] << 8) | header[0x12];
+ int sector_size = get_u16le(&header[0x03]);
+ int sector_per_track = get_u16le(&header[0x10]);
+ int heads = get_u16le(&header[0x12]);
int tracks = header[0x5b];
// int blind = header[0x58]; // 0=DOS, 1=blind, 2=HFS
int density = header[0x59]; // 0=DD, 1=HD, 2=ED
- int comment_size = (header[0x70] << 8) | header[0x6f];
+ int comment_size = get_u16le(&header[0x6f]);
int sector_base = header[0x71] + 1;
// int interleave = header[0x74]; // TODO
// int skew = header[0x75]; // TODO
@@ -295,19 +291,20 @@ bool cqm_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
case 0:
if (form_factor == floppy_image::FF_525 && tracks > 50)
- image->set_variant(heads == 1 ? floppy_image::SSQD : floppy_image::DSQD);
+ image.set_variant(heads == 1 ? floppy_image::SSQD : floppy_image::DSQD);
else
- image->set_variant(heads == 1 ? floppy_image::SSDD : floppy_image::DSDD);
+ image.set_variant(heads == 1 ? floppy_image::SSDD : floppy_image::DSDD);
break;
case 1:
if (heads == 1)
return false; // single side HD ?
- image->set_variant(floppy_image::DSHD);
+ image.set_variant(floppy_image::DSHD);
break;
case 2:
if (heads == 1)
return false; // single side ED ?
- image->set_variant(floppy_image::DSED);
+ image.set_variant(floppy_image::DSED);
+ break;
default:
return false;
}
@@ -317,14 +314,17 @@ bool cqm_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
int rpm = form_factor == floppy_image::FF_8 || (form_factor == floppy_image::FF_525 && rate >= 300000) ? 360 : 300;
int base_cell_count = rate*60/rpm;
- int cqm_size = io_generic_size(io);
- std::vector<uint8_t> cqmbuf(cqm_size);
- io_generic_read(io, &cqmbuf[0], 0, cqm_size);
+ uint64_t cqm_size;
+ if (io.length(cqm_size))
+ return false;
+ auto const [err, cqmbuf, actual] = read_at(io, 0, cqm_size);
+ if (err || (actual != cqm_size))
+ return false;
// decode the RLE data
for (int s = 0, pos = CQM_HEADER_SIZE + comment_size; pos < cqm_size; )
{
- int16_t len = (cqmbuf[pos + 1] << 8) | cqmbuf[pos];
+ int16_t len = get_s16le(&cqmbuf[pos]);
pos += 2;
if(len < 0)
{
@@ -368,14 +368,14 @@ bool cqm_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-bool cqm_format::save(io_generic *io, floppy_image *image)
+bool cqm_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
return false;
}
-bool cqm_format::supports_save() const
+bool cqm_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_CQM_FORMAT = &floppy_image_format_creator<cqm_format>;
+const cqm_format FLOPPY_CQM_FORMAT;
diff --git a/src/lib/formats/cqm_dsk.h b/src/lib/formats/cqm_dsk.h
index b8e590d9021..382861b8740 100644
--- a/src/lib/formats/cqm_dsk.h
+++ b/src/lib/formats/cqm_dsk.h
@@ -19,16 +19,16 @@ class cqm_format : public floppy_image_format_t
public:
cqm_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
-
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_CQM_FORMAT;
+extern const cqm_format FLOPPY_CQM_FORMAT;
#endif // MAME_FORMATS_CQM_DSK_H
diff --git a/src/lib/formats/csw_cas.cpp b/src/lib/formats/csw_cas.cpp
index 84aab50d3b8..4fa4050c0e3 100644
--- a/src/lib/formats/csw_cas.cpp
+++ b/src/lib/formats/csw_cas.cpp
@@ -1,294 +1,220 @@
// license:BSD-3-Clause
-// copyright-holders:Gordon Jefferyes
+// copyright-holders:Nigel Barnes
/*
-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
-
-
-
+ 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 Encoding application description
+ 0x34 BYTE Header extension data (if present HDR>0)
+ 0x34+HDR CSW data
*/
-#include <string.h>
+#include "csw_cas.h"
+#include "imageutl.h"
+#include "uef_cas.h"
+
+#include "osdcomm.h" // little_endianize_int*
#include <zlib.h>
-#include <assert.h>
-#include "uef_cas.h"
-#include "csw_cas.h"
+#include <cstring>
-#define CSW_WAV_FREQUENCY 44100
static const uint8_t CSW_HEADER[] = { "Compressed Square Wave" };
-static uint32_t get_leuint32(const void *ptr)
-{
- uint32_t value;
- memcpy(&value, ptr, sizeof(value));
- return little_endianize_int32(value);
-}
-static int mycaslen;
+/*-------------------------------------------------
+ csw_cassette_identify - identify cassette
+-------------------------------------------------*/
-static int csw_cas_to_wav_size( const uint8_t *casdata, int caslen )
+static cassette_image::error csw_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- uint8_t MajorRevision;
- uint8_t MinorRevision;
- uint8_t HeaderExtensionLength;
- std::vector<uint8_t> gz_ptr;
-
- int total_size;
- z_stream d_stream;
- int err;
- uint8_t *in_ptr;
- int bsize=0;
-
- if ( memcmp( casdata, CSW_HEADER, sizeof(CSW_HEADER)-1 ) )
- {
- LOG_FORMATS( "csw_cas_to_wav_size: cassette image has incompatible header\n" );
- goto cleanup;
- }
+ uint8_t header[0x34];
- if (casdata[0x16]!=0x1a)
- {
- LOG_FORMATS( "csw_cas_to_wav_size: Terminator Code Not Found\n" );
- goto cleanup;
+ cassette->image_read(header, 0, sizeof(header));
+ if (memcmp(&header[0], CSW_HEADER, sizeof(CSW_HEADER) - 1)) {
+ return cassette_image::error::INVALID_IMAGE;
}
- MajorRevision=casdata[0x17];
- MinorRevision=casdata[0x18];
+ opts->bits_per_sample = 8;
+ opts->channels = 1;
+ opts->sample_frequency = little_endianize_int16(*(uint32_t*)(header + 0x19));
+ return cassette_image::error::SUCCESS;
+}
+
- LOG_FORMATS("Version %d : %d\n",MajorRevision,MinorRevision);
+/*-------------------------------------------------
+ csw_cassette_load - load cassette
+-------------------------------------------------*/
- if (casdata[0x17]!=2)
+static cassette_image::error csw_cassette_load(cassette_image *cassette)
+{
+ uint8_t header[0x34];
+ uint64_t image_size = cassette->image_size();
+ std::vector<uint8_t> image_data(image_size);
+
+ int8_t bit;
+ uint8_t compression;
+ int bsize = 0;
+ size_t csw_data = 0;
+ size_t sample_count = 0;
+ uint32_t sample_rate;
+ std::vector<int8_t> samples;
+
+ /* csw header */
+ cassette->image_read(header, 0, sizeof(header));
+
+ if (header[0x16] != 0x1a)
{
- LOG_FORMATS( "csw_cas_to_wav_size: Unsuported Major Version\n" );
- goto cleanup;
+ LOG_FORMATS("csw_cassette_load: Terminator Code Not Found\n");
+ return cassette_image::error::INVALID_IMAGE;
}
- HeaderExtensionLength=casdata[0x23];
-
- mycaslen=caslen;
- //from here on down for now I am assuming it is compressed csw file.
- in_ptr = (uint8_t*) casdata+0x34+HeaderExtensionLength;
-
- gz_ptr.resize( 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[0];
- d_stream.avail_out = 1;
- d_stream.total_out=0;
-
- d_stream.zalloc = nullptr;
- d_stream.zfree = nullptr;
- d_stream.opaque = nullptr;
- d_stream.data_type=0;
-
- err = inflateInit( &d_stream );
- if ( err != Z_OK )
+ LOG_FORMATS("CSW Version %d.%d\n", header[0x17], header[0x18]);
+ switch (header[0x17])
{
- LOG_FORMATS( "inflateInit2 error: %d\n", err );
- goto cleanup;
+ case 1:
+ sample_rate = little_endianize_int16(*(uint32_t*)(header + 0x19));
+ compression = header[0x1b];
+ bit = (header[0x1c] & 1) ? 127 : -128;
+ csw_data = 0x20;
+
+ LOG_FORMATS("Sample Rate: %u\n", sample_rate);
+ LOG_FORMATS("CompressionType: %u Flags: %u\n", header[0x1b], header[0x1c]);
+ break;
+
+ case 2:
+ sample_rate = little_endianize_int32(*(uint32_t*)(header + 0x19));
+ compression = header[0x21];
+ bit = (header[0x22] & 1) ? 127 : -128;
+ csw_data = (size_t) header[0x23] + 0x34;
+
+ LOG_FORMATS("Sample Rate: %u\n", sample_rate);
+ LOG_FORMATS("Number of Pulses: %u\n", little_endianize_int32(*(uint32_t *)(header + 0x1d)));
+ LOG_FORMATS("CompressionType: %u Flags: %u\n", header[0x21], header[0x22]);
+ LOG_FORMATS("Encoder: ");
+ for (int i = 0; i < 16; i++)
+ LOG_FORMATS("%c", header[0x24 + i]);
+ LOG_FORMATS("\n");
+ break;
+
+ default:
+ LOG_FORMATS("Unsupported Major Version\n");
+ return cassette_image::error::INVALID_IMAGE;
}
-
- total_size=1;
- do
+ /* csw data */
+ switch (compression)
{
- d_stream.next_out = &gz_ptr[0];
- d_stream.avail_out=1;
- err=inflate( &d_stream, Z_SYNC_FLUSH );
- if (err==Z_OK)
+ case 0x01:
+ /* RLE (Run Length Encoding) */
+ for (size_t pos = csw_data; pos < image_size; pos++)
{
- bsize=gz_ptr[0];
- if (bsize==0)
+ bsize = image_data[pos];
+ if (bsize == 0)
+ {
+ bsize = little_endianize_int32(*(uint32_t *)(&image_data[pos + 1]));
+ pos += 4;
+ }
+ for (int i = 0; i < bsize; i++)
{
- d_stream.avail_out=4;
- d_stream.next_out = &gz_ptr[0];
- err=inflate( &d_stream, Z_SYNC_FLUSH );
- bsize=get_leuint32(&gz_ptr[0]);
+ samples.resize(sample_count + 1);
+ samples[sample_count++] = bit;
}
- total_size=total_size+bsize;
+ bit ^= 0xff;
}
- }
- while (err==Z_OK);
-
- if ( err != Z_STREAM_END )
- {
- LOG_FORMATS( "inflate error: %d\n", err );
- goto cleanup;
- }
-
- err = inflateEnd( &d_stream );
- if ( err != Z_OK )
- {
- LOG_FORMATS( "inflateEnd error: %d\n", err );
- goto cleanup;
- }
+ break;
- return total_size;
+ case 0x02:
+ /* Z-RLE (CSW v2.xx only) */
+ cassette->image_read(&image_data[0], 0, image_size);
-cleanup:
- return -1;
-}
-
-static int csw_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
-{
- uint32_t SampleRate;
- uint32_t NumberOfPulses;
- uint8_t CompressionType;
- uint8_t Flags;
- uint8_t HeaderExtensionLength;
- int8_t Bit;
+ std::vector<uint8_t> gz_ptr;
+ z_stream d_stream;
+ int err;
- std::vector<uint8_t> gz_ptr;
- int total_size;
- z_stream d_stream;
- int err;
- uint8_t *in_ptr;
- int bsize=0;
- int i;
+ gz_ptr.resize(8);
- LOG_FORMATS("Length %d\n",length);
+ d_stream.next_in = (unsigned char *) &image_data[csw_data];
+ d_stream.avail_in = image_size - 0x34 - header[0x23];
+ d_stream.total_in = 0;
- SampleRate=get_leuint32(bytes+0x19);
- LOG_FORMATS("Sample rate %u\n",SampleRate);
-
- NumberOfPulses=get_leuint32(bytes+0x1d);
- LOG_FORMATS("Number Of Pulses %u\n",NumberOfPulses);
-
- CompressionType=bytes[0x21];
- Flags=bytes[0x22];
- HeaderExtensionLength=bytes[0x23];
-
- Bit = (Flags & 1) ? 100 : -100;
-
- LOG_FORMATS("CompressionType %u Flags %u HeaderExtensionLength %u\n",CompressionType,Flags,HeaderExtensionLength);
-
- LOG_FORMATS("Encoder: ");
- for (i = 0; i < 16; i++)
- LOG_FORMATS("%c", bytes[0x24 + i]);
- LOG_FORMATS("\n");
-
- LOG_FORMATS("Header: ");
- for (i = 0; i < HeaderExtensionLength; i++)
- LOG_FORMATS("%c", bytes[0x34 + i]);
- LOG_FORMATS("\n");
-
- //from here on down for now I am assuming it is compressed csw file.
- in_ptr = (uint8_t*) bytes+0x34+HeaderExtensionLength;
-
- gz_ptr.resize( 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[0];
- d_stream.avail_out = 1;
- d_stream.total_out=0;
+ d_stream.next_out = &gz_ptr[0];
+ d_stream.avail_out = 1;
+ d_stream.total_out = 0;
- d_stream.zalloc = nullptr;
- d_stream.zfree = nullptr;
- d_stream.opaque = nullptr;
- d_stream.data_type=0;
+ d_stream.zalloc = nullptr;
+ d_stream.zfree = nullptr;
+ d_stream.opaque = nullptr;
+ d_stream.data_type = 0;
- err = inflateInit( &d_stream );
- if ( err != Z_OK )
- {
- LOG_FORMATS( "inflateInit2 error: %d\n", err );
- goto cleanup;
- }
-
- total_size=0;
+ err = inflateInit(&d_stream);
+ if (err != Z_OK)
+ {
+ LOG_FORMATS("inflateInit error: %d\n", err);
+ return cassette_image::error::INVALID_IMAGE;
+ }
- do
- {
- d_stream.next_out = &gz_ptr[0];
- d_stream.avail_out=1;
- err=inflate( &d_stream, Z_SYNC_FLUSH );
- if (err==Z_OK)
+ do
{
- bsize=gz_ptr[0];
- if (bsize==0)
- {
- d_stream.avail_out=4;
- d_stream.next_out = &gz_ptr[0];
- err=inflate( &d_stream, Z_SYNC_FLUSH );
- bsize=get_leuint32(&gz_ptr[0]);
- }
- for (i=0;i<bsize;i++)
+ d_stream.next_out = &gz_ptr[0];
+ d_stream.avail_out = 1;
+ err = inflate(&d_stream, Z_SYNC_FLUSH);
+ if (err == Z_OK)
{
- buffer[total_size++]=Bit;
+ bsize = gz_ptr[0];
+ if (bsize == 0)
+ {
+ d_stream.avail_out = 4;
+ d_stream.next_out = &gz_ptr[0];
+ err = inflate(&d_stream, Z_SYNC_FLUSH);
+ bsize = little_endianize_int32(*(uint32_t *)(&gz_ptr[0]));
+ }
+ for (int i = 0; i < bsize; i++)
+ {
+ samples.resize(sample_count + 1);
+ samples[sample_count++] = bit;
+ }
+ bit ^= 0xff;
}
- Bit=-Bit;
}
- }
- while (err==Z_OK);
+ while (err == Z_OK);
- if ( err != Z_STREAM_END )
- {
- LOG_FORMATS( "inflate error: %d\n", err );
- goto cleanup;
- }
+ if (err != Z_STREAM_END)
+ {
+ LOG_FORMATS("inflate error: %d\n", err);
+ return cassette_image::error::INVALID_IMAGE;
+ }
- err = inflateEnd( &d_stream );
- if ( err != Z_OK )
- {
- LOG_FORMATS( "inflateEnd error: %d\n", err );
- goto cleanup;
+ err = inflateEnd(&d_stream);
+ if (err != Z_OK)
+ {
+ LOG_FORMATS("inflateEnd error: %d\n", err);
+ return cassette_image::error::INVALID_IMAGE;
+ }
+ break;
}
- return length;
-
-cleanup:
- return -1;
+ return cassette->put_samples(0, 0.0, (double) sample_count / sample_rate, sample_count, 1, &samples[0], cassette_image::WAVEFORM_8BIT);
}
-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 cassette_image::error csw_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts )
-{
- uint8_t header[22];
-
- cassette_image_read(cassette, header, 0, sizeof(header));
- if (memcmp(&header[0], CSW_HEADER, sizeof(CSW_HEADER) - 1)) {
- return cassette_image::error::INVALID_IMAGE;
- }
- return cassette_legacy_identify( cassette, opts, &csw_legacy_fill_wave );
-}
-
-static cassette_image::error csw_cassette_load( cassette_image *cassette )
-{
- return cassette_legacy_construct( cassette, &csw_legacy_fill_wave );
-}
+/*-------------------------------------------------
+ CassetteFormat csw_cassette_format
+-------------------------------------------------*/
-const struct CassetteFormat csw_cassette_format = {
+const cassette_image::Format csw_cassette_format = {
"csw",
csw_cassette_identify,
csw_cassette_load,
diff --git a/src/lib/formats/csw_cas.h b/src/lib/formats/csw_cas.h
index e6e5a62d764..270d85bcd37 100644
--- a/src/lib/formats/csw_cas.h
+++ b/src/lib/formats/csw_cas.h
@@ -12,7 +12,7 @@
#include "cassimg.h"
-extern const struct CassetteFormat csw_cassette_format;
+extern const cassette_image::Format csw_cassette_format;
CASSETTE_FORMATLIST_EXTERN(csw_cassette_formats);
CASSETTE_FORMATLIST_EXTERN(bbc_cassette_formats);
diff --git a/src/lib/formats/d64_dsk.cpp b/src/lib/formats/d64_dsk.cpp
index f26a3e676fb..e1fb024e74d 100644
--- a/src/lib/formats/d64_dsk.cpp
+++ b/src/lib/formats/d64_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/d64_dsk.c
+ formats/d64_dsk.cpp
Commodore 4040/1541/1551 sector disk image format
@@ -10,9 +10,15 @@
*********************************************************************/
-#include "emu.h" // emu_fatalerror, fatalerror
#include "formats/d64_dsk.h"
+#include "ioprocs.h"
+
+#include "osdcore.h" // osd_printf_*
+
+#include <cstring>
+
+
d64_format::d64_format()
{
formats = file_formats;
@@ -23,17 +29,17 @@ d64_format::d64_format(const format *_formats)
formats = _formats;
}
-const char *d64_format::name() const
+const char *d64_format::name() const noexcept
{
return "d64";
}
-const char *d64_format::description() const
+const char *d64_format::description() const noexcept
{
return "Commodore 4040/1541/1551 disk image";
}
-const char *d64_format::extensions() const
+const char *d64_format::extensions() const noexcept
{
return "d64";
}
@@ -79,9 +85,12 @@ const int d64_format::speed_zone[] =
0, 0 // 41-42
};
-int d64_format::find_size(io_generic *io, uint32_t form_factor) const
+int d64_format::find_size(util::random_read &io, uint32_t form_factor) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if(io.length(size))
+ return 0;
+
for(int i=0; formats[i].sector_count; i++) {
const format &f = formats[i];
if(size == (uint32_t) f.sector_count*f.sector_base_size*f.head_count)
@@ -89,40 +98,41 @@ int d64_format::find_size(io_generic *io, uint32_t form_factor) const
if(size == (uint32_t) (f.sector_count*f.sector_base_size*f.head_count) + f.sector_count)
return i;
}
+
return -1;
}
-int d64_format::identify(io_generic *io, uint32_t form_factor)
+int d64_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ const int type = find_size(io, form_factor);
if (type != -1)
- return 50;
+ return FIFID_SIZE;
return 0;
}
-int d64_format::get_physical_track(const format &f, int head, int track)
+int d64_format::get_physical_track(const format &f, int head, int track) const
{
// skip halftracks
return track * 2;
}
-int d64_format::get_disk_id_offset(const format &f)
+int d64_format::get_disk_id_offset(const format &f) const
{
// t18s0 +0xa2
return 0x165a2;
}
-void d64_format::get_disk_id(const format &f, io_generic *io, uint8_t &id1, uint8_t &id2)
+void d64_format::get_disk_id(const format &f, util::random_read &io, uint8_t &id1, uint8_t &id2) const
{
uint8_t id[2];
- io_generic_read(io, id, get_disk_id_offset(f), 2);
+ /*auto const [err, actual] =*/ read_at(io, get_disk_id_offset(f), id, 2); // FIXME: check for errors and premature EOF
id1 = id[0];
id2 = id[1];
}
-int d64_format::get_image_offset(const format &f, int _head, int _track)
+int d64_format::get_image_offset(const format &f, int _head, int _track) const
{
int offset = 0;
if (_head) {
@@ -136,22 +146,22 @@ int d64_format::get_image_offset(const format &f, int _head, int _track)
return offset;
}
-int d64_format::compute_track_size(const format &f, int track)
+int d64_format::compute_track_size(const format &f, int track) const
{
return this->get_sectors_per_track(f, track) * f.sector_base_size;
}
-uint32_t d64_format::get_cell_size(const format &f, int track)
+uint32_t d64_format::get_cell_size(const format &f, int track) const
{
return cell_size[speed_zone[track]];
}
-int d64_format::get_sectors_per_track(const format &f, int track)
+int d64_format::get_sectors_per_track(const format &f, int track) const
{
return sectors_per_track[track];
}
-floppy_image_format_t::desc_e* d64_format::get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2)
+floppy_image_format_t::desc_e* d64_format::get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2) const
{
static floppy_image_format_t::desc_e desc[] = {
/* 00 */ { SECTOR_LOOP_START, 0, -1 },
@@ -199,14 +209,14 @@ void d64_format::build_sector_description(const format &f, uint8_t *sectdata, ui
}
}
-void d64_format::fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size)
+void d64_format::fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size) const
{
desc[21].p2 = remaining_size / 8;
desc[22].p2 = remaining_size & 7;
desc[22].p1 >>= remaining_size & 0x01;
}
-bool d64_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool d64_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
int type = find_size(io, form_factor);
if(type == -1)
@@ -214,7 +224,9 @@ bool d64_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
const format &f = formats[type];
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if(io.length(size))
+ return false;
std::vector<uint8_t> img;
if(size == (uint32_t)f.sector_count*f.sector_base_size) {
@@ -225,7 +237,7 @@ bool d64_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
img.resize(size);
}
- io_generic_read(io, &img[0], 0, size);
+ /*auto const [err, actual] =*/ read_at(io, 0, &img[0], size); // FIXME: check for errors and premature EOF
int track_offset = 0, error_offset = f.sector_count*f.sector_base_size;
@@ -244,8 +256,10 @@ bool d64_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
floppy_image_format_t::desc_e *desc = this->get_sector_desc(f, current_size, sector_count, id1, id2, gap2);
int remaining_size = total_size - current_size;
- if(remaining_size < 0)
- throw emu_fatalerror("d64_format: Incorrect track layout, max_size=%d, current_size=%d", total_size, current_size);
+ if(remaining_size < 0) {
+ osd_printf_error("d64_format: Incorrect track layout, max_size=%d, current_size=%d\n", total_size, current_size);
+ return false;
+ }
this->fix_end_gap(desc, remaining_size);
@@ -259,12 +273,12 @@ bool d64_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
}
}
- image->set_variant(f.variant);
+ image.set_variant(f.variant);
return true;
}
-bool d64_format::save(io_generic *io, floppy_image *image)
+bool d64_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
const format &f = formats[0];
@@ -278,37 +292,34 @@ bool d64_format::save(io_generic *io, floppy_image *image)
build_sector_description(f, sectdata, 0, 0, sectors, sector_count);
extract_sectors(image, f, sectors, track, head, sector_count);
- io_generic_write(io, sectdata, offset, track_size);
+ /*auto const [err, actual] =*/ write_at(io, offset, sectdata, track_size); // FIXME: check for errors
}
}
return true;
}
-void d64_format::extract_sectors(floppy_image *image, const format &f, desc_s *sdesc, int track, int head, int sector_count)
+void d64_format::extract_sectors(const floppy_image &image, const format &f, desc_s *sdesc, int track, int head, int sector_count) const
{
- uint8_t bitstream[500000/8];
- uint8_t sectdata[50000];
- desc_xs sectors[256];
int physical_track = this->get_physical_track(f, head, track);
int cell_size = this->get_cell_size(f, track);
- int track_size;
// Extract the sectors
- generate_bitstream_from_track(physical_track, head, cell_size, bitstream, track_size, image);
- extract_sectors_from_bitstream_gcr5(bitstream, track_size, sectors, sectdata, sizeof(sectdata), head, f.track_count);
+ auto bitstream = generate_bitstream_from_track(physical_track, head, cell_size, image);
+ auto sectors = extract_sectors_from_bitstream_gcr5(bitstream, head, f.track_count);
for(int i=0; i<sector_count; i++) {
desc_s &ds = sdesc[i];
- desc_xs &xs = sectors[ds.sector_id];
- if(!xs.data)
+ if(ds.sector_id >= sectors.size() || sectors[ds.sector_id].empty())
memset((void *)ds.data, 0, ds.size);
- else if(xs.size < ds.size) {
- memcpy((void *)ds.data, xs.data, xs.size);
- memset((uint8_t *)ds.data + xs.size, 0, xs.size - ds.size);
+
+ else if(sectors[ds.sector_id].size() < ds.size) {
+ memcpy((void *)ds.data, sectors[ds.sector_id].data(), sectors[ds.sector_id].size());
+ memset((uint8_t *)ds.data + sectors[ds.sector_id].size(), 0, ds.size - sectors[ds.sector_id].size());
+
} else
- memcpy((void *)ds.data, xs.data, ds.size);
+ memcpy((void *)ds.data, sectors[ds.sector_id].data(), ds.size);
}
}
-const floppy_format_type FLOPPY_D64_FORMAT = &floppy_image_format_creator<d64_format>;
+const d64_format FLOPPY_D64_FORMAT;
diff --git a/src/lib/formats/d64_dsk.h b/src/lib/formats/d64_dsk.h
index c91f116795f..bddb13462b8 100644
--- a/src/lib/formats/d64_dsk.h
+++ b/src/lib/formats/d64_dsk.h
@@ -33,14 +33,14 @@ public:
d64_format();
d64_format(const format *formats);
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
- virtual bool supports_save() const override { return true; }
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override { return true; }
protected:
enum
@@ -61,19 +61,19 @@ protected:
const format *formats;
- int find_size(io_generic *io, uint32_t form_factor) const;
- virtual int get_physical_track(const format &f, int head, int track);
- virtual uint32_t get_cell_size(const format &f, int track);
- virtual int get_sectors_per_track(const format &f, int track);
- virtual int get_disk_id_offset(const format &f);
- void get_disk_id(const format &f, io_generic *io, uint8_t &id1, uint8_t &id2);
- virtual int get_image_offset(const format &f, int head, int track);
- int compute_track_size(const format &f, int track);
- virtual int get_gap2(const format &f, int head, int track) { return f.gap_2; }
- virtual floppy_image_format_t::desc_e* get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2);
+ int find_size(util::random_read &io, uint32_t form_factor) const;
+ virtual int get_physical_track(const format &f, int head, int track) const;
+ virtual uint32_t get_cell_size(const format &f, int track) const;
+ virtual int get_sectors_per_track(const format &f, int track) const;
+ virtual int get_disk_id_offset(const format &f) const;
+ void get_disk_id(const format &f, util::random_read &io, uint8_t &id1, uint8_t &id2) const;
+ virtual int get_image_offset(const format &f, int head, int track) const;
+ int compute_track_size(const format &f, int track) const;
+ virtual int get_gap2(const format &f, int head, int track) const { return f.gap_2; }
+ virtual floppy_image_format_t::desc_e* get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2) const;
void build_sector_description(const format &f, uint8_t *sectdata, uint32_t sect_offs, uint32_t error_offs, desc_s *sectors, int sector_count) const;
- virtual void fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size);
- void extract_sectors(floppy_image *image, const format &f, desc_s *sdesc, int track, int head, int sector_count);
+ virtual void fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size) const;
+ void extract_sectors(const floppy_image &image, const format &f, desc_s *sdesc, int track, int head, int sector_count) const;
static const format file_formats[];
@@ -82,6 +82,6 @@ protected:
static const int speed_zone[];
};
-extern const floppy_format_type FLOPPY_D64_FORMAT;
+extern const d64_format FLOPPY_D64_FORMAT;
#endif // MAME_FORMATS_D64_DSK_H
diff --git a/src/lib/formats/d71_dsk.cpp b/src/lib/formats/d71_dsk.cpp
index af1186948c6..a7909ac84fb 100644
--- a/src/lib/formats/d71_dsk.cpp
+++ b/src/lib/formats/d71_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/d71_dsk.c
+ formats/d71_dsk.cpp
Commodore 1571 sector disk image format
*********************************************************************/
-#include <assert.h>
-
#include "formats/d71_dsk.h"
d71_format::d71_format() : d64_format(formats)
{
}
-const char *d71_format::name() const
+const char *d71_format::name() const noexcept
{
return "d71";
}
-const char *d71_format::description() const
+const char *d71_format::description() const noexcept
{
return "Commodore 1571 disk image";
}
-const char *d71_format::extensions() const
+const char *d71_format::extensions() const noexcept
{
return "d71";
}
@@ -38,4 +36,4 @@ const d71_format::format d71_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_D71_FORMAT = &floppy_image_format_creator<d71_format>;
+const d71_format FLOPPY_D71_FORMAT;
diff --git a/src/lib/formats/d71_dsk.h b/src/lib/formats/d71_dsk.h
index efe39da3b4f..7afb8d65edd 100644
--- a/src/lib/formats/d71_dsk.h
+++ b/src/lib/formats/d71_dsk.h
@@ -19,14 +19,14 @@ class d71_format : public d64_format
public:
d71_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
protected:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_D71_FORMAT;
+extern const d71_format FLOPPY_D71_FORMAT;
#endif // MAME_FORMATS_D71_DSK_H
diff --git a/src/lib/formats/d80_dsk.cpp b/src/lib/formats/d80_dsk.cpp
index e08ef22f6b9..90fffea307d 100644
--- a/src/lib/formats/d80_dsk.cpp
+++ b/src/lib/formats/d80_dsk.cpp
@@ -2,16 +2,17 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/d80_dsk.c
+ formats/d80_dsk.cpp
Commodore 8050 sector disk image format
*********************************************************************/
-#include <assert.h>
-
#include "formats/d80_dsk.h"
+#include "ioprocs.h"
+
+
d80_format::d80_format() : d64_format(file_formats), formats(nullptr)
{
}
@@ -20,17 +21,17 @@ d80_format::d80_format(const format *_formats) : d64_format(_formats), formats(n
{
}
-const char *d80_format::name() const
+const char *d80_format::name() const noexcept
{
return "d80";
}
-const char *d80_format::description() const
+const char *d80_format::description() const noexcept
{
return "Commodore 8050 disk image";
}
-const char *d80_format::extensions() const
+const char *d80_format::extensions() const noexcept
{
return "d80";
}
@@ -70,28 +71,28 @@ const int d80_format::d80_speed_zone[] =
0, 0, 0, 0, 0, 0, 0 // 78-84
};
-int d80_format::get_physical_track(const format &f, int head, int track)
+int d80_format::get_physical_track(const format &f, int head, int track) const
{
return track;
}
-uint32_t d80_format::get_cell_size(const format &f, int track)
+uint32_t d80_format::get_cell_size(const format &f, int track) const
{
return d80_cell_size[d80_speed_zone[track]];
}
-int d80_format::get_sectors_per_track(const format &f, int track)
+int d80_format::get_sectors_per_track(const format &f, int track) const
{
return d80_sectors_per_track[track];
}
-int d80_format::get_disk_id_offset(const format &f)
+int d80_format::get_disk_id_offset(const format &f) const
{
// t39s0 +0x18
return 0x44e18;
}
-floppy_image_format_t::desc_e* d80_format::get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2)
+floppy_image_format_t::desc_e* d80_format::get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2) const
{
static floppy_image_format_t::desc_e desc[] = {
/* 00 */ { SECTOR_LOOP_START, 0, -1 },
@@ -124,11 +125,11 @@ floppy_image_format_t::desc_e* d80_format::get_sector_desc(const format &f, int
return desc;
}
-void d80_format::fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size)
+void d80_format::fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size) const
{
desc[19].p2 = remaining_size / 8;
desc[20].p2 = remaining_size & 7;
desc[20].p1 >>= remaining_size & 0x01;
}
-const floppy_format_type FLOPPY_D80_FORMAT = &floppy_image_format_creator<d80_format>;
+const d80_format FLOPPY_D80_FORMAT;
diff --git a/src/lib/formats/d80_dsk.h b/src/lib/formats/d80_dsk.h
index b1150ff992a..a9141b3c220 100644
--- a/src/lib/formats/d80_dsk.h
+++ b/src/lib/formats/d80_dsk.h
@@ -20,19 +20,19 @@ public:
d80_format();
d80_format(const format *formats);
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
protected:
const format *formats;
- virtual int get_physical_track(const format &f, int head, int track) override;
- virtual uint32_t get_cell_size(const format &f, int track) override;
- virtual int get_sectors_per_track(const format &f, int track) override;
- virtual int get_disk_id_offset(const format &f) override;
- virtual floppy_image_format_t::desc_e* get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2) override;
- virtual void fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size) override;
+ virtual int get_physical_track(const format &f, int head, int track) const override;
+ virtual uint32_t get_cell_size(const format &f, int track) const override;
+ virtual int get_sectors_per_track(const format &f, int track) const override;
+ virtual int get_disk_id_offset(const format &f) const override;
+ virtual floppy_image_format_t::desc_e* get_sector_desc(const format &f, int &current_size, int sector_count, uint8_t id1, uint8_t id2, int gap_2) const override;
+ virtual void fix_end_gap(floppy_image_format_t::desc_e* desc, int remaining_size) const override;
static const format file_formats[];
@@ -41,6 +41,6 @@ protected:
static const int d80_sectors_per_track[];
};
-extern const floppy_format_type FLOPPY_D80_FORMAT;
+extern const d80_format FLOPPY_D80_FORMAT;
#endif // MAME_FORMATS_D80_DSK_H
diff --git a/src/lib/formats/d81_dsk.cpp b/src/lib/formats/d81_dsk.cpp
index 714b9c3cd9e..96dc9789eb6 100644
--- a/src/lib/formats/d81_dsk.cpp
+++ b/src/lib/formats/d81_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/d81_dsk.c
+ formats/d81_dsk.cpp
Commodore 1581 disk image format
@@ -73,25 +73,23 @@ PER SECTOR ORGANIZATION:
*/
-#include <assert.h>
-
#include "formats/d81_dsk.h"
d81_format::d81_format() : wd177x_format(formats)
{
}
-const char *d81_format::name() const
+const char *d81_format::name() const noexcept
{
return "d81";
}
-const char *d81_format::description() const
+const char *d81_format::description() const noexcept
{
return "Commodore 1581 disk image";
}
-const char *d81_format::extensions() const
+const char *d81_format::extensions() const noexcept
{
return "d81";
}
@@ -117,7 +115,7 @@ const d81_format::format d81_format::formats[] = {
{}
};
-floppy_image_format_t::desc_e* d81_format::get_desc_mfm(const format &f, int &current_size, int &end_gap_index)
+floppy_image_format_t::desc_e* d81_format::get_desc_mfm(const format &f, int &current_size, int &end_gap_index) const
{
static floppy_image_format_t::desc_e desc[25] = {
/* 00 */ { MFM, 0x4e, f.gap_1 },
@@ -161,4 +159,4 @@ floppy_image_format_t::desc_e* d81_format::get_desc_mfm(const format &f, int &cu
return desc;
}
-const floppy_format_type FLOPPY_D81_FORMAT = &floppy_image_format_creator<d81_format>;
+const d81_format FLOPPY_D81_FORMAT;
diff --git a/src/lib/formats/d81_dsk.h b/src/lib/formats/d81_dsk.h
index 00d99807c0e..5ebf62e9dae 100644
--- a/src/lib/formats/d81_dsk.h
+++ b/src/lib/formats/d81_dsk.h
@@ -19,16 +19,16 @@ class d81_format : public wd177x_format
public:
d81_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual floppy_image_format_t::desc_e* get_desc_mfm(const format &f, int &current_size, int &end_gap_index) override;
+ virtual floppy_image_format_t::desc_e* get_desc_mfm(const format &f, int &current_size, int &end_gap_index) const override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_D81_FORMAT;
+extern const d81_format FLOPPY_D81_FORMAT;
#endif // MAME_FORMATS_D81_DSK_H
diff --git a/src/lib/formats/d82_dsk.cpp b/src/lib/formats/d82_dsk.cpp
index 00de1f5fd7c..067e36a36d6 100644
--- a/src/lib/formats/d82_dsk.cpp
+++ b/src/lib/formats/d82_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/d82_dsk.c
+ formats/d82_dsk.cpp
Commodore 8250/SFD-1001 sector disk image format
*********************************************************************/
-#include <assert.h>
-
#include "formats/d82_dsk.h"
d82_format::d82_format() : d80_format(file_formats)
{
}
-const char *d82_format::name() const
+const char *d82_format::name() const noexcept
{
return "d82";
}
-const char *d82_format::description() const
+const char *d82_format::description() const noexcept
{
return "Commodore 8250/SFD-1001 disk image";
}
-const char *d82_format::extensions() const
+const char *d82_format::extensions() const noexcept
{
return "d82";
}
@@ -38,4 +36,4 @@ const d82_format::format d82_format::file_formats[] = {
{}
};
-const floppy_format_type FLOPPY_D82_FORMAT = &floppy_image_format_creator<d82_format>;
+const d82_format FLOPPY_D82_FORMAT;
diff --git a/src/lib/formats/d82_dsk.h b/src/lib/formats/d82_dsk.h
index d2d189ebe0c..a35f82cf4e8 100644
--- a/src/lib/formats/d82_dsk.h
+++ b/src/lib/formats/d82_dsk.h
@@ -19,14 +19,14 @@ class d82_format : public d80_format
public:
d82_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
protected:
static const format file_formats[];
};
-extern const floppy_format_type FLOPPY_D82_FORMAT;
+extern const d82_format FLOPPY_D82_FORMAT;
#endif // MAME_FORMATS_D82_DSK_H
diff --git a/src/lib/formats/d88_dsk.cpp b/src/lib/formats/d88_dsk.cpp
index c2bf9772e7d..93a995a47fb 100644
--- a/src/lib/formats/d88_dsk.cpp
+++ b/src/lib/formats/d88_dsk.cpp
@@ -29,13 +29,19 @@
*
*/
- #include <assert.h>
-
-#include "flopimg.h"
+#include "flopimg_legacy.h"
#include "imageutl.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <tuple>
+
+
#define D88_HEADER_LEN 0x2b0
+#define SPOT_DUPLICATES 0
+
struct d88_tag
{
uint32_t image_size;
@@ -68,7 +74,7 @@ static int d88_get_sector_id(floppy_image_legacy *floppy, int head, int track, i
x=0;
while(x<sector_index)
{
- offset += ((sector_hdr[15] << 8) | sector_hdr[14]);
+ offset += get_u16le(&sector_hdr[14]);
offset += 16;
floppy_image_read(floppy,sector_hdr,offset,16);
x++;
@@ -120,10 +126,10 @@ static floperr_t d88_get_sector_length(floppy_image_legacy *floppy, int head, in
if(sector == sector_hdr[2])
{
if(sector_length)
- *sector_length = (sector_hdr[15] << 8) | sector_hdr[14];
+ *sector_length = get_u16le(&sector_hdr[14]);
return FLOPPY_ERROR_SUCCESS;
}
- len = (sector_hdr[15] << 8) | sector_hdr[14];
+ len = get_u16le(&sector_hdr[14]);
len += 16;
offset += len;
}
@@ -161,7 +167,7 @@ static uint32_t d88_get_sector_offset(floppy_image_legacy* floppy, int head, int
LOG_FORMATS("d88_get_sector_offset - track %i, side %i, sector %02x, returns %08x\n",track,head,sector,offset+16);
return offset + 16;
}
- len = (sector_hdr[15] << 8) | sector_hdr[14];
+ len = get_u16le(&sector_hdr[14]);
len += 16;
offset += len;
}
@@ -191,7 +197,7 @@ static floperr_t d88_get_indexed_sector_info(floppy_image_legacy *floppy, int he
x=0;
while(x<sector_index)
{
- offset += ((sector_hdr[15] << 8) | sector_hdr[14]);
+ offset += get_u16le(&sector_hdr[14]);
offset += 16;
floppy_image_read(floppy,sector_hdr,offset,16);
x++;
@@ -201,7 +207,7 @@ static floperr_t d88_get_indexed_sector_info(floppy_image_legacy *floppy, int he
return FLOPPY_ERROR_SEEKERROR;
if(sector_length)
- *sector_length = (sector_hdr[15] << 8) | sector_hdr[14];
+ *sector_length = get_u16le(&sector_hdr[14]);
if(cylinder)
*cylinder = sector_hdr[0];
if(side)
@@ -275,43 +281,29 @@ static floperr_t d88_write_indexed_sector(floppy_image_legacy *floppy, int head,
static void d88_get_header(floppy_image_legacy* floppy,uint32_t* size, uint8_t* prot, uint8_t* type, uint32_t* offsets)
{
uint8_t header[D88_HEADER_LEN];
- int x,s;
floppy_image_read(floppy,header,0,D88_HEADER_LEN);
-#ifdef SPOT_DUPLICATES
+ if(SPOT_DUPLICATES)
+ {
// there exist many .d88 files with same data and different headers and
// this allows to spot duplicates, making easier to debug softlists.
uint32_t temp_size = floppy_image_size(floppy);
- uint8_t tmp_copy[temp_size - D88_HEADER_LEN];
- floppy_image_read(floppy,tmp_copy,D88_HEADER_LEN,temp_size - D88_HEADER_LEN);
- printf("CRC16: %d\n", ccitt_crc16(0xffff, tmp_copy, temp_size - D88_HEADER_LEN));
-#endif
+ auto tmp_copy = std::make_unique<uint8_t[]>(temp_size - D88_HEADER_LEN);
+ floppy_image_read(floppy,tmp_copy.get(),D88_HEADER_LEN,temp_size - D88_HEADER_LEN);
+ printf("CRC16: %d\n", ccitt_crc16(0xffff, tmp_copy.get(), temp_size - D88_HEADER_LEN));
+ }
if(prot)
*prot = header[0x1a];
if(type)
*type = header[0x1b];
if(size)
- {
- s = 0;
- s |= header[0x1f] << 24;
- s |= header[0x1e] << 16;
- s |= header[0x1d] << 8;
- s |= header[0x1c];
- *size = s;
- }
+ *size = get_u32le(&header[0x1c]);
if(offsets)
{
- for(x=0;x<164;x++)
- {
- s = 0;
- s |= header[0x23 + (x*4)] << 24;
- s |= header[0x22 + (x*4)] << 16;
- s |= header[0x21 + (x*4)] << 8;
- s |= header[0x20 + (x*4)];
- *(offsets+x) = s;
- }
+ for(int x=0;x<164;x++)
+ *(offsets+x) = get_u32le(&header[0x20 + (x*4)]);
}
}
@@ -387,7 +379,7 @@ FLOPPY_CONSTRUCT(d88_dsk_construct)
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/d88_dsk.h
+ formats/d88_dsk.cpp
D88 disk images
@@ -399,39 +391,48 @@ d88_format::d88_format()
{
}
-const char *d88_format::name() const
+const char *d88_format::name() const noexcept
{
return "d88";
}
-const char *d88_format::description() const
+const char *d88_format::description() const noexcept
{
return "D88 disk image";
}
-const char *d88_format::extensions() const
+const char *d88_format::extensions() const noexcept
{
return "d77,d88,1dd";
}
-int d88_format::identify(io_generic *io, uint32_t form_factor)
+int d88_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if(io.length(size))
+ return 0;
+
uint8_t h[32];
+ auto const [err, actual] = read_at(io, 0, h, 32);
+ if(err || (32 != actual))
+ return 0;
- io_generic_read(io, h, 0, 32);
- if((little_endianize_int32(*(uint32_t *)(h+0x1c)) == size) &&
+ if(((get_u32le(h+0x1c) == size) || (get_u32le(h+0x1c) == (size >> 1))) &&
(h[0x1b] == 0x00 || h[0x1b] == 0x10 || h[0x1b] == 0x20 || h[0x1b] == 0x30 || h[0x1b] == 0x40))
- return 100;
+ return FIFID_SIZE|FIFID_STRUCT;
return 0;
}
-bool d88_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool d88_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint8_t h[32];
+ std::error_condition err;
+ size_t actual;
- io_generic_read(io, h, 0, 32);
+ uint8_t h[32];
+ std::tie(err, actual) = read_at(io, 0, h, 32);
+ if(err || (32 != actual))
+ return false;
int cell_count = 0;
int track_count = 0;
@@ -441,45 +442,55 @@ bool d88_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
cell_count = 100000;
track_count = 42;
head_count = 2;
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
break;
case 0x10:
cell_count = 100000;
track_count = 82;
head_count = 2;
- image->set_variant(floppy_image::DSQD);
+ image.set_variant(floppy_image::DSQD);
break;
case 0x20:
cell_count = form_factor == floppy_image::FF_35 ? 200000 : 166666;
track_count = 82;
head_count = 2;
- image->set_variant(floppy_image::DSHD);
+ image.set_variant(floppy_image::DSHD);
break;
case 0x30:
cell_count = 100000;
track_count = 42;
head_count = 1;
- image->set_variant(floppy_image::SSDD);
+ image.set_variant(floppy_image::SSDD);
break;
case 0x40:
cell_count = 100000;
track_count = 82;
head_count = 1;
- image->set_variant(floppy_image::SSQD);
+ image.set_variant(floppy_image::SSQD);
break;
}
if(!head_count)
return false;
+ int img_tracks, img_heads;
+ image.get_maximal_geometry(img_tracks, img_heads);
+ if (track_count > img_tracks)
+ osd_printf_warning("d88: Floppy disk has too many tracks for this drive (floppy tracks=%d, drive tracks=%d).\n", track_count, img_tracks);
+
+ if (head_count > img_heads)
+ osd_printf_warning("d88: Floppy disk has excess of heads for this drive that will be discarded (floppy heads=%d, drive heads=%d).\n", head_count, img_heads);
+
uint32_t track_pos[164];
- io_generic_read(io, track_pos, 32, 164*4);
+ std::tie(err, actual) = read_at(io, 32, track_pos, 164*4); // FIXME: check for errors and premature EOF
- uint64_t file_size = io_generic_size(io);
+ uint64_t file_size;
+ if(io.length(file_size))
+ return false;
for(int track=0; track < track_count; track++)
for(int head=0; head < head_count; head++) {
@@ -490,13 +501,14 @@ bool d88_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
uint8_t sect_data[65536];
int sdatapos = 0;
int sector_count = 1;
+ uint8_t density = 0;
for(int i=0; i<sector_count; i++) {
if (pos + 16 > file_size)
return true;
uint8_t hs[16];
- io_generic_read(io, hs, pos, 16);
+ std::tie(err, actual) = read_at(io, pos, hs, 16); // FIXME: check for errors and premature EOF
pos += 16;
uint16_t size = little_endianize_int16(*(uint16_t *)(hs+14));
@@ -509,6 +521,8 @@ bool d88_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
// Support broken vfman converter
if(sector_count == 0x1000)
sector_count = 0x10;
+
+ density = hs[6];
}
sects[i].track = hs[0];
@@ -521,7 +535,7 @@ bool d88_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
if(size) {
sects[i].data = sect_data + sdatapos;
- io_generic_read(io, sects[i].data, pos, size);
+ std::tie(err, actual) = read_at(io, pos, sects[i].data, size); // FIXME: check for errors and premature EOF
pos += size;
sdatapos += size;
@@ -529,21 +543,20 @@ bool d88_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
sects[i].data = nullptr;
}
- build_pc_track_mfm(track, head, image, cell_count, sector_count, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size));
+ if(head < img_heads) {
+ if(density == 0x40)
+ build_pc_track_fm(track, head, image, cell_count / 2, sector_count, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size));
+ else
+ build_pc_track_mfm(track, head, image, cell_count, sector_count, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size));
+ }
}
return true;
}
-
-bool d88_format::save(io_generic *io, floppy_image *image)
-{
- return false;
-}
-
-bool d88_format::supports_save() const
+bool d88_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_D88_FORMAT = &floppy_image_format_creator<d88_format>;
+const d88_format FLOPPY_D88_FORMAT;
diff --git a/src/lib/formats/d88_dsk.h b/src/lib/formats/d88_dsk.h
index f769aa9f85e..a1f03bf2e2d 100644
--- a/src/lib/formats/d88_dsk.h
+++ b/src/lib/formats/d88_dsk.h
@@ -20,16 +20,15 @@ class d88_format : public floppy_image_format_t
public:
d88_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
-
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_D88_FORMAT;
+extern const d88_format FLOPPY_D88_FORMAT;
#endif // MAME_FORMATS_D88_DSK_H
diff --git a/src/lib/formats/dcp_dsk.cpp b/src/lib/formats/dcp_dsk.cpp
index 179552409f8..dfc9eea27dc 100644
--- a/src/lib/formats/dcp_dsk.cpp
+++ b/src/lib/formats/dcp_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Fabio Priuli
/*********************************************************************
- formats/dcp_dsk.h
+ formats/dcp_dsk.cpp
PC98 DCP & DCU disk images
@@ -20,37 +20,41 @@
*********************************************************************/
-#include <assert.h>
-
#include "dcp_dsk.h"
+#include "ioprocs.h"
+
+
dcp_format::dcp_format()
{
}
-const char *dcp_format::name() const
+const char *dcp_format::name() const noexcept
{
return "dcx";
}
-const char *dcp_format::description() const
+const char *dcp_format::description() const noexcept
{
return "DCP/DCU disk image";
}
-const char *dcp_format::extensions() const
+const char *dcp_format::extensions() const noexcept
{
return "dcp,dcu";
}
-int dcp_format::identify(io_generic *io, uint32_t form_factor)
+int dcp_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return 0;
+
uint8_t h[0xa2];
int heads, tracks, spt, bps, count_tracks = 0;
bool is_hdb = false;
- io_generic_read(io, h, 0, 0xa2);
+ /*auto const [err, actual] =*/ read_at(io, 0, h, 0xa2); // FIXME: check for errors and premature EOF
// First byte is the disk format (see below in load() for details)
switch (h[0])
@@ -103,22 +107,22 @@ int dcp_format::identify(io_generic *io, uint32_t form_factor)
// in theory track map should be enough (former check), but some images have it wrong!
// hence, if this check fails, we also allow for images with all tracks and wrong track map
if (size - 0xa2 == (heads * count_tracks * spt * bps) || size - 0xa2 == (heads * tracks * spt * bps))
- return 100;
+ return FIFID_STRUCT|FIFID_SIZE;
// for disk type 0x11 the head 0 track 0 has 26 sectors of half width, so we need to compensate calculation
if (is_hdb && (size - 0xa2 + (0x80 * 26) == (heads * count_tracks * spt * bps) || size - 0xa2 + (0x80 * 26) == (heads * tracks * spt * bps)))
- return 100;
+ return FIFID_STRUCT|FIFID_SIZE;
return 0;
}
-bool dcp_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool dcp_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t h[0xa2];
int heads, tracks, spt, bps;
bool is_hdb = false;
- io_generic_read(io, h, 0, 0xa2);
+ /*auto const [err, actual] =*/ read_at(io, 0, h, 0xa2); // FIXME: check for errors and premature EOF
// First byte is the disk format:
switch (h[0])
@@ -215,7 +219,7 @@ bool dcp_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
for (int track = 0; track < tracks; track++)
for (int head = 0; head < heads; head++)
{
- io_generic_read(io, sect_data, 0xa2 + bps * spt * (track * heads + head), bps * spt);
+ /*auto const [err, actual] =*/ read_at(io, 0xa2 + bps * spt * (track * heads + head), sect_data, bps * spt); // FIXME: check for errors and premature EOF
for (int i = 0; i < spt; i++)
{
@@ -235,7 +239,7 @@ bool dcp_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
else // FIXME: the code below is untested, because no image was found... there might be some silly mistake in the disk geometry!
{
// Read Head 0 Track 0 is FM with 26 sectors of 128bytes instead of 256
- io_generic_read(io, sect_data, 0xa2, 128 * spt);
+ /*auto const [err, actual] =*/ read_at(io, 0xa2, sect_data, 128 * spt); // FIXME: check for errors and premature EOF
for (int i = 0; i < spt; i++)
{
@@ -252,7 +256,7 @@ bool dcp_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
build_pc_track_fm(0, 0, image, cell_count, spt, sects, calc_default_pc_gap3_size(form_factor, 128));
// Read Head 1 Track 0 is MFM with 26 sectors of 256bytes
- io_generic_read(io, sect_data, 0xa2 + 128 * spt, bps * spt);
+ /*auto const [err, actual] =*/ read_at(io, 0xa2 + 128 * spt, sect_data, bps * spt); // FIXME: check for errors and premature EOF
for (int i = 0; i < spt; i++)
{
@@ -273,7 +277,7 @@ bool dcp_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
for (int track = 1; track < tracks; track++)
for (int head = 0; head < heads; head++)
{
- io_generic_read(io, sect_data, data_offs + bps * spt * ((track - 1) * heads + head), bps * spt);
+ /*auto const [err, actual] =*/ read_at(io, data_offs + bps * spt * ((track - 1) * heads + head), sect_data, bps * spt); // FIXME: check for errors and premature EOF
for (int i = 0; i < spt; i++)
{
@@ -294,9 +298,9 @@ bool dcp_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-bool dcp_format::supports_save() const
+bool dcp_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_DCP_FORMAT = &floppy_image_format_creator<dcp_format>;
+const dcp_format FLOPPY_DCP_FORMAT;
diff --git a/src/lib/formats/dcp_dsk.h b/src/lib/formats/dcp_dsk.h
index 3ecfab71f61..a7d0c073c7b 100644
--- a/src/lib/formats/dcp_dsk.h
+++ b/src/lib/formats/dcp_dsk.h
@@ -20,15 +20,15 @@ class dcp_format : public floppy_image_format_t
public:
dcp_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_DCP_FORMAT;
+extern const dcp_format FLOPPY_DCP_FORMAT;
#endif // MAME_FORMATS_DCP_DSK_H
diff --git a/src/lib/formats/dfi_dsk.cpp b/src/lib/formats/dfi_dsk.cpp
index 6c9f32cbb31..e2ea1fda0e3 100644
--- a/src/lib/formats/dfi_dsk.cpp
+++ b/src/lib/formats/dfi_dsk.cpp
@@ -11,9 +11,16 @@
* Correctly note exact index timing.
*/
-#include "emu.h" // fatalerror
#include "dfi_dsk.h"
-#include <zlib.h>
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include "osdcore.h" // osd_printf_*
+
+#include <tuple>
+
+
#define NUMBER_OF_MULTIREADS 3
// thresholds for brickwall windowing
//define DFI_MIN_CLOCKS 65
@@ -35,50 +42,67 @@ dfi_format::dfi_format() : floppy_image_format_t()
{
}
-const char *dfi_format::name() const
+const char *dfi_format::name() const noexcept
{
return "dfi";
}
-const char *dfi_format::description() const
+const char *dfi_format::description() const noexcept
{
return "DiscFerret flux dump format";
}
-const char *dfi_format::extensions() const
+const char *dfi_format::extensions() const noexcept
{
return "dfi";
}
-bool dfi_format::supports_save() const
+bool dfi_format::supports_save() const noexcept
{
return false;
}
-int dfi_format::identify(io_generic *io, uint32_t form_factor)
+int dfi_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
char sign[4];
- io_generic_read(io, sign, 0, 4);
- if (memcmp(sign, "DFER", 4)==0)
- fatalerror("Old type Discferret image detected; the mess Discferret decoder will not handle this properly, bailing out!\n");
- return memcmp(sign, "DFE2", 4) ? 0 : 100;
+ auto const [err, actual] = read_at(io, 0, sign, 4);
+ if(err || (4 != actual)) {
+ return 0;
+ }
+ return memcmp(sign, "DFE2", 4) ? 0 : FIFID_SIGN;
}
-bool dfi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool dfi_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint64_t size = io_generic_size(io);
+ std::error_condition err;
+ size_t actual;
+
+ char sign[4];
+ std::tie(err, actual) = read_at(io, 0, sign, 4);
+ if(err || (4 != actual)) {
+ return false;
+ }
+ if(memcmp(sign, "DFER", 4) == 0) {
+ osd_printf_error("dfi_dsk: Old type Discferret image detected; the MAME Discferret decoder will not handle this properly, bailing out!\n");
+ return false;
+ }
+
+ uint64_t size;
+ if(io.length(size))
+ return false;
+
uint64_t pos = 4;
std::vector<uint8_t> data;
int onerev_time = 0; // time for one revolution, used to guess clock and rpm for DFE2 files
unsigned long clock_rate = 100000000; // sample clock rate in megahertz
- int rpm=360; // drive rpm
+ [[maybe_unused]] int rpm=360; // drive rpm
while(pos < size) {
uint8_t h[10];
- io_generic_read(io, h, pos, 10);
- uint16_t track = (h[0] << 8) | h[1];
- uint16_t head = (h[2] << 8) | h[3];
+ std::tie(err, actual) = read_at(io, pos, h, 10); // FIXME: check for errors and premature EOF
+ uint16_t track = get_u16be(&h[0]);
+ uint16_t head = get_u16be(&h[2]);
// Ignore sector
- uint32_t tsize = (h[6] << 24) | (h[7] << 16) | (h[8] << 8) | h[9];
+ uint32_t tsize = get_u32be(&h[6]);
// if the position-so-far-in-file plus 10 (for the header) plus track size
// is larger than the size of the file, free buffers and bail out
@@ -90,7 +114,7 @@ bool dfi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
data.resize(tsize);
pos += 10; // skip the header, we already read it
- io_generic_read(io, &data[0], pos, tsize);
+ std::tie(err, actual) = read_at(io, pos, &data[0], tsize); // FIXME: check for errors and premature EOF
pos += tsize; // for next time we read, increment to the beginning of next header
int index_time = 0; // what point the last index happened
@@ -99,7 +123,10 @@ bool dfi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
int total_time = 0; // total sampled time per track
for(int i=0; i<tsize; i++) {
uint8_t v = data[i];
- if (v == 0xFF) { fprintf(stderr,"DFI stream contained a 0xFF at t%d, position%d, THIS SHOULD NEVER HAPPEN! Bailing out!\n", track, i); exit(1); }
+ if (v == 0xFF) {
+ osd_printf_error("dfi_dsk: DFI stream contained a 0xFF at t%d, position%d, THIS SHOULD NEVER HAPPEN! Bailing out!\n", track, i);
+ return false;
+ }
if((v & 0x7f) == 0x7f)
total_time += 0x7f;
else if(v & 0x80) {
@@ -118,38 +145,36 @@ bool dfi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
if (onerev_time == 0) onerev_time = total_time;
if(!track && !head)
- fprintf(stderr, "%02d:%d tt=%10d it=%10d\n", track, head, total_time, index_time);
+ osd_printf_verbose("dfi_dsk: %02d:%d tt=%10d it=%10d\n", track, head, total_time, index_time);
if(!track && !head) {
- fprintf(stderr, "index_count: %d, onerev_time: %d\n", index_count, onerev_time);
+ osd_printf_verbose("dfi_dsk: index_count: %d, onerev_time: %d\n", index_count, onerev_time);
if ((onerev_time > REV25_MIN) && (onerev_time < REV25_MAX)) {
- fprintf(stderr, "Guess: speed: 360rpm, clock 25MHz\n");
+ osd_printf_verbose("dfi_dsk: Guess: speed: 360rpm, clock 25MHz\n");
clock_rate = 25000000; rpm = 360;
} else if ((onerev_time > REV25_MIN*1.2) && (onerev_time < REV25_MAX*1.2)) {
- fprintf(stderr, "Guess: speed: 300rpm, clock 25MHz\n");
+ osd_printf_verbose("dfi_dsk: Guess: speed: 300rpm, clock 25MHz\n");
clock_rate = 25000000; rpm = 300;
} else if ((onerev_time > REV25_MIN*2) && (onerev_time < REV25_MAX*2)) {
- fprintf(stderr, "Guess: speed: 360rpm, clock 50MHz\n");
+ osd_printf_verbose("dfi_dsk: Guess: speed: 360rpm, clock 50MHz\n");
clock_rate = 50000000; rpm = 360;
} else if ((onerev_time > (REV25_MIN*2)*1.2) && (onerev_time < (REV25_MAX*2)*1.2)) {
- fprintf(stderr, "Guess: speed: 300rpm, clock 50MHz\n");
+ osd_printf_verbose("dfi_dsk: Guess: speed: 300rpm, clock 50MHz\n");
clock_rate = 50000000; rpm = 300;
} else if ((onerev_time > REV25_MIN*4) && (onerev_time < REV25_MAX*4)) {
- fprintf(stderr, "Guess: speed: 360rpm, clock 100MHz\n");
+ osd_printf_verbose("dfi_dsk: Guess: speed: 360rpm, clock 100MHz\n");
clock_rate = 100000000; rpm = 360;
} else if ((onerev_time > (REV25_MIN*4)*1.2) && (onerev_time < (REV25_MAX*4)*1.2)) {
- fprintf(stderr, "Guess: speed: 300rpm, clock 100MHz\n");
+ osd_printf_verbose("dfi_dsk: Guess: speed: 300rpm, clock 100MHz\n");
clock_rate = 100000000; rpm = 300;
} else
- fprintf(stderr, "WARNING: Cannot Guess Speed! Assuming 360rpm, 100Mhz clock!\n");
- fprintf(stderr,"Actual rpm based on index: %f\n", ((double)clock_rate/(double)onerev_time)*60);
+ osd_printf_warning("dfi_dsk: WARNING: Cannot Guess Speed! Assuming 360rpm, 100Mhz clock!\n");
+ osd_printf_verbose("dfi_dsk: Actual rpm based on index: %f\n", ((double)clock_rate/(double)onerev_time)*60);
}
- rpm += 0; // HACK: prevent GCC 4.6+ from warning "variable set but unused"
-
if(!index_time)
index_time = total_time;
- std::vector<uint32_t> &buf = image->get_buffer(track, head);
+ std::vector<uint32_t> &buf = image.get_buffer(track, head);
buf.resize(tsize);
int cur_time = 0;
@@ -162,9 +187,7 @@ bool dfi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
#endif
index_count = 0;
//index_polarity = 0;
- uint32_t mg = floppy_image::MG_A;
int tpos = 0;
- buf[tpos++] = mg;
for(int i=0; i<tsize; i++) {
uint8_t v = data[i];
if((v & 0x7f) == 0x7f) // 0x7F : no transition, but a carry (FF is a board-on-fire error and is checked for above)
@@ -184,28 +207,24 @@ bool dfi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
// cur_time and onerev_time need to be converted to something standard, so we'll stick with 300rpm at the standard 200mhz rate that mfi uses internally
// TODO for 4/22/2012: ACTUALLY DO THIS RESCALING STEP
// filter out spurious crap
- //if (trans_time <= MIN_THRESH) fprintf(stderr, "DFI: Throwing out short transition of length %d\n", trans_time);
+ //if (trans_time <= MIN_THRESH) osd_printf_verbose("dfi_dsk: Throwing out short transition of length %d\n", trans_time);
// the normal case: write the transition at the appropriate time
if ((prev_time == 0) || ((trans_time > MIN_THRESH) && (trans_time <= MAX_THRESH))) {
- mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
- buf[tpos++] = mg | uint32_t((200000000ULL*cur_time)/index_time);
+ buf[tpos++] = floppy_image::MG_F | uint32_t((200000000ULL*cur_time)/index_time);
prev_time = cur_time;
}
// the long case: we probably missed a transition, stuff an extra guessed one in there to see if it helps
if (trans_time > MAX_THRESH) {
- mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
- if (((track%2)==0)&&(head==0)) fprintf(stderr,"missed transition, total time for transition is %d\n",trans_time);
+ if (((track%2)==0)&&(head==0)) osd_printf_info("dfi_dsk: missed transition, total time for transition is %d\n",trans_time);
#ifndef FAKETRANS_ONE
- buf[tpos++] = mg | uint32_t((200000000ULL*(cur_time-(trans_time/2)))/index_time); // generate imaginary transition at half period
+ buf[tpos++] = floppy_image::MG_F | uint32_t((200000000ULL*(cur_time-(trans_time/2)))/index_time); // generate imaginary transition at half period
#else
- buf[tpos++] = mg | uint32_t((200000000ULL*(cur_time-((trans_time*2)/3)))/index_time);
- mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
- buf[tpos++] = mg | uint32_t((200000000ULL*(cur_time-(trans_time/3)))/index_time);
+ buf[tpos++] = floppy_image::MG_F | uint32_t((200000000ULL*(cur_time-((trans_time*2)/3)))/index_time);
+ buf[tpos++] = floppy_image::MG_F | uint32_t((200000000ULL*(cur_time-(trans_time/3)))/index_time);
#endif
- mg = mg == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
- buf[tpos++] = mg | uint32_t(200000000ULL*cur_time/index_time); // generate transition now
+ buf[tpos++] = floppy_image::MG_F | uint32_t(200000000ULL*cur_time/index_time); // generate transition now
prev_time = cur_time;
- }
+ }
}
}
#ifdef TRACK_HISTOGRAM
@@ -224,4 +243,4 @@ bool dfi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-const floppy_format_type FLOPPY_DFI_FORMAT = &floppy_image_format_creator<dfi_format>;
+const dfi_format FLOPPY_DFI_FORMAT;
diff --git a/src/lib/formats/dfi_dsk.h b/src/lib/formats/dfi_dsk.h
index 6dd56aa623c..1968b1cb6ad 100644
--- a/src/lib/formats/dfi_dsk.h
+++ b/src/lib/formats/dfi_dsk.h
@@ -12,16 +12,15 @@ class dfi_format : public floppy_image_format_t
public:
dfi_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- // virtual bool save(io_generic *io, floppy_image *image);
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_DFI_FORMAT;
+extern const dfi_format FLOPPY_DFI_FORMAT;
#endif // MAME_FORMATS_DFI_DSK_H
diff --git a/src/lib/formats/dim_dsk.cpp b/src/lib/formats/dim_dsk.cpp
index 67c04adc2bf..1f0902c2859 100644
--- a/src/lib/formats/dim_dsk.cpp
+++ b/src/lib/formats/dim_dsk.cpp
@@ -2,156 +2,64 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/dim_dsk.c
+ formats/dim_dsk.cpp
DIM disk images
*********************************************************************/
-#include <string.h>
-#include <assert.h>
-
#include "dim_dsk.h"
-#include "basicdsk.h"
-
-FLOPPY_IDENTIFY(dim_dsk_identify)
-{
- uint8_t dim_header[16];
-
- floppy_image_read(floppy, (uint8_t*)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_t format_tmp;
- int x;
- uint16_t 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
-
- LOG_FORMATS("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);
-}
-
-/*********************************************************************
-
- formats/dim_dsk.c
+#include "ioprocs.h"
- DIM disk images
+#include <cstring>
+#include <tuple>
-*********************************************************************/
-
-#include "dim_dsk.h"
dim_format::dim_format()
{
}
-const char *dim_format::name() const
+const char *dim_format::name() const noexcept
{
return "dim";
}
-const char *dim_format::description() const
+const char *dim_format::description() const noexcept
{
return "DIM disk image";
}
-const char *dim_format::extensions() const
+const char *dim_format::extensions() const noexcept
{
return "dim";
}
-int dim_format::identify(io_generic *io, uint32_t form_factor)
+int dim_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t h[16];
-
- io_generic_read(io, h, 0xab, 16);
+ auto const [err, actual] = read_at(io, 0xab, h, 16);
+ if(err || (16 != actual))
+ return 0;
if(strncmp((const char *)h, "DIFC HEADER", 11) == 0)
- return 100;
+ return FIFID_SIGN;
return 0;
}
-bool dim_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool dim_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
+ std::error_condition err;
+ size_t actual;
int offset = 0x100;
uint8_t h;
uint8_t track_total = 77;
int cell_count = form_factor == floppy_image::FF_35 ? 200000 : 166666;
- io_generic_read(io, &h, 0, 1);
+ std::tie(err, actual) = read_at(io, 0, &h, 1);
+ if (err || (1 != actual))
+ return false;
int spt, gap3, bps, size;
switch(h) {
@@ -211,7 +119,7 @@ bool dim_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
sects[i].deleted = false;
sects[i].bad_crc = false;
sects[i].data = &sect_data[sdatapos];
- io_generic_read(io, sects[i].data, offset, bps);
+ std::tie(err, actual) = read_at(io, offset, sects[i].data, bps); // FIXME: check for errors and premature EOF
offset += bps;
sdatapos += bps;
}
@@ -223,14 +131,9 @@ bool dim_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
}
-bool dim_format::save(io_generic *io, floppy_image *image)
-{
- return false;
-}
-
-bool dim_format::supports_save() const
+bool dim_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_DIM_FORMAT = &floppy_image_format_creator<dim_format>;
+const dim_format FLOPPY_DIM_FORMAT;
diff --git a/src/lib/formats/dim_dsk.h b/src/lib/formats/dim_dsk.h
index b1c3d60a801..664e566c046 100644
--- a/src/lib/formats/dim_dsk.h
+++ b/src/lib/formats/dim_dsk.h
@@ -16,25 +16,20 @@
/**************************************************************************/
-FLOPPY_IDENTIFY(dim_dsk_identify);
-FLOPPY_CONSTRUCT(dim_dsk_construct);
-
-
class dim_format : public floppy_image_format_t
{
public:
dim_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_DIM_FORMAT;
+extern const dim_format FLOPPY_DIM_FORMAT;
#endif // MAME_FORMATS_DIM_DSK_H
diff --git a/src/lib/formats/dip_dsk.cpp b/src/lib/formats/dip_dsk.cpp
index 310eab56a73..3ef8261d1da 100644
--- a/src/lib/formats/dip_dsk.cpp
+++ b/src/lib/formats/dip_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Fabio Priuli
/*********************************************************************
- formats/dip_dsk.h
+ formats/dip_dsk.cpp
PC98 DIP disk images
@@ -14,40 +14,43 @@
*********************************************************************/
-#include <assert.h>
-
#include "dip_dsk.h"
+#include "ioprocs.h"
+
+
dip_format::dip_format()
{
}
-const char *dip_format::name() const
+const char *dip_format::name() const noexcept
{
return "dip";
}
-const char *dip_format::description() const
+const char *dip_format::description() const noexcept
{
return "DIP disk image";
}
-const char *dip_format::extensions() const
+const char *dip_format::extensions() const noexcept
{
return "dip";
}
-int dip_format::identify(io_generic *io, uint32_t form_factor)
+int dip_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return 0;
if (size == 0x134000 + 0x100)
- return 100;
+ return FIFID_SIZE;
return 0;
}
-bool dip_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool dip_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
int heads, tracks, spt, bps;
@@ -69,7 +72,7 @@ bool dip_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
for (int track = 0; track < tracks; track++)
for (int head = 0; head < heads; head++)
{
- io_generic_read(io, sect_data, 0x100 + bps * spt * (track * heads + head), bps * spt);
+ /*auto const [err, actual] =*/ read_at(io, 0x100 + bps * spt * (track * heads + head), sect_data, bps * spt); // FIXME: check for errors and premature EOF
for (int i = 0; i < spt; i++)
{
@@ -89,9 +92,9 @@ bool dip_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-bool dip_format::supports_save() const
+bool dip_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_DIP_FORMAT = &floppy_image_format_creator<dip_format>;
+const dip_format FLOPPY_DIP_FORMAT;
diff --git a/src/lib/formats/dip_dsk.h b/src/lib/formats/dip_dsk.h
index b504c73fbb0..cc315a813d7 100644
--- a/src/lib/formats/dip_dsk.h
+++ b/src/lib/formats/dip_dsk.h
@@ -20,15 +20,15 @@ class dip_format : public floppy_image_format_t
public:
dip_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_DIP_FORMAT;
+extern const dip_format FLOPPY_DIP_FORMAT;
#endif // MAME_FORMATS_DIP_DSK_H
diff --git a/src/lib/formats/dmk_dsk.cpp b/src/lib/formats/dmk_dsk.cpp
index 5ca0a3b8781..d02401b56cf 100644
--- a/src/lib/formats/dmk_dsk.cpp
+++ b/src/lib/formats/dmk_dsk.cpp
@@ -2,57 +2,91 @@
// copyright-holders:Wilbert Pol
/*********************************************************************
- formats/dmk_dsk.h
+ formats/dmk_dsk.cpp
DMK disk images
TODO:
- Add write/format support.
-- Add single density support.
-- Check support on other drivers besides msx.
+- Check support on other drivers besides MSX.
*********************************************************************/
-#include <assert.h>
-
#include "dmk_dsk.h"
+#include "coretmpl.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <tuple>
+
+
+namespace {
+
+constexpr int HEADER_SIZE = 16;
+
+uint32_t wide_fm(uint16_t val)
+{
+ uint32_t res = 0;
+ for (int i = 15; i >= 0; i--) {
+ res |= (util::BIT(val, i) << (i * 2 + 1));
+ }
+ return res;
+}
+
+uint32_t data_to_wide_fm(uint8_t val)
+{
+ uint16_t res = 0xaaaa; // clock
+ for (int i = 7; i >= 0; i--) {
+ res |= (util::BIT(val, i) << i * 2); // data
+ }
+ return wide_fm(res);
+}
+
+} // anonymous namespace
+
+
dmk_format::dmk_format()
{
}
-const char *dmk_format::name() const
+const char *dmk_format::name() const noexcept
{
return "dmk";
}
-const char *dmk_format::description() const
+const char *dmk_format::description() const noexcept
{
return "DMK disk image";
}
-const char *dmk_format::extensions() const
+const char *dmk_format::extensions() const noexcept
{
return "dmk";
}
-int dmk_format::identify(io_generic *io, uint32_t form_factor)
+int dmk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- const int header_size = 16;
- uint8_t header[header_size];
+ uint64_t size;
+ if (io.length(size))
+ return 0;
- uint64_t size = io_generic_size(io);
+ std::error_condition err;
+ size_t actual;
- io_generic_read(io, header, 0, header_size);
+ uint8_t header[HEADER_SIZE];
+ std::tie(err, actual) = read_at(io, 0, header, HEADER_SIZE);
+ if (err || (HEADER_SIZE != actual))
+ return 0;
- int tracks = header[1];
- int track_size = ( header[3] << 8 ) | header[2];
- int heads = (header[4] & 0x10) ? 1 : 2;
+ const int tracks_from_header = header[1];
+ const int track_size = get_u16le(&header[2]);
+ const int heads = util::BIT(header[4], 4) ? 1 : 2;
// The first header byte must be 00 or FF
if (header[0] != 0x00 && header[0] != 0xff)
@@ -60,149 +94,249 @@ int dmk_format::identify(io_generic *io, uint32_t form_factor)
return 0;
}
- // Bytes C-F must be zero
- if (header[0x0c] != 0 || header[0xd] != 0 || header[0xe] != 0 || header[0xf] != 0)
+ // Verify reserved/unsupported header bytes
+ for (int i = 5; i < 0x10; i++)
{
- return 0;
+ if (header[i] != 0x00)
+ return 0;
}
// Check track size within limits
- if (track_size < 0x80 || track_size > 0x3FFF )
- {
+ if (track_size < 0x80 || track_size > 0x3fff)
return 0;
- }
- if (size == header_size + heads * tracks * track_size)
+ const int tracks_in_file = (size - HEADER_SIZE) / (heads * track_size);
+ for (int track = 0; track < tracks_in_file; track++)
{
- return 70;
- }
+ for (int head = 0; head < heads; head++)
+ {
+ // Read track
+ std::vector<uint8_t> track_data(track_size);
+ std::tie(err, actual) = read_at(io, HEADER_SIZE + (heads * track + head) * track_size, &track_data[0], track_size);
+ if (err || track_size != actual)
+ return 0;
- return 0;
+ // Verify idam entries
+ for (int idam_index = 0; idam_index < 64; idam_index++)
+ {
+ const uint16_t idam_entry = get_u16le(&track_data[2 * idam_index]);
+ if (idam_entry == 0x0000)
+ continue;
+ const uint16_t idam_offset = idam_entry & 0x3fff;
+ if (idam_offset >= track_size)
+ return 0;
+ if (track_data[idam_offset] != 0xfe)
+ return 0;
+ }
+ }
+ }
+ if (size == HEADER_SIZE + heads * tracks_from_header * track_size)
+ return FIFID_HINT|FIFID_SIZE;
+ else
+ return FIFID_HINT;
}
-bool dmk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool dmk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- const int header_size = 16;
- uint8_t header[header_size];
+ std::error_condition err;
+ size_t actual;
- io_generic_read(io, header, 0, header_size);
+ uint64_t size;
+ if (io.length(size))
+ return false;
- const int tracks = header[1];
- const int track_size = ( header[3] << 8 ) | header[2];
- const int heads = (header[4] & 0x10) ? 1 : 2;
- const bool is_sd = (header[4] & 0x40) ? true : false;
- //const int raw_track_size = 2 * 8 * ( track_size - 0x80 );
+ uint8_t header[HEADER_SIZE];
+ std::tie(err, actual) = read_at(io, 0, header, HEADER_SIZE);
+ if (err || (HEADER_SIZE != actual))
+ return false;
- if (is_sd)
- {
- if (heads == 2)
- {
- image->set_variant(floppy_image::DSSD);
- }
- else
- {
- image->set_variant(floppy_image::SSSD);
- }
- }
- else
- {
- if (heads == 2)
- {
- image->set_variant(floppy_image::DSDD);
- }
- else
- {
- image->set_variant(floppy_image::SSDD);
- }
- }
+ const int track_size = get_u16le(&header[2]);
+ const int heads = util::BIT(header[4], 4) ? 1 : 2;
+ const bool is_sd = util::BIT(header[4], 6);
+ const int tracks = (size - HEADER_SIZE) / (heads * track_size);
+
+ const auto variant = is_sd ? (heads == 2 ? floppy_image::DSSD : floppy_image::SSSD)
+ : (heads == 2 ? floppy_image::DSDD : floppy_image::SSDD);
+ image.set_variant(variant);
+
+ const int fm_stride = is_sd ? 1 : 2;
for (int track = 0; track < tracks; track++)
{
for (int head = 0; head < heads; head++)
{
+ int fm_loss = 0;
std::vector<uint8_t> track_data(track_size);
std::vector<uint32_t> raw_track_data;
+ int mark_location[64 * 2 + 1];
+ uint8_t mark_value[64 * 2 + 1];
+ bool mark_is_mfm[64 * 2 + 1];
int iam_location = -1;
- int idam_location[64];
- int dam_location[64];
// Read track
- io_generic_read(io, &track_data[0], header_size + ( heads * track + head ) * track_size, track_size);
+ std::tie(err, actual) = read_at(io, HEADER_SIZE + (heads * track + head) * track_size, &track_data[0], track_size);
+ if (err || track_size != actual)
+ return false;
- for (int i = 0; i < 64; i++)
+ for (int i = 0; i < 64 * 2 + 1; i++)
{
- idam_location[i] = -1;
- dam_location[i] = -1;
+ mark_location[i] = -1;
+ mark_value[i] = 0xfe;
+ mark_is_mfm[i] = !is_sd; // Use default encoding
}
+ int mark_count = 0;
- // Find IDAM locations
+ // Find IDAM/DAM locations
uint16_t track_header_offset = 0;
- uint16_t track_offset = ( ( track_data[track_header_offset + 1] << 8 ) | track_data[track_header_offset] ) & 0x3fff;
+ uint16_t track_offset = get_u16le(&track_data[track_header_offset]) & 0x3fff;
+ bool idam_is_mfm = util::BIT(track_data[track_header_offset + 1], 7);
track_header_offset += 2;
- while ( track_offset != 0 && track_offset >= 0x83 && track_offset < track_size && track_header_offset < 0x80 )
+ while (track_offset != 0 && track_offset >= 0x83 && track_offset < track_size && track_header_offset < 0x80)
{
// Assume 3 bytes before IDAM pointers are the start of IDAM indicators
- idam_location[(track_header_offset/2) - 1] = track_offset - 3;
+ int mark_offset = idam_is_mfm ? 3 : 0;
+ mark_location[mark_count] = track_offset - mark_offset;
+ mark_value[mark_count] = 0xfe;
+ mark_is_mfm[mark_count] = idam_is_mfm;
+ mark_count++;
+ int stride = idam_is_mfm ? 1 : fm_stride;
// Scan for DAM location
- for (int i = track_offset + 53; i > track_offset + 10; i--)
+ for (int i = track_offset + 10 * stride; i < track_offset + 53 * stride; i++)
{
- if ((track_data[i] == 0xfb || track_data[i] == 0xf8) && track_data[i-1] == 0xa1 && track_data[i-2] == 0xa1)
+ if ((track_data[i] >= 0xf8 && track_data[i] <= 0xfb))
{
- dam_location[(track_header_offset/2) - 1] = i - 3;
- break;
+ if (!idam_is_mfm || get_u16le(&track_data[i - 2]) == 0xa1a1)
+ {
+ mark_location[mark_count] = i - mark_offset;
+ mark_value[mark_count] = track_data[i];
+ mark_is_mfm[mark_count] = idam_is_mfm;
+ mark_count++;
+
+ break;
+ }
}
}
- track_offset = ( ( track_data[track_header_offset + 1] << 8 ) | track_data[track_header_offset] ) & 0x3fff;
+ idam_is_mfm = util::BIT(track_data[track_header_offset + 1], 7);
+ track_offset = get_u16le(&track_data[track_header_offset]) & 0x3fff;
track_header_offset += 2;
- };
+ }
+
+ // Prevent encoding from switching after last sector
+ if (mark_count > 0)
+ {
+ mark_is_mfm[mark_count] = mark_is_mfm[mark_count - 1];
+ }
// Find IAM location
- for(int i = idam_location[0] - 1; i >= 3; i--)
+ for (int i = mark_location[0] - 1; i >= 3; i--)
{
// It's usually 3 bytes but several dumped tracks seem to contain only 2 bytes
- if (track_data[i] == 0xfc && track_data[i-1] == 0xc2 && track_data[i-2] == 0xc2)
+ if (track_data[i] == 0xfc && (is_sd || get_u16le(&track_data[i - 2]) == 0xc2c2))
{
- iam_location = i - 3;
+ iam_location = i - (is_sd ? 0 : 3);
break;
}
}
- int idam_index = 0;
- int dam_index = 0;
+ int curr_mark = 0;
+ bool enc_mfm = mark_is_mfm[curr_mark];
for (int offset = 0x80; offset < track_size; offset++)
{
if (offset == iam_location)
{
- // Write IAM
- raw_w(raw_track_data, 16, 0x5224);
- raw_w(raw_track_data, 16, 0x5224);
- raw_w(raw_track_data, 16, 0x5224);
- offset += 3;
+ if (!is_sd)
+ {
+ // Write IAM
+ raw_w(raw_track_data, 16, 0x5224);
+ raw_w(raw_track_data, 16, 0x5224);
+ raw_w(raw_track_data, 16, 0x5224);
+ offset += 3;
+ }
+ else
+ {
+ raw_w(raw_track_data, 32, wide_fm(0xf77a)); // FC clocked with D7
+ offset += fm_stride;
+ }
}
- if (offset == idam_location[idam_index])
+ // If close to mark, switch encoding
+ if (offset + 8 >= mark_location[curr_mark])
+ {
+ bool new_enc = mark_is_mfm[curr_mark];
+ if (new_enc != enc_mfm)
+ {
+ enc_mfm = new_enc;
+ }
+ }
+ if (offset == mark_location[curr_mark]
+ || (!enc_mfm && offset - fm_stride + 1 == mark_location[curr_mark])
+ )
{
- raw_w(raw_track_data, 16, 0x4489);
- raw_w(raw_track_data, 16, 0x4489);
- raw_w(raw_track_data, 16, 0x4489);
- idam_index += 1;
- offset += 3;
+ if (enc_mfm)
+ {
+ raw_w(raw_track_data, 16, 0x4489);
+ raw_w(raw_track_data, 16, 0x4489);
+ raw_w(raw_track_data, 16, 0x4489);
+ offset += 3;
+ if (fm_stride == 1)
+ {
+ fm_loss += 3;
+ }
+ }
+ else
+ {
+ uint16_t mark;
+ switch (mark_value[curr_mark])
+ {
+ default:
+ case 0xfb: mark = 0xf56f; break;
+ case 0xfa: mark = 0xf56e; break;
+ case 0xf9: mark = 0xf56b; break;
+ case 0xf8: mark = 0xf56a; break;
+ case 0xfe: mark = 0xf57e; break;
+ }
+ raw_w(raw_track_data, 32, wide_fm(mark));
+ offset += fm_stride;
+ }
+ curr_mark++;
}
- if (offset == dam_location[dam_index])
+ if (enc_mfm)
{
- raw_w(raw_track_data, 16, 0x4489);
- raw_w(raw_track_data, 16, 0x4489);
- raw_w(raw_track_data, 16, 0x4489);
- dam_index += 1;
- offset += 3;
+ if (fm_stride == 1)
+ {
+ fm_loss++;
+ }
+ mfm_w(raw_track_data, 8, track_data[offset]);
}
+ else
+ raw_w(raw_track_data, 32, data_to_wide_fm(track_data[offset]));
+
+
+ if (!enc_mfm)
+ offset += fm_stride - 1;
+ }
- mfm_w(raw_track_data, 8, track_data[offset]);
+ if (fm_loss != 0)
+ {
+ if (enc_mfm)
+ {
+ for (int jj = 0; jj < fm_loss; jj++)
+ {
+ mfm_w(raw_track_data, 8, 0x4e);
+ }
+ }
+ else
+ {
+ for (int jj = 0; jj < fm_loss/2; jj++)
+ {
+ raw_w(raw_track_data, 32, data_to_wide_fm(0xff));
+ }
+ }
}
generate_track_from_levels(track, head, raw_track_data, 0, image);
@@ -212,17 +346,10 @@ bool dmk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-
-bool dmk_format::save(io_generic *io, floppy_image *image)
-{
- return false;
-}
-
-
-bool dmk_format::supports_save() const
+bool dmk_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_DMK_FORMAT = &floppy_image_format_creator<dmk_format>;
+const dmk_format FLOPPY_DMK_FORMAT;
diff --git a/src/lib/formats/dmk_dsk.h b/src/lib/formats/dmk_dsk.h
index 44993f8df32..1c7850f1384 100644
--- a/src/lib/formats/dmk_dsk.h
+++ b/src/lib/formats/dmk_dsk.h
@@ -21,16 +21,15 @@ class dmk_format : public floppy_image_format_t
public:
dmk_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
-
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_DMK_FORMAT;
+extern const dmk_format FLOPPY_DMK_FORMAT;
#endif // MAME_FORMATS_DMK_DSK_H
diff --git a/src/lib/formats/dmv_dsk.cpp b/src/lib/formats/dmv_dsk.cpp
index 02469ed74d4..25f1019d08e 100644
--- a/src/lib/formats/dmv_dsk.cpp
+++ b/src/lib/formats/dmv_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/dmv_dsk.c
+ formats/dmv_dsk.cpp
NCR Decision Mate V format
*********************************************************************/
-#include <assert.h>
-
#include "formats/dmv_dsk.h"
dmv_format::dmv_format() : upd765_format(formats)
{
}
-const char *dmv_format::name() const
+const char *dmv_format::name() const noexcept
{
return "dmv";
}
-const char *dmv_format::description() const
+const char *dmv_format::description() const noexcept
{
return "Decision Mate V disk image";
}
-const char *dmv_format::extensions() const
+const char *dmv_format::extensions() const noexcept
{
return "img";
}
@@ -52,4 +50,4 @@ const dmv_format::format dmv_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_DMV_FORMAT = &floppy_image_format_creator<dmv_format>;
+const dmv_format FLOPPY_DMV_FORMAT;
diff --git a/src/lib/formats/dmv_dsk.h b/src/lib/formats/dmv_dsk.h
index 035eed0db50..44801ac269e 100644
--- a/src/lib/formats/dmv_dsk.h
+++ b/src/lib/formats/dmv_dsk.h
@@ -19,14 +19,14 @@ class dmv_format : public upd765_format
public:
dmv_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_DMV_FORMAT;
+extern const dmv_format FLOPPY_DMV_FORMAT;
#endif // MAME_FORMATS_DMV_DSK_H
diff --git a/src/lib/formats/ds9_dsk.cpp b/src/lib/formats/ds9_dsk.cpp
index 0866d78d58f..c0cbf0dccbc 100644
--- a/src/lib/formats/ds9_dsk.cpp
+++ b/src/lib/formats/ds9_dsk.cpp
@@ -14,45 +14,12 @@
************************************************************************/
-#include <assert.h>
-
#include "formats/ds9_dsk.h"
+#include "ioprocs.h"
-static FLOPPY_IDENTIFY(ds9_dsk_identify)
-{
- switch (floppy_image_size(floppy))
- {
- case (80 * 2 * 21 * 256):
- case 860164:
- case 860288:
- *vote = 100;
- break;
-
- default:
- *vote = 0;
- break;
- }
-
- return FLOPPY_ERROR_SUCCESS;
-}
-
-static FLOPPY_CONSTRUCT(ds9_dsk_construct)
-{
- struct basicdsk_geometry geometry;
- memset(&geometry, 0, sizeof(geometry));
- geometry.heads = 2;
- geometry.first_sector_id = 0;
- geometry.sector_length = 256;
- geometry.tracks = 80;
- geometry.sectors = 21;
- return basicdsk_construct(floppy, &geometry);
-}
+#include <cstring>
-LEGACY_FLOPPY_OPTIONS_START( ds9 )
- LEGACY_FLOPPY_OPTION( ds9_dsk, "ds9,dsk,raw", "Agat 840K DSK image",
- ds9_dsk_identify, ds9_dsk_construct, nullptr, nullptr)
-LEGACY_FLOPPY_OPTIONS_END
// exactly 6500 bytes
const floppy_image_format_t::desc_e ds9_format::ds9_desc[] = {
@@ -82,48 +49,47 @@ ds9_format::ds9_format()
{
}
-const char *ds9_format::name() const
+const char *ds9_format::name() const noexcept
{
return "a9dsk";
}
-const char *ds9_format::description() const
+const char *ds9_format::description() const noexcept
{
return "Agat-9 840K floppy image";
}
-const char *ds9_format::extensions() const
+const char *ds9_format::extensions() const noexcept
{
return "ds9";
}
-void ds9_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
+void ds9_format::find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
{
- uint32_t expected_size = 0;
- uint64_t size = io_generic_size(io);
-
head_count = 2;
track_count = 80;
sector_count = 21;
- expected_size = 256 * track_count * head_count * sector_count;
+ uint32_t const expected_size = 256 * track_count * head_count * sector_count;
- if (size >= expected_size) // standard format has 860160 bytes
+ uint64_t size;
+ if (!io.length(size) && (size >= expected_size)) // standard format has 860160 bytes
return;
track_count = head_count = sector_count = 0;
}
-int ds9_format::identify(io_generic *io, uint32_t form_factor)
+int ds9_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
- if (track_count) return 50;
+ if (track_count)
+ return FIFID_SIZE;
return 0;
}
-bool ds9_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool ds9_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
@@ -143,14 +109,14 @@ bool ds9_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
for (int head = 0; head < head_count; head++)
{
- io_generic_read(io, sectdata, (track * head_count + head) * track_size, track_size);
+ /*auto const [err, actual] =*/ read_at(io, (track * head_count + head) * track_size, sectdata, track_size); // FIXME: check for errors and premature EOF
generate_track(ds9_desc, track, head, sectors, sector_count, 104000, image);
}
}
- image->set_variant(floppy_image::DSQD);
+ image.set_variant(floppy_image::DSQD);
return true;
}
-const floppy_format_type FLOPPY_DS9_FORMAT = &floppy_image_format_creator<ds9_format>;
+const ds9_format FLOPPY_DS9_FORMAT;
diff --git a/src/lib/formats/ds9_dsk.h b/src/lib/formats/ds9_dsk.h
index 540c49a7ce7..cfed4ea85d1 100644
--- a/src/lib/formats/ds9_dsk.h
+++ b/src/lib/formats/ds9_dsk.h
@@ -5,34 +5,32 @@
formats/ds9_dsk.h
*********************************************************************/
+#ifndef MAME_FORMATS_DS9_DSK_H
+#define MAME_FORMATS_DS9_DSK_H
-#ifndef DS9_DSK_H_
-#define DS9_DSK_H_
+#pragma once
#include "flopimg.h"
-#include "formats/basicdsk.h"
-
-LEGACY_FLOPPY_OPTIONS_EXTERN(ds9);
class ds9_format : public floppy_image_format_t
{
public:
ds9_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override { return false; }
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override { return false; }
static const desc_e ds9_desc[];
private:
- void find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
+ static void find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
};
-extern const floppy_format_type FLOPPY_DS9_FORMAT;
+extern const ds9_format FLOPPY_DS9_FORMAT;
-#endif /* DS9_DSK_H_ */
+#endif // MAME_FORMATS_DS9_DSK_H
diff --git a/src/lib/formats/dsk_dsk.cpp b/src/lib/formats/dsk_dsk.cpp
index db9d54920e5..e10e94f80be 100644
--- a/src/lib/formats/dsk_dsk.cpp
+++ b/src/lib/formats/dsk_dsk.cpp
@@ -2,21 +2,28 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/dsk_dsk.c
+ formats/dsk_dsk.cpp
DSK disk images
*********************************************************************/
-#include <string.h>
-#include <assert.h>
-
+#include "dsk_dsk.h"
+#include "flopimg_legacy.h"
#include "imageutl.h"
-#include "flopimg.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include "osdcore.h" // osd_printf_*
+
+#include <cstring>
#define MV_CPC "MV - CPC"
#define EXTENDED "EXTENDED"
+#define SPOT_DUPLICATES 0
+
struct dskdsk_tag
{
int disk_image_type; /* image type: standard or extended */
@@ -97,10 +104,10 @@ static floperr_t get_offset(floppy_image_legacy *floppy, int head, int track, in
get_tag(floppy)->sector_size = (1<<(track_info[0x014]+7));
offs = track_offset + 0x100 +sector * get_tag(floppy)->sector_size;
} else {
- get_tag(floppy)->sector_size = track_info[0x18 + (sector<<3) + 6] + (track_info[0x18+(sector<<3) + 7]<<8);
+ get_tag(floppy)->sector_size = get_u16le(&track_info[0x18 + (sector<<3) + 6]);
offs = track_offset + 0x100;
for (i=0;i<sector;i++) {
- offs += track_info[0x18 + (i<<3) + 6] + (track_info[0x18+(i<<3) + 7]<<8);
+ offs += get_u16le(&track_info[0x18 + (i<<3) + 6]);
}
}
@@ -218,13 +225,14 @@ FLOPPY_CONSTRUCT( dsk_dsk_construct )
}
floppy_image_read(floppy, header, 0, 0x100);
-#ifdef SPOT_DUPLICATES
- // this allow to spot .dsk files with same data and different headers, making easier to debug softlists.
- uint32_t temp_size = floppy_image_size(floppy);
- uint8_t tmp_copy[temp_size - 0x100];
- floppy_image_read(floppy,tmp_copy,0x100,temp_size - 0x100);
- printf("CRC16: %d\n", ccitt_crc16(0xffff, tmp_copy, temp_size - 0x100));
-#endif
+ if(SPOT_DUPLICATES)
+ {
+ // this allow to spot .dsk files with same data and different headers, making easier to debug softlists.
+ uint32_t temp_size = floppy_image_size(floppy);
+ auto tmp_copy = std::make_unique<uint8_t[]>(temp_size - 0x100);
+ floppy_image_read(floppy,tmp_copy.get(),0x100,temp_size - 0x100);
+ printf("CRC16: %d\n", ccitt_crc16(0xffff, tmp_copy.get(), temp_size - 0x100));
+ }
tag = (struct dskdsk_tag *) floppy_create_tag(floppy, sizeof(struct dskdsk_tag));
if (!tag)
@@ -244,7 +252,7 @@ FLOPPY_CONSTRUCT( dsk_dsk_construct )
for (i=0; i<tag->tracks * tag->heads; i++)
{
tag->track_offsets[cnt] = tmp;
- tmp += pick_integer_le(header, 0x32, 2);
+ tmp += get_u16le(&header[0x32]);
cnt += skip;
}
} else {
@@ -270,7 +278,8 @@ FLOPPY_CONSTRUCT( dsk_dsk_construct )
return FLOPPY_ERROR_SUCCESS;
}
-#include "dsk_dsk.h"
+
+
#define DSK_FORMAT_HEADER "MV - CPC"
#define EXT_FORMAT_HEADER "EXTENDED CPC DSK"
@@ -279,36 +288,38 @@ dsk_format::dsk_format() : floppy_image_format_t()
{
}
-const char *dsk_format::name() const
+const char *dsk_format::name() const noexcept
{
return "dsk";
}
-const char *dsk_format::description() const
+const char *dsk_format::description() const noexcept
{
return "CPC DSK Format";
}
-const char *dsk_format::extensions() const
+const char *dsk_format::extensions() const noexcept
{
return "dsk";
}
-bool dsk_format::supports_save() const
+bool dsk_format::supports_save() const noexcept
{
return false;
}
-int dsk_format::identify(io_generic *io, uint32_t form_factor)
+int dsk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t header[16];
-
- io_generic_read(io, &header, 0, sizeof(header));
- if ( memcmp( header, DSK_FORMAT_HEADER, 8 ) ==0) {
- return 100;
+ auto const [err, actual] = read_at(io, 0, &header, sizeof(header));
+ if (err) {
+ return 0;
}
- if ( memcmp( header, EXT_FORMAT_HEADER, 16 ) ==0) {
- return 100;
+ if ((8 <= actual) && !memcmp(header, DSK_FORMAT_HEADER, 8)) {
+ return FIFID_SIGN;
+ }
+ if ((16 <= actual) && !memcmp(header, EXT_FORMAT_HEADER, 16)) {
+ return FIFID_SIGN;
}
return 0;
}
@@ -344,14 +355,16 @@ struct sector_header
#pragma pack()
-bool dsk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool dsk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t header[0x100];
bool extendformat = false;
- uint64_t image_size = io_generic_size(io);
+ uint64_t image_size;
+ if (io.length(image_size))
+ return false;
- io_generic_read(io, &header, 0, sizeof(header));
+ read_at(io, 0, &header, sizeof(header)); // FIXME: check for errors and premature EOF
if ( memcmp( header, EXT_FORMAT_HEADER, 16 ) ==0) {
extendformat = true;
}
@@ -364,7 +377,7 @@ bool dsk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
int tracks = header[0x30];
int img_tracks, img_heads;
- image->get_maximal_geometry(img_tracks, img_heads);
+ image.get_maximal_geometry(img_tracks, img_heads);
if (tracks > img_tracks)
{
if (tracks - img_tracks > DUMP_THRESHOLD)
@@ -381,7 +394,7 @@ bool dsk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
}
}
if (heads > img_heads)
- return false;
+ osd_printf_warning("dsk: Floppy disk has excess of heads for this drive that will be discarded (floppy heads=%d, drive heads=%d).\n", heads, img_heads);
uint64_t track_offsets[84*2];
int cnt =0;
@@ -390,7 +403,7 @@ bool dsk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
for (int i=0; i<tracks * heads; i++)
{
track_offsets[cnt] = tmp;
- tmp += pick_integer_le(header, 0x32, 2);
+ tmp += get_u16le(&header[0x32]);
cnt += skip;
}
} else {
@@ -412,13 +425,12 @@ bool dsk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
}
}
- int counter = 0;
for(int track=0; track < tracks; track++) {
- for(int side=0; side < heads; side++) {
+ for(int side=0; side < std::min(heads, img_heads); side++) {
if(track_offsets[(track<<1)+side] >= image_size)
continue;
track_header tr;
- io_generic_read(io, &tr,track_offsets[(track<<1)+side],sizeof(tr));
+ read_at(io, track_offsets[(track<<1)+side], &tr, sizeof(tr)); // FIXME: check for errors and premature EOF
// skip if there are no sectors in this track
if (tr.number_of_sector == 0)
@@ -428,7 +440,7 @@ bool dsk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
int first_sector_code = -1;
for(int j=0;j<tr.number_of_sector;j++) {
sector_header sector;
- io_generic_read(io, &sector,track_offsets[(track<<1)+side]+sizeof(tr)+(sizeof(sector)*j),sizeof(sector));
+ read_at(io, track_offsets[(track<<1)+side]+sizeof(tr)+(sizeof(sector)*j), &sector, sizeof(sector)); // FIXME: check for errors and premature EOF
if (j == 0)
first_sector_code = sector.sector_size_code;
@@ -447,7 +459,7 @@ bool dsk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
for(int j=0;j<tr.number_of_sector;j++) {
sector_header sector;
- io_generic_read(io, &sector,track_offsets[(track<<1)+side]+sizeof(tr)+(sizeof(sector)*j),sizeof(sector));
+ read_at(io, track_offsets[(track<<1)+side]+sizeof(tr)+(sizeof(sector)*j), &sector, sizeof(sector)); // FIXME: check for errors and premature EOF
sects[j].track = sector.track;
sects[j].head = sector.side;
@@ -470,7 +482,7 @@ bool dsk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
if(!(sector.fdc_status_reg1 & 0x04)) {
sects[j].data = sect_data + sdatapos;
- io_generic_read(io, sects[j].data, pos, sects[j].actual_size);
+ read_at(io, pos, sects[j].data, sects[j].actual_size); // FIXME: check for errors and premature EOF
sdatapos += sects[j].actual_size;
} else
@@ -483,10 +495,9 @@ bool dsk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
}
// larger cell count (was 100000) to allow for slightly out of spec images (theatre europe on einstein)
build_pc_track_mfm(track, side, image, 105000, tr.number_of_sector, sects, tr.gap3_length);
- counter++;
}
}
return true;
}
-const floppy_format_type FLOPPY_DSK_FORMAT = &floppy_image_format_creator<dsk_format>;
+const dsk_format FLOPPY_DSK_FORMAT;
diff --git a/src/lib/formats/dsk_dsk.h b/src/lib/formats/dsk_dsk.h
index e6b289a3cca..d3a7b7c704c 100644
--- a/src/lib/formats/dsk_dsk.h
+++ b/src/lib/formats/dsk_dsk.h
@@ -19,15 +19,15 @@ class dsk_format : public floppy_image_format_t
public:
dsk_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_DSK_FORMAT;
+extern const dsk_format FLOPPY_DSK_FORMAT;
#endif // MAME_FORMATS_DSK_DSK_H
diff --git a/src/lib/formats/dvk_mx_dsk.cpp b/src/lib/formats/dvk_mx_dsk.cpp
index 477dd07690f..c384dc38e00 100644
--- a/src/lib/formats/dvk_mx_dsk.cpp
+++ b/src/lib/formats/dvk_mx_dsk.cpp
@@ -18,88 +18,74 @@
************************************************************************/
-#include <assert.h>
-
-#include "flopimg.h"
#include "formats/dvk_mx_dsk.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+constexpr uint32_t ID_PATTERN = 0xaaaaffaf;
+
const floppy_image_format_t::desc_e dvk_mx_format::dvk_mx_new_desc[] = {
/* 01 */ { FM, 0x00, 8*2 }, // eight 0x0000 words
- /* 03 */ { FM, 0x00, 1 },
- /* 02 */ { FM, 0xf3, 1 }, // word 0x00f3
- /* 05 */ { FM, 0x00, 1 },
- /* 04 */ { TRACK_ID_FM }, // track number word
- /* 05 */ { SECTOR_LOOP_START, 0, 10 }, // 11 sectors
+ /* 02 */ { FM, 0x00, 1 },
+ /* 03 */ { FM, 0xf3, 1 }, // word 0x00f3
+ /* 04 */ { FM, 0x00, 1 },
+ /* 05 */ { TRACK_ID_FM }, // track number word
+ /* 06 */ { SECTOR_LOOP_START, 0, 10 }, // 11 sectors
/* 07 */ { SECTOR_DATA_MX, -1 },
- /* 10 */ { SECTOR_LOOP_END },
- /* 13 */ { FM, 0x83, 1 },
+ /* 08 */ { SECTOR_LOOP_END },
+ /* 09 */ { FM, 0x83, 1 },
+ /* 10 */ { OFFSET_ID_FM },
+ /* 11 */ { FM, 0x83, 1 },
/* 12 */ { OFFSET_ID_FM },
- /* 15 */ { FM, 0x83, 1 },
+ /* 13 */ { FM, 0x83, 1 },
/* 14 */ { OFFSET_ID_FM },
- /* 17 */ { FM, 0x83, 1 },
- /* 16 */ { OFFSET_ID_FM },
- /* 18 */ { END }
+ /* 15 */ { END }
};
const floppy_image_format_t::desc_e dvk_mx_format::dvk_mx_old_desc[] = {
/* 01 */ { FM, 0x00, 30*2 },
- /* 03 */ { FM, 0x00, 1 },
- /* 02 */ { FM, 0xf3, 1 }, // word 0x00f3
- /* 05 */ { FM, 0x00, 1 },
- /* 04 */ { TRACK_ID_FM }, // track number word
+ /* 02 */ { FM, 0x00, 1 },
+ /* 03 */ { FM, 0xf3, 1 }, // word 0x00f3
+ /* 04 */ { FM, 0x00, 1 },
+ /* 05 */ { TRACK_ID_FM }, // track number word
/* 06 */ { SECTOR_LOOP_START, 0, 10 }, // 11 sectors
/* 07 */ { SECTOR_DATA_MX, -1 },
- /* 10 */ { SECTOR_LOOP_END },
- /* 13 */ { FM, 0x83, 1 },
- /* 11 */ { FM, 0x01, 1 },
- /* 15 */ { FM, 0x83, 1 },
- /* 14 */ { FM, 0x01, 1 },
- /* 16 */ { END }
+ /* 08 */ { SECTOR_LOOP_END },
+ /* 09 */ { FM, 0x00, 1 },
+ /* 10 */ { FM, 0116, 1 },
+ /* 11 */ { FM, 0x00, 4*2 }, // four 0x0000 words
+ /* 12 */ { END }
};
dvk_mx_format::dvk_mx_format()
{
}
-const char *dvk_mx_format::name() const
-{
- return "mx";
-}
-
-const char *dvk_mx_format::description() const
-{
- return "DVK MX: floppy image";
-}
-
-const char *dvk_mx_format::extensions() const
-{
- return "mx";
-}
-
-bool dvk_mx_format::supports_save() const
+void dvk_mx_format::find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
{
- return false;
-}
-
-void dvk_mx_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
-{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ {
+ track_count = head_count = sector_count = 0;
+ return;
+ }
switch (size)
{
case 112640:
track_count = 40;
- sector_count = 11;
+ sector_count = MX_SECTORS;
head_count = 1;
break;
case 225280:
track_count = 40;
- sector_count = 11;
+ sector_count = MX_SECTORS;
head_count = 2;
break;
case 450560:
track_count = 80;
- sector_count = 11;
+ sector_count = MX_SECTORS;
head_count = 2;
break;
default:
@@ -108,7 +94,7 @@ void dvk_mx_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &hea
}
}
-int dvk_mx_format::identify(io_generic *io, uint32_t form_factor)
+int dvk_mx_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t track_count, head_count, sector_count;
@@ -117,61 +103,122 @@ int dvk_mx_format::identify(io_generic *io, uint32_t form_factor)
if (track_count)
{
uint8_t sectdata[512];
- io_generic_read(io, sectdata, 512, 512);
+ /*auto const [err, actual] =*/ read_at(io, 512, sectdata, 512); // FIXME: check for errors and premature EOF
// check value in RT-11 home block. see src/tools/imgtool/modules/rt11.cpp
- if (pick_integer_le(sectdata, 0724, 2) == 6)
- return 100;
+ if (get_u16le(&sectdata[0724]) == 6)
+ return FIFID_SIGN|FIFID_SIZE;
else
- return 75;
+ return FIFID_SIZE;
}
return 0;
}
-bool dvk_mx_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool dvk_mx_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
if (track_count == 0) return false;
- uint8_t sectdata[11 * 256];
- desc_s sectors[11];
+ uint8_t sectdata[MX_SECTORS * MX_SECTOR_SIZE];
+ desc_s sectors[MX_SECTORS];
for (int i = 0; i < sector_count; i++)
{
- sectors[i].data = sectdata + 256 * i;
- sectors[i].size = 256;
+ sectors[i].data = sectdata + MX_SECTOR_SIZE * i;
+ sectors[i].size = MX_SECTOR_SIZE;
sectors[i].sector_id = i;
}
- int track_size = sector_count * 256;
+ int track_size = sector_count * MX_SECTOR_SIZE;
for (int track = 0; track < track_count; track++)
{
for (int head = 0; head < head_count; head++)
{
- io_generic_read(io, sectdata, (track * head_count + head) * track_size, track_size);
+ /*auto const [err, actual] =*/ read_at(io, (track * head_count + head) * track_size, sectdata, track_size); // FIXME: check for errors and premature EOF
generate_track(dvk_mx_new_desc, track, head, sectors, sector_count, 45824, image);
}
}
if (head_count == 1)
{
- image->set_variant(floppy_image::SSDD);
+ image.set_variant(floppy_image::SSDD);
}
else
{
if (track_count > 40)
{
- image->set_variant(floppy_image::DSQD);
+ image.set_variant(floppy_image::DSQD);
}
else
{
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
}
}
return true;
}
-const floppy_format_type FLOPPY_DVK_MX_FORMAT = &floppy_image_format_creator<dvk_mx_format>;
+bool dvk_mx_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ int tracks;
+ int heads;
+ bool res = false;
+ image.get_actual_geometry(tracks, heads);
+
+ for (int cyl = 0; cyl < tracks; cyl++)
+ {
+ for (int head = 0; head < heads; head++)
+ {
+ auto bitstream = generate_bitstream_from_track(cyl, head, 4000, image, 0);
+ uint8_t sector_data[MX_SECTORS * MX_SECTOR_SIZE];
+ if (get_next_sector(bitstream, sector_data))
+ {
+ unsigned const offset_in_image = (cyl * heads + head) * MX_SECTOR_SIZE * MX_SECTORS;
+ res = true;
+ // FIXME: check for errors
+ /*auto const [err, actual] =*/ write_at(io, offset_in_image, sector_data, MX_SECTOR_SIZE * MX_SECTORS);
+ }
+ }
+ }
+ return res;
+}
+
+bool dvk_mx_format::get_next_sector(const std::vector<bool> &bitstream, uint8_t *sector_data)
+{
+ uint32_t sr = 0;
+ int pos = 0;
+ while (pos < bitstream.size() && sr != ID_PATTERN)
+ {
+ sr = (sr << 1) | bitstream[pos];
+ pos++;
+ }
+ if (pos == bitstream.size())
+ {
+ // End of track reached
+ return false;
+ }
+ unsigned to_dump = MX_SECTORS * (MX_SECTOR_SIZE + 2);
+ pos += 32; // skip track ID
+ for (unsigned i = 0, k = 0; i < to_dump && pos < bitstream.size(); i++)
+ {
+ uint8_t byte = 0;
+ unsigned j;
+ for (j = 0; j < 8 && pos < bitstream.size(); j++)
+ {
+ bool bit = bitstream[pos + 1];
+ pos += 2;
+ byte <<= 1;
+ byte |= bit;
+ }
+ if (j == 8 && (i % (MX_SECTOR_SIZE + 2)) < MX_SECTOR_SIZE)
+ {
+ sector_data[k ^ 1] = byte;
+ k++;
+ }
+ }
+ return true;
+}
+
+const dvk_mx_format FLOPPY_DVK_MX_FORMAT;
diff --git a/src/lib/formats/dvk_mx_dsk.h b/src/lib/formats/dvk_mx_dsk.h
index a95b635588c..b0d64deab9f 100644
--- a/src/lib/formats/dvk_mx_dsk.h
+++ b/src/lib/formats/dvk_mx_dsk.h
@@ -5,35 +5,38 @@
formats/dvk_mx_dsk.h
*********************************************************************/
-
-#ifndef DVK_MX_DSK_H_
-#define DVK_MX_DSK_H_
+#ifndef MAME_FORMATS_DVK_MX_DSK_H
+#define MAME_FORMATS_DVK_MX_DSK_H
#pragma once
#include "flopimg.h"
-#include "imageutl.h"
class dvk_mx_format : public floppy_image_format_t
{
public:
dvk_mx_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override { return "mx"; }
+ virtual const char *description() const noexcept override { return "DVK MX disk image"; }
+ virtual const char *extensions() const noexcept override { return "mx"; }
+ virtual bool supports_save() const noexcept override { return true; }
static const desc_e dvk_mx_old_desc[];
static const desc_e dvk_mx_new_desc[];
private:
- void find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
+ static constexpr unsigned MX_SECTORS = 11;
+ static constexpr unsigned MX_SECTOR_SIZE = 256;
+
+ static void find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
+ static bool get_next_sector(const std::vector<bool> &bitstream , uint8_t *sector_data);
};
-extern const floppy_format_type FLOPPY_DVK_MX_FORMAT;
+extern const dvk_mx_format FLOPPY_DVK_MX_FORMAT;
-#endif /* DVK_MX_DSK_H_ */
+#endif // MAME_FORMATS_DVK_MX_DSK_H
diff --git a/src/lib/formats/ep64_dsk.cpp b/src/lib/formats/ep64_dsk.cpp
index 80981bf698e..010352a9476 100644
--- a/src/lib/formats/ep64_dsk.cpp
+++ b/src/lib/formats/ep64_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/ep64_dsk.c
+ formats/ep64_dsk.cpp
Enterprise Sixty Four disk image format
*********************************************************************/
-#include <assert.h>
-
#include "formats/ep64_dsk.h"
ep64_format::ep64_format() : wd177x_format(formats)
{
}
-const char *ep64_format::name() const
+const char *ep64_format::name() const noexcept
{
return "ep64";
}
-const char *ep64_format::description() const
+const char *ep64_format::description() const noexcept
{
return "Enteprise Sixty Four disk image";
}
-const char *ep64_format::extensions() const
+const char *ep64_format::extensions() const noexcept
{
return "img";
}
@@ -40,4 +38,4 @@ const ep64_format::format ep64_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_EP64_FORMAT = &floppy_image_format_creator<ep64_format>;
+const ep64_format FLOPPY_EP64_FORMAT;
diff --git a/src/lib/formats/ep64_dsk.h b/src/lib/formats/ep64_dsk.h
index 07acca4a217..0423cb0a544 100644
--- a/src/lib/formats/ep64_dsk.h
+++ b/src/lib/formats/ep64_dsk.h
@@ -19,14 +19,14 @@ class ep64_format : public wd177x_format
public:
ep64_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_EP64_FORMAT;
+extern const ep64_format FLOPPY_EP64_FORMAT;
#endif // MAME_FORMATS_EP64_DSK_H
diff --git a/src/lib/formats/esq16_dsk.cpp b/src/lib/formats/esq16_dsk.cpp
index 1f6e86f295f..f487e835665 100644
--- a/src/lib/formats/esq16_dsk.cpp
+++ b/src/lib/formats/esq16_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:R. Belmont, Olivier Galibert
/*********************************************************************
- formats/esq16_dsk.c
+ formats/esq16_dsk.cpp
Formats for 16-bit Ensoniq synthesizers and samplers
@@ -10,10 +10,10 @@
*********************************************************************/
-#include <assert.h>
+#include "esq16_dsk.h"
+
+#include "ioprocs.h"
-#include "flopimg.h"
-#include "formats/esq16_dsk.h"
const floppy_image_format_t::desc_e esqimg_format::esq_10_desc[] = {
{ MFM, 0x4e, 80 },
@@ -51,53 +51,53 @@ esqimg_format::esqimg_format()
{
}
-const char *esqimg_format::name() const
+const char *esqimg_format::name() const noexcept
{
return "esq16";
}
-const char *esqimg_format::description() const
+const char *esqimg_format::description() const noexcept
{
return "Ensoniq VFX-SD/SD-1/EPS-16 floppy disk image";
}
-const char *esqimg_format::extensions() const
+const char *esqimg_format::extensions() const noexcept
{
return "img";
}
-bool esqimg_format::supports_save() const
+bool esqimg_format::supports_save() const noexcept
{
return true;
}
-void esqimg_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
+void esqimg_format::find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
{
- uint64_t size = io_generic_size(io);
- track_count = 80;
- head_count = 2;
- sector_count = 10;
-
- uint32_t expected_size = 512 * track_count*head_count*sector_count;
- if (size == expected_size)
- {
- return;
- }
+ uint64_t size;
+ if(!io.length(size)) {
+ track_count = 80;
+ head_count = 2;
+ sector_count = 10;
+ uint32_t expected_size = 512 * track_count*head_count*sector_count;
+ if(size == expected_size) {
+ return;
+ }
+ }
track_count = head_count = sector_count = 0;
}
-int esqimg_format::identify(io_generic *io, uint32_t form_factor)
+int esqimg_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
if(track_count)
- return 50;
+ return FIFID_SIZE;
return 0;
}
-bool esqimg_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool esqimg_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
@@ -113,17 +113,17 @@ bool esqimg_format::load(io_generic *io, uint32_t form_factor, floppy_image *ima
int track_size = sector_count*512;
for(int track=0; track < track_count; track++) {
for(int head=0; head < head_count; head++) {
- io_generic_read(io, sectdata, (track*head_count + head)*track_size, track_size);
+ /*auto const [err, actual] =*/ read_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF
generate_track(esq_10_desc, track, head, sectors, sector_count, 110528, image);
}
}
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
return true;
}
-bool esqimg_format::save(io_generic *io, floppy_image *image)
+bool esqimg_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
int track_count, head_count, sector_count;
get_geometry_mfm_pc(image, 2000, track_count, head_count, sector_count);
@@ -144,11 +144,11 @@ bool esqimg_format::save(io_generic *io, floppy_image *image)
for(int track=0; track < track_count; track++) {
for(int head=0; head < head_count; head++) {
get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata);
- io_generic_write(io, sectdata, (track*head_count + head)*track_size, track_size);
+ /*auto const [err, actual] =*/ write_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors
}
}
return true;
}
-const floppy_format_type FLOPPY_ESQIMG_FORMAT = &floppy_image_format_creator<esqimg_format>;
+const esqimg_format FLOPPY_ESQIMG_FORMAT;
diff --git a/src/lib/formats/esq16_dsk.h b/src/lib/formats/esq16_dsk.h
index 91bd03b8151..c97a195e57f 100644
--- a/src/lib/formats/esq16_dsk.h
+++ b/src/lib/formats/esq16_dsk.h
@@ -21,21 +21,21 @@ class esqimg_format : public floppy_image_format_t
public:
esqimg_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
static const desc_e esq_10_desc[];
private:
- void find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
+ static void find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
};
-extern const floppy_format_type FLOPPY_ESQIMG_FORMAT;
+extern const esqimg_format FLOPPY_ESQIMG_FORMAT;
#endif // MAME_FORMATS_ESQ16_DSK_H
diff --git a/src/lib/formats/esq8_dsk.cpp b/src/lib/formats/esq8_dsk.cpp
index c575fe0db2f..b7225d839a9 100644
--- a/src/lib/formats/esq8_dsk.cpp
+++ b/src/lib/formats/esq8_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:R. Belmont, Olivier Galibert
/*********************************************************************
- formats/esq8_dsk.c
+ formats/esq8_dsk.cpp
Formats for 8-bit Ensoniq synthesizers and samplers
@@ -12,10 +12,12 @@
*********************************************************************/
-#include <assert.h>
+#include "esq8_dsk.h"
+
+#include "ioprocs.h"
+
+#include "osdcore.h" // osd_printf_*
-#include "flopimg.h"
-#include "formats/esq8_dsk.h"
const floppy_image_format_t::desc_e esq8img_format::esq_6_desc[] = {
{ MFM, 0x4e, 80 },
@@ -53,60 +55,64 @@ esq8img_format::esq8img_format()
{
}
-const char *esq8img_format::name() const
+const char *esq8img_format::name() const noexcept
{
return "esq8";
}
-const char *esq8img_format::description() const
+const char *esq8img_format::description() const noexcept
{
return "Ensoniq Mirage/SQ-80 floppy disk image";
}
-const char *esq8img_format::extensions() const
+const char *esq8img_format::extensions() const noexcept
{
return "img";
}
-bool esq8img_format::supports_save() const
+bool esq8img_format::supports_save() const noexcept
{
return true;
}
-void esq8img_format::find_size(io_generic *io, int &track_count, int &head_count, int &sector_count)
+void esq8img_format::find_size(util::random_read &io, int &track_count, int &head_count, int &sector_count)
{
- uint64_t size = io_generic_size(io);
- track_count = 80;
- head_count = 1;
- sector_count = 6;
-
- if (size == 5632 * 80)
+ uint64_t size;
+ if(!io.length(size))
{
- return;
- }
+ track_count = 80;
+ head_count = 1;
+ sector_count = 6;
+ if(size == 5632 * 80)
+ {
+ return;
+ }
+ }
track_count = head_count = sector_count = 0;
}
-int esq8img_format::identify(io_generic *io, uint32_t form_factor)
+int esq8img_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
int track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
if(track_count)
- return 50;
+ return FIFID_SIZE;
+
return 0;
}
-bool esq8img_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool esq8img_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
int track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
uint8_t sectdata[(5*1024) + 512];
desc_s sectors[6];
- for(int i=0; i<sector_count; i++) {
- if (i < 5)
+ for(int i=0; i<sector_count; i++)
+ {
+ if(i < 5)
{
sectors[i].data = sectdata + (1024*i); // 5 1024 byte sectors
sectors[i].size = 1024;
@@ -126,18 +132,19 @@ bool esq8img_format::load(io_generic *io, uint32_t form_factor, floppy_image *im
{
for(int head=0; head < head_count; head++)
{
- io_generic_read(io, sectdata, (track*head_count + head)*track_size, track_size);
+ /*auto const [err, actual] =*/ read_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF
generate_track(esq_6_desc, track, head, sectors, sector_count, 109376, image);
}
}
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
return true;
}
-bool esq8img_format::save(io_generic *io, floppy_image *image)
+bool esq8img_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
+ uint64_t file_offset = 0;
int track_count, head_count, sector_count;
get_geometry_mfm_pc(image, 2000, track_count, head_count, sector_count);
@@ -151,17 +158,34 @@ bool esq8img_format::save(io_generic *io, floppy_image *image)
if(sector_count != 6)
sector_count = 6;
- uint8_t sectdata[10*512];
- int track_size = (5*1024) + 512;
-
- for(int track=0; track < track_count; track++) {
- for(int head=0; head < head_count; head++) {
- get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata);
- io_generic_write(io, sectdata, (track*head_count + head)*track_size, track_size);
+ for(int track=0; track < track_count; track++)
+ {
+ for(int head=0; head < head_count; head++)
+ {
+ auto bitstream = generate_bitstream_from_track(track, head, 2000, image);
+ auto sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
+ int sector_expected_size;
+
+ for(int sector = 0; sector < sector_count; sector++)
+ {
+ if(sector < 5)
+ sector_expected_size = 1024;
+ else
+ sector_expected_size = 512;
+
+ if(sectors[sector].size() != sector_expected_size)
+ {
+ osd_printf_error("esq8img_format: track %d, sector %d invalid size: %d\n", track, sector, sectors[sector].size());
+ return false;
+ }
+
+ /*auto const [err, actual] =*/ write_at(io, file_offset, sectors[sector].data(), sector_expected_size); // FIXME: check for errors
+ file_offset += sector_expected_size;
+ }
}
}
return true;
}
-const floppy_format_type FLOPPY_ESQ8IMG_FORMAT = &floppy_image_format_creator<esq8img_format>;
+const esq8img_format FLOPPY_ESQ8IMG_FORMAT;
diff --git a/src/lib/formats/esq8_dsk.h b/src/lib/formats/esq8_dsk.h
index af0cb46eecd..f15f2e82527 100644
--- a/src/lib/formats/esq8_dsk.h
+++ b/src/lib/formats/esq8_dsk.h
@@ -23,21 +23,21 @@ class esq8img_format : public floppy_image_format_t
public:
esq8img_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
static const desc_e esq_6_desc[];
private:
- void find_size(io_generic *io, int &track_count, int &head_count, int &sector_count);
+ static void find_size(util::random_read &io, int &track_count, int &head_count, int &sector_count);
};
-extern const floppy_format_type FLOPPY_ESQ8IMG_FORMAT;
+extern const esq8img_format FLOPPY_ESQ8IMG_FORMAT;
#endif // MAME_FORMATS_ESQ8_DSK_H
diff --git a/src/lib/formats/excali64_dsk.cpp b/src/lib/formats/excali64_dsk.cpp
index 5d41c39f4e6..de864b32bef 100644
--- a/src/lib/formats/excali64_dsk.cpp
+++ b/src/lib/formats/excali64_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Robbbert
/*********************************************************************
- formats/excali64_dsk.c
+ formats/excali64_dsk.cpp
Excalibur 64 disk image format
*********************************************************************/
-#include <assert.h>
-
#include "formats/excali64_dsk.h"
excali64_format::excali64_format() : wd177x_format(formats)
{
}
-const char *excali64_format::name() const
+const char *excali64_format::name() const noexcept
{
return "excali64";
}
-const char *excali64_format::description() const
+const char *excali64_format::description() const noexcept
{
return "Excalibur 64 disk image";
}
-const char *excali64_format::extensions() const
+const char *excali64_format::extensions() const noexcept
{
return "raw";
}
@@ -40,4 +38,4 @@ const excali64_format::format excali64_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_EXCALI64_FORMAT = &floppy_image_format_creator<excali64_format>;
+const excali64_format FLOPPY_EXCALI64_FORMAT;
diff --git a/src/lib/formats/excali64_dsk.h b/src/lib/formats/excali64_dsk.h
index 03d4cb3b237..083ffbf81ba 100644
--- a/src/lib/formats/excali64_dsk.h
+++ b/src/lib/formats/excali64_dsk.h
@@ -19,14 +19,14 @@ class excali64_format : public wd177x_format
public:
excali64_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_EXCALI64_FORMAT;
+extern const excali64_format FLOPPY_EXCALI64_FORMAT;
#endif // MAME_FORMATS_EXCALI64_DSK_H
diff --git a/src/lib/formats/fc100_cas.cpp b/src/lib/formats/fc100_cas.cpp
index 5004da147d3..d991ce8de6a 100644
--- a/src/lib/formats/fc100_cas.cpp
+++ b/src/lib/formats/fc100_cas.cpp
@@ -10,8 +10,6 @@ it's all a guess.
********************************************************************/
-#include <assert.h>
-
#include "fc100_cas.h"
#define WAVEENTRY_LOW -32768
@@ -21,7 +19,7 @@ it's all a guess.
#define FC100_HEADER_BYTES 16
// image size
-static int fc100_image_size;
+static int fc100_image_size; // FIXME: global variable prevents multiple instances
static int fc100_put_samples(int16_t *buffer, int sample_pos, int count, int level)
{
@@ -106,7 +104,7 @@ static int fc100_handle_cassette(int16_t *buffer, const uint8_t *bytes)
Generate samples for the tape image
********************************************************************/
-static int fc100_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int fc100_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return fc100_handle_cassette(buffer, bytes);
}
@@ -122,7 +120,7 @@ static int fc100_cassette_calculate_size_in_samples(const uint8_t *bytes, int le
return fc100_handle_cassette(nullptr, bytes);
}
-static const struct CassetteLegacyWaveFiller fc100_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller fc100_legacy_fill_wave =
{
fc100_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -133,17 +131,17 @@ static const struct CassetteLegacyWaveFiller fc100_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error fc100_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error fc100_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &fc100_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &fc100_legacy_fill_wave);
}
static cassette_image::error fc100_cassette_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &fc100_legacy_fill_wave);
+ return cassette->legacy_construct(&fc100_legacy_fill_wave);
}
-static const struct CassetteFormat fc100_cassette_image_format =
+static const cassette_image::Format fc100_cassette_image_format =
{
"cas",
fc100_cassette_identify,
diff --git a/src/lib/formats/fdd_dsk.cpp b/src/lib/formats/fdd_dsk.cpp
index 5e34c4ad7df..7519eb593dd 100644
--- a/src/lib/formats/fdd_dsk.cpp
+++ b/src/lib/formats/fdd_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Fabio Priuli
/*********************************************************************
- formats/fdd_dsk.h
+ formats/fdd_dsk.cpp
PC98 FDD disk images
@@ -31,41 +31,48 @@
*********************************************************************/
-#include <assert.h>
-
#include "fdd_dsk.h"
+#include "ioprocs.h"
+
+#include "osdcomm.h" // little_endianize_int32
+
+#include <cstring>
+
+
fdd_format::fdd_format()
{
}
-const char *fdd_format::name() const
+const char *fdd_format::name() const noexcept
{
return "fdd";
}
-const char *fdd_format::description() const
+const char *fdd_format::description() const noexcept
{
return "FDD disk image";
}
-const char *fdd_format::extensions() const
+const char *fdd_format::extensions() const noexcept
{
return "fdd";
}
-int fdd_format::identify(io_generic *io, uint32_t form_factor)
+int fdd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t h[7];
- io_generic_read(io, h, 0, 7);
+ auto const [err, actual] = read_at(io, 0, h, 7); // FIXME: should it really be reading six bytes? also check for premature EOF.
+ if (err)
+ return false;
- if (strncmp((const char *)h, "VFD1.0", 6) == 0)
- return 100;
+ if (memcmp(h, "VFD1.0", 6) == 0)
+ return FIFID_SIGN;
return 0;
}
-bool fdd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool fdd_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t hsec[0x0c];
@@ -82,11 +89,11 @@ bool fdd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
for (int track = 0; track < 160; track++)
{
- int curr_num_sec = 0, curr_track_size = 0;
+ int curr_num_sec = 0; [[maybe_unused]] int curr_track_size = 0;
for (int sect = 0; sect < 26; sect++)
{
// read sector map for this sector
- io_generic_read(io, hsec, pos, 0x0c);
+ /*auto const [err, actual] =*/ read_at(io, pos, hsec, 0x0c); // FIXME: check for errors and premature EOF
pos += 0x0c;
if (hsec[0] == 0xff) // unformatted/unused sector
@@ -121,7 +128,7 @@ bool fdd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
if (sec_offs[cur_sec_map] == 0xffffffff)
memset(sect_data + cur_pos, fill_vals[cur_sec_map], sector_size);
else
- io_generic_read(io, sect_data + cur_pos, sec_offs[cur_sec_map], sector_size);
+ /*auto const [err, actual] =*/ read_at(io, sec_offs[cur_sec_map], sect_data + cur_pos, sector_size); // FIXME: check for errors and premature EOF
sects[i].track = tracks[cur_sec_map];
sects[i].head = heads[cur_sec_map];
@@ -140,9 +147,9 @@ bool fdd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-bool fdd_format::supports_save() const
+bool fdd_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_FDD_FORMAT = &floppy_image_format_creator<fdd_format>;
+const fdd_format FLOPPY_FDD_FORMAT;
diff --git a/src/lib/formats/fdd_dsk.h b/src/lib/formats/fdd_dsk.h
index 86a43d5d0ee..9dbd0accc5a 100644
--- a/src/lib/formats/fdd_dsk.h
+++ b/src/lib/formats/fdd_dsk.h
@@ -20,15 +20,15 @@ class fdd_format : public floppy_image_format_t
public:
fdd_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_FDD_FORMAT;
+extern const fdd_format FLOPPY_FDD_FORMAT;
#endif // MAME_FORMATS_FDD_DSK_H
diff --git a/src/lib/formats/fdi_dsk.cpp b/src/lib/formats/fdi_dsk.cpp
index c703f29cc69..edeeeea65ad 100644
--- a/src/lib/formats/fdi_dsk.cpp
+++ b/src/lib/formats/fdi_dsk.cpp
@@ -16,9 +16,10 @@
*/
-#include <assert.h>
#include "imageutl.h"
-#include "flopimg.h"
+#include "flopimg_legacy.h"
+
+#include "multibyte.h"
/***************************************************************************
PARAMETERS
@@ -238,7 +239,7 @@ FLOPPY_CONSTRUCT( fdi_dsk_construct )
floppy_image_read(floppy, &header, 0, sizeof(header));
tag->version = header.version[0];
- tag->tracks = pick_integer_be(header.ltrack, 0, 2) + 1;
+ tag->tracks = get_u16be(header.ltrack) + 1;
tag->heads = header.lhead + 1;
if (LOG)
diff --git a/src/lib/formats/fdos_dsk.cpp b/src/lib/formats/fdos_dsk.cpp
new file mode 100644
index 00000000000..138ad986011
--- /dev/null
+++ b/src/lib/formats/fdos_dsk.cpp
@@ -0,0 +1,189 @@
+// license:BSD-3-Clause copyright-holders:Michael R. Furman
+/*
+ * fdos_dsk.cpp - FDOS compatible disk images
+ *
+ * Created on: 24/08/2022
+ *
+ * This FDOS floppy disk image support leverages the wd177x_format support with
+ * verification of the disk formatting and the the ability to support format
+ * variations.
+ *
+ * The FDOS Disk format is as follows:
+ *
+ * Geometry:: 10 Sectors * 35 Tracks * 256 Bytes * 1 Side
+ * Sectors Numbered from 0-9
+ * Gap Bytes: Verified from FDOS source code
+ *
+ * Disk Format::
+ *
+ * Operating System:: Tracks 0-1
+ * Directory:: Track 2
+ * Data:: Tracks 3-34
+ *
+ *
+ * Note: Due to hardware differences between the original DC-1 (WD1771)
+ * controller for which FDOS was designed and DC-4 (WD1797) or DC-5 (WD2797)
+ * Controllers (DC-5 is Emulated in MAME), the machine can only be booted with
+ * a specific disk image from deramp.com (Mike Douglas) at the following
+ * location. The other disks available in the same directory can then be read
+ * once FDOS is loaded. This disk image also works on real hardware with a DC-4
+ * or PT FD-2A controllers.
+ *
+ * https://deramp.com/downloads/swtpc/software/FDOS/Disk%20Images/FDOSMPS.DSK
+ *
+ * This disk contains the all of these required patches:
+ *
+ * https://deramp.com/downloads/swtpc/software/FDOS/Disk%20Images/Patches/
+ */
+
+#include "fdos_dsk.h"
+
+#include "imageutl.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <tuple>
+
+
+namespace
+{
+ class fdos_formats : public wd177x_format
+ {
+ public:
+ struct dirent_entry_fdos
+ {
+ char filename[8] = {};
+ char password[8] = {};
+ uint8_t start_track = 0;
+ uint8_t start_sector = 0;
+ uint8_t num_sectors[2] = {};
+ uint8_t file_type = 0;
+ uint8_t start_addr[2] = {};
+ uint8_t end_addr[2] = {};
+ uint8_t exec_addr[2] = {};
+ uint8_t basic_high_line[2] = {};
+ uint8_t spares[3] = {};
+ };
+
+ static const format formats[];
+ };
+}
+
+fdos_format::fdos_format() : wd177x_format(fdos_formats::formats)
+{
+}
+
+const char *fdos_format::name() const noexcept
+{
+ return "fdos";
+}
+
+const char *fdos_format::description() const noexcept
+{
+ return "FDOS compatible disk image";
+}
+
+const char *fdos_format::extensions() const noexcept
+{
+ return "dsk";
+}
+
+int fdos_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ int type = find_size(io, form_factor, variants);
+
+ if (type != -1)
+ return FIFID_SIZE;
+ return 0;
+}
+
+
+int fdos_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ uint8_t boot0[256];
+ fdos_formats::dirent_entry_fdos info;
+ size_t actual;
+ std::error_condition ec;
+
+ for (int i=0; fdos_formats::formats[i].form_factor; i++) {
+ const format &f = fdos_formats::formats[i];
+
+ // Format Check
+ // Check byte 0 and byte 3 of Track 0 Sector 0
+ // 00320 2400 BD 240C START JSR BOOT
+ // 00330 2403 DE OB RESTRT LDX PROGX
+ // Should be $BD and $DE respectively
+ // There could be additional variations
+ std::tie(ec, actual) = read_at(io, 0, &boot0, f.sector_base_size);
+ if (ec || actual != f.sector_base_size)
+ return -1;
+ if (boot0[0] != 0xbd && boot0[3] != 0xde)
+ continue;
+
+ LOG_FORMATS("FDOS floppy dsk: size %u bytes, %u total sectors, %u remaining bytes, expected form factor %x\n",
+ size,
+ size / f.sector_base_size,
+ size % f.sector_base_size,
+ form_factor);
+
+ // Directory entries start at Track 2 Sector 0
+ std::tie(ec, actual) = read_at(io, 2 * f.sector_count * f.sector_base_size, &info, sizeof(struct fdos_formats::dirent_entry_fdos));
+ if (ec || actual != sizeof(struct fdos_formats::dirent_entry_fdos))
+ continue;
+
+ // First directory entry should be "$DOS"
+ if (memcmp(info.filename, "$DOS ", 8) != 0)
+ continue;
+ if (memcmp(info.password, " ", 8) != 0)
+ continue;
+ if (info.start_track != 0)
+ continue;
+ if (info.start_sector != 0)
+ continue;
+ if (get_u16be(info.num_sectors) != 0x0014)
+ continue;
+ // $DOS File type is supposed to be $11 but some disks (FDOSMPS) have $00
+ if (info.file_type != 0 && info.file_type != 0x11)
+ continue;
+ if (get_u16be(info.start_addr) != 0x2400)
+ continue;
+ if (get_u16be(info.end_addr) != 0x2fff)
+ continue;
+ // FDOS entry is supposed to be $2600 but some disks have $2400
+ uint16_t exec = get_u16be(info.exec_addr);
+ if (exec != 0x2600 && exec != 0x2400)
+ continue;
+
+
+ unsigned int format_size = 0;
+ for (int track=0; track < f.track_count; track++) {
+ for (int head=0; head < f.head_count; head++) {
+ const format &tf = get_track_format(f, head, track);
+ format_size += compute_track_size(tf);
+ }
+ }
+
+ if (format_size != size)
+ continue;
+
+
+ LOG_FORMATS("FDOS matching format index %d\n", i);
+ return i;
+ }
+ return -1;
+}
+
+const fdos_formats::format fdos_formats::formats[] = {
+ { // 0 89.6K 5 1/4 inch single density fdos format
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 1, 256, {}, 0, {}, 11, 11, 11
+ },
+ {}
+};
+
+const fdos_format FLOPPY_FDOS_FORMAT;
diff --git a/src/lib/formats/fdos_dsk.h b/src/lib/formats/fdos_dsk.h
new file mode 100644
index 00000000000..73bd7d7156a
--- /dev/null
+++ b/src/lib/formats/fdos_dsk.h
@@ -0,0 +1,30 @@
+// license:BSD-3-Clause
+// copyright-holders:Michael R. Furman
+/*
+ * fdos_dsk.h
+ *
+ * Created on: 24/08/2022
+ */
+#ifndef MAME_FORMATS_FDOS_DSK_H
+#define MAME_FORMATS_FDOS_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+#include "wd177x_dsk.h"
+
+class fdos_format : public wd177x_format
+{
+public:
+ fdos_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+};
+
+extern const fdos_format FLOPPY_FDOS_FORMAT;
+
+#endif // MAME_FORMATS_FDOS_DSK_H
diff --git a/src/lib/formats/fl1_dsk.cpp b/src/lib/formats/fl1_dsk.cpp
new file mode 100644
index 00000000000..8e72ce7e049
--- /dev/null
+++ b/src/lib/formats/fl1_dsk.cpp
@@ -0,0 +1,57 @@
+// license:BSD-3-Clause
+// copyright-holders:MetalliC
+/*********************************************************************
+
+ formats/fl1_dsk.cpp
+
+ FloppyOne DOS disk images
+
+*********************************************************************/
+
+#include "formats/fl1_dsk.h"
+
+fl1_format::fl1_format() : wd177x_format(formats)
+{
+}
+
+const char *fl1_format::name() const noexcept
+{
+ return "fl1";
+}
+
+const char *fl1_format::description() const noexcept
+{
+ return "FloppyOne floppy disk image";
+}
+
+const char *fl1_format::extensions() const noexcept
+{
+ return "fl1";
+}
+
+int fl1_format::get_image_offset(const format &f, int head, int track) const
+{
+ return (f.track_count * head + track) * compute_track_size(f);
+}
+
+const fl1_format::format fl1_format::formats[] = {
+ { // 5"25 800K 80 track double sided double density
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 5, 80, 2, 1024, {}, 0, {}, 80, 22, 54
+ },
+ { // 5"25 400K 80 track single sided double density
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 5, 80, 1, 1024, {}, 0, {}, 80, 22, 54
+ },
+ { // 3'5 800K 80 track double sided double density
+ floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 5, 80, 2, 1024, {}, 0, {}, 80, 22, 54
+ },
+ { // 3'5 400K 80 track single sided double density
+ floppy_image::FF_35, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 5, 80, 1, 1024, {}, 0, {}, 80, 22, 54
+ },
+ {}
+};
+
+const fl1_format FLOPPY_FL1_FORMAT;
diff --git a/src/lib/formats/fl1_dsk.h b/src/lib/formats/fl1_dsk.h
new file mode 100644
index 00000000000..6c6abe6a99a
--- /dev/null
+++ b/src/lib/formats/fl1_dsk.h
@@ -0,0 +1,32 @@
+// license:BSD-3-Clause
+// copyright-holders:MetalliC
+/*********************************************************************
+
+ formats/fl1_dsk.h
+
+ FloppyOne DOS disk images
+
+*********************************************************************/
+#ifndef MAME_FORMATS_FL1_DSK_H
+#define MAME_FORMATS_FL1_DSK_H
+
+#pragma once
+
+#include "wd177x_dsk.h"
+
+class fl1_format : public wd177x_format {
+public:
+ fl1_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const format formats[];
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+};
+
+extern const fl1_format FLOPPY_FL1_FORMAT;
+
+#endif // MAME_FORMATS_FL1_DSK_H
diff --git a/src/lib/formats/flacfile.cpp b/src/lib/formats/flacfile.cpp
new file mode 100644
index 00000000000..c594a809a8e
--- /dev/null
+++ b/src/lib/formats/flacfile.cpp
@@ -0,0 +1,152 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+/*********************************************************************
+
+ flacfile.cpp
+
+ Format code for flac files.
+
+*********************************************************************/
+
+#include "flacfile.h"
+
+#include "flac.h"
+
+#include <memory>
+#include <new>
+
+
+namespace {
+
+constexpr int MAX_CHANNELS = 8;
+
+
+// Copied from cassimg.cpp; put somewhere central?
+/*********************************************************************
+ helper code
+*********************************************************************/
+constexpr double map_double(double d, uint64_t low, uint64_t high, uint64_t value)
+{
+ return d * (value - low) / (high - low);
+}
+
+constexpr size_t waveform_bytes_per_sample(int waveform_flags)
+{
+ return size_t(1 << ((waveform_flags & 0x06) / 2));
+}
+
+
+cassette_image::error flacfile_identify(cassette_image *cassette, cassette_image::Options *opts)
+{
+ cassette->get_raw_cassette_image()->seek(0, SEEK_SET);
+ flac_decoder decoder(*cassette->get_raw_cassette_image());
+ const int channels = decoder.channels();
+ const int sample_rate = decoder.sample_rate();
+ const int bits_per_sample = decoder.bits_per_sample();
+ const int total_samples = decoder.total_samples();
+ decoder.finish();
+
+ opts->channels = channels;
+ opts->sample_frequency = sample_rate;
+ opts->bits_per_sample = bits_per_sample;
+
+ if (channels > MAX_CHANNELS)
+ return cassette_image::error::INVALID_IMAGE;
+ else if (channels > 0 && sample_rate > 0 && total_samples > 0)
+ return cassette_image::error::SUCCESS;
+ else
+ return cassette_image::error::INVALID_IMAGE;
+}
+
+
+cassette_image::error flacfile_load(cassette_image *cassette)
+{
+ cassette->get_raw_cassette_image()->seek(0, SEEK_SET);
+ flac_decoder decoder(*cassette->get_raw_cassette_image());
+ const int channels = decoder.channels();
+ const int total_samples = decoder.total_samples();
+
+ std::unique_ptr<int16_t []> samples[MAX_CHANNELS];
+ int16_t *channel_samples[MAX_CHANNELS];
+ for (int channel = 0; channel < channels; channel++)
+ {
+ samples[channel].reset(new (std::nothrow) int16_t [total_samples]);
+ if (!samples[channel])
+ return cassette_image::error::OUT_OF_MEMORY;
+ channel_samples[channel] = samples[channel].get();
+ }
+ if (!decoder.decode(channel_samples, decoder.total_samples()))
+ return cassette_image::error::INVALID_IMAGE;
+ for (int channel = 0; channel < channels; channel++)
+ {
+ const cassette_image::error err = cassette->put_samples(
+ channel, 0.0, (double)total_samples/decoder.sample_rate(), total_samples,
+ 2, channel_samples[channel], cassette_image::WAVEFORM_16BIT);
+ if (err != cassette_image::error::SUCCESS)
+ return err;
+ }
+
+ return cassette_image::error::SUCCESS;
+}
+
+
+cassette_image::error flacfile_save(cassette_image *cassette, const cassette_image::Info *info)
+{
+ if (info->channels > MAX_CHANNELS)
+ return cassette_image::error::INVALID_IMAGE;
+
+ cassette->get_raw_cassette_image()->seek(0, SEEK_SET);
+ flac_encoder encoder;
+ encoder.set_num_channels(info->channels);
+ encoder.set_sample_rate(info->sample_frequency);
+ if (!encoder.reset(*cassette->get_raw_cassette_image()))
+ return cassette_image::error::INTERNAL;
+
+ size_t samples_saved = 0;
+ int16_t buffer[MAX_CHANNELS][4096];
+ int16_t *buffer_ptr[MAX_CHANNELS];
+ const size_t bytes_per_sample = waveform_bytes_per_sample(cassette_image::WAVEFORM_16BIT);
+ const size_t sample_spacing = bytes_per_sample * info->channels;
+ const double sample_period = info->sample_count / (double) info->sample_frequency;
+
+ for (int channel = 0; channel < MAX_CHANNELS; channel++)
+ {
+ buffer_ptr[channel] = &buffer[channel][0];
+ }
+
+ while (samples_saved < info->sample_count)
+ {
+ size_t chunk_sample_count = std::min(sizeof(buffer) / sample_spacing, (info->sample_count - samples_saved));
+ double chunk_sample_period = map_double(sample_period, 0, info->sample_count, chunk_sample_count);
+ double chunk_time_index = map_double(sample_period, 0, info->sample_count, samples_saved);
+
+ for (int channel = 0; channel < info->channels; channel++)
+ {
+ cassette_image::error err = cassette->get_samples(channel, chunk_time_index, chunk_sample_period,
+ chunk_sample_count, sample_spacing, &buffer[channel * bytes_per_sample], cassette_image::WAVEFORM_16BIT);
+
+ if (err != cassette_image::error::SUCCESS)
+ return err;
+ }
+
+ if (!encoder.encode(buffer_ptr, chunk_sample_count))
+ return cassette_image::error::INTERNAL;
+
+ samples_saved += chunk_sample_count;
+ }
+
+ encoder.finish();
+
+ return cassette_image::error::SUCCESS;
+}
+
+} // anonymous namespace
+
+
+const cassette_image::Format cassette_image::flacfile_format =
+{
+ "flac",
+ flacfile_identify,
+ flacfile_load,
+ flacfile_save
+};
diff --git a/src/lib/formats/flacfile.h b/src/lib/formats/flacfile.h
new file mode 100644
index 00000000000..8af8c3263b4
--- /dev/null
+++ b/src/lib/formats/flacfile.h
@@ -0,0 +1,10 @@
+// license:BSD-3-Clause
+// copyright-holders:Wilbert Pol
+#ifndef MAME_FORMATS_FLACFILE_H
+#define MAME_FORMATS_FLACFILE_H
+
+#pragma once
+
+#include "cassimg.h"
+
+#endif // MAME_FORMATS_FLACFILE_H
diff --git a/src/lib/formats/flex_dsk.cpp b/src/lib/formats/flex_dsk.cpp
index 0d4aa038e60..11a72963471 100644
--- a/src/lib/formats/flex_dsk.cpp
+++ b/src/lib/formats/flex_dsk.cpp
@@ -1,146 +1,106 @@
// license:BSD-3-Clause
-// copyright-holders:Barry Rodewald
+// copyright-holders:Barry Rodewald, 68bit
/*
- * flex_dsk.c - FLEX compatible disk images
+ * flex_dsk.cpp - FLEX compatible disk images
*
* Created on: 24/06/2014
*
- * TODO This format does not yet handle double density disks with a single
- * density track 0. FLEX DSK files are generally 'fixed' to have a consistent
- * number of sectors per track which makes them easier to work with and gains
- * more storage space, and tools and emulators generally only work with these
- * formats. For now use single density disks, or patch the ROM to load the
- * boot sector(s) in double density and patch the drivers to use double
- * density on track zero. Drivers developed for emulators commonly have other
- * issues and need work anyway.
+ * This FLEX floppy disk image support leverages the wd177x_format support
+ * with the few differences handled by format variations.
+ *
+ * FLEX numbers sectors on the second side of a track continuing from the
+ * first side, so there are separate formats for each head on double sided
+ * disk formats.
+ *
+ * FLEX on the 6809 typically encodes the first track, on both heads, in
+ * single density for double density disks. This simplifies the ROM boot code
+ * which need only handle single density. It might also have been a strategy
+ * at the time for reading both single and double density disks. So there are
+ * double density disk format variations that have the first track encoded in
+ * single density.
+ *
+ * However FLEX double density disks did not necessarily have the first sector
+ * encoded in single density. FLEX floppy disk drivers for the 6800 were
+ * typically just patched for double density support, with the controller
+ * jumpered for double density, and so did not encode the first sector in
+ * single density. Also there are many 'emulator' disk images distributed for
+ * which the first track has been padded to give a consistent number of
+ * sectors per track. So there are variations for which the first track is
+ * also encoded in double density.
+ *
+ * FLEX generally numbers sectors starting with ID 1, but there are exceptions
+ * for 6800 boot sectors where the first one or two sectors of track 0 and
+ * head 0 are numbered starting with ID 0 and so there are format variations
+ * for these. This strategy simplified the 6800 ROM boot loader, minimising
+ * its code size, which simply issues a multi-sector read and relies on a gap
+ * in the sector IDs to halt that operation. So if only one sector is to be
+ * loaded then the sector IDs are 0, 2, 3, ..., and if two sectors are to be
+ * loaded then the sector IDs are 0, 1, 3, 4, .... This strategy was abandoned
+ * for FLEX 6809 which numbers the boot sectors starting from ID 1 too.
+ *
+ * The formats below include interleaved sectors to improve performance. The
+ * interleave has been chosen to perform well on slower hardware and software
+ * combinations while still offering some performance improvement. Tighter
+ * interleaving may well be possible but it depends on the systems and
+ * software.
+ *
+ * Not all of the formats are practical on all hardware and software. E.g. The
+ * higher density formats can require data rates that the CPU can not keep up
+ * with.
*/
#include "flex_dsk.h"
-#include "formats/imageutl.h"
+
+#include "imageutl.h"
+
+#include "ioprocs.h"
+
flex_format::flex_format() : wd177x_format(formats)
{
}
-const char *flex_format::name() const
+const char *flex_format::name() const noexcept
{
return "flex";
}
-const char *flex_format::description() const
+const char *flex_format::description() const noexcept
{
return "FLEX compatible disk image";
}
-const char *flex_format::extensions() const
+const char *flex_format::extensions() const noexcept
{
return "dsk";
}
-int flex_format::identify(io_generic *io, uint32_t form_factor)
+int flex_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int type = find_size(io, form_factor, variants);
if (type != -1)
- return 75;
+ return FIFID_SIZE;
return 0;
}
-int flex_format::find_size(io_generic *io, uint32_t form_factor)
+int flex_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
uint8_t boot0[256], boot1[256];
// Look at the boot sector.
// Density, sides, link??
- io_generic_read(io, &boot0, 256 * 0, sizeof(boot0));
- io_generic_read(io, &boot1, 256 * 1, sizeof(boot1));
- // Look at the system information sector.
- io_generic_read(io, &info, 256 * 2, sizeof(struct sysinfo_sector));
+ read_at(io, 256 * 0, &boot0, sizeof(boot0)); // FIXME: check for errors and premature EOF
+ read_at(io, 256 * 1, &boot1, sizeof(boot1)); // FIXME: check for errors and premature EOF
- LOG_FORMATS("FLEX floppy dsk size %d %d %d\n", (uint32_t)size, (uint32_t)size / 256, (uint32_t)size % 256);
+ LOG_FORMATS("FLEX floppy dsk: size %d bytes, %d total sectors, %d remaining bytes, expected form factor %x\n", (uint32_t)size, (uint32_t)size / 256, (uint32_t)size % 256, form_factor);
- LOG_FORMATS(" boot0:");
- for (int i = 0; i < 16; i++) {
- LOG_FORMATS(" %02x", boot0[i]);
- }
- LOG_FORMATS("\n");
-
- LOG_FORMATS(" boot1:");
- for (int i = 0; i < 16; i++) {
- LOG_FORMATS(" %02x", boot1[i]);
- }
- LOG_FORMATS("\n");
-
-
- // Check that the 'unused' area is actually unused.
- LOG_FORMATS(" unused1:");
- for (int i = 0; i < sizeof(info.unused1); i++) {
- LOG_FORMATS(" %02x", info.unused1[i]);
- }
- LOG_FORMATS("\n");
-
- LOG_FORMATS(" disk_name: \"");
- for (int i = 0; i < sizeof(info.disk_name); i++) {
- uint8_t ch = info.disk_name[i];
- if (ch < 0x20 || ch > 0x7f) {
- LOG_FORMATS("[%02x]", ch);
- } else {
- LOG_FORMATS("%c", ch);
- }
- }
- if (info.disk_ext[0] || info.disk_ext[1] || info.disk_ext[2]) {
- LOG_FORMATS(".");
- for (int i = 0; i < sizeof(info.disk_ext); i++) {
- uint8_t ch = info.disk_ext[i];
- if (ch < 0x20 || ch > 0x7f) {
- LOG_FORMATS("[%02x]", ch);
- } else {
- LOG_FORMATS("%c", ch);
- }
- }
- }
- LOG_FORMATS("\"\n");
-
- LOG_FORMATS(" fc_start_trk %d, fc_start_sec %d\n", info.fc_start_trk, info.fc_start_sec);
- LOG_FORMATS(" fc_end_trk: %d, fc_end_sec: %d\n", info.fc_end_trk, info.fc_end_sec);
- LOG_FORMATS(" free: %02x %02x\n", info.free[0], info.free[0]);
-
- LOG_FORMATS(" month %d day %d year %d\n", info.month, info.day, info.year);
- LOG_FORMATS(" last_trk %d, last_sec %d\n", info.last_trk, info.last_sec);
-
- LOG_FORMATS(" unused2:");
- for (int i = 0; i < 16; i++) {
- LOG_FORMATS(" %02x", info.unused2[i]);
- }
- LOG_FORMATS("\n");
-
-#if 0
- // Check that the first 'unused' area is zero filled.
- // Unfortunately an occasional dsk image has non-zero values here.
- for (int i = 0; i < sizeof(info.unused1); i++)
- if (info.unused1[i] != 0) return -1;
-#endif
-
- // Consistency checks.
- if (info.fc_start_trk > info.last_trk || info.fc_end_trk > info.last_trk)
- return -1;
- if (info.fc_start_sec > info.last_sec || info.fc_end_sec > info.last_sec)
- return -1;
- if (info.month < 1 || info.month > 12 || info.day < 1 || info.day > 31)
- return -1;
-
- // FLEX sector numbers start at one generally, however the 6800 ROM
- // boot loaders load the boot code from track zero, side zero,
- // starting at sector zero. The boot code attempts to read multiple
- // sectors and a gap in the sector numbering appears to be used to
- // terminate the sequence. So if only one sector is to be loaded then
- // the sector numbering is 0, 2, 3, .... If two sectors are to be
- // loaded then the sector numbering is 0, 1, 3, 4 ... The boot loaders
- // for 6809 FLEX systems appear to load from sector one so do not have
- // this inconsistency to handle.
- boot0_sector_id = 1;
- boot1_sector_id = 2;
+ uint8_t boot0_sector_id = 1;
+ // uint8_t boot1_sector_id = 2;
// This floppy format uses a strategy of looking for 6800 boot code to
// set the numbering of the first two sectors. If this is shown to not
@@ -151,196 +111,1197 @@ int flex_format::find_size(io_generic *io, uint32_t form_factor)
// Found a 6800 stack load and branch, looks like a 6800 boot sector.
boot0_sector_id = 0;
+ // boot1 is not actually used (yet?)
// Look for a link to the next sector, normal usage.
- if (boot1[0] != 0 || boot1[1] != 3)
- {
+ // if (boot1[0] != 0 || boot1[1] != 3)
+ // {
// If not then assume it is a boot sector.
- boot1_sector_id = 1;
- }
+ // boot1_sector_id = 1;
+ // }
}
- LOG_FORMATS(" boot sector ids: %d %d\n", boot0_sector_id, boot1_sector_id);
- for(int i=0; formats[i].form_factor; i++) {
+ for (int i=0; formats[i].form_factor; i++) {
const format &f = formats[i];
- if(form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
+ if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
continue;
- if(size != (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
- continue;
+ if (f.sector_base_size == 128) {
+ // FLEX 1.0: Look at the system information sector.
+ sysinfo_sector_flex10 info;
+ read_at(io, f.sector_base_size * 2, &info, sizeof(struct sysinfo_sector_flex10)); // FIXME: check for errors and premature EOF
- // Check consistency with the sysinfo record sector.
- if (f.track_count != info.last_trk + 1)
- continue;
+ LOG_FORMATS("FLEX floppy dsk: size %d bytes, %d total sectors, %d remaining bytes, expected form factor %x\n", (uint32_t)size, (uint32_t)size / f.sector_base_size, (uint32_t)size % f.sector_base_size, form_factor);
+
+ // Consistency checks.
+ if (info.fc_start_trk > (f.track_count - 1) || info.fc_end_trk > (f.track_count - 1))
+ return -1;
+ if (info.fc_start_sec > f.sector_count || info.fc_end_sec > f.sector_count)
+ return -1;
- if (f.sector_count * f.head_count != info.last_sec)
+ } else {
+ // FLEX 2+: Look at the system information sector.
+ sysinfo_sector info;
+ read_at(io, f.sector_base_size * 2, &info, sizeof(struct sysinfo_sector)); // FIXME: check for errors and premature EOF
+
+ LOG_FORMATS("FLEX floppy dsk: size %d bytes, %d total sectors, %d remaining bytes, expected form factor %x\n", (uint32_t)size, (uint32_t)size / f.sector_base_size, (uint32_t)size % f.sector_base_size, form_factor);
+
+ // Consistency checks.
+ if (info.fc_start_trk > info.last_trk || info.fc_end_trk > info.last_trk)
+ return -1;
+ if (info.fc_start_sec > info.last_sec || info.fc_end_sec > info.last_sec)
+ return -1;
+ if (info.month < 1 || info.month > 12 || info.day < 1 || info.day > 31)
+ return -1;
+
+ // Check consistency with the sysinfo record sector.
+ if (f.track_count != info.last_trk + 1)
+ continue;
+
+ if (f.sector_count * f.head_count != info.last_sec)
+ continue;
+ }
+
+ unsigned int format_size = 0;
+ for (int track=0; track < f.track_count; track++) {
+ for (int head=0; head < f.head_count; head++) {
+ const format &tf = get_track_format(f, head, track);
+ format_size += compute_track_size(tf);
+ }
+ }
+
+ if (format_size != size)
continue;
+ // Check that the boot sector ID matches.
+ const format &ft0 = formats_track0[i];
+ if (ft0.form_factor) {
+ // There is a specialized track 0 format.
+ if (ft0.sector_base_id == -1) {
+ if (ft0.per_sector_id[0] != boot0_sector_id)
+ continue;
+ } else {
+ if (ft0.sector_base_id != boot0_sector_id)
+ continue;
+ }
+ } else {
+ // Otherwise check the default track format.
+ if (f.sector_base_id == -1) {
+ if (f.per_sector_id[0] != boot0_sector_id)
+ continue;
+ } else {
+ if (f.sector_base_id != boot0_sector_id)
+ continue;
+ }
+ }
+
+ LOG_FORMATS("FLEX matching format index %d\n", i);
return i;
}
return -1;
}
-// FLEX numbers sectors on the second side of a track continuing from the
-// first side which is a variation not handled by the generic code.
-//
-// FLEX generally numbers sectors starting at 1, however the 6800 boot sectors
-// are numbers starting at zero.
-//
-void flex_format::build_sector_description(const format &f, uint8_t *sectdata, desc_s *sectors, int track, int head) const
+const wd177x_format::format &flex_format::get_track_format(const format &f, int head, int track) const
{
- if(f.sector_base_id == -1) {
- for(int i=0; i<f.sector_count; i++) {
- int cur_offset = 0;
- for(int j=0; j<f.sector_count; j++)
- if(f.per_sector_id[j] < f.per_sector_id[i])
- cur_offset += f.sector_base_size ? f.sector_base_size : f.per_sector_size[j];
- sectors[i].data = sectdata + cur_offset;
- sectors[i].size = f.sector_base_size ? f.sector_base_size : f.per_sector_size[i];
- uint8_t sector_id = f.per_sector_id[i];
- if (track == 0 && head == 0 && sector_id <= 2) {
- if (sector_id == 0)
- sector_id = boot0_sector_id;
- else
- sector_id = boot1_sector_id;
- }
- sectors[i].sector_id = f.sector_count * head + sector_id;
- }
- } else {
- int cur_offset = 0;
- for(int i=0; i<f.sector_count; i++) {
- sectors[i].data = sectdata + cur_offset;
- sectors[i].size = f.sector_base_size ? f.sector_base_size : f.per_sector_size[i];
- cur_offset += sectors[i].size;
- uint8_t sector_id = i + f.sector_base_id;
- if (track == 0 && head == 0 && i < 2) {
- if (i == 0)
- sector_id = boot0_sector_id;
- else
- sector_id = boot1_sector_id;
- }
- sectors[i].sector_id = f.sector_count * head + sector_id;
+ int n = -1;
+
+ for (int i = 0; formats[i].form_factor; i++) {
+ if (&formats[i] == &f) {
+ n = i;
+ break;
}
}
-}
-// For FLEX just use track 1 rather than the generic code that looks a track
-// 0. This is enough to avoid the odd sector numbering for the boot sectors,
-// while following the generic code.
-void flex_format::check_compatibility(floppy_image *image, std::vector<int> &candidates)
-{
- uint8_t bitstream[500000/8];
- uint8_t sectdata[50000];
- desc_xs sectors[256];
- int track_size;
+ if (n < 0) {
+ LOG_FORMATS("Error format not found\n");
+ return f;
+ }
- // Extract the sectors
- generate_bitstream_from_track(1, 0, formats[candidates[0]].cell_size, bitstream, track_size, image);
+ if (head >= f.head_count) {
+ LOG_FORMATS("Error invalid head %d\n", head);
+ return f;
+ }
- switch (formats[candidates[0]].encoding)
- {
- case floppy_image::FM:
- extract_sectors_from_bitstream_fm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
- break;
- case floppy_image::MFM:
- extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
- break;
+ if (track >= f.track_count) {
+ LOG_FORMATS("Error invalid track %d\n", track);
+ return f;
}
- // Check compatibility with every candidate, copy in-place
- int *ok_cands = &candidates[0];
- for(unsigned int i=0; i != candidates.size(); i++) {
- const format &f = formats[candidates[i]];
- int ns = 0;
- for(int j=0; j<256; j++)
- if(sectors[j].data) {
- int sid;
- if(f.sector_base_id == -1) {
- for(sid=0; sid < f.sector_count; sid++)
- if(f.per_sector_id[sid] == j)
- break;
- } else
- sid = j - f.sector_base_id;
- if(sid < 0 || sid > f.sector_count)
- goto fail;
- if(f.sector_base_size) {
- if(sectors[j].size != f.sector_base_size)
- goto fail;
- } else {
- if(sectors[j].size != f.per_sector_size[sid])
- goto fail;
- }
- ns++;
+ if (track > 0) {
+ if (head == 1) {
+ const format &fh1 = formats_head1[n];
+ if (!fh1.form_factor) {
+ LOG_FORMATS("Error expected a head 1 format\n");
+ return f;
}
- if(ns == f.sector_count)
- *ok_cands++ = candidates[i];
- fail:
- ;
+ return fh1;
+ }
+ return f;
}
- candidates.resize(ok_cands - &candidates[0]);
+
+ // Track 0
+
+ if (head == 1) {
+ const format &fh1t0 = formats_head1_track0[n];
+ if (fh1t0.form_factor) {
+ return fh1t0;
+ }
+ const format &fh1 = formats_head1[n];
+ if (fh1.form_factor) {
+ return fh1;
+ }
+ LOG_FORMATS("Error expected a head 1 format\n");
+ return f;
+ }
+
+ // Head 0
+
+ const format &ft0 = formats_track0[n];
+ if (ft0.form_factor) {
+ return ft0;
+ }
+
+ return f;
}
+
const flex_format::format flex_format::formats[] = {
- { // 87.5K 5 1/4 inch single density - gaps unverified
+ { // 0 80.6K 5 1/4 inch single density flex 1.0 format
floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
- 4000, 10, 35, 1, 256, {}, 1, {}, 40, 16, 11
+ 4000, 18, 35, 1, 128, {}, -1, {1, 4, 7, 10, 13, 16, 2, 5, 8, 11, 14, 17, 3, 6, 9, 12, 15, 18}, 8, 11, 11
},
- { // 100K 5 1/4 inch single density - gaps unverified
+ { // 1 87.5K 5 1/4 inch single density
floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
- 4000, 10, 40, 1, 256, {}, 1, {}, 40, 16, 11
+ 4000, 10, 35, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
},
- { // 200K 5 1/4 inch single density - gaps unverified
+ { // 2 87.5K 5 1/4 inch single density, 6800 one boot sector
floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
- 4000, 10, 80, 1, 256, {}, 1, {}, 40, 16, 11
+ 4000, 10, 35, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
},
- { // 175K 5 1/4 inch single density - gaps unverified
+ { // 3 87.5K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 4 100K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 5 100K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 6 100K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 7 200K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 8 200K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 9 200K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 10 175K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 11 175K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 12 175K 5 1/4 inch single density, 6800 two boot sectors
floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
- 4000, 10, 35, 2, 256, {}, 1, {}, 40, 16, 11
+ 4000, 10, 35, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
},
- { // 200K 5 1/4 inch single density - gaps unverified
+ { // 13 200K 5 1/4 inch single density
floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
- 4000, 10, 40, 2, 256, {}, 1, {}, 40, 16, 11
+ 4000, 10, 40, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
},
- { // 400K 5 1/4 inch single density - gaps unverified
+ { // 14 200K 5 1/4 inch single density, 6800 one boot sector
floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
- 4000, 10, 80, 2, 256, {}, 1, {}, 40, 16, 11
+ 4000, 10, 40, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
},
- { // 320K 5 1/4 inch double density - gaps unverified
+ { // 15 200K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 16 400K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 17 400K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 18 400K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 19 155.5K 5 1/4 inch double density (single density track 0)
floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
- 2000, 18, 40, 1, 256, {}, 1, {}, 80, 22, 24
+ 2000, 18, 35, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 20 155.5K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 35, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 21 155.5K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 35, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 22 157.5K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 35, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 23 157.5K 5 1/4 inch double density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 35, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 24 157.5K 5 1/4 inch double density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 35, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 25 311K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 26 311K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 27 311K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 28 315K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 29 315K 5 1/4 inch double density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 30 315K 5 1/4 inch double density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 31 178K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 40, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 32 178K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 40, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 33 178K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 40, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 34 180K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 40, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 35 180K 5 1/4 inch double density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 40, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 36 180K 5 1/4 inch double density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 40, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 37 356K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 38 356K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 39 356K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 40 360K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 41 360K 5 1/4 inch double density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
},
- { // 320K 5 1/4 inch double density - gaps unverified
+ { // 42 360K 5 1/4 inch double density, 6800 two boot sectors
floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
- 2000, 18, 40, 2, 256, {}, 1, {}, 80, 22, 24
+ 2000, 18, 40, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 43 358K 5 1/4 inch quad density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 18, 80, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 44 358K 5 1/4 inch quad density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 18, 80, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 45 358K 5 1/4 inch quad density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 18, 80, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 46 360K 5 1/4 inch quad density
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 18, 80, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
},
- { // 360K 5 1/4 inch quad density - gaps unverified
+ { // 47 360K 5 1/4 inch quad density, 6800 one boot sector
floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
- 2000, 18, 80, 1, 256, {}, 1, {}, 80, 22, 24
+ 2000, 18, 80, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
},
- { // 720K 5 1/4 inch quad density - gaps unverified
+ { // 48 360K 5 1/4 inch quad density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 18, 80, 1, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 49 716K 5 1/4 inch quad density (single density track 0)
floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
- 2000, 18, 80, 2, 256, {}, 1, {}, 80, 22, 24
+ 2000, 18, 80, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
},
- { // 288.75K 8 inch single density - gaps unverified
+ { // 50 716K 5 1/4 inch quad density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 51 716K 5 1/4 inch quad density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 52 720K 5 1/4 inch quad density
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 53 720K 5 1/4 inch quad density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 54 720K 5 1/4 inch quad density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 55 288.75K 8 inch single density
+ floppy_image::FF_8, floppy_image::SSSD, floppy_image::FM,
+ 2000, 15, 77, 1, 256, {}, -1, {1, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 56 288.75K 8 inch single density, 6800 one boot sector
floppy_image::FF_8, floppy_image::SSSD, floppy_image::FM,
- 2000, 15, 77, 1, 256, {}, 1, {}, 40, 12, 12
+ 2000, 15, 77, 1, 256, {}, -1, {1, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 57 288.75K 8 inch single density, 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::SSSD, floppy_image::FM,
+ 2000, 15, 77, 1, 256, {}, -1, {1, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 58 577.5K 8 inch single density
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {1, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14}, 40, 12, 12
},
- { // 577.5K 8 inch single density - gaps unverified
+ { // 59 577.5K 8 inch single density, 6800 one boot sector
floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
- 2000, 15, 77, 2, 256, {}, 1, {}, 40, 12, 12
+ 2000, 15, 77, 2, 256, {}, -1, {1, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 60 577.5K 8 inch single density, 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {1, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 61 497.75K 8 inch double density (single density track 0)
+ floppy_image::FF_8, floppy_image::SSDD, floppy_image::MFM,
+ 1000, 26, 77, 1, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 62 497.75K 8 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_8, floppy_image::SSDD, floppy_image::MFM,
+ 1000, 26, 77, 1, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 63 497.75K 8 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::SSDD, floppy_image::MFM,
+ 1000, 26, 77, 1, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
},
- { // 500.5K 8 inch double density - gaps unverified
+ { // 64 500.5K 8 inch double density
floppy_image::FF_8, floppy_image::SSDD, floppy_image::MFM,
- 1000, 26, 77, 1, 256, {}, 1, {}, 80, 22, 24
+ 1000, 26, 77, 1, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
},
- { // 1001K 8 inch double density - gaps unverified
+ { // 65 500.5K 8 inch double density, 6800 one boot sector
+ floppy_image::FF_8, floppy_image::SSDD, floppy_image::MFM,
+ 1000, 26, 77, 1, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 66 500.5K 8 inch double density, 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::SSDD, floppy_image::MFM,
+ 1000, 26, 77, 1, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 67 995.5K 8 inch double density (single density track 0)
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 68 995.5K 8 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 69 995.5K 8 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 70 1001K 8 inch double density
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 71 1001K 8 inch double density, 6800 one boot sector
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 72 1001K 8 inch double density, 6800 two boot sectors
floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
- 1000, 26, 77, 2, 256, {}, 1, {}, 80, 22, 24
+ 1000, 26, 77, 2, 256, {}, -1, {1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 73 1440K 3 1/2 inch high density (single density track 0)
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {1, 26, 15, 4, 29, 18, 7, 32, 21, 10, 35, 24, 13, 2, 27, 16, 5, 30, 19, 8, 33, 22, 11, 36, 25, 14, 3, 28, 17, 6, 31, 20, 9, 34, 23, 12}, 80, 22, 24
+ },
+ { // 74 1440K 3 1/2 inch high density (single density track 0), 6800 one boot sector
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {1, 26, 15, 4, 29, 18, 7, 32, 21, 10, 35, 24, 13, 2, 27, 16, 5, 30, 19, 8, 33, 22, 11, 36, 25, 14, 3, 28, 17, 6, 31, 20, 9, 34, 23, 12}, 80, 22, 24
},
- { /* 1440K 3 1/2 inch high density */
+ { // 75 1440K 3 1/2 inch high density (single density track 0), 6800 two boot sectors
floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
- 1000, 36, 80, 2, 256, {}, 1, {}, 80, 22, 24
+ 1000, 36, 80, 2, 256, {}, -1, {1, 26, 15, 4, 29, 18, 7, 32, 21, 10, 35, 24, 13, 2, 27, 16, 5, 30, 19, 8, 33, 22, 11, 36, 25, 14, 3, 28, 17, 6, 31, 20, 9, 34, 23, 12}, 80, 22, 24
+ },
+ { // 76 1440K 3 1/2 inch high density.
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {1, 26, 15, 4, 29, 18, 7, 32, 21, 10, 35, 24, 13, 2, 27, 16, 5, 30, 19, 8, 33, 22, 11, 36, 25, 14, 3, 28, 17, 6, 31, 20, 9, 34, 23, 12}, 80, 22, 24
+ },
+ { // 77 1440K 3 1/2 inch high density, 6800 one boot sector
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {1, 26, 15, 4, 29, 18, 7, 32, 21, 10, 35, 24, 13, 2, 27, 16, 5, 30, 19, 8, 33, 22, 11, 36, 25, 14, 3, 28, 17, 6, 31, 20, 9, 34, 23, 12}, 80, 22, 24
+ },
+ { // 78 1440K 3 1/2 inch high density, 6800 two boot sectors
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {1, 26, 15, 4, 29, 18, 7, 32, 21, 10, 35, 24, 13, 2, 27, 16, 5, 30, 19, 8, 33, 22, 11, 36, 25, 14, 3, 28, 17, 6, 31, 20, 9, 34, 23, 12}, 80, 22, 24
+ },
+ {}
+};
+
+const flex_format::format flex_format::formats_head1[] = {
+ { // 0 80.6K 5 1/4 inch single density flex 1.0 format
+ },
+ { // 1 87.5K 5 1/4 inch single density
+ },
+ { // 2 87.5K 5 1/4 inch single density, 6800 one boot sector
+ },
+ { // 3 87.5K 5 1/4 inch single density, 6800 two boot sectors
+ },
+ { // 4 100K 5 1/4 inch single density
+ },
+ { // 5 100K 5 1/4 inch single density, 6800 one boot sector
+ },
+ { // 6 100K 5 1/4 inch single density, 6800 two boot sectors
+ },
+ { // 7 200K 5 1/4 inch single density
+ },
+ { // 8 200K 5 1/4 inch single density, 6800 one boot sector
+ },
+ { // 9 200K 5 1/4 inch single density, 6800 two boot sectors
+ },
+ { // 10 175K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 11 175K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 12 175K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 13 200K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 14 200K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 15 200K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 16 400K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 17 400K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 18 400K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 19 155.5K 5 1/4 inch double density (single density track 0)
+ },
+ { // 20 155.5K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ },
+ { // 21 155.5K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ },
+ { // 22 157.5K 5 1/4 inch double density
+ },
+ { // 23 157.5K 5 1/4 inch double density, 6800 one boot sector
+ },
+ { // 24 157.5K 5 1/4 inch double density, 6800 two boot sectors
+ },
+ { // 25 311K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 26 311K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 27 311K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 28 315K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 29 315K 5 1/4 inch double density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 30 315K 5 1/4 inch double density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 31 178K 5 1/4 inch double density (single density track 0)
+ },
+ { // 32 178K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ },
+ { // 33 178K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ },
+ { // 34 180K 5 1/4 inch double density
+ },
+ { // 35 180K 5 1/4 inch double density, 6800 one boot sector
+ },
+ { // 36 180K 5 1/4 inch double density, 6800 two boot sectors
+ },
+ { // 37 356K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 38 356K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 39 356K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 40 360K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 41 360K 5 1/4 inch double density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 42 360K 5 1/4 inch double density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 43 358K 5 1/4 inch quad density (single density track 0)
+ },
+ { // 44 358K 5 1/4 inch quad density (single density track 0), 6800 one boot sector
+ },
+ { // 45 358K 5 1/4 inch quad density (single density track 0), 6800 two boot sectors
+ },
+ { // 46 360K 5 1/4 inch quad density
+ },
+ { // 47 360K 5 1/4 inch quad density, 6800 one boot sector
+ },
+ { // 48 360K 5 1/4 inch quad density, 6800 two boot sectors
+ },
+ { // 49 716K 5 1/4 inch quad density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 50 716K 5 1/4 inch quad density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 51 716K 5 1/4 inch quad density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 52 720K 5 1/4 inch quad density
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 53 720K 5 1/4 inch quad density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 54 720K 5 1/4 inch quad density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 55 288.75K 8 inch single density
+ },
+ { // 56 288.75K 8 inch single density, 6800 one boot sector
+ },
+ { // 57 288.75K 8 inch single density, 6800 two boot sectors
+ },
+ { // 58 577.5K 8 inch single density
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {16, 18, 20, 22, 24, 26, 28, 30, 17, 19, 21, 23, 25, 27, 29}, 40, 12, 12
+ },
+ { // 59 577.5K 8 inch single density, 6800 one boot sector
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {16, 18, 20, 22, 24, 26, 28, 30, 17, 19, 21, 23, 25, 27, 29}, 40, 12, 12
+ },
+ { // 60 577.5K 8 inch single density, 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {16, 18, 20, 22, 24, 26, 28, 30, 17, 19, 21, 23, 25, 27, 29}, 40, 12, 12
+ },
+ { // 61 497.75K 8 inch single density (single density track 0)
+ },
+ { // 62 497.75K 8 inch single density (single density track 0), 6800 one boot sector
+ },
+ { // 63 497.75K 8 inch single density (single density track 0), 6800 two boot sectors
+ },
+ { // 64 500.5K 8 inch double density
+ },
+ { // 65 500.5K 8 inch double density, 6800 one boot sector
+ },
+ { // 66 500.5K 8 inch double density, 6800 two boot sectors
+ },
+ { // 67 995.5K 8 inch double density (single density track 0)
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {27, 46, 39, 32, 51, 44, 37, 30, 49, 42, 35, 28, 47, 40, 33, 52, 45, 38, 31, 50, 43, 36, 29, 48, 41, 34}, 80, 22, 24
+ },
+ { // 68 995.5K 8 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {27, 46, 39, 32, 51, 44, 37, 30, 49, 42, 35, 28, 47, 40, 33, 52, 45, 38, 31, 50, 43, 36, 29, 48, 41, 34}, 80, 22, 24
+ },
+ { // 69 995.5K 8 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {27, 46, 39, 32, 51, 44, 37, 30, 49, 42, 35, 28, 47, 40, 33, 52, 45, 38, 31, 50, 43, 36, 29, 48, 41, 34}, 80, 22, 24
+ },
+ { // 70 1001K 8 inch double density
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {27, 46, 39, 32, 51, 44, 37, 30, 49, 42, 35, 28, 47, 40, 33, 52, 45, 38, 31, 50, 43, 36, 29, 48, 41, 34}, 80, 22, 24
+ },
+ { // 71 1001K 8 inch double density, 6800 one boot sector
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {27, 46, 39, 32, 51, 44, 37, 30, 49, 42, 35, 28, 47, 40, 33, 52, 45, 38, 31, 50, 43, 36, 29, 48, 41, 34}, 80, 22, 24
+ },
+ { // 72 1001K 8 inch double density, 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {27, 46, 39, 32, 51, 44, 37, 30, 49, 42, 35, 28, 47, 40, 33, 52, 45, 38, 31, 50, 43, 36, 29, 48, 41, 34}, 80, 22, 24
+ },
+ { // 73 1440K 3 1/2 inch high density (single density track 0)
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {37, 62, 51, 40, 65, 54, 43, 68, 57, 46, 71, 60, 49, 38, 63, 52, 41, 66, 55, 44, 69, 58, 47, 72, 61, 50, 39, 64, 53, 42, 67, 56, 45, 70, 59, 48}, 80, 22, 24
+ },
+ { // 74 1440K 3 1/2 inch high density (single density track 0), 6800 one boot sector
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {37, 62, 51, 40, 65, 54, 43, 68, 57, 46, 71, 60, 49, 38, 63, 52, 41, 66, 55, 44, 69, 58, 47, 72, 61, 50, 39, 64, 53, 42, 67, 56, 45, 70, 59, 48}, 80, 22, 24
+ },
+ { // 75 1440K 3 1/2 inch high density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {37, 62, 51, 40, 65, 54, 43, 68, 57, 46, 71, 60, 49, 38, 63, 52, 41, 66, 55, 44, 69, 58, 47, 72, 61, 50, 39, 64, 53, 42, 67, 56, 45, 70, 59, 48}, 80, 22, 24
+ },
+ { // 76 1440K 3 1/2 inch high density
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {37, 62, 51, 40, 65, 54, 43, 68, 57, 46, 71, 60, 49, 38, 63, 52, 41, 66, 55, 44, 69, 58, 47, 72, 61, 50, 39, 64, 53, 42, 67, 56, 45, 70, 59, 48}, 80, 22, 24
+ },
+ { // 77 1440K 3 1/2 inch high density, 6800 one boot sector
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {37, 62, 51, 40, 65, 54, 43, 68, 57, 46, 71, 60, 49, 38, 63, 52, 41, 66, 55, 44, 69, 58, 47, 72, 61, 50, 39, 64, 53, 42, 67, 56, 45, 70, 59, 48}, 80, 22, 24
+ },
+ { // 78 1440K 3 1/2 inch high density, 6800 two boot sectors
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {37, 62, 51, 40, 65, 54, 43, 68, 57, 46, 71, 60, 49, 38, 63, 52, 41, 66, 55, 44, 69, 58, 47, 72, 61, 50, 39, 64, 53, 42, 67, 56, 45, 70, 59, 48}, 80, 22, 24
+ },
+ {}
+};
+
+const flex_format::format flex_format::formats_track0[] = {
+ { // 0 80.6K 5 1/4 inch single density flex 1.0 format
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 18, 35, 1, 128, {}, -1, {0, 4, 7, 10, 13, 16, 1, 5, 8, 11, 14, 17, 3, 6, 9, 12, 15, 18}, 8, 11, 11
+ },
+ { // 1 87.5K 5 1/4 inch single density
+ },
+ { // 2 87.5K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 3 87.5K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 4 100K 5 1/4 inch single density
+ },
+ { // 5 100K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 6 100K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 7 200K 5 1/4 inch single density
+ },
+ { // 8 200K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 9 200K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 10 175K 5 1/4 inch single density
+ },
+ { // 11 175K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 12 175K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 13 200K 5 1/4 inch single density
+ },
+ { // 14 200K 5 1/4 inch single densityo, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 15 200K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 16 400K 5 1/4 inch single density
+ },
+ { // 17 400K 5 1/4 inch single density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 18 400K 5 1/4 inch single density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 19 155.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 20 155.5K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 21 155.5K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 22 157.5K 5 1/4 inch double density
+ },
+ { // 23 157.5K 5 1/4 inch double density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 35, 1, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 24 157.5K 5 1/4 inch double density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 35, 1, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 1, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 25 311K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 26 311K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 27 311K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 28 315K 5 1/4 inch double density
+ },
+ { // 29 315K 5 1/4 inch double density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 30 315K 5 1/4 inch double density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 1, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 31 178K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 32 178K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 33 178K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 34 180K 5 1/4 inch double density
+ },
+ { // 35 180K 5 1/4 inch double density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 40, 1, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 36 180K 5 1/4 inch double density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 40, 1, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 1, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 37 356K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 38 356K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 39 356K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 40 360K 5 1/4 inch double density
+ },
+ { // 41 360K 5 1/4 inch double density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 42 360K 5 1/4 inch double density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 1, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 43 358K 5 1/4 inch quad density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 1, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 44 358K 5 1/4 inch quad density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 45 358K 5 1/4 inch quad density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 1, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 46 360K 5 1/4 inch quad density
+ },
+ { // 47 360K 5 1/4 inch quad density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 18, 80, 1, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 48 360K 5 1/4 inch quad density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 18, 80, 1, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 1, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 49 716K 5 1/4 inch quad density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {1, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 50 716K 5 1/4 inch quad density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 2, 5, 8}, 40, 16, 11
+ },
+ { // 51 716K 5 1/4 inch quad density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {0, 4, 7, 10, 3, 6, 9, 1, 5, 8}, 40, 16, 11
+ },
+ { // 52 720K 5 1/4 inch quad density
+ },
+ { // 53 720K 5 1/4 inch quad density, 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 54 720K 5 1/4 inch quad density, 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 1, 7, 12, 17, 4, 9, 14}, 80, 22, 24
+ },
+ { // 55 288.75K 8 inch single density
+ },
+ { // 56 288.75K 8 inch single density, 6800 one boot sector
+ },
+ { // 57 288.75K 8 inch single density, 6800 two boot sectors
+ },
+ { // 58 577.5K 8 inch single density
+ },
+ { // 59 577.5K 8 inch single density, 6800 one boot sector
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {0, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 60 577.5K 8 inch single density, 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {0, 3, 5, 7, 9, 11, 13, 15, 1, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 61 497.75K 8 inch double density (single density track 0)
+ floppy_image::FF_8, floppy_image::SSSD, floppy_image::FM,
+ 2000, 15, 77, 1, 256, {}, -1, {1, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 62 497.75K 8 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_8, floppy_image::SSSD, floppy_image::FM,
+ 2000, 15, 77, 1, 256, {}, -1, {0, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 63 497.75K 8 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::SSSD, floppy_image::FM,
+ 2000, 15, 77, 1, 256, {}, -1, {0, 3, 5, 7, 9, 11, 13, 15, 1, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 64 500.5K 8 inch double density
+ },
+ { // 65 500.5K 8 inch double density, 6800 one boot sector
+ floppy_image::FF_8, floppy_image::SSDD, floppy_image::MFM,
+ 1000, 26, 77, 1, 256, {}, -1, {0, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 66 500.5K 8 inch double density, 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::SSDD, floppy_image::MFM,
+ 1000, 26, 77, 1, 256, {}, -1, {0, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 1, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 67 995.5K 8 inch double density (single density track 0)
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {1, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 68 995.5K 8 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {0, 3, 5, 7, 9, 11, 13, 15, 2, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 69 995.5K 8 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {0, 3, 5, 7, 9, 11, 13, 15, 1, 4, 6, 8, 10, 12, 14}, 40, 12, 12
+ },
+ { // 70 1001K 8 inch double density
+ },
+ { // 71 1001K 8 inch double density, 6800 one boot sector
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {0, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 72 1001K 8 inch double density, 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {0, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 1, 21, 14, 7, 26, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8}, 80, 22, 24
+ },
+ { // 73 1440K 3 1/2 inch high density (single density track 0)
+ floppy_image::FF_35, floppy_image::DSSD, floppy_image::FM,
+ 2000, 18, 80, 2, 256, {}, -1, {1, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 40, 12, 12
+ },
+ { // 74 1440K 3 1/2 inch high density (single density track 0), 6800 one boot sector
+ floppy_image::FF_35, floppy_image::DSSD, floppy_image::FM,
+ 2000, 18, 80, 2, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14}, 40, 12, 12
+ },
+ { // 75 1440K 3 1/2 inch high density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_35, floppy_image::DSSD, floppy_image::FM,
+ 2000, 18, 80, 2, 256, {}, -1, {0, 6, 11, 16, 3, 8, 13, 18, 5, 10, 15, 1, 7, 12, 17, 4, 9, 14}, 40, 12, 12
+ },
+ { // 76 1440K 3 1/2 inch high density
+ },
+ { // 77 1440K 3 1/2 inch high density, 6800 one boot sector
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {0, 26, 15, 4, 29, 18, 7, 32, 21, 10, 35, 24, 13, 2, 27, 16, 5, 30, 19, 8, 33, 22, 11, 36, 25, 14, 3, 28, 17, 6, 31, 20, 9, 34, 23, 12}, 80, 22, 24
+ },
+ { // 78 1440K 3 1/2 inch high density, 6800 two boot sectors
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {0, 26, 15, 4, 29, 18, 7, 32, 21, 10, 35, 24, 13, 1, 27, 16, 5, 30, 19, 8, 33, 22, 11, 36, 25, 14, 3, 28, 17, 6, 31, 20, 9, 34, 23, 12}, 80, 22, 24
+ },
+ {}
+};
+
+const flex_format::format flex_format::formats_head1_track0[] = {
+ { // 0 80.6K 5 1/4 inch single density flex 1.0 format
+ },
+ { // 1 87.5K 5 1/4 inch single density
+ },
+ { // 2 87.5K 5 1/4 inch single density, 6800 one boot sector
+ },
+ { // 3 87.5K 5 1/4 inch single density, 6800 two boot sectors
+ },
+ { // 4 100K 5 1/4 inch single density
+ },
+ { // 5 100K 5 1/4 inch single density, 6800 one boot sector
+ },
+ { // 6 100K 5 1/4 inch single density, 6800 two boot sectors
+ },
+ { // 7 200K 5 1/4 inch single density
+ },
+ { // 8 200K 5 1/4 inch single density, 6800 one boot sector
+ },
+ { // 9 200K 5 1/4 inch single density, 6800 two boot sectors
+ },
+ { // 10 175K 5 1/4 inch single density
+ },
+ { // 11 175K 5 1/4 inch single density, 6800 one boot sector
+ },
+ { // 12 175K 5 1/4 inch single density, 6800 two boot sectors
+ },
+ { // 13 200K 5 1/4 inch single density
+ },
+ { // 14 200K 5 1/4 inch single density, 6800 one boot sector
+ },
+ { // 15 200K 5 1/4 inch single density, 6800 two boot sectors
+ },
+ { // 16 400K 5 1/4 inch single density
+ },
+ { // 17 400K 5 1/4 inch single density, 6800 one boot sector
+ },
+ { // 18 400K 5 1/4 inch single density, 6800 two boot sectors
+ },
+ { // 19 155.5K 5 1/4 inch double density (single density track 0)
+ },
+ { // 20 155.5K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ },
+ { // 21 155.5K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ },
+ { // 22 157.5K 5 1/4 inch double density
+ },
+ { // 23 157.5K 5 1/4 inch double density
+ },
+ { // 24 157.5K 5 1/4 inch double density
+ },
+ { // 25 311K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 26 311K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 27 311K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 28 315K 5 1/4 inch double density
+ },
+ { // 29 315K 5 1/4 inch double density, 6800 one boot sector
+ },
+ { // 30 315K 5 1/4 inch double density, 6800 two boot sectors
+ },
+ { // 31 178K 5 1/4 inch double density (single density track 0)
+ },
+ { // 32 178K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ },
+ { // 33 178K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ },
+ { // 34 180K 5 1/4 inch double density
+ },
+ { // 35 180K 5 1/4 inch double density, 6800 one boot sector
+ },
+ { // 36 180K 5 1/4 inch double density, 6800 two boot sectors
+ },
+ { // 37 356K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 38 356K 5 1/4 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 39 356K 5 1/4 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 40 360K 5 1/4 inch double density
+ },
+ { // 41 360K 5 1/4 inch double density, 6800 one boot sector
+ },
+ { // 42 360K 5 1/4 inch double density, 6800 two boot sectors
+ },
+ { // 43 358K 5 1/4 inch quad density (single density track 0)
+ },
+ { // 44 358K 5 1/4 inch quad density (single density track 0), 6800 one boot sector
+ },
+ { // 45 358K 5 1/4 inch quad density (single density track 0), 6800 two boot sectors
+ },
+ { // 46 360K 5 1/4 inch quad density
+ },
+ { // 47 360K 5 1/4 inch quad density, 6800 one boot sector
+ },
+ { // 48 360K 5 1/4 inch quad density, 6800 two boot sectors
+ },
+ { // 49 716K 5 1/4 inch quad density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 50 716K 5 1/4 inch quad density (single density track 0), 6800 one boot sector
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 51 716K 5 1/4 inch quad density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {11, 14, 17, 20, 13, 16, 19, 12, 15, 18}, 40, 16, 11
+ },
+ { // 52 720K 5 1/4 inch quad density
+ },
+ { // 53 720K 5 1/4 inch quad density, 6800 one boot sector
+ },
+ { // 54 720K 5 1/4 inch quad density, 6800 two boot sectors
+ },
+ { // 55 288.75K 8 inch single density
+ },
+ { // 56 288.75K 8 inch single density, 6800 one boot sector
+ },
+ { // 57 288.75K 8 inch single density, 6800 two boot sectors
+ },
+ { // 58 577.5K 8 inch single density
+ },
+ { // 59 577.5K 8 inch single density, 6800 one boot sector
+ },
+ { // 60 577.5K 8 inch single density, 6800 two boot sectors
+ },
+ { // 61 497.75K 8 inch double density (single density track 0)
+ },
+ { // 62 497.75K 8 inch double density (single density track 0), 6800 one boot sector
+ },
+ { // 63 497.75K 8 inch double density (single density track 0), 6800 two boot sectors
+ },
+ { // 64 500.5K 8 inch double density
+ },
+ { // 65 500.5K 8 inch double density, 6800 one boot sector
+ },
+ { // 66 500.5K 8 inch double density, 6800 two boot sectors
+ },
+ { // 67 995.5K 8 inch double density (single density track 0)
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {16, 18, 20, 22, 24, 26, 28, 30, 17, 19, 21, 23, 25, 27, 29}, 40, 12, 12
+ },
+ { // 68 995.5K 8 inch double density (single density track 0), 6800 one boot sector
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {16, 18, 20, 22, 24, 26, 28, 30, 17, 19, 21, 23, 25, 27, 29}, 40, 12, 12
+ },
+ { // 69 995.5K 8 inch double density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {16, 18, 20, 22, 24, 26, 28, 30, 17, 19, 21, 23, 25, 27, 29}, 40, 12, 12
+ },
+ { // 70 1001K 8 inch double density
+ },
+ { // 71 1001K 8 inch double density, 6800 one boot sector
+ },
+ { // 72 1001K 8 inch double density, 6800 two boot sectors
+ },
+ { // 73 1440K 3 1/2 inch high density (single density track 0)
+ floppy_image::FF_35, floppy_image::DSSD, floppy_image::FM,
+ 2000, 18, 80, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 74 1440K 3 1/2 inch high density (single density track 0), 6800 one boot sector
+ floppy_image::FF_35, floppy_image::DSSD, floppy_image::FM,
+ 2000, 18, 80, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 75 1440K 3 1/2 inch high density (single density track 0), 6800 two boot sectors
+ floppy_image::FF_35, floppy_image::DSSD, floppy_image::FM,
+ 2000, 18, 80, 2, 256, {}, -1, {19, 24, 29, 34, 21, 26, 31, 36, 23, 28, 33, 20, 25, 30, 35, 22, 27, 32}, 80, 22, 24
+ },
+ { // 76 1440K 3 1/2 inch high density
+ },
+ { // 77 1440K 3 1/2 inch high density, 6800 one boot sector
+ },
+ { // 78 1440K 3 1/2 inch high density, 6800 two boot sectors
},
{}
};
-const floppy_format_type FLOPPY_FLEX_FORMAT = &floppy_image_format_creator<flex_format>;
+const flex_format FLOPPY_FLEX_FORMAT;
diff --git a/src/lib/formats/flex_dsk.h b/src/lib/formats/flex_dsk.h
index 3c61bdd4243..d2a34eaf213 100644
--- a/src/lib/formats/flex_dsk.h
+++ b/src/lib/formats/flex_dsk.h
@@ -18,39 +18,51 @@ class flex_format : public wd177x_format
public:
flex_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual int find_size(io_generic *io, uint32_t form_factor) override;
- virtual void build_sector_description(const format &f, uint8_t *sectdata, desc_s *sectors, int track, int head) const override;
- virtual void check_compatibility(floppy_image *image, std::vector<int> &candidates) override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual const wd177x_format::format &get_track_format(const format &f, int head, int track) const override;
private:
struct sysinfo_sector
{
- uint8_t unused1[16];
- uint8_t disk_name[8];
- uint8_t disk_ext[3];
- uint8_t disk_number[2];
- uint8_t fc_start_trk;
- uint8_t fc_start_sec;
- uint8_t fc_end_trk;
- uint8_t fc_end_sec;
- uint8_t free[2];
- uint8_t month;
- uint8_t day;
- uint8_t year;
- uint8_t last_trk;
- uint8_t last_sec;
- uint8_t unused2[216];
- } info;
- static const format formats[];
+ uint8_t unused1[16]{};
+ uint8_t disk_name[8]{};
+ uint8_t disk_ext[3]{};
+ uint8_t disk_number[2]{};
+ uint8_t fc_start_trk = 0;
+ uint8_t fc_start_sec = 0;
+ uint8_t fc_end_trk = 0;
+ uint8_t fc_end_sec = 0;
+ uint8_t free[2]{};
+ uint8_t month = 0;
+ uint8_t day = 0;
+ uint8_t year = 0;
+ uint8_t last_trk = 0;
+ uint8_t last_sec = 0;
+ uint8_t unused2[216]{};
+ };
+ // FLEX 1.0 SIR (System Information Record)
+ // Ref: https://deramp.com/downloads/swtpc/software/FLEX/FLEX%201.0%20(MiniFLEX)/Source/NEWDISK.LST
+ struct sysinfo_sector_flex10
+ {
+ uint8_t unused[21]{};
+ uint8_t fc_start_trk = 0;
+ uint8_t fc_start_sec = 0;
+ uint8_t fc_end_trk = 0;
+ uint8_t fc_end_sec = 0;
+ uint8_t free[2]{};
+ uint8_t unused2[101]{};
+ };
- uint8_t boot0_sector_id;
- uint8_t boot1_sector_id;
+ static const format formats[];
+ static const format formats_head1[];
+ static const format formats_track0[];
+ static const format formats_head1_track0[];
};
-extern const floppy_format_type FLOPPY_FLEX_FORMAT;
+extern const flex_format FLOPPY_FLEX_FORMAT;
#endif // MAME_FORMATS_FLEX_DSK_H
diff --git a/src/lib/formats/flopimg.cpp b/src/lib/formats/flopimg.cpp
index ea812506f50..fa4903447fb 100644
--- a/src/lib/formats/flopimg.cpp
+++ b/src/lib/formats/flopimg.cpp
@@ -1,921 +1,84 @@
// license:BSD-3-Clause
-// copyright-holders:Nathan Woods
-/*********************************************************************
-
- flopimg.c
+// copyright-holders:Olivier Galibert
+/***************************************************************************
- Floppy disk image abstraction code
+ flopimg.cpp
-*********************************************************************/
+ Floppy disk image abstraction code (new implementation)
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <limits.h>
-#include <assert.h>
+****************************************************************************/
-#include "emu.h" // emu_fatalerror
-#include "osdcore.h"
-#include "ioprocs.h"
#include "flopimg.h"
-#include "pool.h"
-#include "imageutl.h"
-
-#define TRACK_LOADED 0x01
-#define TRACK_DIRTY 0x02
-
-
-struct floppy_image_legacy
-{
- struct io_generic io;
-
- const struct FloppyFormat *floppy_option;
- struct FloppyCallbacks format;
-
- /* loaded track stuff */
- int loaded_track_head;
- int loaded_track_index;
- uint32_t loaded_track_size;
- void *loaded_track_data;
- uint8_t loaded_track_status;
- uint8_t flags;
-
- /* tagging system */
- object_pool *tags;
- void *tag_data;
-};
-
-
-
-struct floppy_params
-{
- int param;
- int value;
-};
-
-
-
-static floperr_t floppy_track_unload(floppy_image_legacy *floppy);
-
-OPTION_GUIDE_START(floppy_option_guide)
- OPTION_INT('H', "heads", "Heads")
- OPTION_INT('T', "tracks", "Tracks")
- OPTION_INT('S', "sectors", "Sectors")
- OPTION_INT('L', "sectorlength", "Sector Bytes")
- OPTION_INT('I', "interleave", "Interleave")
- OPTION_INT('F', "firstsectorid", "First Sector")
-OPTION_GUIDE_END
-
-
-static void floppy_close_internal(floppy_image_legacy *floppy, bool close_file);
-
-/*********************************************************************
- opening, closing and creating of floppy images
-*********************************************************************/
-
-/* basic floppy_image_legacy initialization common to floppy_open() and floppy_create() */
-static floppy_image_legacy *floppy_init(void *fp, const struct io_procs *procs, int flags)
-{
- floppy_image_legacy *floppy;
-
- floppy = (floppy_image_legacy *)malloc(sizeof(floppy_image_legacy));
- if (!floppy)
- return nullptr;
-
- memset(floppy, 0, sizeof(*floppy));
- floppy->tags = pool_alloc_lib(nullptr);
- floppy->tag_data = nullptr;
- floppy->io.file = fp;
- floppy->io.procs = procs;
- floppy->io.filler = 0xFF;
- floppy->flags = (uint8_t) flags;
- return floppy;
-}
-
-
-
-/* main code for identifying and maybe opening a disk image; not exposed
- * directly because this function is big and hideous */
-static floperr_t floppy_open_internal(void *fp, const struct io_procs *procs, const std::string &extension,
- const struct FloppyFormat *floppy_options, int max_options, int flags, floppy_image_legacy **outfloppy,
- int *outoption)
-{
- floperr_t err;
- floppy_image_legacy *floppy;
- int best_option = -1;
- int best_vote = 0;
- int vote;
- size_t i;
-
- floppy = floppy_init(fp, procs, flags);
- if (!floppy)
- {
- err = FLOPPY_ERROR_OUTOFMEMORY;
- goto done;
- }
-
- /* vote on the best format */
- for (i = 0; (i < max_options) && floppy_options[i].construct; i++)
- {
- if (extension.empty() || !floppy_options[i].extensions || image_find_extension(floppy_options[i].extensions, extension.c_str()))
- {
- if (floppy_options[i].identify)
- {
- vote = 0;
- err = floppy_options[i].identify(floppy, &floppy_options[i], &vote);
- if (err)
- goto done;
- }
- else
- {
- vote = 1;
- }
-
- /* is this option a better one? */
- if (vote > best_vote)
- {
- best_vote = vote;
- best_option = i;
- }
- }
- }
-
- /* did we find a format? */
- if (best_option == -1)
- {
- err = FLOPPY_ERROR_INVALIDIMAGE;
- goto done;
- }
-
- if (outfloppy)
- {
- /* call the format constructor */
- err = floppy_options[best_option].construct(floppy, &floppy_options[best_option], nullptr);
- if (err)
- goto done;
-
- floppy->floppy_option = &floppy_options[best_option];
- }
- if (best_vote != 100)
- {
- printf("Loading image that is not 100%% recognized\n");
- }
- err = FLOPPY_ERROR_SUCCESS;
-
-done:
- /* if we have a floppy disk and we either errored or are not keeping it, close it */
- if (floppy && (!outfloppy || err))
- {
- floppy_close_internal(floppy, false);
- floppy = nullptr;
- }
-
- if (outoption)
- *outoption = err ? -1 : best_option;
- if (outfloppy)
- *outfloppy = floppy;
- return err;
-}
-
-
-
-floperr_t floppy_identify(void *fp, const struct io_procs *procs, const char *extension,
- const struct FloppyFormat *formats, int *identified_format)
-{
- return floppy_open_internal(fp, procs, extension, formats, INT_MAX, FLOPPY_FLAGS_READONLY, nullptr, identified_format);
-}
-
-
-
-floperr_t floppy_open(void *fp, const struct io_procs *procs, const std::string &extension,
- const struct FloppyFormat *format, int flags, floppy_image_legacy **outfloppy)
-{
- return floppy_open_internal(fp, procs, extension, format, 1, flags, outfloppy, nullptr);
-}
-
-
-
-floperr_t floppy_open_choices(void *fp, const struct io_procs *procs, const std::string &extension,
- const struct FloppyFormat *formats, int flags, floppy_image_legacy **outfloppy)
-{
- return floppy_open_internal(fp, procs, extension, formats, INT_MAX, flags, outfloppy, nullptr);
-}
-
-
-
-floperr_t floppy_create(void *fp, const struct io_procs *procs, const struct FloppyFormat *format, util::option_resolution *parameters, floppy_image_legacy **outfloppy)
-{
- floppy_image_legacy *floppy = nullptr;
- floperr_t err;
- int heads, tracks, h, t;
- std::unique_ptr<util::option_resolution> alloc_resolution;
-
- assert(format);
-
- /* create the new image */
- floppy = floppy_init(fp, procs, 0);
- if (!floppy)
- {
- err = FLOPPY_ERROR_OUTOFMEMORY;
- goto done;
- }
-
- /* if this format expects creation parameters and none were specified, create some */
- if (!parameters && format->param_guidelines)
- {
- try { alloc_resolution = std::make_unique<util::option_resolution>(floppy_option_guide); }
- catch (...)
- {
- err = FLOPPY_ERROR_OUTOFMEMORY;
- goto done;
- }
- alloc_resolution->set_specification(format->param_guidelines);
- parameters = alloc_resolution.get();
- }
-
- /* call the format constructor */
- err = format->construct(floppy, format, parameters);
- if (err)
- goto done;
-
- /* format the disk, ignoring if formatting not implemented */
- if (floppy->format.format_track)
- {
- heads = floppy_get_heads_per_disk(floppy);
- tracks = floppy_get_tracks_per_disk(floppy);
-
- for (h = 0; h < heads; h++)
- {
- for (t = 0; t < tracks; t++)
- {
- err = floppy->format.format_track(floppy, h, t, parameters);
- if (err)
- goto done;
- }
- }
- }
-
- /* call the post_format function, if present */
- if (floppy->format.post_format)
- {
- err = floppy->format.post_format(floppy, parameters);
- if (err)
- goto done;
- }
-
- floppy->floppy_option = format;
- err = FLOPPY_ERROR_SUCCESS;
-
-done:
- if (err && floppy)
- {
- floppy_close_internal(floppy, false);
- floppy = nullptr;
- }
-
- if (outfloppy)
- *outfloppy = floppy;
- else if (floppy)
- floppy_close_internal(floppy, false);
- return err;
-}
-
-
-
-static void floppy_close_internal(floppy_image_legacy *floppy, bool close_file)
-{
- if (floppy) {
- floppy_track_unload(floppy);
-
- if(floppy->floppy_option && floppy->floppy_option->destruct)
- floppy->floppy_option->destruct(floppy, floppy->floppy_option);
- if (close_file)
- io_generic_close(&floppy->io);
- if (floppy->loaded_track_data)
- free(floppy->loaded_track_data);
- pool_free_lib(floppy->tags);
-
- free(floppy);
- }
-}
-
-
-
-void floppy_close(floppy_image_legacy *floppy)
-{
- floppy_close_internal(floppy, true);
-}
-
-
-
-/*********************************************************************
- functions useful in format constructors
-*********************************************************************/
-
-struct FloppyCallbacks *floppy_callbacks(floppy_image_legacy *floppy)
-{
- assert(floppy);
- return &floppy->format;
-}
-
-
-
-void *floppy_tag(floppy_image_legacy *floppy)
-{
- assert(floppy);
- return floppy->tag_data;
-}
-
-
-
-void *floppy_create_tag(floppy_image_legacy *floppy, size_t tagsize)
-{
- floppy->tag_data = pool_malloc_lib(floppy->tags,tagsize);
- return floppy->tag_data;
-}
-
-
-
-uint8_t floppy_get_filler(floppy_image_legacy *floppy)
-{
- return floppy->io.filler;
-}
-
-
-
-void floppy_set_filler(floppy_image_legacy *floppy, uint8_t filler)
-{
- floppy->io.filler = filler;
-}
-
-
-
-/*********************************************************************
- calls for accessing the raw disk image
-*********************************************************************/
-
-void floppy_image_read(floppy_image_legacy *floppy, void *buffer, uint64_t offset, size_t length)
-{
- io_generic_read(&floppy->io, buffer, offset, length);
-}
-
-
-
-void floppy_image_write(floppy_image_legacy *floppy, const void *buffer, uint64_t offset, size_t length)
-{
- io_generic_write(&floppy->io, buffer, offset, length);
-}
-
-
-
-void floppy_image_write_filler(floppy_image_legacy *floppy, uint8_t filler, uint64_t offset, size_t length)
-{
- io_generic_write_filler(&floppy->io, filler, offset, length);
-}
-
-
-
-uint64_t floppy_image_size(floppy_image_legacy *floppy)
-{
- return io_generic_size(&floppy->io);
-}
-
-
-
-/*********************************************************************
- calls for accessing disk image data
-*********************************************************************/
-
-static floperr_t floppy_readwrite_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset,
- void *buffer, size_t buffer_len, bool writing, bool indexed, int ddam)
-{
- floperr_t err;
- const struct FloppyCallbacks *fmt;
- size_t this_buffer_len;
- std::vector<uint8_t> alloc_buf;
- uint32_t sector_length;
- uint8_t *buffer_ptr = (uint8_t *)buffer;
- floperr_t (*read_sector)(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen);
- floperr_t (*write_sector)(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
-
- fmt = floppy_callbacks(floppy);
-
- /* choose proper calls for indexed vs non-indexed */
- if (indexed)
- {
- read_sector = fmt->read_indexed_sector;
- write_sector = fmt->write_indexed_sector;
- if (!fmt->get_indexed_sector_info)
- {
- err = FLOPPY_ERROR_UNSUPPORTED;
- goto done;
- }
- }
- else
- {
- read_sector = fmt->read_sector;
- write_sector = fmt->write_sector;
- if (!fmt->get_sector_length)
- {
- err = FLOPPY_ERROR_UNSUPPORTED;
- goto done;
- }
- }
-
- /* check to make sure that the operation is supported */
- if (!read_sector || (writing && !write_sector))
- {
- err = FLOPPY_ERROR_UNSUPPORTED;
- goto done;
- }
-
- /* main loop */
- while(buffer_len > 0)
- {
- /* find out the size of this sector */
- if (indexed)
- err = fmt->get_indexed_sector_info(floppy, head, track, sector, nullptr, nullptr, nullptr, &sector_length, nullptr);
- else
- err = fmt->get_sector_length(floppy, head, track, sector, &sector_length);
- if (err)
- goto done;
-
- /* do we even do anything with this sector? */
- if (offset < sector_length)
- {
- /* ok we will be doing something */
- if ((offset > 0) || (buffer_len < sector_length))
- {
- /* we will be doing an partial read/write; in other words we
- * will not be reading/writing a full sector */
- alloc_buf.resize(sector_length);
-
- /* read the sector (we need to do this even when writing */
- err = read_sector(floppy, head, track, sector, &alloc_buf[0], sector_length);
- if (err)
- goto done;
-
- this_buffer_len = std::min(buffer_len, size_t(sector_length - offset));
-
- if (writing)
- {
- memcpy(&alloc_buf[offset], buffer_ptr, this_buffer_len);
-
- err = write_sector(floppy, head, track, sector, &alloc_buf[0], sector_length, ddam);
- if (err)
- goto done;
- }
- else
- {
- memcpy(buffer_ptr, &alloc_buf[offset], this_buffer_len);
- }
- offset += this_buffer_len;
- offset %= sector_length;
- }
- else
- {
- this_buffer_len = sector_length;
-
- if (writing)
- err = write_sector(floppy, head, track, sector, buffer_ptr, sector_length, ddam);
- else
- err = read_sector(floppy, head, track, sector, buffer_ptr, sector_length);
- if (err)
- goto done;
- }
- }
- else
- {
- /* skip this sector */
- offset -= sector_length;
- this_buffer_len = 0;
- }
-
- buffer_ptr += this_buffer_len;
- buffer_len -= this_buffer_len;
- sector++;
- }
-
- err = FLOPPY_ERROR_SUCCESS;
-
-done:
- return err;
-}
-
-
-floperr_t floppy_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len)
-{
- return floppy_readwrite_sector(floppy, head, track, sector, offset, buffer, buffer_len, false, false, 0);
-}
-
-
-
-floperr_t floppy_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset, const void *buffer, size_t buffer_len, int ddam)
-{
- return floppy_readwrite_sector(floppy, head, track, sector, offset, (void *) buffer, buffer_len, true, false, ddam);
-}
-
-
-
-floperr_t floppy_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector_index, int offset, void *buffer, size_t buffer_len)
-{
- return floppy_readwrite_sector(floppy, head, track, sector_index, offset, buffer, buffer_len, false, true, 0);
-}
-
-
-
-floperr_t floppy_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector_index, int offset, const void *buffer, size_t buffer_len, int ddam)
-{
- return floppy_readwrite_sector(floppy, head, track, sector_index, offset, (void *) buffer, buffer_len, true, true, ddam);
-}
-
-
-static floperr_t floppy_get_track_data_offset(floppy_image_legacy *floppy, int head, int track, uint64_t *offset)
-{
- floperr_t err;
- const struct FloppyCallbacks *callbacks;
-
- *offset = 0;
- callbacks = floppy_callbacks(floppy);
- if (callbacks->get_track_data_offset)
- {
- err = callbacks->get_track_data_offset(floppy, head, track, offset);
- if (err)
- return err;
- }
- return FLOPPY_ERROR_SUCCESS;
-}
-
-
-
-static floperr_t floppy_read_track_offset(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buffer_len)
-{
- floperr_t err;
- const struct FloppyCallbacks *format;
-
- format = floppy_callbacks(floppy);
-
- if (!format->read_track)
- return FLOPPY_ERROR_UNSUPPORTED;
-
- err = floppy_track_unload(floppy);
- if (err)
- return err;
-
- err = format->read_track(floppy, head, track, offset, buffer, buffer_len);
- if (err)
- return err;
-
- return FLOPPY_ERROR_SUCCESS;
-}
-
-
-
-floperr_t floppy_read_track(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t buffer_len)
-{
- return floppy_read_track_offset(floppy, head, track, 0, buffer, buffer_len);
-}
-
-
-
-floperr_t floppy_read_track_data(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t buffer_len)
-{
- floperr_t err;
- uint64_t offset;
-
- err = floppy_get_track_data_offset(floppy, head, track, &offset);
- if (err)
- return err;
-
- return floppy_read_track_offset(floppy, head, track, offset, buffer, buffer_len);
-}
-
-
-
-static floperr_t floppy_write_track_offset(floppy_image_legacy *floppy, int head, int track, uint64_t offset, const void *buffer, size_t buffer_len)
-{
- floperr_t err;
-
- /* track writing supported? */
- if (!floppy_callbacks(floppy)->write_track)
- return FLOPPY_ERROR_UNSUPPORTED;
-
- /* read only? */
- if (floppy->flags & FLOPPY_FLAGS_READONLY)
- return FLOPPY_ERROR_READONLY;
-
- err = floppy_track_unload(floppy);
- if (err)
- return err;
-
- err = floppy_callbacks(floppy)->write_track(floppy, head, track, offset, buffer, buffer_len);
- if (err)
- return err;
-
- return FLOPPY_ERROR_SUCCESS;
-}
-
-
-
-floperr_t floppy_write_track(floppy_image_legacy *floppy, int head, int track, const void *buffer, size_t buffer_len)
-{
- return floppy_write_track_offset(floppy, head, track, 0, buffer, buffer_len);
-}
-
-
-
-floperr_t floppy_write_track_data(floppy_image_legacy *floppy, int head, int track, const void *buffer, size_t buffer_len)
-{
- floperr_t err;
- uint64_t offset;
-
- err = floppy_get_track_data_offset(floppy, head, track, &offset);
- if (err)
- return err;
-
- return floppy_write_track_offset(floppy, head, track, offset, buffer, buffer_len);
-}
-
-
-
-floperr_t floppy_format_track(floppy_image_legacy *floppy, int head, int track, util::option_resolution *parameters)
-{
- floperr_t err;
- struct FloppyCallbacks *format;
- std::unique_ptr<util::option_resolution> alloc_resolution;
-
- /* supported? */
- format = floppy_callbacks(floppy);
- if (!format->format_track)
- return FLOPPY_ERROR_UNSUPPORTED;
-
- /* create a dummy resolution; if no parameters were specified */
- if (!parameters)
- {
- try
- {
- alloc_resolution = std::make_unique<util::option_resolution>(floppy_option_guide);
- }
- catch (...)
- {
- return FLOPPY_ERROR_OUTOFMEMORY;
- }
- alloc_resolution->set_specification(floppy->floppy_option->param_guidelines);
-
- parameters = alloc_resolution.get();
- }
-
- err = format->format_track(floppy, head, track, parameters);
- if (err)
- return err;
-
- return FLOPPY_ERROR_SUCCESS;
-}
-
-
-
-int floppy_get_tracks_per_disk(floppy_image_legacy *floppy)
-{
- return floppy_callbacks(floppy)->get_tracks_per_disk(floppy);
-}
-
-
-
-int floppy_get_heads_per_disk(floppy_image_legacy *floppy)
-{
- return floppy_callbacks(floppy)->get_heads_per_disk(floppy);
-}
+#include "ioprocs.h"
+#include "multibyte.h"
+#include "strformat.h"
+#include <algorithm>
+#include <array>
+#include <cstdio>
+#include <cstring>
+#include <stdexcept>
+#include <utility>
-uint32_t floppy_get_track_size(floppy_image_legacy *floppy, int head, int track)
+floppy_image::floppy_image(int _tracks, int _heads, uint32_t _form_factor)
{
- const struct FloppyCallbacks *fmt;
+ tracks = _tracks;
+ heads = _heads;
- fmt = floppy_callbacks(floppy);
- if (!fmt->get_track_size)
- return 0;
+ form_factor = _form_factor;
+ variant = 0;
- return fmt->get_track_size(floppy, head, track);
+ track_array.resize(tracks*4+1);
+ for(int i=0; i<tracks*4+1; i++)
+ track_array[i].resize(heads);
}
-
-
-floperr_t floppy_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length)
+floppy_image::~floppy_image()
{
- const struct FloppyCallbacks *fmt;
-
- fmt = floppy_callbacks(floppy);
- if (!fmt->get_sector_length)
- return FLOPPY_ERROR_UNSUPPORTED;
-
- return fmt->get_sector_length(floppy, head, track, sector, sector_length);
}
-
-
-floperr_t floppy_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags)
+void floppy_image::set_variant(uint32_t _variant)
{
- const struct FloppyCallbacks *fmt;
-
- fmt = floppy_callbacks(floppy);
- if (!fmt->get_indexed_sector_info)
- return FLOPPY_ERROR_UNSUPPORTED;
-
- return fmt->get_indexed_sector_info(floppy, head, track, sector_index, cylinder, side, sector, sector_length, flags);
-}
-
-
+ variant = _variant;
-floperr_t floppy_get_sector_count(floppy_image_legacy *floppy, int head, int track, int *sector_count)
-{
- floperr_t err;
- int sector_index = 0;
+ // Initialize hard sectors
+ index_array.clear();
- do
- {
- err = floppy_get_indexed_sector_info(floppy, head, track, sector_index, nullptr, nullptr, nullptr, nullptr, nullptr);
- if (!err)
- sector_index++;
+ uint32_t sectors;
+ switch(variant) {
+ case SSDD16:
+ case SSQD16:
+ case DSDD16:
+ case DSQD16:
+ sectors = 16;
+ break;
+ default:
+ sectors = 0;
}
- while(!err);
-
- if (sector_index && (err == FLOPPY_ERROR_SEEKERROR))
- err = FLOPPY_ERROR_SUCCESS;
- if (sector_count)
- *sector_count = err ? 0 : sector_index;
- return err;
-}
-
-
-
-int floppy_is_read_only(floppy_image_legacy *floppy)
-{
- return floppy->flags & FLOPPY_FLAGS_READONLY;
-}
-
-
-
-uint8_t floppy_random_byte(floppy_image_legacy *floppy)
-{
- /* can't use mame_rand(); this might not be in the core */
-#ifdef rand
-#undef rand
-#endif
- return rand();
-}
-
-
-
-/*********************************************************************
- calls for track based IO
-*********************************************************************/
-
-floperr_t floppy_load_track(floppy_image_legacy *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length)
-{
- floperr_t err;
- void *new_loaded_track_data;
- uint32_t track_size;
-
- /* have we already loaded this track? */
- if (((floppy->loaded_track_status & TRACK_LOADED) == 0) || (head != floppy->loaded_track_head) || (track != floppy->loaded_track_index))
- {
- err = floppy_track_unload(floppy);
- if (err)
- goto error;
-
- track_size = floppy_callbacks(floppy)->get_track_size(floppy, head, track);
-
- if (floppy->loaded_track_data) free(floppy->loaded_track_data);
- new_loaded_track_data = malloc(track_size);
- if (!new_loaded_track_data)
- {
- err = FLOPPY_ERROR_OUTOFMEMORY;
- goto error;
- }
-
- floppy->loaded_track_data = new_loaded_track_data;
- floppy->loaded_track_size = track_size;
- floppy->loaded_track_head = head;
- floppy->loaded_track_index = track;
-
- err = floppy_callbacks(floppy)->read_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data, floppy->loaded_track_size);
- if (err)
- goto error;
-
- floppy->loaded_track_status |= TRACK_LOADED | (dirtify ? TRACK_DIRTY : 0);
+ if(sectors) {
+ uint32_t sector_angle = 200000000/sectors;
+ for(int i = 1; i < sectors; i++)
+ index_array.push_back(i*sector_angle);
+ index_array.push_back((sectors-1)*sector_angle + sector_angle/2);
}
- else
- floppy->loaded_track_status |= (dirtify ? TRACK_DIRTY : 0);
-
- if (track_data)
- *track_data = floppy->loaded_track_data;
- if (track_length)
- *track_length = floppy->loaded_track_size;
- return FLOPPY_ERROR_SUCCESS;
-
-error:
- if (track_data)
- *track_data = nullptr;
- if (track_length)
- *track_length = 0;
- return err;
}
-
-
-static floperr_t floppy_track_unload(floppy_image_legacy *floppy)
+void floppy_image::find_index_hole(uint32_t pos, uint32_t &last, uint32_t &next) const
{
- int err;
- if (floppy->loaded_track_status & TRACK_DIRTY)
- {
- err = floppy_callbacks(floppy)->write_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data, floppy->loaded_track_size);
- if (err)
- return (floperr_t)err;
- }
-
- floppy->loaded_track_status &= ~(TRACK_LOADED | TRACK_DIRTY);
- return FLOPPY_ERROR_SUCCESS;
+ auto nexti = std::lower_bound(index_array.begin(), index_array.end(), pos+1);
+ next = nexti == index_array.end() ? 200000000 : *nexti;
+ last = nexti == index_array.begin() ? 0 : *--nexti;
}
-
-
-/*********************************************************************
- accessors for meta information about the image
-*********************************************************************/
-
-const char *floppy_format_description(floppy_image_legacy *floppy)
-{
- return floppy->floppy_option->description;
-}
-
-
-
-/*********************************************************************
- misc calls
-*********************************************************************/
-
-const char *floppy_error(floperr_t err)
-{
- static const char *const error_messages[] =
- {
- "The operation completed successfully",
- "Fatal internal error",
- "This operation is unsupported",
- "Out of memory",
- "Seek error",
- "Invalid image",
- "Attempted to write to read only image",
- "No space left on image",
- "Parameter out of range",
- "Required parameter not specified"
- };
-
- if ((err < 0) || (err >= ARRAY_LENGTH(error_messages)))
- return nullptr;
- return error_messages[err];
-}
-
-
-LEGACY_FLOPPY_OPTIONS_START(default)
-LEGACY_FLOPPY_OPTIONS_END
-
-
-// license:BSD-3-Clause
-// copyright-holders:Olivier Galibert
-/***************************************************************************
-
- New implementation
-
-****************************************************************************/
-
-
-floppy_image::floppy_image(int _tracks, int _heads, uint32_t _form_factor)
-{
- tracks = _tracks;
- heads = _heads;
-
- form_factor = _form_factor;
- variant = 0;
-
- track_array.resize(tracks*4+1);
- for(int i=0; i<tracks*4+1; i++)
- track_array[i].resize(heads);
-}
-
-floppy_image::~floppy_image()
-{
-}
-
-void floppy_image::get_maximal_geometry(int &_tracks, int &_heads) const
+void floppy_image::get_maximal_geometry(int &_tracks, int &_heads) const noexcept
{
_tracks = tracks;
_heads = heads;
}
-void floppy_image::get_actual_geometry(int &_tracks, int &_heads)
+void floppy_image::get_actual_geometry(int &_tracks, int &_heads) const noexcept
{
int maxt = (tracks-1)*4, maxh = heads-1;
@@ -933,12 +96,15 @@ void floppy_image::get_actual_geometry(int &_tracks, int &_heads)
goto head_done;
maxh--;
}
+ else
+ maxh = -1;
+
head_done:
_tracks = (maxt+4)/4;
_heads = maxh+1;
}
-int floppy_image::get_resolution() const
+int floppy_image::get_resolution() const noexcept
{
int mask = 0;
for(int i=0; i<=(tracks-1)*4; i++)
@@ -952,38 +118,62 @@ int floppy_image::get_resolution() const
return 0;
}
-const char *floppy_image::get_variant_name(uint32_t form_factor, uint32_t variant)
-{
- switch(variant) {
- case SSSD: return "Single side, single density";
- case SSDD: return "Single side, double density";
- case SSQD: return "Single side, quad density";
- case DSDD: return "Double side, double density";
- case DSQD: return "Double side, quad density";
- case DSHD: return "Double side, high density";
- case DSED: return "Double side, extended density";
- }
- return "Unknown";
-}
-
-floppy_image_format_t::floppy_image_format_t()
+bool floppy_image::track_is_formatted(int track, int head, int subtrack) const noexcept
{
- next = nullptr;
+ int idx = track*4 + subtrack;
+ if(int(track_array.size()) <= idx)
+ return false;
+ if(int(track_array[idx].size()) <= head)
+ return false;
+ const auto &data = track_array[idx][head].cell_data;
+ if(data.empty())
+ return false;
+ for(uint32_t mg : data)
+ if((mg & floppy_image::MG_MASK) == floppy_image::MG_F)
+ return true;
+ return false;
}
-floppy_image_format_t::~floppy_image_format_t()
+const char *floppy_image::get_variant_name(uint32_t form_factor, uint32_t variant) noexcept
{
+ switch(variant) {
+ case SSSD: return "Single side, single density";
+ case SSSD10: return "Single side, single density, 10-sector";
+ case SSSD16: return "Single side, single density, 16-sector";
+ case SSSD32: return "Single side, single density, 32-sector";
+ case SSDD: return "Single side, double density";
+ case SSDD10: return "Single side, double density, 10-sector";
+ case SSDD16: return "Single side, double density, 16 hard sector";
+ case SSDD32: return "Single side, double density, 32-sector";
+ case SSQD: return "Single side, quad density";
+ case SSQD10: return "Single side, quad density, 10-sector";
+ case SSQD16: return "Single side, quad density, 16 hard sector";
+ case DSSD: return "Double side, single density";
+ case DSSD10: return "Double side, single density, 10-sector";
+ case DSSD16: return "Double side, single density, 16-sector";
+ case DSSD32: return "Double side, single density, 32-sector";
+ case DSDD: return "Double side, double density";
+ case DSDD10: return "Double side, double density, 10-sector";
+ case DSDD16: return "Double side, double density, 16 hard sector";
+ case DSDD32: return "Double side, double density, 32-sector";
+ case DSQD: return "Double side, quad density";
+ case DSQD10: return "Double side, quad density, 10-sector";
+ case DSQD16: return "Double side, quad density, 16 hard sector";
+ case DSHD: return "Double side, high density";
+ case DSED: return "Double side, extended density";
+ }
+ return "Unknown";
}
-void floppy_image_format_t::append(floppy_image_format_t *_next)
+bool floppy_image_format_t::has_variant(const std::vector<uint32_t> &variants, uint32_t variant) noexcept
{
- if(next)
- next->append(_next);
- else
- next = _next;
+ for(uint32_t v : variants)
+ if(variant == v)
+ return true;
+ return false;
}
-bool floppy_image_format_t::save(io_generic *, floppy_image *)
+bool floppy_image_format_t::save(util::random_read_write &io, const std::vector<uint32_t> &, const floppy_image &) const
{
return false;
}
@@ -1009,7 +199,7 @@ bool floppy_image_format_t::extension_matches(const char *file_name) const
return false;
}
-bool floppy_image_format_t::type_no_data(int type) const
+bool floppy_image_format_t::type_no_data(int type)
{
return
type == CRC_CCITT_START ||
@@ -1026,7 +216,7 @@ bool floppy_image_format_t::type_no_data(int type) const
type == END;
}
-bool floppy_image_format_t::type_data_mfm(int type, int p1, const gen_crc_info *crcs) const
+bool floppy_image_format_t::type_data_mfm(int type, int p1, const gen_crc_info *crcs)
{
return
type == MFM ||
@@ -1049,7 +239,7 @@ bool floppy_image_format_t::type_data_mfm(int type, int p1, const gen_crc_info *
(type == CRC && (crcs[p1].type == CRC_CCITT || crcs[p1].type == CRC_AMIGA));
}
-void floppy_image_format_t::collect_crcs(const desc_e *desc, gen_crc_info *crcs) const
+void floppy_image_format_t::collect_crcs(const desc_e *desc, gen_crc_info *crcs)
{
memset(crcs, 0, MAX_CRC_COUNT * sizeof(*crcs));
for(int i=0; i != MAX_CRC_COUNT; i++)
@@ -1091,7 +281,7 @@ void floppy_image_format_t::collect_crcs(const desc_e *desc, gen_crc_info *crcs)
}
}
-int floppy_image_format_t::crc_cells_size(int type) const
+int floppy_image_format_t::crc_cells_size(int type)
{
switch(type) {
case CRC_CCITT: return 32;
@@ -1361,7 +551,7 @@ int floppy_image_format_t::calc_sector_index(int num, int interleave, int skew,
sec++;
// This line prevents lock-ups of the emulator when the interleave is not appropriate
if (sec > total_sectors)
- throw emu_fatalerror("Format error: interleave %d not appropriate for %d sectors per track\n", interleave, total_sectors);
+ throw std::invalid_argument(util::string_format("Format error: interleave %d not appropriate for %d sectors per track", interleave, total_sectors));
}
// use skew param
sec -= track_head * skew;
@@ -1370,7 +560,7 @@ int floppy_image_format_t::calc_sector_index(int num, int interleave, int skew,
return sec;
}
-void floppy_image_format_t::generate_track(const desc_e *desc, int track, int head, const desc_s *sect, int sect_count, int track_size, floppy_image *image)
+void floppy_image_format_t::generate_track(const desc_e *desc, int track, int head, const desc_s *sect, int sect_count, int track_size, floppy_image &image)
{
std::vector<uint32_t> buffer;
@@ -1698,151 +888,62 @@ void floppy_image_format_t::generate_track(const desc_e *desc, int track, int he
}
if(int(buffer.size()) != track_size)
- throw emu_fatalerror("Wrong track size in generate_track, expected %d, got %d\n", track_size, int(buffer.size()));
+ throw std::invalid_argument(util::string_format("Wrong track size in generate_track, expected %d, got %d", track_size, buffer.size()));
fixup_crcs(buffer, crcs);
generate_track_from_levels(track, head, buffer, 0, image);
}
-void floppy_image_format_t::normalize_times(std::vector<uint32_t> &buffer)
+void floppy_image_format_t::normalize_times(std::vector<uint32_t> &buffer, uint32_t last_position)
{
- unsigned int total_sum = 0;
- for(unsigned int i=0; i != buffer.size(); i++)
- total_sum += buffer[i] & floppy_image::TIME_MASK;
-
- unsigned int current_sum = 0;
for(unsigned int i=0; i != buffer.size(); i++) {
uint32_t time = buffer[i] & floppy_image::TIME_MASK;
- buffer[i] = (buffer[i] & floppy_image::MG_MASK) | (200000000ULL * current_sum / total_sum);
- current_sum += time;
+ buffer[i] = (buffer[i] & floppy_image::MG_MASK) | (200000000ULL * time / last_position);
}
}
-void floppy_image_format_t::generate_track_from_bitstream(int track, int head, const uint8_t *trackbuf, int track_size, floppy_image *image, int subtrack, int splice)
+void floppy_image_format_t::generate_track_from_bitstream(int track, int head, const uint8_t *trackbuf, int track_size, floppy_image &image, int subtrack, int splice)
{
- std::vector<uint32_t> &dest = image->get_buffer(track, head, subtrack);
+ std::vector<uint32_t> &dest = image.get_buffer(track, head, subtrack);
dest.clear();
- // If the bitstream has an odd number of inversions, one needs to be added.
- // Put in in the middle of the half window after the center inversion, where
- // any fdc ignores it.
-
- int inversions = 0;
for(int i=0; i != track_size; i++)
if(trackbuf[i >> 3] & (0x80 >> (i & 7)))
- inversions++;
- bool need_flux = inversions & 1;
+ dest.push_back(floppy_image::MG_F | (i*2+1));
- uint32_t cbit = floppy_image::MG_A;
- uint32_t count = 0;
- for(int i=0; i != track_size; i++)
- if(trackbuf[i >> 3] & (0x80 >> (i & 7))) {
- dest.push_back(cbit | (count+2));
- cbit = cbit == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
- if(need_flux) {
- need_flux = false;
- dest.push_back(cbit | 1);
- cbit = cbit == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
- count = 1;
- } else
- count = 2;
- } else
- count += 4;
-
- if(count)
- dest.push_back(cbit | count);
+ normalize_times(dest, track_size*2);
- normalize_times(dest);
-
- if(splice >= 0 || splice < track_size) {
+ if(splice >= 0 && splice < track_size) {
int splpos = uint64_t(200000000) * splice / track_size;
- image->set_write_splice_position(track, head, splpos, subtrack);
+ image.set_write_splice_position(track, head, splpos, subtrack);
}
}
-void floppy_image_format_t::generate_track_from_levels(int track, int head, std::vector<uint32_t> &trackbuf, int splice_pos, floppy_image *image)
+void floppy_image_format_t::generate_track_from_levels(int track, int head, const std::vector<uint32_t> &trackbuf, int splice_pos, floppy_image &image)
{
// Retrieve the angular splice pos before messing with the data
splice_pos = splice_pos % trackbuf.size();
uint32_t splice_angular_pos = trackbuf[splice_pos] & floppy_image::TIME_MASK;
- // Check if we need to invert a cell to get an even number of
- // transitions on the whole track
- //
- // Also check if all MG values are valid
-
- int transition_count = 0;
- for(auto & elem : trackbuf) {
- switch(elem & floppy_image::MG_MASK) {
- case MG_1:
- transition_count++;
- break;
-
- case MG_W:
- throw emu_fatalerror("Weak bits not yet handled, track %d head %d\n", track, head);
-
- case MG_0:
- case floppy_image::MG_N:
- case floppy_image::MG_D:
- break;
-
- case floppy_image::MG_A:
- case floppy_image::MG_B:
- default:
- throw emu_fatalerror("Incorrect MG information in generate_track_from_levels, track %d head %d\n", track, head);
- }
- }
-
- if(transition_count & 1) {
- int pos = splice_pos;
- while((trackbuf[pos] & floppy_image::MG_MASK) != MG_0 && (trackbuf[pos] & floppy_image::MG_MASK) != MG_1) {
- pos++;
- if(pos == int(trackbuf.size()))
- pos = 0;
- if(pos == splice_pos)
- goto meh;
- }
- if((trackbuf[pos] & floppy_image::MG_MASK) == MG_0)
- trackbuf[pos] = (trackbuf[pos] & floppy_image::TIME_MASK) | MG_1;
- else
- trackbuf[pos] = (trackbuf[pos] & floppy_image::TIME_MASK) | MG_0;
-
- meh:
- ;
-
- }
-
- // Maximal number of cells which happens when the buffer is all MG_1/MG_N alternated, which would be 3/2
- std::vector<uint32_t> &dest = image->get_buffer(track, head);
+ std::vector<uint32_t> &dest = image.get_buffer(track, head);
dest.clear();
- uint32_t cbit = floppy_image::MG_A;
- uint32_t count = 0;
+ uint32_t total_time = 0;
for(auto & elem : trackbuf) {
uint32_t bit = elem & floppy_image::MG_MASK;
uint32_t time = elem & floppy_image::TIME_MASK;
- if(bit == MG_0) {
- count += time;
- continue;
- }
- if(bit == MG_1) {
- count += time >> 1;
- dest.push_back(cbit | count);
- cbit = cbit == floppy_image::MG_A ? floppy_image::MG_B : floppy_image::MG_A;
- count = time - (time >> 1);
- continue;
- }
- dest.push_back(cbit | count);
- dest.push_back(elem);
- count = 0;
- }
+ if(bit == MG_1)
+ dest.push_back(floppy_image::MG_F | (total_time + (time >> 1)));
- if(count)
- dest.push_back(cbit | count);
+ else if(bit != MG_0)
+ dest.push_back(bit | total_time);
- normalize_times(dest);
- image->set_write_splice_position(track, head, splice_angular_pos);
+ total_time += time;
+ }
+
+ normalize_times(dest, total_time);
+ image.set_write_splice_position(track, head, splice_angular_pos);
}
const uint8_t floppy_image_format_t::gcr5fw_tb[0x10] =
@@ -2251,177 +1352,281 @@ const floppy_image_format_t::desc_e floppy_image_format_t::amiga_22[] = {
{ END }
};
-void floppy_image_format_t::generate_bitstream_from_track(int track, int head, int cell_size, uint8_t *trackbuf, int &track_size, floppy_image *image, int subtrack)
+std::vector<bool> floppy_image_format_t::generate_bitstream_from_track(int track, int head, int cell_size, const floppy_image &image, int subtrack, int *max_delta)
{
- std::vector<uint32_t> &tbuf = image->get_buffer(track, head, subtrack);
- if(tbuf.size() <= 1) {
+ std::vector<bool> trackbuf;
+ const std::vector<uint32_t> &tbuf = image.get_buffer(track, head, subtrack);
+ bool track_has_info = false;
+ for(uint32_t mg : tbuf)
+ if((mg & floppy_image::MG_MASK) == floppy_image::MG_F) {
+ track_has_info = true;
+ break;
+ }
+
+ if(!track_has_info) {
// Unformatted track
- track_size = 200000000/cell_size;
- memset(trackbuf, 0, (track_size+7)/8);
- return;
+ int track_size = 200000000/cell_size;
+ trackbuf.resize(track_size, false);
+ return trackbuf;
}
- // Start at the write splice
- uint32_t splice = image->get_write_splice_position(track, head, subtrack);
- int cur_pos = splice;
- int cur_entry = 0;
- while(cur_entry < int(tbuf.size())-1 && (tbuf[cur_entry+1] & floppy_image::TIME_MASK) < cur_pos)
- cur_entry++;
-
- int cur_bit = 0;
-
- int period = cell_size;
- int period_adjust_base = period * 0.05;
-
- int min_period = int(cell_size*0.75);
- int max_period = int(cell_size*1.25);
- int phase_adjust = 0;
- int freq_hist = 0;
-
- uint32_t scanned = 0;
- while(scanned < 200000000) {
- // Note that all magnetic cell type changes are considered
- // edges. No randomness added for neutral/damaged cells
- int edge = tbuf[cur_entry] & floppy_image::TIME_MASK;
- if(edge < cur_pos)
- edge += 200000000;
- int next = cur_pos + period + phase_adjust;
- scanned += period + phase_adjust;
-
- if(edge >= next) {
- // No transition in the window means 0 and pll in free run mode
- trackbuf[cur_bit >> 3] &= ~(0x80 >> (cur_bit & 7));
- cur_bit++;
+ class pll {
+ private:
+ const std::vector<uint32_t> &tbuf;
+ int cur_pos;
+ int cur_entry;
+ int period;
+ int period_adjust_base;
+ int min_period;
+ int max_period;
+ int phase_adjust;
+ int freq_hist;
+ bool next_is_first;
+
+ public:
+ int min_delta, max_delta;
+
+ pll(const std::vector<uint32_t> &_tbuf, int cell_size) : tbuf(_tbuf) {
+ period = cell_size;
+ period_adjust_base = period * 0.05;
+
+ min_period = int(cell_size*0.75);
+ max_period = int(cell_size*1.25);
phase_adjust = 0;
+ freq_hist = 0;
+ min_delta = 0;
+ max_delta = 0;
+
+ // Try to go back 16 flux changes from the end of the track, or at most at the start
+ int flux_to_step = 16;
+ cur_entry = tbuf.size()-1;
+ while(cur_entry > 0 && flux_to_step) {
+ if((tbuf[cur_entry] & floppy_image::MG_MASK) == floppy_image::MG_F)
+ flux_to_step --;
+ cur_entry--;
+ }
- } else {
- // Transition in the window means 1, and the pll is adjusted
- trackbuf[cur_bit >> 3] |= 0x80 >> (cur_bit & 7);
- cur_bit++;
+ // Go back by half-a-period
+ cur_pos = (tbuf[cur_entry] & floppy_image::TIME_MASK) - period/2;
- int delta = edge - (next - period/2);
+ // Adjust the entry accordingly
+ while(cur_entry > 0 && (cur_pos > (tbuf[cur_entry] & floppy_image::TIME_MASK)))
+ cur_entry --;
- phase_adjust = 0.65*delta;
+ // Now go to the next flux change from there (the no-MG_F case has been handled earlier)
+ while((tbuf[cur_entry] & floppy_image::MG_MASK) != floppy_image::MG_F)
+ cur_entry ++;
- if(delta < 0) {
- if(freq_hist < 0)
- freq_hist--;
- else
- freq_hist = -1;
- } else if(delta > 0) {
- if(freq_hist > 0)
- freq_hist++;
- else
- freq_hist = 1;
- } else
- freq_hist = 0;
-
- if(freq_hist) {
- int afh = freq_hist < 0 ? -freq_hist : freq_hist;
- if(afh > 1) {
- int aper = period_adjust_base*delta/period;
- if(!aper)
- aper = freq_hist < 0 ? -1 : 1;
- period += aper;
-
- if(period < min_period)
- period = min_period;
- else if(period > max_period)
- period = max_period;
+ next_is_first = false;
+ }
+
+ std::pair<bool, bool> get() {
+ bool bit, first;
+ int edge = tbuf[cur_entry] & floppy_image::TIME_MASK;
+ if(edge < cur_pos)
+ edge += 200000000;
+ int next = cur_pos + period + phase_adjust;
+
+ if(edge >= next) {
+ // No transition in the window means 0 and pll in free run mode
+ bit = false;
+ phase_adjust = 0;
+
+ } else {
+ // Transition in the window means 1, and the pll is adjusted
+ bit = true;
+
+ int delta = edge - (next - period/2);
+ if(delta < min_delta)
+ min_delta = delta;
+ if(delta > max_delta)
+ max_delta = delta;
+
+ phase_adjust = 0.65*delta;
+
+ if(delta < 0) {
+ if(freq_hist < 0)
+ freq_hist--;
+ else
+ freq_hist = -1;
+ } else if(delta > 0) {
+ if(freq_hist > 0)
+ freq_hist++;
+ else
+ freq_hist = 1;
+ } else
+ freq_hist = 0;
+
+ if(freq_hist) {
+ int afh = freq_hist < 0 ? -freq_hist : freq_hist;
+ if(afh > 1) {
+ int aper = period_adjust_base*delta/period;
+ if(!aper)
+ aper = freq_hist < 0 ? -1 : 1;
+ period += aper;
+
+ if(period < min_period)
+ period = min_period;
+ else if(period > max_period)
+ period = max_period;
+ }
}
}
- }
- cur_pos = next;
- if(cur_pos >= 200000000) {
- cur_pos -= 200000000;
- cur_entry = 0;
+ first = next_is_first;
+ next_is_first = false;
+
+ cur_pos = next;
+ if(cur_pos >= 200000000) {
+ cur_pos -= 200000000;
+ cur_entry = 0;
+
+ if(cur_pos >= period/2)
+ first = true;
+ else
+ next_is_first = true;
+ }
+ while(cur_entry < int(tbuf.size())-1 && (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos)
+ cur_entry++;
+
+ // Wrap around
+ if(cur_entry == int(tbuf.size())-1 &&
+ (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos)
+ cur_entry = 0;
+
+ return std::make_pair(bit, first);
}
- while(cur_entry < int(tbuf.size())-1 && (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos)
- cur_entry++;
-
- // Wrap around
- if(cur_entry == int(tbuf.size())-1 &&
- (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos) {
- // Wrap to index 0 or 1 depending on whether there is a transition exactly at the index hole
- cur_entry = (tbuf[int(tbuf.size())-1] & floppy_image::MG_MASK) != (tbuf[0] & floppy_image::MG_MASK) ?
- 0 : 1;
+ };
+
+ pll cpll(tbuf, cell_size);
+
+ for(;;) {
+ auto r = cpll.get();
+ if(r.second) {
+ trackbuf.push_back(r.first);
+ break;
}
}
- // Clean the leftover bottom bits just in case
- trackbuf[cur_bit >> 3] &= ~(0x7f >> (cur_bit & 7));
- track_size = cur_bit;
+ for(;;) {
+ auto r = cpll.get();
+ if(r.second)
+ break;
+ trackbuf.push_back(r.first);
+ }
+
+ if(max_delta) {
+ *max_delta = -cpll.min_delta;
+ if(*max_delta < cpll.max_delta)
+ *max_delta = cpll.max_delta;
+ }
+
+ return trackbuf;
}
-int floppy_image_format_t::sbit_r(const uint8_t *bitstream, int pos)
+std::vector<uint8_t> floppy_image_format_t::generate_nibbles_from_bitstream(const std::vector<bool> &bitstream)
{
- return (bitstream[pos >> 3] & (0x80 >> (pos & 7))) != 0;
+ std::vector<uint8_t> res;
+ uint32_t pos = 0;
+ while(pos < bitstream.size()) {
+ while(pos < bitstream.size() && bitstream[pos] == 0)
+ pos++;
+ if(pos == bitstream.size()) {
+ pos = 0;
+ while(pos < bitstream.size() && bitstream[pos] == 0)
+ pos++;
+ if(pos == bitstream.size())
+ return res;
+ goto found;
+ }
+ pos += 8;
+ }
+ while(pos >= bitstream.size())
+ pos -= bitstream.size();
+ while(pos < bitstream.size() && bitstream[pos] == 0)
+ pos++;
+ found:
+ for(;;) {
+ uint8_t v = 0;
+ for(uint32_t i=0; i != 8; i++) {
+ if(bitstream[pos++])
+ v |= 0x80 >> i;
+ if(pos == bitstream.size())
+ pos = 0;
+ }
+ res.push_back(v);
+ if(pos < 8)
+ return res;
+ while(pos < bitstream.size() && bitstream[pos] == 0)
+ pos++;
+ if(pos == bitstream.size())
+ return res;
+ }
}
-int floppy_image_format_t::sbit_rp(const uint8_t *bitstream, int &pos, int track_size)
+int floppy_image_format_t::sbit_rp(const std::vector<bool> &bitstream, uint32_t &pos)
{
- int res = sbit_r(bitstream, pos);
+ int res = bitstream[pos];
pos ++;
- if(pos == track_size)
+ if(pos == bitstream.size())
pos = 0;
return res;
}
-uint8_t floppy_image_format_t::sbyte_mfm_r(const uint8_t *bitstream, int &pos, int track_size)
+uint8_t floppy_image_format_t::sbyte_mfm_r(const std::vector<bool> &bitstream, uint32_t &pos)
{
uint8_t res = 0;
for(int i=0; i<8; i++) {
- sbit_rp(bitstream, pos, track_size);
- if(sbit_rp(bitstream, pos, track_size))
+ sbit_rp(bitstream, pos);
+ if(sbit_rp(bitstream, pos))
res |= 0x80 >> i;
}
return res;
}
-uint8_t floppy_image_format_t::sbyte_gcr5_r(const uint8_t *bitstream, int &pos, int track_size)
+uint8_t floppy_image_format_t::sbyte_gcr5_r(const std::vector<bool> &bitstream, uint32_t &pos)
{
uint16_t gcr = 0;
for(int i=0; i<10; i++) {
- if(sbit_rp(bitstream, pos, track_size))
+ if(sbit_rp(bitstream, pos))
gcr |= 0x200 >> i;
}
return (gcr5bw_tb[gcr >> 5] << 4) | gcr5bw_tb[gcr & 0x1f];
}
-void floppy_image_format_t::extract_sectors_from_bitstream_mfm_pc(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size)
+std::vector<std::vector<uint8_t>> floppy_image_format_t::extract_sectors_from_bitstream_mfm_pc(const std::vector<bool> &bitstream)
{
- memset(sectors, 0, 256*sizeof(desc_xs));
+ std::vector<std::vector<uint8_t>> sectors;
// Don't bother if it's just too small
- if(track_size < 100)
- return;
+ if(bitstream.size() < 100)
+ return sectors;
// Start by detecting all id and data blocks
// If 100 is not enough, that track is too funky to be worth
// bothering anyway
- int idblk[100], dblk[100];
- int idblk_count = 0, dblk_count = 0;
+ uint32_t idblk[100], dblk[100];
+ uint32_t idblk_count = 0, dblk_count = 0;
// Precharge the shift register to detect over-the-index stuff
uint16_t shift_reg = 0;
- for(int i=0; i<16; i++)
- if(sbit_r(bitstream, track_size-16+i))
+ for(uint32_t i=0; i<16; i++)
+ if(bitstream[bitstream.size()-16+i])
shift_reg |= 0x8000 >> i;
// Scan the bitstream for sync marks and follow them to check for
// blocks
- for(int i=0; i<track_size; i++) {
- shift_reg = (shift_reg << 1) | sbit_r(bitstream, i);
+ for(uint32_t i=0; i<bitstream.size(); i++) {
+ shift_reg = (shift_reg << 1) | bitstream[i];
if(shift_reg == 0x4489) {
uint16_t header;
- int pos = i+1;
+ uint32_t pos = i+1;
do {
header = 0;
for(int j=0; j<16; j++)
- if(sbit_rp(bitstream, pos, track_size))
+ if(sbit_rp(bitstream, pos))
header |= 0x8000 >> j;
// Accept strings of sync marks as long and they're not wrapping
@@ -2445,22 +1650,17 @@ void floppy_image_format_t::extract_sectors_from_bitstream_mfm_pc(const uint8_t
}
// Then extract the sectors
- int sectdata_pos = 0;
for(int i=0; i<idblk_count; i++) {
- int pos = idblk[i];
- uint8_t track = sbyte_mfm_r(bitstream, pos, track_size);
- uint8_t head = sbyte_mfm_r(bitstream, pos, track_size);
- uint8_t sector = sbyte_mfm_r(bitstream, pos, track_size);
- uint8_t size = sbyte_mfm_r(bitstream, pos, track_size);
+ uint32_t pos = idblk[i];
+ [[maybe_unused]] uint8_t track = sbyte_mfm_r(bitstream, pos);
+ [[maybe_unused]] uint8_t head = sbyte_mfm_r(bitstream, pos);
+ uint8_t sector = sbyte_mfm_r(bitstream, pos);
+ uint8_t size = sbyte_mfm_r(bitstream, pos);
if(size >= 8)
continue;
int ssize = 128 << size;
- // If we don't have enough space for a sector's data, skip it
- if(ssize + sectdata_pos > sectdata_size)
- continue;
-
// Start of IDAM and DAM are supposed to be exactly 704 cells
// apart in normal format or 1008 cells apart in perpendicular
// format. Of course the hardware is tolerant. Accept +/-
@@ -2477,29 +1677,26 @@ void floppy_image_format_t::extract_sectors_from_bitstream_mfm_pc(const uint8_t
pos = dblk[d_index];
- sectors[sector].track = track;
- sectors[sector].head = head;
- sectors[sector].size = ssize;
- sectors[sector].data = sectdata + sectdata_pos;
+ if(sectors.size() <= sector)
+ sectors.resize(sector+1);
+ auto &sdata = sectors[sector];
+ sdata.resize(ssize);
for(int j=0; j<ssize; j++)
- sectdata[sectdata_pos++] = sbyte_mfm_r(bitstream, pos, track_size);
+ sdata[j] = sbyte_mfm_r(bitstream, pos);
}
+
+ return sectors;
}
-void floppy_image_format_t::get_geometry_mfm_pc(floppy_image *image, int cell_size, int &track_count, int &head_count, int &sector_count)
+void floppy_image_format_t::get_geometry_mfm_pc(const floppy_image &image, int cell_size, int &track_count, int &head_count, int &sector_count)
{
- image->get_actual_geometry(track_count, head_count);
+ image.get_actual_geometry(track_count, head_count);
if(!track_count) {
sector_count = 0;
return;
}
- uint8_t bitstream[500000/8];
- uint8_t sectdata[50000];
- desc_xs sectors[256];
- int track_size;
-
// Extract an arbitrary track to get an idea of the number of
// sectors
@@ -2507,29 +1704,23 @@ void floppy_image_format_t::get_geometry_mfm_pc(floppy_image *image, int cell_si
// 0-10, not near the end like 70+, no special effects on sync
// like 33
- generate_bitstream_from_track(track_count > 20 ? 20 : 0, 0, cell_size, bitstream, track_size, image);
- extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
-
- for(sector_count = 44; sector_count > 0 && !sectors[sector_count].data; sector_count--);
+ auto buf = generate_bitstream_from_track(track_count > 20 ? 20 : 0, 0, cell_size, image);
+ auto sectors = extract_sectors_from_bitstream_mfm_pc(buf);
+ sector_count = sectors.size();
}
-void floppy_image_format_t::get_track_data_mfm_pc(int track, int head, floppy_image *image, int cell_size, int sector_size, int sector_count, uint8_t *sectdata)
+void floppy_image_format_t::get_track_data_mfm_pc(int track, int head, const floppy_image &image, int cell_size, int sector_size, int sector_count, uint8_t *sectdata)
{
- uint8_t bitstream[500000/8];
- uint8_t sectbuf[50000];
- desc_xs sectors[256];
- int track_size;
-
- generate_bitstream_from_track(track, head, cell_size, bitstream, track_size, image);
- extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sectbuf, sizeof(sectbuf));
+ auto bitstream = generate_bitstream_from_track(track, head, cell_size, image);
+ auto sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
for(int sector=1; sector <= sector_count; sector++) {
uint8_t *sd = sectdata + (sector-1)*sector_size;
- if(sectors[sector].data && sectors[sector].track == track && sectors[sector].head == head) {
- int asize = sectors[sector].size;
+ if(sector < sectors.size() && !sectors[sector].empty()) {
+ unsigned int asize = sectors[sector].size();
if(asize > sector_size)
asize = sector_size;
- memcpy(sd, sectors[sector].data, asize);
+ memcpy(sd, sectors[sector].data(), asize);
if(asize < sector_size)
memset(sd+asize, 0, sector_size-asize);
} else
@@ -2538,26 +1729,26 @@ void floppy_image_format_t::get_track_data_mfm_pc(int track, int head, floppy_im
}
-void floppy_image_format_t::extract_sectors_from_bitstream_fm_pc(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size)
+std::vector<std::vector<uint8_t>> floppy_image_format_t::extract_sectors_from_bitstream_fm_pc(const std::vector<bool> &bitstream)
{
- memset(sectors, 0, 256*sizeof(desc_xs));
+ std::vector<std::vector<uint8_t>> sectors;
// Don't bother if it's just too small
- if(track_size < 100)
- return;
+ if(bitstream.size() < 100)
+ return sectors;
// Start by detecting all id and data blocks
// If 100 is not enough, that track is too funky to be worth
// bothering anyway
- int idblk[100], dblk[100];
- int idblk_count = 0, dblk_count = 0;
+ uint32_t idblk[100], dblk[100];
+ uint32_t idblk_count = 0, dblk_count = 0;
// Precharge the shift register to detect over-the-index stuff
uint16_t shift_reg = 0;
for(int i=0; i<16; i++)
- if(sbit_r(bitstream, track_size-16+i))
+ if(bitstream[bitstream.size()-16+i])
shift_reg |= 0x8000 >> i;
// Scan the bitstream for sync marks and follow them to check for
@@ -2565,8 +1756,8 @@ void floppy_image_format_t::extract_sectors_from_bitstream_fm_pc(const uint8_t *
// We scan for address marks only, as index marks are not mandatory,
// and many formats actually do not use them
- for(int i=0; i<track_size; i++) {
- shift_reg = (shift_reg << 1) | sbit_r(bitstream, i);
+ for(uint32_t i=0; i<bitstream.size(); i++) {
+ shift_reg = (shift_reg << 1) | bitstream[i];
// fe
if(shift_reg == 0xf57e) { // address mark
@@ -2582,21 +1773,16 @@ void floppy_image_format_t::extract_sectors_from_bitstream_fm_pc(const uint8_t *
}
// Then extract the sectors
- int sectdata_pos = 0;
- for(int i=0; i<idblk_count; i++) {
- int pos = idblk[i];
- uint8_t track = sbyte_mfm_r(bitstream, pos, track_size);
- uint8_t head = sbyte_mfm_r(bitstream, pos, track_size);
- uint8_t sector = sbyte_mfm_r(bitstream, pos, track_size);
- uint8_t size = sbyte_mfm_r(bitstream, pos, track_size);
+ for(uint32_t i=0; i<idblk_count; i++) {
+ uint32_t pos = idblk[i];
+ [[maybe_unused]] uint8_t track = sbyte_mfm_r(bitstream, pos);
+ [[maybe_unused]] uint8_t head = sbyte_mfm_r(bitstream, pos);
+ uint8_t sector = sbyte_mfm_r(bitstream, pos);
+ uint8_t size = sbyte_mfm_r(bitstream, pos);
if(size >= 8)
continue;
int ssize = 128 << size;
- // If we don't have enough space for a sector's data, skip it
- if(ssize + sectdata_pos > sectdata_size)
- continue;
-
// Start of IDAM and DAM are supposed to be exactly 384 cells
// apart. Of course the hardware is tolerant. Accept +/- 128
// cells of shift.
@@ -2612,29 +1798,26 @@ void floppy_image_format_t::extract_sectors_from_bitstream_fm_pc(const uint8_t *
pos = dblk[d_index];
- sectors[sector].track = track;
- sectors[sector].head = head;
- sectors[sector].size = ssize;
- sectors[sector].data = sectdata + sectdata_pos;
+ if(sectors.size() <= sector)
+ sectors.resize(sector+1);
+ auto &sdata = sectors[sector];
+ sdata.resize(ssize);
for(int j=0; j<ssize; j++)
- sectdata[sectdata_pos++] = sbyte_mfm_r(bitstream, pos, track_size);
+ sdata[j] = sbyte_mfm_r(bitstream, pos);
}
+
+ return sectors;
}
-void floppy_image_format_t::get_geometry_fm_pc(floppy_image *image, int cell_size, int &track_count, int &head_count, int &sector_count)
+void floppy_image_format_t::get_geometry_fm_pc(const floppy_image &image, int cell_size, int &track_count, int &head_count, int &sector_count)
{
- image->get_actual_geometry(track_count, head_count);
+ image.get_actual_geometry(track_count, head_count);
if(!track_count) {
sector_count = 0;
return;
}
- uint8_t bitstream[500000/8];
- uint8_t sectdata[50000];
- desc_xs sectors[256];
- int track_size;
-
// Extract an arbitrary track to get an idea of the number of
// sectors
@@ -2642,29 +1825,23 @@ void floppy_image_format_t::get_geometry_fm_pc(floppy_image *image, int cell_siz
// 0-10, not near the end like 70+, no special effects on sync
// like 33
- generate_bitstream_from_track(track_count > 20 ? 20 : 0, 0, cell_size, bitstream, track_size, image);
- extract_sectors_from_bitstream_fm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
-
- for(sector_count = 44; sector_count > 0 && !sectors[sector_count].data; sector_count--);
+ auto bitstream = generate_bitstream_from_track(track_count > 20 ? 20 : 0, 0, cell_size, image);
+ auto sectors = extract_sectors_from_bitstream_fm_pc(bitstream);
+ sector_count = sectors.size();
}
-void floppy_image_format_t::get_track_data_fm_pc(int track, int head, floppy_image *image, int cell_size, int sector_size, int sector_count, uint8_t *sectdata)
+void floppy_image_format_t::get_track_data_fm_pc(int track, int head, const floppy_image &image, int cell_size, int sector_size, int sector_count, uint8_t *sectdata)
{
- uint8_t bitstream[500000/8];
- uint8_t sectbuf[50000];
- desc_xs sectors[256];
- int track_size;
-
- generate_bitstream_from_track(track, head, cell_size, bitstream, track_size, image);
- extract_sectors_from_bitstream_fm_pc(bitstream, track_size, sectors, sectbuf, sizeof(sectbuf));
- for(int sector=1; sector <= sector_count; sector++) {
+ auto bitstream = generate_bitstream_from_track(track, head, cell_size, image);
+ auto sectors = extract_sectors_from_bitstream_fm_pc(bitstream);
+ for(unsigned int sector=1; sector < sector_count; sector++) {
uint8_t *sd = sectdata + (sector-1)*sector_size;
- if(sectors[sector].data && sectors[sector].track == track && sectors[sector].head == head) {
- int asize = sectors[sector].size;
+ if(sector < sectors.size() && !sectors[sector].empty()) {
+ unsigned int asize = sectors[sector].size();
if(asize > sector_size)
asize = sector_size;
- memcpy(sd, sectors[sector].data, asize);
+ memcpy(sd, sectors[sector].data(), asize);
if(asize < sector_size)
memset(sd+asize, 0, sector_size-asize);
} else
@@ -2681,21 +1858,21 @@ int floppy_image_format_t::calc_default_pc_gap3_size(uint32_t form_factor, int s
(form_factor == floppy_image::FF_35 ? 84 : 80);
}
-void floppy_image_format_t::build_wd_track_fm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2)
+void floppy_image_format_t::build_wd_track_fm(int track, int head, floppy_image &image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2)
{
build_pc_track_fm(track, head, image, cell_count, sector_count, sects, gap_3, -1, gap_1, gap_2);
}
-void floppy_image_format_t::build_wd_track_mfm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2)
+void floppy_image_format_t::build_wd_track_mfm(int track, int head, floppy_image &image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2)
{
build_pc_track_mfm(track, head, image, cell_count, sector_count, sects, gap_3, -1, gap_1, gap_2);
}
-void floppy_image_format_t::build_pc_track_fm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_4a, int gap_1, int gap_2)
+void floppy_image_format_t::build_pc_track_fm(int track, int head, floppy_image &image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_4a, int gap_1, int gap_2)
{
std::vector<uint32_t> track_data;
- // gap 4a , IAM and gap 1
+ // gap 4a, IAM and gap 1
if(gap_4a != -1) {
for(int i=0; i<gap_4a; i++) fm_w(track_data, 8, 0xff);
for(int i=0; i< 6; i++) fm_w(track_data, 8, 0x00);
@@ -2710,7 +1887,7 @@ void floppy_image_format_t::build_pc_track_fm(int track, int head, floppy_image
unsigned int etpos = track_data.size() + (sector_count*(6+5+2+gap_2+6+1+2) + total_size)*16;
if(etpos > cell_count)
- throw emu_fatalerror("Incorrect layout on track %d head %d, expected_size=%d, current_size=%d", track, head, cell_count, etpos);
+ throw std::invalid_argument(util::string_format("Incorrect layout on track %d head %d, expected_size=%d, current_size=%d", track, head, cell_count, etpos));
if(etpos + gap_3*16*(sector_count-1) > cell_count)
gap_3 = (cell_count - etpos) / 16 / (sector_count-1);
@@ -2757,11 +1934,11 @@ void floppy_image_format_t::build_pc_track_fm(int track, int head, floppy_image
generate_track_from_levels(track, head, track_data, 0, image);
}
-void floppy_image_format_t::build_pc_track_mfm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_4a, int gap_1, int gap_2)
+void floppy_image_format_t::build_pc_track_mfm(int track, int head, floppy_image &image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_4a, int gap_1, int gap_2)
{
std::vector<uint32_t> track_data;
- // gap 4a , IAM and gap 1
+ // gap 4a, IAM and gap 1
if(gap_4a != -1) {
for(int i=0; i<gap_4a; i++) mfm_w(track_data, 8, 0x4e);
for(int i=0; i<12; i++) mfm_w(track_data, 8, 0x00);
@@ -2777,7 +1954,7 @@ void floppy_image_format_t::build_pc_track_mfm(int track, int head, floppy_image
int etpos = int(track_data.size()) + (sector_count*(12+3+5+2+gap_2+12+3+1+2) + total_size)*16;
if(etpos > cell_count)
- throw emu_fatalerror("Incorrect layout on track %d head %d, expected_size=%d, current_size=%d", track, head, cell_count, etpos);
+ throw std::invalid_argument(util::string_format("Incorrect layout on track %d head %d, expected_size=%d, current_size=%d", track, head, cell_count, etpos));
if(etpos + gap_3*16*(sector_count-1) > cell_count)
gap_3 = (cell_count - etpos) / 16 / (sector_count-1);
@@ -2825,32 +2002,255 @@ void floppy_image_format_t::build_pc_track_mfm(int track, int head, floppy_image
generate_track_from_levels(track, head, track_data, 0, image);
}
-void floppy_image_format_t::extract_sectors_from_bitstream_gcr5(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size, int head, int tracks)
+void floppy_image_format_t::build_mac_track_gcr(int track, int head, floppy_image &image, const desc_gcr_sector *sects)
+{
+ // 30318342 = 60.0 / 1.979e-6
+ static const uint32_t cells_per_speed_zone[5] = {
+ 30318342 / 394,
+ 30318342 / 429,
+ 30318342 / 472,
+ 30318342 / 525,
+ 30318342 / 590
+ };
+
+ static const std::array<uint8_t, 12> notag{};
+
+ uint32_t speed_zone = track/16;
+ if(speed_zone > 4)
+ speed_zone = 4;
+
+ uint32_t sectors = 12 - speed_zone;
+ uint32_t pregap = cells_per_speed_zone[speed_zone] - 6208 * sectors;
+
+ std::vector<uint32_t> buffer;
+
+ uint32_t prepregap = pregap % 48;
+ if(prepregap >= 24) {
+ raw_w(buffer, prepregap - 24, 0xff3fcf);
+ raw_w(buffer, 24, 0xf3fcff);
+ } else
+ raw_w(buffer, prepregap, 0xf3fcff);
+
+ for(uint32_t i = 0; i != pregap / 48; i++) {
+ raw_w(buffer, 24, 0xff3fcf);
+ raw_w(buffer, 24, 0xf3fcff);
+ }
+
+ for(uint32_t s = 0; s != sectors; s++) {
+ for(uint32_t i=0; i != 8; i++) {
+ raw_w(buffer, 24, 0xff3fcf);
+ raw_w(buffer, 24, 0xf3fcff);
+ }
+
+ raw_w(buffer, 24, 0xd5aa96);
+ raw_w(buffer, 8, gcr6fw_tb[sects[s].track & 0x3f]);
+ raw_w(buffer, 8, gcr6fw_tb[sects[s].sector & 0x3f]);
+ raw_w(buffer, 8, gcr6fw_tb[(sects[s].track & 0x40 ? 1 : 0) | (sects[s].head ? 0x20 : 0)]);
+ raw_w(buffer, 8, gcr6fw_tb[sects[s].info & 0x3f]);
+ uint8_t check = sects[s].track ^ sects[s].sector ^ ((sects[s].track & 0x40 ? 1 : 0) | (sects[s].head ? 0x20 : 0)) ^ sects[s].info;
+ raw_w(buffer, 8, gcr6fw_tb[check & 0x3f]);
+ raw_w(buffer, 24, 0xdeaaff);
+
+ raw_w(buffer, 24, 0xff3fcf);
+ raw_w(buffer, 24, 0xf3fcff);
+
+ raw_w(buffer, 24, 0xd5aaad);
+ raw_w(buffer, 8, gcr6fw_tb[sects[s].sector & 0x3f]);
+
+ const uint8_t *data = sects[s].tag;
+ if(!data)
+ data = notag.data();
+
+ uint8_t ca = 0, cb = 0, cc = 0;
+ for(int i=0; i < 175; i ++) {
+ if(i == 4)
+ data = sects[s].data - 3*4;
+
+ uint8_t va = data[3*i];
+ uint8_t vb = data[3*i+1];
+ uint8_t vc = i != 174 ? data[3*i+2] : 0;
+
+ cc = (cc << 1) | (cc >> 7);
+ uint16_t suma = ca + va + (cc & 1);
+ ca = suma;
+ va = va ^ cc;
+ uint16_t sumb = cb + vb + (suma >> 8);
+ cb = sumb;
+ vb = vb ^ ca;
+ if(i != 174)
+ cc = cc + vc + (sumb >> 8);
+ vc = vc ^ cb;
+
+ uint32_t nb = i != 174 ? 32 : 24;
+ raw_w(buffer, nb, gcr6_encode(va, vb, vc) >> (32-nb));
+ }
+
+ raw_w(buffer, 32, gcr6_encode(ca, cb, cc));
+ raw_w(buffer, 32, 0xdeaaffff);
+ }
+
+ generate_track_from_levels(track, head, buffer, 0, image);
+}
+
+std::vector<std::vector<uint8_t>> floppy_image_format_t::extract_sectors_from_track_mac_gcr6(int head, int track, const floppy_image &image)
{
- memset(sectors, 0, 256*sizeof(desc_xs));
+ // 200000000 / 60.0 * 1.979e-6 ~= 6.5967
+ static const int cell_size_per_speed_zone[5] = {
+ 394 * 65967 / 10000,
+ 429 * 65967 / 10000,
+ 472 * 65967 / 10000,
+ 525 * 65967 / 10000,
+ 590 * 65967 / 10000
+ };
+
+ uint32_t speed_zone = track/16;
+ if(speed_zone > 4)
+ speed_zone = 4;
+
+ uint32_t sectors = 12 - speed_zone;
+
+ std::vector<std::vector<uint8_t>> sector_data(sectors);
+
+ auto buf = generate_bitstream_from_track(track, head, cell_size_per_speed_zone[speed_zone], image);
+ auto nib = generate_nibbles_from_bitstream(buf);
+
+ if(nib.size() < 300)
+ return sector_data;
+
+ std::vector<uint32_t> hpos;
+
+ uint32_t hstate = get_u16be(&nib[nib.size() - 2]);
+ for(uint32_t pos = 0; pos != nib.size(); pos++) {
+ hstate = ((hstate << 8) | nib[pos]) & 0xffffff;
+ if(hstate == 0xd5aa96)
+ hpos.push_back(pos == nib.size() - 1 ? 0 : pos+1);
+ }
+
+ for(uint32_t pos : hpos) {
+ uint8_t h[7];
+
+ for(auto &e : h) {
+ e = nib[pos];
+ pos ++;
+ if(pos == nib.size())
+ pos = 0;
+ }
+
+ uint8_t v2 = gcr6bw_tb[h[2]];
+ uint8_t v3 = gcr6bw_tb[h[3]];
+ uint8_t tr = gcr6bw_tb[h[0]] | (v2 & 1 ? 0x40 : 0x00);
+ uint8_t se = gcr6bw_tb[h[1]];
+ // uint8_t si = v2 & 0x20 ? 1 : 0;
+ // uint8_t ds = v3 & 0x20 ? 1 : 0;
+ // uint8_t fmt = v3 & 0x1f;
+ uint8_t c1 = (tr^se^v2^v3) & 0x3f;
+ uint8_t chk = gcr6bw_tb[h[4]];
+ if(chk != c1 || se >= sectors || h[5] != 0xde || h[6] != 0xaa)
+ continue;
+
+ auto &sdata = sector_data[se];
+ uint8_t ca = 0, cb = 0, cc = 0;
+
+ uint32_t hstate = (nib[pos] << 8);
+ pos ++;
+ if(pos == nib.size())
+ pos = 0;
+ hstate |= nib[pos];
+ pos ++;
+ if(pos == nib.size())
+ pos = 0;
+ for(;;) {
+ hstate = ((hstate << 8) | nib[pos]) & 0xffffff;
+ pos ++;
+ if(pos == nib.size())
+ pos = 0;
+ if(hstate == 0xd5aa96)
+ goto no_data_field;
+ if(hstate == 0xd5aaad)
+ break;
+ }
+ pos ++; // skip the sector byte
+ if(pos == nib.size())
+ pos = 0;
+
+ sdata.resize(512+12);
+ for(int i=0; i < 175; i++) {
+ uint8_t e0 = nib[pos++];
+ if(pos == nib.size())
+ pos = 0;
+ uint8_t e1 = nib[pos++];
+ if(pos == nib.size())
+ pos = 0;
+ uint8_t e2 = nib[pos++];
+ if(pos == nib.size())
+ pos = 0;
+ uint8_t e3 = i < 174 ? nib[pos++] : 0x96;
+ if(pos == nib.size())
+ pos = 0;
+
+ uint8_t va, vb, vc;
+ gcr6_decode(e0, e1, e2, e3, va, vb, vc);
+ cc = (cc << 1) | (cc >> 7);
+ va = va ^ cc;
+ uint16_t suma = ca + va + (cc & 1);
+ ca = suma;
+ vb = vb ^ ca;
+ uint16_t sumb = cb + vb + (suma >> 8);
+ cb = sumb;
+ vc = vc ^ cb;
+
+ sdata[3*i] = va;
+ sdata[3*i+1] = vb;
+
+ if(i != 174) {
+ cc = cc + vc + (sumb >> 8);
+ sdata[3*i+2] = vc;
+ }
+ }
+ for(auto &e : h) {
+ e = nib[pos];
+ pos ++;
+ if(pos == nib.size())
+ pos = 0;
+ }
+ uint8_t va, vb, vc;
+ gcr6_decode(h[0], h[1], h[2], h[3], va, vb, vc);
+ if(va != ca || vb != cb || vc != cc || h[4] != 0xde || h[5] != 0xaa)
+ sdata.clear();
+ no_data_field:
+ ;
+ }
+
+ return sector_data;
+}
+
+
+std::vector<std::vector<uint8_t>> floppy_image_format_t::extract_sectors_from_bitstream_gcr5(const std::vector<bool> &bitstream, int head, int tracks)
+{
+ std::vector<std::vector<uint8_t>> sectors;
// Don't bother if it's just too small
- if(track_size < 100)
- return;
+ if(bitstream.size() < 100)
+ return sectors;
// Start by detecting all id and data blocks
- int hblk[100], dblk[100];
- int hblk_count = 0, dblk_count = 0;
+ uint32_t hblk[100]{}, dblk[100]{};
+ uint32_t hblk_count = 0, dblk_count = 0;
// Precharge the shift register to detect over-the-index stuff
uint16_t shift_reg = 0;
- for(int i=0; i<16; i++)
- if(sbit_r(bitstream, track_size-16+i))
+ for(uint32_t i=0; i<16; i++)
+ if(bitstream[bitstream.size()-16+i])
shift_reg |= 0x8000 >> i;
// Scan the bitstream for sync marks and follow them to check for blocks
bool sync = false;
- for(int i=0; i<track_size; i++) {
- int bit = sbit_r(bitstream, i);
+ for(uint32_t i=0; i<bitstream.size(); i++) {
+ int bit = bitstream[i];
shift_reg = ((shift_reg << 1) | bit) & 0x3ff;
if (sync && !bit) {
- uint8_t id = sbyte_gcr5_r(bitstream, i, track_size);
+ uint8_t id = sbyte_gcr5_r(bitstream, i);
switch (id) {
case 0x08:
@@ -2869,67 +2269,70 @@ void floppy_image_format_t::extract_sectors_from_bitstream_gcr5(const uint8_t *b
}
// Then extract the sectors
- int sectdata_pos = 0;
for(int i=0; i<hblk_count; i++) {
- int pos = hblk[i];
- ATTR_UNUSED uint8_t block_id = sbyte_gcr5_r(bitstream, pos, track_size);
- uint8_t crc = sbyte_gcr5_r(bitstream, pos, track_size);
- uint8_t sector = sbyte_gcr5_r(bitstream, pos, track_size);
- uint8_t track = sbyte_gcr5_r(bitstream, pos, track_size);
- uint8_t id2 = sbyte_gcr5_r(bitstream, pos, track_size);
- uint8_t id1 = sbyte_gcr5_r(bitstream, pos, track_size);
+ uint32_t pos = hblk[i];
+ [[maybe_unused]] uint8_t block_id = sbyte_gcr5_r(bitstream, pos);
+ uint8_t crc = sbyte_gcr5_r(bitstream, pos);
+ uint8_t sector = sbyte_gcr5_r(bitstream, pos);
+ uint8_t track = sbyte_gcr5_r(bitstream, pos);
+ uint8_t id2 = sbyte_gcr5_r(bitstream, pos);
+ uint8_t id1 = sbyte_gcr5_r(bitstream, pos);
if (crc ^ sector ^ track ^ id2 ^ id1) {
// header crc mismatch
+ continue;
}
pos = dblk[i];
- block_id = sbyte_gcr5_r(bitstream, pos, track_size);
+ block_id = sbyte_gcr5_r(bitstream, pos);
if (track > tracks) track -= tracks;
- sectors[sector].track = track;
- sectors[sector].head = head;
- sectors[sector].size = 256;
- sectors[sector].data = sectdata + sectdata_pos;
+ if(sectors.size() <= sector)
+ sectors.resize(sector+1);
+ auto &sdata = sectors[sector];
+ sdata.resize(256);
uint8_t data_crc = 0;
- for(int j=0; j<sectors[sector].size; j++) {
- uint8_t data = sbyte_gcr5_r(bitstream, pos, track_size);
+ for(int j=0; j<256; j++) {
+ uint8_t data = sbyte_gcr5_r(bitstream, pos);
data_crc ^= data;
- sectdata[sectdata_pos++] = data;
+ sdata[j] = data;
}
- data_crc ^= sbyte_gcr5_r(bitstream, pos, track_size);
+ data_crc ^= sbyte_gcr5_r(bitstream, pos);
if (data_crc) {
// data crc mismatch
+ sdata.clear();
}
}
+
+ return sectors;
}
-void floppy_image_format_t::extract_sectors_from_bitstream_victor_gcr5(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size)
+std::vector<std::vector<uint8_t>> floppy_image_format_t::extract_sectors_from_bitstream_victor_gcr5(const std::vector<bool> &bitstream)
{
- memset(sectors, 0, 256*sizeof(desc_xs));
+ std::vector<std::vector<uint8_t>> sectors;
// Don't bother if it's just too small
- if(track_size < 100)
- return;
+ if(bitstream.size() < 100)
+ return sectors;
// Start by detecting all id and data blocks
- int hblk[100], dblk[100];
- int hblk_count = 0, dblk_count = 0;
+ uint32_t hblk[100]{}, dblk[100]{};
+ uint32_t hblk_count = 0, dblk_count = 0;
// Precharge the shift register to detect over-the-index stuff
uint16_t shift_reg = 0;
- for(int i=0; i<16; i++)
- if(sbit_r(bitstream, track_size-16+i))
+ for(uint32_t i=0; i<16; i++)
+ if(bitstream[bitstream.size()-16+i])
shift_reg |= 0x8000 >> i;
// Scan the bitstream for sync marks and follow them to check for blocks
bool sync = false;
- for(int i=0; i<track_size; i++) {
- int bit = sbit_r(bitstream, i);
+ for(uint32_t i=0; i<bitstream.size(); i++) {
+ int bit = bitstream[i];
shift_reg = ((shift_reg << 1) | bit) & 0x3ff;
if (sync && !bit) {
- uint8_t id = sbyte_gcr5_r(bitstream, i, track_size);
+ uint8_t id = sbyte_gcr5_r(bitstream, i);
switch (id) {
case 0x07:
@@ -2948,23 +2351,23 @@ void floppy_image_format_t::extract_sectors_from_bitstream_victor_gcr5(const uin
}
// Then extract the sectors
- int sectdata_pos = 0;
for(int i=0; i<hblk_count; i++) {
- int pos = hblk[i];
- ATTR_UNUSED uint8_t block_id = sbyte_gcr5_r(bitstream, pos, track_size);
- uint8_t track = sbyte_gcr5_r(bitstream, pos, track_size);
- uint8_t sector = sbyte_gcr5_r(bitstream, pos, track_size);
+ uint32_t pos = hblk[i];
+ [[maybe_unused]] uint8_t block_id = sbyte_gcr5_r(bitstream, pos);
+ [[maybe_unused]] uint8_t track = sbyte_gcr5_r(bitstream, pos);
+ uint8_t sector = sbyte_gcr5_r(bitstream, pos);
pos = dblk[i];
- block_id = sbyte_gcr5_r(bitstream, pos, track_size);
-
- sectors[sector].track = track & 0x7f;
- sectors[sector].head = BIT(track, 7);
- sectors[sector].size = 512;
- sectors[sector].data = sectdata + sectdata_pos;
- for(int j=0; j<sectors[sector].size; j++) {
- uint8_t data = sbyte_gcr5_r(bitstream, pos, track_size);
- sectdata[sectdata_pos++] = data;
- }
+ block_id = sbyte_gcr5_r(bitstream, pos);
+
+ if(sectors.size() <= sector)
+ sectors.resize(sector+1);
+ auto &sdata = sectors[sector];
+ sdata.resize(512);
+
+ for(int j=0; j<512; j++)
+ sdata[j] = sbyte_gcr5_r(bitstream, pos);
}
+
+ return sectors;
}
diff --git a/src/lib/formats/flopimg.h b/src/lib/formats/flopimg.h
index d5edd6b0315..18cc17ea73b 100644
--- a/src/lib/formats/flopimg.h
+++ b/src/lib/formats/flopimg.h
@@ -7,211 +7,20 @@
Floppy disk image abstraction code
*********************************************************************/
+
#ifndef MAME_FORMATS_FLOPIMG_H
#define MAME_FORMATS_FLOPIMG_H
#pragma once
-#include "osdcore.h"
-#include "ioprocs.h"
-#include "opresolv.h"
-#include "coretmpl.h"
+#include "utilfwd.h"
+#include <memory>
#include <vector>
-#ifndef LOG_FORMATS
-#define LOG_FORMATS if (0) printf
-#endif
-
-
-/***************************************************************************
-
- Constants
-
-***************************************************************************/
-
-#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
-
-
-/***************************************************************************
-
- Type definitions
-
-***************************************************************************/
-
-enum floperr_t
-{
- FLOPPY_ERROR_SUCCESS, /* no error */
- FLOPPY_ERROR_INTERNAL, /* fatal internal error */
- FLOPPY_ERROR_UNSUPPORTED, /* this operation is unsupported */
- FLOPPY_ERROR_OUTOFMEMORY, /* ran out of memory */
- FLOPPY_ERROR_SEEKERROR, /* attempted to seek to nonexistent location */
- FLOPPY_ERROR_INVALIDIMAGE, /* this image in invalid */
- FLOPPY_ERROR_READONLY, /* attempt to write to read-only image */
- FLOPPY_ERROR_NOSPACE,
- FLOPPY_ERROR_PARAMOUTOFRANGE,
- FLOPPY_ERROR_PARAMNOTSPECIFIED
-};
-
-struct floppy_image_legacy;
-
-struct FloppyCallbacks
-{
- floperr_t (*read_sector)(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen);
- floperr_t (*write_sector)(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
- floperr_t (*read_indexed_sector)(floppy_image_legacy *floppy, int head, int track, int sector_index, void *buffer, size_t buflen);
- floperr_t (*write_indexed_sector)(floppy_image_legacy *floppy, int head, int track, int sector_index, const void *buffer, size_t buflen, int ddam);
- floperr_t (*read_track)(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buflen);
- floperr_t (*write_track)(floppy_image_legacy *floppy, int head, int track, uint64_t offset, const void *buffer, size_t buflen);
- floperr_t (*format_track)(floppy_image_legacy *floppy, int head, int track, util::option_resolution *params);
- floperr_t (*post_format)(floppy_image_legacy *floppy, util::option_resolution *params);
- int (*get_heads_per_disk)(floppy_image_legacy *floppy);
- int (*get_tracks_per_disk)(floppy_image_legacy *floppy);
- int (*get_sectors_per_track)(floppy_image_legacy *floppy, int head, int track);
- uint32_t (*get_track_size)(floppy_image_legacy *floppy, int head, int track);
- floperr_t (*get_sector_length)(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length);
- floperr_t (*get_indexed_sector_info)(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags);
- floperr_t (*get_track_data_offset)(floppy_image_legacy *floppy, int head, int track, uint64_t *offset);
-};
-
-
-
-struct FloppyFormat
-{
- const char *name;
- const char *extensions;
- const char *description;
- floperr_t (*identify)(floppy_image_legacy *floppy, const struct FloppyFormat *format, int *vote);
- floperr_t (*construct)(floppy_image_legacy *floppy, const struct FloppyFormat *format, util::option_resolution *params);
- floperr_t (*destruct)(floppy_image_legacy *floppy, const struct FloppyFormat *format);
- const char *param_guidelines;
-};
-
-#define FLOPPY_IDENTIFY(name) floperr_t name(floppy_image_legacy *floppy, const struct FloppyFormat *format, int *vote)
-#define FLOPPY_CONSTRUCT(name) floperr_t name(floppy_image_legacy *floppy, const struct FloppyFormat *format, util::option_resolution *params)
-#define FLOPPY_DESTRUCT(name) floperr_t name(floppy_image_legacy *floppy, const struct FloppyFormat *format)
-
-FLOPPY_IDENTIFY(td0_dsk_identify);
-FLOPPY_CONSTRUCT(td0_dsk_construct);
-FLOPPY_DESTRUCT(td0_dsk_destruct);
-
-FLOPPY_IDENTIFY(imd_dsk_identify);
-FLOPPY_CONSTRUCT(imd_dsk_construct);
-
-FLOPPY_IDENTIFY(cqm_dsk_identify);
-FLOPPY_CONSTRUCT(cqm_dsk_construct);
-
-FLOPPY_IDENTIFY(dsk_dsk_identify);
-FLOPPY_CONSTRUCT(dsk_dsk_construct);
-
-FLOPPY_IDENTIFY(d88_dsk_identify);
-FLOPPY_CONSTRUCT(d88_dsk_construct);
-
-FLOPPY_IDENTIFY(fdi_dsk_identify);
-FLOPPY_CONSTRUCT(fdi_dsk_construct);
-
-#define LEGACY_FLOPPY_OPTIONS_NAME(name) floppyoptions_##name
-
-#define LEGACY_FLOPPY_OPTIONS_START(name) \
- const struct FloppyFormat floppyoptions_##name[] = \
- {
-#define LEGACY_FLOPPY_OPTIONS_END0 \
- { nullptr } \
- };
-
-#define LEGACY_FLOPPY_OPTIONS_EXTERN(name) \
- extern const struct FloppyFormat floppyoptions_##name[]
-#define LEGACY_FLOPPY_OPTION(name, extensions_, description_, identify_, construct_, destruct_, ranges_)\
- { #name, extensions_, description_, identify_, construct_, destruct_, ranges_ },
-#define LEGACY_FLOPPY_OPTIONS_END \
- LEGACY_FLOPPY_OPTION( fdi, "fdi", "Formatted Disk Image", fdi_dsk_identify, fdi_dsk_construct, nullptr, nullptr) \
- LEGACY_FLOPPY_OPTION( td0, "td0", "Teledisk floppy disk image", td0_dsk_identify, td0_dsk_construct, td0_dsk_destruct, nullptr) \
- LEGACY_FLOPPY_OPTION( imd, "imd", "IMD floppy disk image", imd_dsk_identify, imd_dsk_construct, nullptr, nullptr) \
- LEGACY_FLOPPY_OPTION( cqm, "cqm,dsk", "CopyQM floppy disk image", cqm_dsk_identify, cqm_dsk_construct, nullptr, nullptr) \
- LEGACY_FLOPPY_OPTION( dsk, "dsk", "DSK floppy disk image", dsk_dsk_identify, dsk_dsk_construct, nullptr, nullptr) \
- LEGACY_FLOPPY_OPTION( d88, "d77,d88,1dd", "D88 Floppy Disk image", d88_dsk_identify, d88_dsk_construct, nullptr, nullptr) \
- LEGACY_FLOPPY_OPTIONS_END0
-
-LEGACY_FLOPPY_OPTIONS_EXTERN(default);
-
-#define PARAM_END '\0'
-#define PARAM_HEADS 'H'
-#define PARAM_TRACKS 'T'
-#define PARAM_SECTORS 'S'
-#define PARAM_SECTOR_LENGTH 'L'
-#define PARAM_INTERLEAVE 'I'
-#define PARAM_FIRST_SECTOR_ID 'F'
-
-#define HEADS(range) "H" #range
-#define TRACKS(range) "T" #range
-#define SECTORS(range) "S" #range
-#define SECTOR_LENGTH(range) "L" #range
-#define INTERLEAVE(range) "I" #range
-#define FIRST_SECTOR_ID(range) "F" #range
-
-/***************************************************************************
-
- Prototypes
-
-***************************************************************************/
-
-OPTION_GUIDE_EXTERN(floppy_option_guide);
-
-/* opening, closing and creating of floppy images */
-floperr_t floppy_open(void *fp, const struct io_procs *procs, const std::string &extension, const struct FloppyFormat *format, int flags, floppy_image_legacy **outfloppy);
-floperr_t floppy_open_choices(void *fp, const struct io_procs *procs, const std::string &extension, const struct FloppyFormat *formats, int flags, floppy_image_legacy **outfloppy);
-floperr_t floppy_create(void *fp, const struct io_procs *procs, const struct FloppyFormat *format, util::option_resolution *parameters, floppy_image_legacy **outfloppy);
-void floppy_close(floppy_image_legacy *floppy);
-
-/* useful for identifying a floppy image */
-floperr_t floppy_identify(void *fp, const struct io_procs *procs, const char *extension, const struct FloppyFormat *formats, int *identified_format);
-
-/* functions useful within format constructors */
-void *floppy_tag(floppy_image_legacy *floppy);
-void *floppy_create_tag(floppy_image_legacy *floppy, size_t tagsize);
-struct FloppyCallbacks *floppy_callbacks(floppy_image_legacy *floppy);
-uint8_t floppy_get_filler(floppy_image_legacy *floppy);
-void floppy_set_filler(floppy_image_legacy *floppy, uint8_t filler);
-
-/* calls for accessing disk image data */
-floperr_t floppy_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len);
-floperr_t floppy_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset, const void *buffer, size_t buffer_len, int ddam);
-floperr_t floppy_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector_index, int offset, void *buffer, size_t buffer_len);
-floperr_t floppy_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector_index, int offset, const void *buffer, size_t buffer_len, int ddam);
-floperr_t floppy_read_track(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t buffer_len);
-floperr_t floppy_write_track(floppy_image_legacy *floppy, int head, int track, const void *buffer, size_t buffer_len);
-floperr_t floppy_read_track_data(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t buffer_len);
-floperr_t floppy_write_track_data(floppy_image_legacy *floppy, int head, int track, const void *buffer, size_t buffer_len);
-floperr_t floppy_format_track(floppy_image_legacy *floppy, int head, int track, util::option_resolution *params);
-int floppy_get_tracks_per_disk(floppy_image_legacy *floppy);
-int floppy_get_heads_per_disk(floppy_image_legacy *floppy);
-uint32_t floppy_get_track_size(floppy_image_legacy *floppy, int head, int track);
-floperr_t floppy_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length);
-floperr_t floppy_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags);
-floperr_t floppy_get_sector_count(floppy_image_legacy *floppy, int head, int track, int *sector_count);
-floperr_t floppy_load_track(floppy_image_legacy *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length);
-int floppy_is_read_only(floppy_image_legacy *floppy);
-uint8_t floppy_random_byte(floppy_image_legacy *floppy);
-
-/* accessors for meta information about the image */
-const char *floppy_format_description(floppy_image_legacy *floppy);
-
-/* calls for accessing the raw disk image */
-void floppy_image_read(floppy_image_legacy *floppy, void *buffer, uint64_t offset, size_t length);
-void floppy_image_write(floppy_image_legacy *floppy, const void *buffer, uint64_t offset, size_t length);
-void floppy_image_write_filler(floppy_image_legacy *floppy, uint8_t filler, uint64_t offset, size_t length);
-uint64_t floppy_image_size(floppy_image_legacy *floppy);
-
-/* misc */
-const char *floppy_error(floperr_t err);
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
//////////////////////////////////////////////////////////
@@ -224,8 +33,19 @@ class floppy_image;
class floppy_image_format_t
{
public:
- floppy_image_format_t();
- virtual ~floppy_image_format_t();
+ virtual ~floppy_image_format_t() = default;
+
+ // The result of identify is a binary or of these flags, comparison afterwards is numerical.
+ // If a match is incorrect (bad signature for instance), result must be 0. The non-zero
+ // result helps to decide how reliable the identification is, for choice classification.
+
+ enum {
+ FIFID_HINT = 0x01, // All other things being equal, favorise this format
+ FIFID_EXT = 0x02, // Extension matches one of the list (set outside of identify)
+ FIFID_SIZE = 0x04, // File size matches what is expected
+ FIFID_SIGN = 0x08, // The file signature matches
+ FIFID_STRUCT = 0x10, // Some file internal structure aspects have been verified
+ };
/*! @brief Identify an image.
The identify function tests if the image is valid
@@ -233,44 +53,43 @@ public:
@param io buffer containing the image data.
@param form_factor Physical form factor of disk, from the enum
in floppy_image
- @return 1 if image valid, 0 otherwise.
+ @param variants the variants from floppy_image the drive can handle
+ @return Binary or of FIFID flags, 0 if invalid for that format
*/
- virtual int identify(io_generic *io, uint32_t form_factor) = 0;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const = 0;
/*! @brief Load an image.
The load function opens an image file and converts it to the
- internal MESS floppy representation.
+ internal MAME floppy representation.
@param io source buffer containing the image data.
@param form_factor Physical form factor of disk, from the enum
in floppy_image
- @param image output buffer for data in MESS internal format.
+ @param variants the variants from floppy_image the drive can handle
+ @param image output buffer for data in MAME internal format.
@return true on success, false otherwise.
*/
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) = 0;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const = 0;
/*! @brief Save an image.
- The save function writes back an image from the MESS internal
+ The save function writes back an image from the MAME internal
floppy representation to the appropriate format on disk.
@param io output buffer for the data in the on-disk format.
- @param image source buffer containing data in MESS internal format.
+ @param variants the variants from floppy_image the drive can handle
+ @param image source buffer containing data in MAME internal format.
@return true on success, false otherwise.
*/
- virtual bool save(io_generic *io, floppy_image *image);
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const;
//! @returns string containing name of format.
- virtual const char *name() const = 0;
+ virtual const char *name() const noexcept = 0;
//! @returns string containing description of format.
- virtual const char *description() const = 0;
+ virtual const char *description() const noexcept = 0;
//! @returns string containing comma-separated list of file
//! extensions the format may use.
- virtual const char *extensions() const = 0;
+ virtual const char *extensions() const noexcept = 0;
//! @returns true if format supports saving.
- virtual bool supports_save() const = 0;
+ virtual bool supports_save() const noexcept = 0;
- //! Used if a linked list of formats is needed
- floppy_image_format_t *next;
- //! This appends a format to the linked list of formats, needed for floppy_image_device().
- void append(floppy_image_format_t *_next);
//! This checks if the file has the proper extension for this format.
//! @param file_name
//! @returns true if file matches the extension.
@@ -366,6 +185,14 @@ protected:
SECTOR_INTERLEAVE_SKEW //!< Defines interleave and skew for sector counting
};
+
+ /*! @brief Test if a variant is present in the variant vector
+ @param variants the variant vector
+ @param variant the variant to test
+ @result true if variant is in variants
+ */
+ static bool has_variant(const std::vector<uint32_t> &variants, uint32_t variant) noexcept;
+
//! Sector data description
struct desc_s
{
@@ -385,7 +212,7 @@ protected:
@param track_size in _cells_, i.e. 100000 for a usual 2us-per-cell track at 300rpm.
@param image
*/
- void generate_track(const desc_e *desc, int track, int head, const desc_s *sect, int sect_count, int track_size, floppy_image *image);
+ static void generate_track(const desc_e *desc, int track, int head, const desc_s *sect, int sect_count, int track_size, floppy_image &image);
/*! @brief Generate a track from cell binary values, MSB-first.
@param track
@@ -396,7 +223,7 @@ protected:
@param subtrack subtrack index, 0-3
@param splice write splice position
*/
- void generate_track_from_bitstream(int track, int head, const uint8_t *trackbuf, int track_size, floppy_image *image, int subtrack = 0, int splice = 0);
+ static void generate_track_from_bitstream(int track, int head, const uint8_t *trackbuf, int track_size, floppy_image &image, int subtrack = 0, int splice = 0);
//! @brief Generate a track from cell level values (0/1/W/D/N).
@@ -413,10 +240,11 @@ protected:
know. trackbuf may be modified at that position or after.
@param image
*/
- void generate_track_from_levels(int track, int head, std::vector<uint32_t> &trackbuf, int splice_pos, floppy_image *image);
+ static void generate_track_from_levels(int track, int head, const std::vector<uint32_t> &trackbuf, int splice_pos, floppy_image &image);
- //! Normalize the times in a cell buffer to sum up to 200000000
- void normalize_times(std::vector<uint32_t> &buffer);
+ //! Normalize the times in a cell buffer to bring the
+ //! 0..last_position range up to 0..200000000
+ static void normalize_times(std::vector<uint32_t> &buffer, uint32_t last_position);
// Some conversion tables for gcr
static const uint8_t gcr5fw_tb[0x10], gcr5bw_tb[0x20];
@@ -493,16 +321,8 @@ protected:
@endverbatim
*/
- void generate_bitstream_from_track(int track, int head, int cell_size, uint8_t *trackbuf, int &track_size, floppy_image *image, int subtrack = 0);
-
- //! Defines a standard sector for extracting.
- struct desc_xs
- {
- int track, //!< Track for this sector
- head, //!< Head for this sector
- size; //!< Size of this sector
- const uint8_t *data; //!< Data within this sector
- };
+ static std::vector<bool> generate_bitstream_from_track(int track, int head, int cell_size, const floppy_image &image, int subtrack = 0, int *max_delta = nullptr);
+ static std::vector<uint8_t> generate_nibbles_from_bitstream(const std::vector<bool> &bitstream);
struct desc_pc_sector
{
@@ -513,84 +333,91 @@ protected:
bool bad_crc;
};
- int calc_default_pc_gap3_size(uint32_t form_factor, int sector_size);
- void build_wd_track_fm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2);
- void build_wd_track_mfm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2=22);
- void build_pc_track_fm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_4a=40, int gap_1=26, int gap_2=11);
- void build_pc_track_mfm(int track, int head, floppy_image *image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_4a=80, int gap_1=50, int gap_2=22);
+ struct desc_gcr_sector
+ {
+ uint8_t track, head, sector, info;
+ uint8_t *tag;
+ uint8_t *data;
+ };
+ static int calc_default_pc_gap3_size(uint32_t form_factor, int sector_size);
+ static void build_wd_track_fm(int track, int head, floppy_image &image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2);
+ static void build_wd_track_mfm(int track, int head, floppy_image &image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2=22);
+ static void build_pc_track_fm(int track, int head, floppy_image &image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_4a=40, int gap_1=26, int gap_2=11);
+ static void build_pc_track_mfm(int track, int head, floppy_image &image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_4a=80, int gap_1=50, int gap_2=22);
+ static void build_mac_track_gcr(int track, int head, floppy_image &image, const desc_gcr_sector *sects);
//! @brief Extract standard sectors from a regenerated bitstream.
- //! Sectors must point to an array of 256 desc_xs.
-
- //! An existing sector is recognizable by having ->data non-null.
- //! Sector data is written in sectdata up to sectdata_size bytes.
-
- //! The ones implemented here are the ones used by multiple
- //! systems.
+ //! Returns a vector of the vector contents, indexed by the sector id. Missing sectors have size zero.
//! PC-type sectors with MFM encoding, sector size can go from 128 bytes to 16K.
- void extract_sectors_from_bitstream_mfm_pc(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size);
+ static std::vector<std::vector<uint8_t>> extract_sectors_from_bitstream_mfm_pc(const std::vector<bool> &bitstream);
+
//! PC-type sectors with FM encoding
- void extract_sectors_from_bitstream_fm_pc(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size);
+ static std::vector<std::vector<uint8_t>> extract_sectors_from_bitstream_fm_pc(const std::vector<bool> &bitstream);
+
//! Commodore type sectors with GCR5 encoding
- void extract_sectors_from_bitstream_gcr5(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size, int head, int tracks);
+ static std::vector<std::vector<uint8_t>> extract_sectors_from_bitstream_gcr5(const std::vector<bool> &bitstream, int head, int tracks);
+
//! Victor 9000 type sectors with GCR5 encoding
- void extract_sectors_from_bitstream_victor_gcr5(const uint8_t *bitstream, int track_size, desc_xs *sectors, uint8_t *sectdata, int sectdata_size);
+ static std::vector<std::vector<uint8_t>> extract_sectors_from_bitstream_victor_gcr5(const std::vector<bool> &bitstream);
+
+ //! Mac type sectors with GCR6 encoding
+ static std::vector<std::vector<uint8_t>> extract_sectors_from_track_mac_gcr6(int head, int track, const floppy_image &image);
//! @brief Get a geometry (including sectors) from an image.
//! PC-type sectors with MFM encoding
- void get_geometry_mfm_pc(floppy_image *image, int cell_size, int &track_count, int &head_count, int &sector_count);
+ static void get_geometry_mfm_pc(const floppy_image &image, int cell_size, int &track_count, int &head_count, int &sector_count);
//! PC-type sectors with FM encoding
- void get_geometry_fm_pc(floppy_image *image, int cell_size, int &track_count, int &head_count, int &sector_count);
+ static void get_geometry_fm_pc(const floppy_image &image, int cell_size, int &track_count, int &head_count, int &sector_count);
//! Regenerate the data for a full track.
//! PC-type sectors with MFM encoding and fixed-size.
- void get_track_data_mfm_pc(int track, int head, floppy_image *image, int cell_size, int sector_size, int sector_count, uint8_t *sectdata);
+ static void get_track_data_mfm_pc(int track, int head, const floppy_image &image, int cell_size, int sector_size, int sector_count, uint8_t *sectdata);
//! Regenerate the data for a full track.
//! PC-type sectors with FM encoding and fixed-size.
- void get_track_data_fm_pc(int track, int head, floppy_image *image, int cell_size, int sector_size, int sector_count, uint8_t *sectdata);
+ static void get_track_data_fm_pc(int track, int head, const floppy_image &image, int cell_size, int sector_size, int sector_count, uint8_t *sectdata);
//! Look up a bit in a level-type stream.
- bool bit_r(const std::vector<uint32_t> &buffer, int offset);
+ static bool bit_r(const std::vector<uint32_t> &buffer, int offset);
//! Look up multiple bits
- uint32_t bitn_r(const std::vector<uint32_t> &buffer, int offset, int count);
+ static uint32_t bitn_r(const std::vector<uint32_t> &buffer, int offset, int count);
//! Write a bit with a given size.
- void bit_w(std::vector<uint32_t> &buffer, bool val, uint32_t size = 1000);
- void bit_w(std::vector<uint32_t> &buffer, bool val, uint32_t size, int offset);
+ static void bit_w(std::vector<uint32_t> &buffer, bool val, uint32_t size = 1000);
+ static void bit_w(std::vector<uint32_t> &buffer, bool val, uint32_t size, int offset);
//! Calculate a CCITT-type CRC.
- uint16_t calc_crc_ccitt(const std::vector<uint32_t> &buffer, int start, int end);
+ static uint16_t calc_crc_ccitt(const std::vector<uint32_t> &buffer, int start, int end);
//! Write a series of (raw) bits
- void raw_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size = 1000);
- void raw_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size, int offset);
+ static void raw_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size = 1000);
+ static void raw_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size, int offset);
//! FM-encode and write a series of bits
- void fm_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size = 1000);
- void fm_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size, int offset);
+ static void fm_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size = 1000);
+ static void fm_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size, int offset);
//! MFM-encode and write a series of bits
- void mfm_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size = 1000);
- void mfm_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size, int offset);
+ static void mfm_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size = 1000);
+ static void mfm_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size, int offset);
//! MFM-encode every two bits and write
- void mfm_half_w(std::vector<uint32_t> &buffer, int start_bit, uint32_t val, uint32_t size = 1000);
+ static void mfm_half_w(std::vector<uint32_t> &buffer, int start_bit, uint32_t val, uint32_t size = 1000);
//! GCR5-encode and write a series of bits
- void gcr5_w(std::vector<uint32_t> &buffer, uint8_t val, uint32_t size = 1000);
- void gcr5_w(std::vector<uint32_t> &buffer, uint8_t val, uint32_t size, int offset);
+ static void gcr5_w(std::vector<uint32_t> &buffer, uint8_t val, uint32_t size = 1000);
+ static void gcr5_w(std::vector<uint32_t> &buffer, uint8_t val, uint32_t size, int offset);
//! 8N1-encode and write a series of bits
- void _8n1_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size = 1000);
+ static void _8n1_w(std::vector<uint32_t> &buffer, int n, uint32_t val, uint32_t size = 1000);
//! GCR4 encode (Apple II sector header)
- uint16_t gcr4_encode(uint8_t va);
+ static uint16_t gcr4_encode(uint8_t va);
//! GCR4 decode
- uint8_t gcr4_decode(uint8_t e0, uint8_t e1);
+ static uint8_t gcr4_decode(uint8_t e0, uint8_t e1);
//! GCR6 encode (Apple II 16-sector and Mac-style GCR)
- uint32_t gcr6_encode(uint8_t va, uint8_t vb, uint8_t vc);
+ static uint32_t gcr6_encode(uint8_t va, uint8_t vb, uint8_t vc);
//! GCR6 decode
- void gcr6_decode(uint8_t e0, uint8_t e1, uint8_t e2, uint8_t e3, uint8_t &va, uint8_t &vb, uint8_t &vc);
+ static void gcr6_decode(uint8_t e0, uint8_t e1, uint8_t e2, uint8_t e3, uint8_t &va, uint8_t &vb, uint8_t &vc);
- uint8_t sbyte_mfm_r(const uint8_t *bitstream, int &pos, int track_size);
- uint8_t sbyte_gcr5_r(const uint8_t *bitstream, int &pos, int track_size);
+ static uint8_t sbyte_mfm_r(const std::vector<bool> &bitstream, uint32_t &pos);
+ static uint8_t sbyte_gcr5_r(const std::vector<bool> &bitstream, uint32_t &pos);
//! Max number of excess tracks to be discarded from disk image to fit floppy drive
enum { DUMP_THRESHOLD = 2 };
@@ -609,36 +436,25 @@ private:
bool fixup_mfm_clock; //!< would the MFM clock bit after the CRC need to be fixed?
};
- bool type_no_data(int type) const;
- bool type_data_mfm(int type, int p1, const gen_crc_info *crcs) const;
-
- int crc_cells_size(int type) const;
- void fixup_crc_amiga(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
- void fixup_crc_cbm(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
- void fixup_crc_ccitt(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
- void fixup_crc_ccitt_fm(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
- void fixup_crc_machead(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
- void fixup_crc_fcs(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
- void fixup_crc_victor_header(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
- void fixup_crc_victor_data(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
- void fixup_crcs(std::vector<uint32_t> &buffer, gen_crc_info *crcs);
- void collect_crcs(const desc_e *desc, gen_crc_info *crcs) const;
-
- int sbit_r(const uint8_t *bitstream, int pos);
- int sbit_rp(const uint8_t *bitstream, int &pos, int track_size);
-
- int calc_sector_index(int num, int interleave, int skew, int total_sectors, int track_head);
-};
+ static bool type_no_data(int type);
+ static bool type_data_mfm(int type, int p1, const gen_crc_info *crcs);
-// a dce_type is simply a pointer to its alloc function
-typedef floppy_image_format_t *(*floppy_format_type)();
+ static int crc_cells_size(int type);
+ static void fixup_crc_amiga(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
+ static void fixup_crc_cbm(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
+ static void fixup_crc_ccitt(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
+ static void fixup_crc_ccitt_fm(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
+ static void fixup_crc_machead(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
+ static void fixup_crc_fcs(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
+ static void fixup_crc_victor_header(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
+ static void fixup_crc_victor_data(std::vector<uint32_t> &buffer, const gen_crc_info *crc);
+ static void fixup_crcs(std::vector<uint32_t> &buffer, gen_crc_info *crcs);
+ static void collect_crcs(const desc_e *desc, gen_crc_info *crcs);
-// this template function creates a stub which constructs a image format
-template<class _FormatClass>
-floppy_image_format_t *floppy_image_format_creator()
-{
- return new _FormatClass();
-}
+ static int sbit_rp(const std::vector<bool> &bitstream, uint32_t &pos);
+
+ static int calc_sector_index(int num, int interleave, int skew, int total_sectors, int track_head);
+};
// ======================> floppy_image
@@ -647,19 +463,22 @@ floppy_image_format_t *floppy_image_format_creator()
//! Internal format is close but not identical to the mfi format.
//!
//!
+
//! Track data consists of a series of 32-bits lsb-first values
-//! representing magnetic cells. Bits 0-27 indicate the absolute
-//! position of the start of the cell (not the size), and bits
-//! 28-31 the type. Type can be:
-//! - 0, MG_A -> Magnetic orientation A
-//! - 1, MG_B -> Magnetic orientation B
-//! - 2, MG_N -> Non-magnetized zone (neutral)
-//! - 3, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
+//! representing the magnetic state. Bits 0-27 indicate the absolute
+//! position of encoded event, and bits ! 28-31 the type. Type can be:
+//! - 0, MG_F -> Flux orientation change
+//! - 1, MG_N -> Start of a non-magnetized zone (neutral)
+//! - 2, MG_D -> Start of a damaged zone, reads as neutral but cannot be changed by writing
+//! - 3, MG_E -> End of one of the previous zones, *inclusive*
//!
//! The position is in angular units of 1/200,000,000th of a turn.
-//! The last cell implicit end position is of course 200,000,000.
+//! A N or D zone must not wrap at the 200,000,000 position, it has to
+//! be split in two (the first finishing at 199,999,999, the second
+//! starting at 0)
//!
-//! Unformatted tracks are encoded as zero-size.
+//! Unformatted tracks are encoded as zero-size, and are strictly equivalent
+//! to (MG_N, 0), (MG_E, 199,999,999)
//!
//! The "track splice" information indicates where to start writing
//! if you try to rewrite a physical disk with the data. Some
@@ -688,10 +507,10 @@ public:
TIME_MASK = 0x0fffffff,
MG_MASK = 0xf0000000,
MG_SHIFT = 28, //!< Bitshift constant for magnetic orientation data
- MG_A = (0 << MG_SHIFT), //!< - 0, MG_A -> Magnetic orientation A
- MG_B = (1 << MG_SHIFT), //!< - 1, MG_B -> Magnetic orientation B
- MG_N = (2 << MG_SHIFT), //!< - 2, MG_N -> Non-magnetized zone (neutral)
- MG_D = (3 << MG_SHIFT) //!< - 3, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
+ MG_F = (0 << MG_SHIFT), //!< - 0, MG_F -> Flux orientation change
+ MG_N = (1 << MG_SHIFT), //!< - 1, MG_N -> Non-magnetized zone (neutral)
+ MG_D = (2 << MG_SHIFT), //!< - 2, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
+ MG_E = (3 << MG_SHIFT) //!< - 3, MG_E -> End of zone
};
@@ -706,14 +525,30 @@ public:
//! Variants
enum {
- SSSD = 0x44535353, //!< "SSSD", Single-sided single-density
- SSDD = 0x44445353, //!< "SSDD", Single-sided double-density
- SSQD = 0x44515353, //!< "SSQD", Single-sided quad-density
- DSSD = 0x44535344, //!< "DSSD", Double-sided single-density
- DSDD = 0x44445344, //!< "DSDD", Double-sided double-density (720K in 3.5, 360K in 5.25)
- DSQD = 0x44515344, //!< "DSQD", Double-sided quad-density (720K in 5.25, means DD+80 tracks)
- DSHD = 0x44485344, //!< "DSHD", Double-sided high-density (1440K)
- DSED = 0x44455344 //!< "DSED", Double-sided extra-density (2880K)
+ SSSD = 0x44535353, //!< "SSSD", Single-sided single-density
+ SSSD10 = 0x30315353, //!< "SS10", Single-sided single-density 10 hard sector
+ SSSD16 = 0x36315353, //!< "SS16", Single-sided single-density 16 hard sector
+ SSSD32 = 0x32335353, //!< "SS32", Single-sided single-density 32 hard sector
+ SSDD = 0x44445353, //!< "SSDD", Single-sided double-density
+ SSDD10 = 0x30314453, //!< "SD10", Single-sided double-density 10 hard sector
+ SSDD16 = 0x36314453, //!< "SD16", Single-sided double-density 16 hard sector
+ SSDD32 = 0x32334453, //!< "SD32", Single-sided double-density 32 hard sector
+ SSQD = 0x44515353, //!< "SSQD", Single-sided quad-density
+ SSQD10 = 0x30315153, //!< "SQ10", Single-sided quad-density 10 hard sector
+ SSQD16 = 0x36315153, //!< "SQ16", Single-sided quad-density 16 hard sector
+ DSSD = 0x44535344, //!< "DSSD", Double-sided single-density
+ DSSD10 = 0x30315344, //!< "DS10", Double-sided single-density 10 hard sector
+ DSSD16 = 0x36315344, //!< "DS16", Double-sided single-density 16 hard sector
+ DSSD32 = 0x32335344, //!< "DS32", Double-sided single-density 32 hard sector
+ DSDD = 0x44445344, //!< "DSDD", Double-sided double-density (720K in 3.5, 360K in 5.25)
+ DSDD10 = 0x30314444, //!< "DD10", Double-sided double-density 10 hard sector
+ DSDD16 = 0x36314444, //!< "DD16", Double-sided double-density 16 hard sector (360K in 5.25)
+ DSDD32 = 0x32334444, //!< "DD32", Double-sided double-density 32 hard sector
+ DSQD = 0x44515344, //!< "DSQD", Double-sided quad-density (720K in 5.25, means DD+80 tracks)
+ DSQD10 = 0x30315144, //!< "DQ10", Double-sided quad-density 10 hard sector
+ DSQD16 = 0x36315144, //!< "DQ16", Double-sided quad-density 16 hard sector (720K in 5.25, means DD+80 tracks)
+ DSHD = 0x44485344, //!< "DSHD", Double-sided high-density (1440K)
+ DSED = 0x44455344 //!< "DSED", Double-sided extra-density (2880K)
};
//! Encodings
@@ -723,24 +558,49 @@ public:
M2FM = 0x4D32464D //!< "M2FM", modified modified frequency modulation
};
+ //! Sectoring
+ enum {
+ SOFT = 0x54464F53, //!< "SOFT", Soft-sectored
+ H10 = 0x20303148, //!< "H10 ", Hard 10-sectored
+ H16 = 0x20363148, //!< "H16 ", Hard 16-sectored
+ H32 = 0x20323348 //!< "H32 ", Hard 32-sectored (8 inch disk)
+ };
+
// construction/destruction
//! floppy_image constructor
/*!
- @param _tracks number of tracks.
- @param _heads number of heads.
- @param _form_factor form factor of drive (from enum)
+ @param tracks number of tracks.
+ @param heads number of heads.
+ @param form_factor form factor of drive (from enum)
*/
floppy_image(int tracks, int heads, uint32_t form_factor);
- virtual ~floppy_image();
+ ~floppy_image();
//! @return the form factor.
- uint32_t get_form_factor() const { return form_factor; }
+ uint32_t get_form_factor() const noexcept { return form_factor; }
//! @return the variant.
- uint32_t get_variant() const { return variant; }
+ uint32_t get_variant() const noexcept { return variant; }
+ //! @return the disk sectoring.
+ uint32_t get_sectoring() const noexcept { return sectoring; }
//! @param v the variant.
- void set_variant(uint32_t v) { variant = v; }
+ void set_variant(uint32_t v);
+ //! @param v the variant.
+ void set_form_variant(uint32_t f, uint32_t v) { if(form_factor == FF_UNKNOWN) form_factor = f; set_variant(v); }
+ //! @param s the sectoring.
+ void set_sectoring(uint32_t s) { sectoring = s; }
+
+ //! Find most recent and next index hole for provided angular position.
+ //! The most recent hole may be equal to provided position. The next
+ //! hole will be 200000000 if all holes of the current rotation are in
+ //! the past.
+
+ /*! @param pos angular position
+ @param last most recent index hole
+ @param next next index hole
+ */
+ void find_index_hole(uint32_t pos, uint32_t &last, uint32_t &next) const;
/*!
@param track
@@ -748,7 +608,8 @@ public:
@param head head number
@return a pointer to the data buffer for this track and head
*/
- std::vector<uint32_t> &get_buffer(int track, int head, int subtrack = 0) { assert(track < tracks && head < heads); return track_array[track*4+subtrack][head].cell_data; }
+ std::vector<uint32_t> &get_buffer(int track, int head, int subtrack = 0) noexcept { assert(track < tracks && head < heads); return track_array[track*4+subtrack][head].cell_data; }
+ const std::vector<uint32_t> &get_buffer(int track, int head, int subtrack = 0) const noexcept { assert(track < tracks && head < heads); return track_array[track*4+subtrack][head].cell_data; }
//! Sets the write splice position.
//! The "track splice" information indicates where to start writing
@@ -762,28 +623,31 @@ public:
@param head
@param pos the position
*/
- void set_write_splice_position(int track, int head, uint32_t pos, int subtrack = 0) { assert(track < tracks && head < heads); track_array[track*4+subtrack][head].write_splice = pos; }
+ void set_write_splice_position(int track, int head, uint32_t pos, int subtrack = 0) noexcept { assert(track < tracks && head < heads); track_array[track*4+subtrack][head].write_splice = pos; }
//! @return the current write splice position.
- uint32_t get_write_splice_position(int track, int head, int subtrack = 0) const { assert(track < tracks && head < heads); return track_array[track*4+subtrack][head].write_splice; }
+ uint32_t get_write_splice_position(int track, int head, int subtrack = 0) const noexcept { assert(track < tracks && head < heads); return track_array[track*4+subtrack][head].write_splice; }
//! @return the maximal geometry supported by this format.
- void get_maximal_geometry(int &tracks, int &heads) const;
+ void get_maximal_geometry(int &tracks, int &heads) const noexcept;
//! @return the current geometry of the loaded image.
- void get_actual_geometry(int &tracks, int &heads);
+ void get_actual_geometry(int &tracks, int &heads) const noexcept;
//! @return the track resolution (0=full track, 1 = half-track, 2 = quarter track)
- int get_resolution() const;
+ int get_resolution() const noexcept;
+
+ //! @return whether a given track is formatted
+ bool track_is_formatted(int track, int head, int subtrack = 0) const noexcept;
//! Returns the variant name for the particular disk form factor/variant
//! @param form_factor
//! @param variant
- //! @param returns a string containing the variant name.
- static const char *get_variant_name(uint32_t form_factor, uint32_t variant);
+ //! @return a string containing the variant name.
+ static const char *get_variant_name(uint32_t form_factor, uint32_t variant) noexcept;
private:
int tracks, heads;
- uint32_t form_factor, variant;
+ uint32_t form_factor, variant, sectoring;
struct track_info
{
@@ -796,6 +660,14 @@ private:
// track number multiplied by 4 then head
// last array size may be bigger than actual track size
std::vector<std::vector<track_info> > track_array;
+
+ // Additional index holes in increasing order. Entries are absolute
+ // positions of index holes in the same units as cell_data. The
+ // positions are the start of the hole, not the center of the hole. The
+ // hole at angular position 0 is implicit, so an empty list encodes a
+ // regular soft-sectored disk. Additional holes are found on
+ // hard-sectored disks.
+ std::vector<uint32_t> index_array;
};
#endif // MAME_FORMATS_FLOPIMG_H
diff --git a/src/lib/formats/flopimg_legacy.cpp b/src/lib/formats/flopimg_legacy.cpp
new file mode 100644
index 00000000000..c209b7cab58
--- /dev/null
+++ b/src/lib/formats/flopimg_legacy.cpp
@@ -0,0 +1,877 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/*********************************************************************
+
+ flopimg_legacy.cpp
+
+ Floppy disk image abstraction code (legacy implementation)
+
+*********************************************************************/
+
+#include "flopimg_legacy.h"
+#include "imageutl.h"
+
+#include "ioprocs.h"
+#include "opresolv.h"
+
+#include <cassert>
+#include <climits>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+#define TRACK_LOADED 0x01
+#define TRACK_DIRTY 0x02
+
+
+struct floppy_image_legacy
+{
+ util::random_read_write::ptr io = nullptr;
+
+ const struct FloppyFormat *floppy_option = nullptr;
+ struct FloppyCallbacks format = { 0 };
+
+ /* loaded track stuff */
+ int loaded_track_head = 0;
+ int loaded_track_index = 0;
+ uint32_t loaded_track_size = 0;
+ std::unique_ptr<uint8_t[]> loaded_track_data;
+ uint8_t loaded_track_status = 0;
+ uint8_t flags = 0;
+
+ /* tagging system */
+ std::unique_ptr<uint8_t[]> tag_data;
+};
+
+
+
+struct floppy_params
+{
+ int param;
+ int value;
+};
+
+
+
+static floperr_t floppy_track_unload(floppy_image_legacy *floppy);
+
+OPTION_GUIDE_START(floplgcy_option_guide)
+ OPTION_INT('H', "heads", "Heads")
+ OPTION_INT('T', "tracks", "Tracks")
+ OPTION_INT('S', "sectors", "Sectors")
+ OPTION_INT('L', "sectorlength", "Sector Bytes")
+ OPTION_INT('I', "interleave", "Interleave")
+ OPTION_INT('F', "firstsectorid", "First Sector")
+OPTION_GUIDE_END
+
+const util::option_guide &floppy_option_guide()
+{
+ return floplgcy_option_guide;
+}
+
+
+static void floppy_close_internal(floppy_image_legacy *floppy);
+
+/*********************************************************************
+ opening, closing and creating of floppy images
+*********************************************************************/
+
+/* basic floppy_image_legacy initialization common to floppy_open() and floppy_create() */
+static floppy_image_legacy *floppy_init(util::random_read_write::ptr &&io, int flags)
+{
+ floppy_image_legacy *floppy;
+
+ floppy = new floppy_image_legacy;
+
+ floppy->io = std::move(io);
+ floppy->flags = (uint8_t) flags;
+ return floppy;
+}
+
+
+
+/* main code for identifying and maybe opening a disk image; not exposed
+ * directly because this function is big and hideous */
+static floperr_t floppy_open_internal(util::random_read_write::ptr &&io, const std::string &extension,
+ const struct FloppyFormat *floppy_options, int max_options, int flags, floppy_image_legacy **outfloppy,
+ int *outoption)
+{
+ floperr_t err;
+ floppy_image_legacy *floppy;
+ int best_option = -1;
+ int best_vote = 0;
+ int vote;
+ size_t i;
+
+ floppy = floppy_init(std::move(io), flags);
+ if (!floppy)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* vote on the best format */
+ for (i = 0; (i < max_options) && floppy_options[i].construct; i++)
+ {
+ if (extension.empty() || !floppy_options[i].extensions || image_find_extension(floppy_options[i].extensions, extension.c_str()))
+ {
+ if (floppy_options[i].identify)
+ {
+ vote = 0;
+ err = floppy_options[i].identify(floppy, &floppy_options[i], &vote);
+ if (err)
+ goto done;
+ }
+ else
+ {
+ vote = 1;
+ }
+
+ /* is this option a better one? */
+ if (vote > best_vote)
+ {
+ best_vote = vote;
+ best_option = i;
+ }
+ }
+ }
+
+ /* did we find a format? */
+ if (best_option == -1)
+ {
+ err = FLOPPY_ERROR_INVALIDIMAGE;
+ goto done;
+ }
+
+ if (outfloppy)
+ {
+ /* call the format constructor */
+ err = floppy_options[best_option].construct(floppy, &floppy_options[best_option], nullptr);
+ if (err)
+ goto done;
+
+ floppy->floppy_option = &floppy_options[best_option];
+ }
+ if (best_vote != 100)
+ {
+ printf("Loading image that is not 100%% recognized\n");
+ }
+ err = FLOPPY_ERROR_SUCCESS;
+
+done:
+ /* if we have a floppy disk and we either errored or are not keeping it, close it */
+ if (floppy && (!outfloppy || err))
+ {
+ floppy_close_internal(floppy);
+ floppy = nullptr;
+ }
+
+ if (outoption)
+ *outoption = err ? -1 : best_option;
+ if (outfloppy)
+ *outfloppy = floppy;
+ return err;
+}
+
+
+
+floperr_t floppy_identify(util::random_read_write::ptr &&io, const char *extension,
+ const struct FloppyFormat *formats, int *identified_format)
+{
+ return floppy_open_internal(std::move(io), extension, formats, INT_MAX, FLOPPY_FLAGS_READONLY, nullptr, identified_format);
+}
+
+
+
+floperr_t floppy_open(util::random_read_write::ptr &&io, const std::string &extension,
+ const struct FloppyFormat *format, int flags, floppy_image_legacy **outfloppy)
+{
+ return floppy_open_internal(std::move(io), extension, format, 1, flags, outfloppy, nullptr);
+}
+
+
+
+floperr_t floppy_open_choices(util::random_read_write::ptr &&io, const std::string &extension,
+ const struct FloppyFormat *formats, int flags, floppy_image_legacy **outfloppy)
+{
+ return floppy_open_internal(std::move(io), extension, formats, INT_MAX, flags, outfloppy, nullptr);
+}
+
+
+
+floperr_t floppy_create(util::random_read_write::ptr &&io, const struct FloppyFormat *format, util::option_resolution *parameters, floppy_image_legacy **outfloppy)
+{
+ floppy_image_legacy *floppy = nullptr;
+ floperr_t err;
+ int heads, tracks, h, t;
+ std::unique_ptr<util::option_resolution> alloc_resolution;
+
+ assert(format);
+
+ /* create the new image */
+ floppy = floppy_init(std::move(io), 0);
+ if (!floppy)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+
+ /* if this format expects creation parameters and none were specified, create some */
+ if (!parameters && format->param_guidelines)
+ {
+ try { alloc_resolution = std::make_unique<util::option_resolution>(floplgcy_option_guide); }
+ catch (...)
+ {
+ err = FLOPPY_ERROR_OUTOFMEMORY;
+ goto done;
+ }
+ alloc_resolution->set_specification(format->param_guidelines);
+ parameters = alloc_resolution.get();
+ }
+
+ /* call the format constructor */
+ err = format->construct(floppy, format, parameters);
+ if (err)
+ goto done;
+
+ /* format the disk, ignoring if formatting not implemented */
+ if (floppy->format.format_track)
+ {
+ heads = floppy_get_heads_per_disk(floppy);
+ tracks = floppy_get_tracks_per_disk(floppy);
+
+ for (h = 0; h < heads; h++)
+ {
+ for (t = 0; t < tracks; t++)
+ {
+ err = floppy->format.format_track(floppy, h, t, parameters);
+ if (err)
+ goto done;
+ }
+ }
+ }
+
+ /* call the post_format function, if present */
+ if (floppy->format.post_format)
+ {
+ err = floppy->format.post_format(floppy, parameters);
+ if (err)
+ goto done;
+ }
+
+ floppy->floppy_option = format;
+ err = FLOPPY_ERROR_SUCCESS;
+
+done:
+ if (err && floppy)
+ {
+ floppy_close_internal(floppy);
+ floppy = nullptr;
+ }
+
+ if (outfloppy)
+ *outfloppy = floppy;
+ else if (floppy)
+ floppy_close_internal(floppy);
+ return err;
+}
+
+
+
+static void floppy_close_internal(floppy_image_legacy *floppy)
+{
+ if (floppy) {
+ floppy_track_unload(floppy);
+
+ if(floppy->floppy_option && floppy->floppy_option->destruct)
+ floppy->floppy_option->destruct(floppy, floppy->floppy_option);
+
+ delete floppy;
+ }
+}
+
+
+
+void floppy_close(floppy_image_legacy *floppy)
+{
+ floppy_close_internal(floppy);
+}
+
+
+
+/*********************************************************************
+ functions useful in format constructors
+*********************************************************************/
+
+struct FloppyCallbacks *floppy_callbacks(floppy_image_legacy *floppy)
+{
+ assert(floppy);
+ return &floppy->format;
+}
+
+
+
+void *floppy_tag(floppy_image_legacy *floppy)
+{
+ assert(floppy);
+ return floppy->tag_data.get();
+}
+
+
+
+void *floppy_create_tag(floppy_image_legacy *floppy, size_t tagsize)
+{
+ floppy->tag_data = std::make_unique<uint8_t[]>(tagsize);
+ return floppy->tag_data.get();
+}
+
+
+
+uint8_t floppy_get_filler(floppy_image_legacy *floppy)
+{
+ // FIXME: remove this function, it's here for legacy reasons
+ // the caller actually sets the filler byte - in practice it's always 0xff but there's no actual guarantee
+ return 0xff;
+}
+
+
+
+util::random_read_write &floppy_get_io(floppy_image_legacy *floppy)
+{
+ return *floppy->io;
+}
+
+
+
+/*********************************************************************
+ calls for accessing the raw disk image
+*********************************************************************/
+
+void floppy_image_read(floppy_image_legacy *floppy, void *buffer, uint64_t offset, size_t length)
+{
+ /*auto const [err, actual] =*/ read_at(*floppy->io, offset, buffer, length); // FIXME: check for errors and premature EOF
+}
+
+
+
+void floppy_image_write(floppy_image_legacy *floppy, const void *buffer, uint64_t offset, size_t length)
+{
+ /*auto const [err, actual] =*/ write_at(*floppy->io, offset, buffer, length); // FIXME: check for errors
+}
+
+
+
+void floppy_image_write_filler(floppy_image_legacy *floppy, uint8_t filler, uint64_t offset, size_t length)
+{
+ uint8_t buffer[512];
+ memset(buffer, filler, std::min(sizeof(buffer), length));
+
+ while (length)
+ {
+ size_t const block = std::min(sizeof(buffer), length);
+ /*auto const [err, actual] =*/ write_at(*floppy->io, offset, buffer, block); // FIXME: check for errors
+ offset += block;
+ length -= block;
+ }
+}
+
+
+
+uint64_t floppy_image_size(floppy_image_legacy *floppy)
+{
+ uint64_t result;
+ floppy->io->length(result);
+ return result;
+}
+
+
+
+/*********************************************************************
+ calls for accessing disk image data
+*********************************************************************/
+
+static floperr_t floppy_readwrite_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset,
+ void *buffer, size_t buffer_len, bool writing, bool indexed, int ddam)
+{
+ floperr_t err;
+ const struct FloppyCallbacks *fmt;
+ size_t this_buffer_len;
+ std::vector<uint8_t> alloc_buf;
+ uint32_t sector_length;
+ uint8_t *buffer_ptr = (uint8_t *)buffer;
+ floperr_t (*read_sector)(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen);
+ floperr_t (*write_sector)(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
+
+ fmt = floppy_callbacks(floppy);
+
+ /* choose proper calls for indexed vs non-indexed */
+ if (indexed)
+ {
+ read_sector = fmt->read_indexed_sector;
+ write_sector = fmt->write_indexed_sector;
+ if (!fmt->get_indexed_sector_info)
+ {
+ err = FLOPPY_ERROR_UNSUPPORTED;
+ goto done;
+ }
+ }
+ else
+ {
+ read_sector = fmt->read_sector;
+ write_sector = fmt->write_sector;
+ if (!fmt->get_sector_length)
+ {
+ err = FLOPPY_ERROR_UNSUPPORTED;
+ goto done;
+ }
+ }
+
+ /* check to make sure that the operation is supported */
+ if (!read_sector || (writing && !write_sector))
+ {
+ err = FLOPPY_ERROR_UNSUPPORTED;
+ goto done;
+ }
+
+ /* main loop */
+ while(buffer_len > 0)
+ {
+ /* find out the size of this sector */
+ if (indexed)
+ err = fmt->get_indexed_sector_info(floppy, head, track, sector, nullptr, nullptr, nullptr, &sector_length, nullptr);
+ else
+ err = fmt->get_sector_length(floppy, head, track, sector, &sector_length);
+ if (err)
+ goto done;
+
+ /* do we even do anything with this sector? */
+ if (offset < sector_length)
+ {
+ /* ok we will be doing something */
+ if ((offset > 0) || (buffer_len < sector_length))
+ {
+ /* we will be doing an partial read/write; in other words we
+ * will not be reading/writing a full sector */
+ alloc_buf.resize(sector_length);
+
+ /* read the sector (we need to do this even when writing */
+ err = read_sector(floppy, head, track, sector, &alloc_buf[0], sector_length);
+ if (err)
+ goto done;
+
+ this_buffer_len = std::min(buffer_len, size_t(sector_length - offset));
+
+ if (writing)
+ {
+ memcpy(&alloc_buf[offset], buffer_ptr, this_buffer_len);
+
+ err = write_sector(floppy, head, track, sector, &alloc_buf[0], sector_length, ddam);
+ if (err)
+ goto done;
+ }
+ else
+ {
+ memcpy(buffer_ptr, &alloc_buf[offset], this_buffer_len);
+ }
+ offset += this_buffer_len;
+ offset %= sector_length;
+ }
+ else
+ {
+ this_buffer_len = sector_length;
+
+ if (writing)
+ err = write_sector(floppy, head, track, sector, buffer_ptr, sector_length, ddam);
+ else
+ err = read_sector(floppy, head, track, sector, buffer_ptr, sector_length);
+ if (err)
+ goto done;
+ }
+ }
+ else
+ {
+ /* skip this sector */
+ offset -= sector_length;
+ this_buffer_len = 0;
+ }
+
+ buffer_ptr += this_buffer_len;
+ buffer_len -= this_buffer_len;
+ sector++;
+ }
+
+ err = FLOPPY_ERROR_SUCCESS;
+
+done:
+ return err;
+}
+
+
+
+floperr_t floppy_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector, offset, buffer, buffer_len, false, false, 0);
+}
+
+
+
+floperr_t floppy_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset, const void *buffer, size_t buffer_len, int ddam)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector, offset, (void *) buffer, buffer_len, true, false, ddam);
+}
+
+
+
+floperr_t floppy_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector_index, int offset, void *buffer, size_t buffer_len)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector_index, offset, buffer, buffer_len, false, true, 0);
+}
+
+
+
+floperr_t floppy_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector_index, int offset, const void *buffer, size_t buffer_len, int ddam)
+{
+ return floppy_readwrite_sector(floppy, head, track, sector_index, offset, (void *) buffer, buffer_len, true, true, ddam);
+}
+
+
+static floperr_t floppy_get_track_data_offset(floppy_image_legacy *floppy, int head, int track, uint64_t *offset)
+{
+ floperr_t err;
+ const struct FloppyCallbacks *callbacks;
+
+ *offset = 0;
+ callbacks = floppy_callbacks(floppy);
+ if (callbacks->get_track_data_offset)
+ {
+ err = callbacks->get_track_data_offset(floppy, head, track, offset);
+ if (err)
+ return err;
+ }
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+static floperr_t floppy_read_track_offset(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+ const struct FloppyCallbacks *format;
+
+ format = floppy_callbacks(floppy);
+
+ if (!format->read_track)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ err = floppy_track_unload(floppy);
+ if (err)
+ return err;
+
+ err = format->read_track(floppy, head, track, offset, buffer, buffer_len);
+ if (err)
+ return err;
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+floperr_t floppy_read_track(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t buffer_len)
+{
+ return floppy_read_track_offset(floppy, head, track, 0, buffer, buffer_len);
+}
+
+
+
+floperr_t floppy_read_track_data(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+ uint64_t offset;
+
+ err = floppy_get_track_data_offset(floppy, head, track, &offset);
+ if (err)
+ return err;
+
+ return floppy_read_track_offset(floppy, head, track, offset, buffer, buffer_len);
+}
+
+
+
+static floperr_t floppy_write_track_offset(floppy_image_legacy *floppy, int head, int track, uint64_t offset, const void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+
+ /* track writing supported? */
+ if (!floppy_callbacks(floppy)->write_track)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ /* read only? */
+ if (floppy->flags & FLOPPY_FLAGS_READONLY)
+ return FLOPPY_ERROR_READONLY;
+
+ err = floppy_track_unload(floppy);
+ if (err)
+ return err;
+
+ err = floppy_callbacks(floppy)->write_track(floppy, head, track, offset, buffer, buffer_len);
+ if (err)
+ return err;
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+floperr_t floppy_write_track(floppy_image_legacy *floppy, int head, int track, const void *buffer, size_t buffer_len)
+{
+ return floppy_write_track_offset(floppy, head, track, 0, buffer, buffer_len);
+}
+
+
+
+floperr_t floppy_write_track_data(floppy_image_legacy *floppy, int head, int track, const void *buffer, size_t buffer_len)
+{
+ floperr_t err;
+ uint64_t offset;
+
+ err = floppy_get_track_data_offset(floppy, head, track, &offset);
+ if (err)
+ return err;
+
+ return floppy_write_track_offset(floppy, head, track, offset, buffer, buffer_len);
+}
+
+
+
+floperr_t floppy_format_track(floppy_image_legacy *floppy, int head, int track, util::option_resolution *parameters)
+{
+ floperr_t err;
+ struct FloppyCallbacks *format;
+ std::unique_ptr<util::option_resolution> alloc_resolution;
+
+ /* supported? */
+ format = floppy_callbacks(floppy);
+ if (!format->format_track)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ /* create a dummy resolution; if no parameters were specified */
+ if (!parameters)
+ {
+ try
+ {
+ alloc_resolution = std::make_unique<util::option_resolution>(floplgcy_option_guide);
+ }
+ catch (...)
+ {
+ return FLOPPY_ERROR_OUTOFMEMORY;
+ }
+ alloc_resolution->set_specification(floppy->floppy_option->param_guidelines);
+
+ parameters = alloc_resolution.get();
+ }
+
+ err = format->format_track(floppy, head, track, parameters);
+ if (err)
+ return err;
+
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+int floppy_get_tracks_per_disk(floppy_image_legacy *floppy)
+{
+ return floppy_callbacks(floppy)->get_tracks_per_disk(floppy);
+}
+
+
+
+int floppy_get_heads_per_disk(floppy_image_legacy *floppy)
+{
+ return floppy_callbacks(floppy)->get_heads_per_disk(floppy);
+}
+
+
+
+uint32_t floppy_get_track_size(floppy_image_legacy *floppy, int head, int track)
+{
+ const struct FloppyCallbacks *fmt;
+
+ fmt = floppy_callbacks(floppy);
+ if (!fmt->get_track_size)
+ return 0;
+
+ return fmt->get_track_size(floppy, head, track);
+}
+
+
+
+floperr_t floppy_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length)
+{
+ const struct FloppyCallbacks *fmt;
+
+ fmt = floppy_callbacks(floppy);
+ if (!fmt->get_sector_length)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ return fmt->get_sector_length(floppy, head, track, sector, sector_length);
+}
+
+
+
+floperr_t floppy_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags)
+{
+ const struct FloppyCallbacks *fmt;
+
+ fmt = floppy_callbacks(floppy);
+ if (!fmt->get_indexed_sector_info)
+ return FLOPPY_ERROR_UNSUPPORTED;
+
+ return fmt->get_indexed_sector_info(floppy, head, track, sector_index, cylinder, side, sector, sector_length, flags);
+}
+
+
+
+floperr_t floppy_get_sector_count(floppy_image_legacy *floppy, int head, int track, int *sector_count)
+{
+ floperr_t err;
+ int sector_index = 0;
+
+ do
+ {
+ err = floppy_get_indexed_sector_info(floppy, head, track, sector_index, nullptr, nullptr, nullptr, nullptr, nullptr);
+ if (!err)
+ sector_index++;
+ }
+ while(!err);
+
+ if (sector_index && (err == FLOPPY_ERROR_SEEKERROR))
+ err = FLOPPY_ERROR_SUCCESS;
+ if (sector_count)
+ *sector_count = err ? 0 : sector_index;
+ return err;
+}
+
+
+
+int floppy_is_read_only(floppy_image_legacy *floppy)
+{
+ return floppy->flags & FLOPPY_FLAGS_READONLY;
+}
+
+
+
+uint8_t floppy_random_byte(floppy_image_legacy *floppy)
+{
+ /* can't use mame_rand(); this might not be in the core */
+#ifdef rand
+#undef rand
+#endif
+ return rand();
+}
+
+
+
+/*********************************************************************
+ calls for track based IO
+*********************************************************************/
+
+floperr_t floppy_load_track(floppy_image_legacy *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length)
+{
+ floperr_t err;
+ uint32_t track_size;
+
+ /* have we already loaded this track? */
+ if (((floppy->loaded_track_status & TRACK_LOADED) == 0) || (head != floppy->loaded_track_head) || (track != floppy->loaded_track_index))
+ {
+ err = floppy_track_unload(floppy);
+ if (err)
+ goto error;
+
+ track_size = floppy_callbacks(floppy)->get_track_size(floppy, head, track);
+
+ floppy->loaded_track_data = std::make_unique<uint8_t[]>(track_size);
+ floppy->loaded_track_size = track_size;
+ floppy->loaded_track_head = head;
+ floppy->loaded_track_index = track;
+
+ err = floppy_callbacks(floppy)->read_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data.get(), floppy->loaded_track_size);
+ if (err)
+ goto error;
+
+ floppy->loaded_track_status |= TRACK_LOADED | (dirtify ? TRACK_DIRTY : 0);
+ }
+ else
+ floppy->loaded_track_status |= (dirtify ? TRACK_DIRTY : 0);
+
+ if (track_data)
+ *track_data = floppy->loaded_track_data.get();
+ if (track_length)
+ *track_length = floppy->loaded_track_size;
+ return FLOPPY_ERROR_SUCCESS;
+
+error:
+ if (track_data)
+ *track_data = nullptr;
+ if (track_length)
+ *track_length = 0;
+ return err;
+}
+
+
+
+static floperr_t floppy_track_unload(floppy_image_legacy *floppy)
+{
+ int err;
+ if (floppy->loaded_track_status & TRACK_DIRTY)
+ {
+ err = floppy_callbacks(floppy)->write_track(floppy, floppy->loaded_track_head, floppy->loaded_track_index, 0, floppy->loaded_track_data.get(), floppy->loaded_track_size);
+ if (err)
+ return (floperr_t)err;
+ }
+
+ floppy->loaded_track_status &= ~(TRACK_LOADED | TRACK_DIRTY);
+ return FLOPPY_ERROR_SUCCESS;
+}
+
+
+
+/*********************************************************************
+ accessors for meta information about the image
+*********************************************************************/
+
+const char *floppy_format_description(floppy_image_legacy *floppy)
+{
+ return floppy->floppy_option->description;
+}
+
+
+
+/*********************************************************************
+ misc calls
+*********************************************************************/
+
+const char *floppy_error(floperr_t err)
+{
+ static const char *const error_messages[] =
+ {
+ "The operation completed successfully",
+ "Fatal internal error",
+ "This operation is unsupported",
+ "Out of memory",
+ "Seek error",
+ "Invalid image",
+ "Attempted to write to read only image",
+ "No space left on image",
+ "Parameter out of range",
+ "Required parameter not specified"
+ };
+
+ if ((err < 0) || (err >= std::size(error_messages)))
+ return nullptr;
+ return error_messages[err];
+}
+
+
+LEGACY_FLOPPY_OPTIONS_START(default)
+LEGACY_FLOPPY_OPTIONS_END
diff --git a/src/lib/formats/flopimg_legacy.h b/src/lib/formats/flopimg_legacy.h
new file mode 100644
index 00000000000..02953e15bdb
--- /dev/null
+++ b/src/lib/formats/flopimg_legacy.h
@@ -0,0 +1,214 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/*********************************************************************
+
+ flopimg_legacy.h
+
+ Floppy disk image abstraction code (legacy implementation)
+
+*********************************************************************/
+
+#ifndef MAME_FORMATS_FLOPIMG_LEGACY_H
+#define MAME_FORMATS_FLOPIMG_LEGACY_H
+
+#pragma once
+
+#include "utilfwd.h"
+
+#include <memory>
+#include <string>
+
+#include <cstddef>
+#include <cstdint>
+
+
+/***************************************************************************
+
+ Constants
+
+***************************************************************************/
+
+#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
+
+
+/***************************************************************************
+
+ Type definitions
+
+***************************************************************************/
+
+enum floperr_t
+{
+ FLOPPY_ERROR_SUCCESS, /* no error */
+ FLOPPY_ERROR_INTERNAL, /* fatal internal error */
+ FLOPPY_ERROR_UNSUPPORTED, /* this operation is unsupported */
+ FLOPPY_ERROR_OUTOFMEMORY, /* ran out of memory */
+ FLOPPY_ERROR_SEEKERROR, /* attempted to seek to nonexistent location */
+ FLOPPY_ERROR_INVALIDIMAGE, /* this image in invalid */
+ FLOPPY_ERROR_READONLY, /* attempt to write to read-only image */
+ FLOPPY_ERROR_NOSPACE,
+ FLOPPY_ERROR_PARAMOUTOFRANGE,
+ FLOPPY_ERROR_PARAMNOTSPECIFIED
+};
+
+struct floppy_image_legacy;
+
+struct FloppyCallbacks
+{
+ floperr_t (*read_sector)(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen);
+ floperr_t (*write_sector)(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
+ floperr_t (*read_indexed_sector)(floppy_image_legacy *floppy, int head, int track, int sector_index, void *buffer, size_t buflen);
+ floperr_t (*write_indexed_sector)(floppy_image_legacy *floppy, int head, int track, int sector_index, const void *buffer, size_t buflen, int ddam);
+ floperr_t (*read_track)(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buflen);
+ floperr_t (*write_track)(floppy_image_legacy *floppy, int head, int track, uint64_t offset, const void *buffer, size_t buflen);
+ floperr_t (*format_track)(floppy_image_legacy *floppy, int head, int track, util::option_resolution *params);
+ floperr_t (*post_format)(floppy_image_legacy *floppy, util::option_resolution *params);
+ int (*get_heads_per_disk)(floppy_image_legacy *floppy);
+ int (*get_tracks_per_disk)(floppy_image_legacy *floppy);
+ int (*get_sectors_per_track)(floppy_image_legacy *floppy, int head, int track);
+ uint32_t (*get_track_size)(floppy_image_legacy *floppy, int head, int track);
+ floperr_t (*get_sector_length)(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length);
+ floperr_t (*get_indexed_sector_info)(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags);
+ floperr_t (*get_track_data_offset)(floppy_image_legacy *floppy, int head, int track, uint64_t *offset);
+};
+
+
+
+struct FloppyFormat
+{
+ const char *name;
+ const char *extensions;
+ const char *description;
+ floperr_t (*identify)(floppy_image_legacy *floppy, const struct FloppyFormat *format, int *vote);
+ floperr_t (*construct)(floppy_image_legacy *floppy, const struct FloppyFormat *format, util::option_resolution *params);
+ floperr_t (*destruct)(floppy_image_legacy *floppy, const struct FloppyFormat *format);
+ const char *param_guidelines;
+};
+
+#define FLOPPY_IDENTIFY(name) floperr_t name(floppy_image_legacy *floppy, const struct FloppyFormat *format, int *vote)
+#define FLOPPY_CONSTRUCT(name) floperr_t name(floppy_image_legacy *floppy, const struct FloppyFormat *format, util::option_resolution *params)
+#define FLOPPY_DESTRUCT(name) floperr_t name(floppy_image_legacy *floppy, const struct FloppyFormat *format)
+
+FLOPPY_IDENTIFY(td0_dsk_identify);
+FLOPPY_CONSTRUCT(td0_dsk_construct);
+FLOPPY_DESTRUCT(td0_dsk_destruct);
+
+FLOPPY_IDENTIFY(imd_dsk_identify);
+FLOPPY_CONSTRUCT(imd_dsk_construct);
+
+FLOPPY_IDENTIFY(cqm_dsk_identify);
+FLOPPY_CONSTRUCT(cqm_dsk_construct);
+
+FLOPPY_IDENTIFY(dsk_dsk_identify);
+FLOPPY_CONSTRUCT(dsk_dsk_construct);
+
+FLOPPY_IDENTIFY(d88_dsk_identify);
+FLOPPY_CONSTRUCT(d88_dsk_construct);
+
+FLOPPY_IDENTIFY(fdi_dsk_identify);
+FLOPPY_CONSTRUCT(fdi_dsk_construct);
+
+#define LEGACY_FLOPPY_OPTIONS_NAME(name) floppyoptions_##name
+
+#define LEGACY_FLOPPY_OPTIONS_START(name) \
+ const struct FloppyFormat floppyoptions_##name[] = \
+ {
+#define LEGACY_FLOPPY_OPTIONS_END0 \
+ { nullptr } \
+ };
+
+#define LEGACY_FLOPPY_OPTIONS_EXTERN(name) \
+ extern const struct FloppyFormat floppyoptions_##name[]
+#define LEGACY_FLOPPY_OPTION(name, extensions_, description_, identify_, construct_, destruct_, ranges_)\
+ { #name, extensions_, description_, identify_, construct_, destruct_, ranges_ },
+#define LEGACY_FLOPPY_OPTIONS_END \
+ LEGACY_FLOPPY_OPTION( fdi, "fdi", "Formatted Disk Image", fdi_dsk_identify, fdi_dsk_construct, nullptr, nullptr) \
+ LEGACY_FLOPPY_OPTION( td0, "td0", "Teledisk floppy disk image", td0_dsk_identify, td0_dsk_construct, td0_dsk_destruct, nullptr) \
+ LEGACY_FLOPPY_OPTION( imd, "imd", "IMD floppy disk image", imd_dsk_identify, imd_dsk_construct, nullptr, nullptr) \
+ LEGACY_FLOPPY_OPTION( cqm, "cqm,dsk", "CopyQM floppy disk image", cqm_dsk_identify, cqm_dsk_construct, nullptr, nullptr) \
+ LEGACY_FLOPPY_OPTION( dsk, "dsk", "DSK floppy disk image", dsk_dsk_identify, dsk_dsk_construct, nullptr, nullptr) \
+ LEGACY_FLOPPY_OPTION( d88, "d77,d88,1dd", "D88 Floppy Disk image", d88_dsk_identify, d88_dsk_construct, nullptr, nullptr) \
+ LEGACY_FLOPPY_OPTIONS_END0
+
+LEGACY_FLOPPY_OPTIONS_EXTERN(default);
+
+#define PARAM_END '\0'
+#define PARAM_HEADS 'H'
+#define PARAM_TRACKS 'T'
+#define PARAM_SECTORS 'S'
+#define PARAM_SECTOR_LENGTH 'L'
+#define PARAM_INTERLEAVE 'I'
+#define PARAM_FIRST_SECTOR_ID 'F'
+
+#define HEADS(range) "H" #range
+#define TRACKS(range) "T" #range
+#define SECTORS(range) "S" #range
+#define SECTOR_LENGTH(range) "L" #range
+#define INTERLEAVE(range) "I" #range
+#define FIRST_SECTOR_ID(range) "F" #range
+
+/***************************************************************************
+
+ Prototypes
+
+***************************************************************************/
+
+/* opening, closing and creating of floppy images */
+floperr_t floppy_open(std::unique_ptr<util::random_read_write> &&io, const std::string &extension, const struct FloppyFormat *format, int flags, floppy_image_legacy **outfloppy);
+floperr_t floppy_open_choices(std::unique_ptr<util::random_read_write> &&io, const std::string &extension, const struct FloppyFormat *formats, int flags, floppy_image_legacy **outfloppy);
+floperr_t floppy_create(std::unique_ptr<util::random_read_write> &&io, const struct FloppyFormat *format, util::option_resolution *parameters, floppy_image_legacy **outfloppy);
+void floppy_close(floppy_image_legacy *floppy);
+
+/* useful for identifying a floppy image */
+floperr_t floppy_identify(std::unique_ptr<util::random_read_write> &&io, const char *extension, const struct FloppyFormat *formats, int *identified_format);
+
+/* functions useful within format constructors */
+void *floppy_tag(floppy_image_legacy *floppy);
+void *floppy_create_tag(floppy_image_legacy *floppy, size_t tagsize);
+struct FloppyCallbacks *floppy_callbacks(floppy_image_legacy *floppy);
+uint8_t floppy_get_filler(floppy_image_legacy *floppy);
+util::random_read_write &floppy_get_io(floppy_image_legacy *floppy);
+
+/* calls for accessing disk image data */
+floperr_t floppy_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset, void *buffer, size_t buffer_len);
+floperr_t floppy_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, int offset, const void *buffer, size_t buffer_len, int ddam);
+floperr_t floppy_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector_index, int offset, void *buffer, size_t buffer_len);
+floperr_t floppy_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector_index, int offset, const void *buffer, size_t buffer_len, int ddam);
+floperr_t floppy_read_track(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t buffer_len);
+floperr_t floppy_write_track(floppy_image_legacy *floppy, int head, int track, const void *buffer, size_t buffer_len);
+floperr_t floppy_read_track_data(floppy_image_legacy *floppy, int head, int track, void *buffer, size_t buffer_len);
+floperr_t floppy_write_track_data(floppy_image_legacy *floppy, int head, int track, const void *buffer, size_t buffer_len);
+floperr_t floppy_format_track(floppy_image_legacy *floppy, int head, int track, util::option_resolution *params);
+int floppy_get_tracks_per_disk(floppy_image_legacy *floppy);
+int floppy_get_heads_per_disk(floppy_image_legacy *floppy);
+uint32_t floppy_get_track_size(floppy_image_legacy *floppy, int head, int track);
+floperr_t floppy_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length);
+floperr_t floppy_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags);
+floperr_t floppy_get_sector_count(floppy_image_legacy *floppy, int head, int track, int *sector_count);
+floperr_t floppy_load_track(floppy_image_legacy *floppy, int head, int track, int dirtify, void **track_data, size_t *track_length);
+int floppy_is_read_only(floppy_image_legacy *floppy);
+uint8_t floppy_random_byte(floppy_image_legacy *floppy);
+
+/* accessors for meta information about the image */
+const char *floppy_format_description(floppy_image_legacy *floppy);
+
+/* calls for accessing the raw disk image */
+void floppy_image_read(floppy_image_legacy *floppy, void *buffer, uint64_t offset, size_t length);
+void floppy_image_write(floppy_image_legacy *floppy, const void *buffer, uint64_t offset, size_t length);
+void floppy_image_write_filler(floppy_image_legacy *floppy, uint8_t filler, uint64_t offset, size_t length);
+uint64_t floppy_image_size(floppy_image_legacy *floppy);
+
+/* misc */
+const char *floppy_error(floperr_t err);
+
+const util::option_guide &floppy_option_guide();
+
+#endif // MAME_FORMATS_FLOPIMG_LEGACY_H
diff --git a/src/lib/formats/fm7_cas.cpp b/src/lib/formats/fm7_cas.cpp
index 32cd08e3b27..25953627361 100644
--- a/src/lib/formats/fm7_cas.cpp
+++ b/src/lib/formats/fm7_cas.cpp
@@ -4,18 +4,19 @@
* Fujitsu FM-7 series cassette handling
*/
-#include <assert.h>
-
#include "fm7_cas.h"
+#include "multibyte.h"
+
+#include <cstring>
+
#define WAVE_HIGH 0x5a9e
#define WAVE_LOW -0x5a9e
-static int cas_size;
+static int cas_size; // FIXME: global variable prevents multiple instances
-static int fm7_fill_wave(int16_t* buffer, uint8_t high, uint8_t low, int sample_pos)
+static int fm7_fill_wave(int16_t* buffer, uint16_t data, int sample_pos)
{
- uint16_t data = (high << 8) + low;
int sample_count = 0;
int x = 0;
int count = (data & 0x7fff);
@@ -51,7 +52,7 @@ static int fm7_handle_t77(int16_t* buffer, const uint8_t* casdata)
while(data_pos < cas_size)
{
- sample_count += fm7_fill_wave(buffer,casdata[data_pos],casdata[data_pos+1],sample_count);
+ sample_count += fm7_fill_wave(buffer,get_u16be(&casdata[data_pos]),sample_count);
data_pos+=2;
}
@@ -71,12 +72,12 @@ static int fm7_cas_to_wav_size (const uint8_t *casdata, int caslen)
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int fm7_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int fm7_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return fm7_handle_t77(buffer,bytes);
}
-static const struct CassetteLegacyWaveFiller fm7_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller fm7_legacy_fill_wave =
{
fm7_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -87,20 +88,20 @@ static const struct CassetteLegacyWaveFiller fm7_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error fm7_cas_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error fm7_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &fm7_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &fm7_legacy_fill_wave);
}
static cassette_image::error fm7_cas_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &fm7_legacy_fill_wave);
+ return cassette->legacy_construct(&fm7_legacy_fill_wave);
}
-static const struct CassetteFormat fm7_cassette_format = {
+static const cassette_image::Format fm7_cassette_format = {
"t77",
fm7_cas_identify,
fm7_cas_load,
diff --git a/src/lib/formats/fmsx_cas.cpp b/src/lib/formats/fmsx_cas.cpp
index 6da8fc47f81..c7969b28eca 100644
--- a/src/lib/formats/fmsx_cas.cpp
+++ b/src/lib/formats/fmsx_cas.cpp
@@ -1,8 +1,9 @@
// license:BSD-3-Clause
// copyright-holders:Sean Young
-#include <assert.h>
-#include "formats/fmsx_cas.h"
+#include "fmsx_cas.h"
+
+#include <cstring>
#define CAS_PERIOD (16)
@@ -48,7 +49,7 @@ static int fmsx_cas_to_wav_size (const uint8_t *casdata, int caslen)
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int fmsx_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int fmsx_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
int cas_pos, bit, state = 1, samples_pos, size, n, i, p;
@@ -104,7 +105,7 @@ static int fmsx_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
}
-static const struct CassetteLegacyWaveFiller fmsx_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller fmsx_legacy_fill_wave =
{
fmsx_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -117,21 +118,21 @@ static const struct CassetteLegacyWaveFiller fmsx_legacy_fill_wave =
-static cassette_image::error fmsx_cas_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error fmsx_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &fmsx_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &fmsx_legacy_fill_wave);
}
static cassette_image::error fmsx_cas_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &fmsx_legacy_fill_wave);
+ return cassette->legacy_construct(&fmsx_legacy_fill_wave);
}
-static const struct CassetteFormat fmsx_cas_format =
+static const cassette_image::Format fmsx_cas_format =
{
"tap,cas",
fmsx_cas_identify,
diff --git a/src/lib/formats/fmtowns_dsk.cpp b/src/lib/formats/fmtowns_dsk.cpp
index 633b53fcd87..633286ed33a 100644
--- a/src/lib/formats/fmtowns_dsk.cpp
+++ b/src/lib/formats/fmtowns_dsk.cpp
@@ -1,32 +1,30 @@
// license:BSD-3-Clause
// copyright-holders:Barry Rodewald
/*
- * fmtowns_dsk.c
+ * fmtowns_dsk.cpp
*
* FM Towns floppy image format
*
* Created on: 23/03/2014
*/
- #include <assert.h>
-
#include "formats/fmtowns_dsk.h"
fmtowns_format::fmtowns_format() : wd177x_format(formats)
{
}
-const char *fmtowns_format::name() const
+const char *fmtowns_format::name() const noexcept
{
return "fmtowns";
}
-const char *fmtowns_format::description() const
+const char *fmtowns_format::description() const noexcept
{
return "FM Towns disk image";
}
-const char *fmtowns_format::extensions() const
+const char *fmtowns_format::extensions() const noexcept
{
return "bin";
}
@@ -40,4 +38,4 @@ const fmtowns_format::format fmtowns_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_FMTOWNS_FORMAT = &floppy_image_format_creator<fmtowns_format>;
+const fmtowns_format FLOPPY_FMTOWNS_FORMAT;
diff --git a/src/lib/formats/fmtowns_dsk.h b/src/lib/formats/fmtowns_dsk.h
index 9fc136bb078..98f3cd41881 100644
--- a/src/lib/formats/fmtowns_dsk.h
+++ b/src/lib/formats/fmtowns_dsk.h
@@ -17,14 +17,14 @@ class fmtowns_format : public wd177x_format
public:
fmtowns_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_FMTOWNS_FORMAT;
+extern const fmtowns_format FLOPPY_FMTOWNS_FORMAT;
#endif // MAME_FORMATS_FMTOWNS_DSK_H
diff --git a/src/lib/formats/fs_cbmdos.cpp b/src/lib/formats/fs_cbmdos.cpp
new file mode 100644
index 00000000000..44c810a04a6
--- /dev/null
+++ b/src/lib/formats/fs_cbmdos.cpp
@@ -0,0 +1,821 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods, Wilbert Pol
+/***************************************************************************
+
+ fs_cbmdos.cpp
+
+ Management of CBM (Commodore) DOS disk images
+
+ http://fileformats.archiveteam.org/wiki/CBMFS
+
+Current limitations:
+- Writing is limited to the first 35 tracks.
+- Determine or select the file type. Currently defaulting to PRG; 0 byte files
+ will be assigned file type DEL.
+
+***************************************************************************/
+
+#include "fs_cbmdos.h"
+
+#include "d64_dsk.h"
+#include "fsblk.h"
+
+#include "corestr.h"
+#include "multibyte.h"
+#include "strformat.h"
+
+#include <array>
+#include <optional>
+#include <regex>
+#include <set>
+#include <string_view>
+#include <tuple>
+
+
+namespace fs {
+ const cbmdos_image CBMDOS;
+};
+
+using namespace fs;
+
+namespace {
+
+class impl : public filesystem_t {
+public:
+ static constexpr u8 SECTOR_DIRECTORY_COUNT = 8;
+
+ struct cbmdos_dirent
+ {
+ u8 m_next_directory_track;
+ u8 m_next_directory_sector;
+ u8 m_file_type;
+ u8 m_file_first_track;
+ u8 m_file_first_sector;
+ char m_file_name[16];
+ u8 m_first_side_sector_block_track;
+ u8 m_first_side_sector_block_sector;
+ u8 m_rel_file_record_length;
+ u8 m_unused[6];
+ u8 m_sector_count_low;
+ u8 m_sector_count_high;
+ };
+
+ class block_iterator
+ {
+ public:
+ block_iterator(const impl &fs, u8 first_track, u8 first_sector);
+ bool next();
+ const void *data() const;
+ const std::array<cbmdos_dirent, SECTOR_DIRECTORY_COUNT> &dirent_data() const;
+ u8 size() const;
+ u8 track() const { return m_track; }
+ u8 sector() const { return m_sector; }
+
+ private:
+ const impl & m_fs;
+ fsblk_t::block_t::ptr m_block;
+ std::set<std::tuple<u8, u8>> m_visited_set;
+ u8 m_track;
+ u8 m_sector;
+ u8 m_next_track;
+ u8 m_next_sector;
+ };
+
+ impl(fsblk_t &blockdev);
+ virtual ~impl() = default;
+
+ virtual meta_data volume_metadata() override;
+ virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override;
+ virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+ virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+ virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override;
+ virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
+
+private:
+ static constexpr u32 BLOCK_SIZE = 256;
+ static constexpr u8 DIRECTORY_ENTRY_SIZE = 0x20;
+ static constexpr u8 FILE_TYPE_DEL = 0x80;
+ static constexpr u8 FILE_TYPE_SEQ = 0x81;
+ static constexpr u8 FILE_TYPE_PRG = 0x82;
+ static constexpr u8 FILE_TYPE_USR = 0x83;
+ static constexpr u8 FILE_TYPE_REL = 0x84;
+ static constexpr u8 TRACK_VARIANTS = 5;
+ static constexpr u8 MAX_SECTORS = 21;
+ static constexpr u8 MAX_TRACKS = 40;
+ static constexpr u8 DIRECTORY_TRACK = 18;
+ static constexpr u8 BAM_SECTOR = 0;
+ static constexpr u8 FIRST_DIRECTORY_SECTOR = 1;
+ static constexpr u8 SECTOR_DATA_BYTES = 254;
+ static constexpr u8 OFFSET_FILE_TYPE = 0x02;
+ static constexpr u8 OFFSET_FILE_FIRST_TRACK = 0x03;
+ static constexpr u8 OFFSET_FILE_FIRST_SECTOR = 0x04;
+ static constexpr u8 OFFSET_FILE_NAME = 0x05;
+ static constexpr u8 OFFSET_SECTOR_COUNT = 0x1e;
+ static constexpr u8 OFFSET_CHAIN_TRACK = 0x00;
+ static constexpr u8 OFFSET_CHAIN_SECTOR = 0x01;
+ static constexpr u8 CHAIN_END = 0x00;
+
+ static const struct smap
+ {
+ u8 first_track;
+ u8 last_track;
+ int sector[MAX_SECTORS];
+ } s_track_sector_map[TRACK_VARIANTS];
+ static const u8 s_data_track_order[MAX_TRACKS - 1];
+ u8 m_max_track;
+
+ fsblk_t::block_t::ptr read_sector(int track, int sector) const;
+ std::optional<cbmdos_dirent> dirent_from_path(const std::vector<std::string> &path) const;
+ void iterate_directory_entries(const std::function<bool(u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent)> &callback) const;
+ void iterate_all_directory_entries(const std::function<bool(u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent)> &callback) const;
+ meta_data metadata_from_dirent(const cbmdos_dirent &dirent) const;
+ bool is_valid_filename(const std::string &filename) const;
+ std::pair<std::error_condition, u8> claim_track_sector(u8 track) const;
+ std::tuple<std::error_condition, u8, u8> claim_sector() const;
+ std::error_condition free_sector(u8 track, u8 sector) const;
+ u8 determine_file_type(const std::vector<u8> &data) const;
+};
+
+// methods
+std::string_view strtrimright_cbm(std::string_view str);
+template<size_t N> std::string_view strtrimright_cbm(const char (&str)[N]);
+
+} // anonymous namespace
+
+
+//-------------------------------------------------
+// name
+//-------------------------------------------------
+
+const char *fs::cbmdos_image::name() const
+{
+ return "cbmdos";
+}
+
+
+//-------------------------------------------------
+// description
+//-------------------------------------------------
+
+const char *fs::cbmdos_image::description() const
+{
+ return "CBMDOS";
+}
+
+
+//-------------------------------------------------
+// enumerate_f
+//-------------------------------------------------
+
+void fs::cbmdos_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_D64_FORMAT, floppy_image::FF_525, floppy_image::SSSD, 174848, "d64_cbmdos_35", "D64 CBMDOS single-sided 35 tracks");
+ fe.add(FLOPPY_D64_FORMAT, floppy_image::FF_525, floppy_image::SSSD, 192256, "d64_cbmdos_40", "D64 CBMDOS single-sided 40 tracks");
+}
+
+
+//-------------------------------------------------
+// can_format
+//-------------------------------------------------
+
+bool fs::cbmdos_image::can_format() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// can_read
+//-------------------------------------------------
+
+bool fs::cbmdos_image::can_read() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// can_write
+//-------------------------------------------------
+
+bool fs::cbmdos_image::can_write() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// has_rsrc
+//-------------------------------------------------
+
+bool fs::cbmdos_image::has_rsrc() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// volume_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> fs::cbmdos_image::volume_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 16; }, "Volume name, up to 16 characters");
+ return results;
+}
+
+
+//-------------------------------------------------
+// file_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> fs::cbmdos_image::file_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_name::name, "", false, [](const meta_value &m) { return m.as_string().size() <= 16; }, "File name, up to 16 characters");
+ results.emplace_back(meta_name::file_type, "", true, nullptr, "Type of the file");
+ results.emplace_back(meta_name::length, 0, true, nullptr, "Size of the file in bytes");
+ return results;
+}
+
+
+//-------------------------------------------------
+// mount
+//-------------------------------------------------
+
+std::unique_ptr<filesystem_t> fs::cbmdos_image::mount(fsblk_t &blockdev) const
+{
+ return std::make_unique<impl>(blockdev);
+}
+
+
+//-------------------------------------------------
+// strtrimright_cbm
+//-------------------------------------------------
+
+namespace {
+
+std::string_view strtrimright_cbm(std::string_view str)
+{
+ return strtrimright(str, [](char c) { return c != (char)0xA0; });
+}
+
+
+//-------------------------------------------------
+// strtrimright_cbm
+//-------------------------------------------------
+
+template<size_t N>
+std::string_view strtrimright_cbm(const char (&str)[N])
+{
+ std::string_view sv(str, std::size(str));
+ return strtrimright_cbm(sv);
+}
+
+
+//-------------------------------------------------
+// impl ctor
+//-------------------------------------------------
+
+const u8 impl::s_data_track_order[MAX_TRACKS - 1] = {
+ 17, 19, 16, 20, 15, 21, 14, 22, 13, 23, 12, 24, 11, 25, 10, 26, 9, 27, 8, 28, 7, 29, 6, 30, 5, 31, 4, 32, 3, 33, 2, 34, 1, 35, 36, 37, 38, 39, 40
+};
+
+const impl::smap impl::s_track_sector_map[TRACK_VARIANTS] = {
+ { 1, 17, { 0, 11, 1, 12, 2, 13, 3, 14, 4, 15, 5, 16, 6, 17, 7, 18, 8, 19, 9, 20, 10 } },
+ { 18, 18, { 0, 1, 4, 7, 10, 13, 16, 2, 5, 8, 11, 14, 17, 3, 6, 9, 12, 15, 18, -1, -1 } },
+ { 19, 24, { 0, 10, 1, 11, 2, 12, 3, 13, 4, 14, 5, 15, 6, 16, 7, 17, 8, 18, 9, -1, -1 } },
+ { 25, 30, { 0, 9, 1, 10, 2, 11, 3, 12, 4, 13, 5, 14, 6, 15, 7, 16, 8, 17, -1, -1, -1 } },
+ { 31, 40, { 0, 9, 1, 10, 2, 11, 3, 12, 4, 13, 5, 14, 6, 15, 7, 16, 8, -1, -1, -1, -1 } }
+};
+
+
+impl::impl(fsblk_t &blockdev)
+ : filesystem_t(blockdev, BLOCK_SIZE)
+ , m_max_track(35)
+{
+}
+
+
+//-------------------------------------------------
+// impl::volume_metadata
+//-------------------------------------------------
+
+meta_data impl::volume_metadata()
+{
+ auto bam_block = read_sector(DIRECTORY_TRACK, BAM_SECTOR);
+ std::string_view disk_name = bam_block->rstr(0x90, 16);
+
+ meta_data results;
+ results.set(meta_name::name, strtrimright_cbm(disk_name));
+ return results;
+}
+
+
+//-------------------------------------------------
+// impl::metadata
+//-------------------------------------------------
+
+std::pair<std::error_condition, meta_data> impl::metadata(const std::vector<std::string> &path)
+{
+ std::optional<cbmdos_dirent> dirent = dirent_from_path(path);
+ if (!dirent)
+ return std::make_pair(error::not_found, meta_data());
+
+ return std::make_pair(std::error_condition(), metadata_from_dirent(*dirent));
+}
+
+
+//-------------------------------------------------
+// impl::directory_contents
+//-------------------------------------------------
+
+std::pair<std::error_condition, std::vector<dir_entry>> impl::directory_contents(const std::vector<std::string> &path)
+{
+ std::vector<dir_entry> results;
+ auto callback = [this, &results](u8 track, u8 sector, u8 file_index, const cbmdos_dirent &ent)
+ {
+ results.emplace_back(dir_entry_type::file, metadata_from_dirent(ent));
+ return false;
+ };
+ iterate_directory_entries(callback);
+ return std::make_pair(std::error_condition(), std::move(results));
+}
+
+
+//-------------------------------------------------
+// impl::file_read
+//-------------------------------------------------
+
+std::pair<std::error_condition, std::vector<u8>> impl::file_read(const std::vector<std::string> &path)
+{
+ // find the file
+ std::optional<cbmdos_dirent> dirent = dirent_from_path(path);
+ if (!dirent)
+ return std::make_pair(error::not_found, std::vector<u8>());
+
+ // and get the data
+ std::vector<u8> result;
+ block_iterator iter(*this, dirent->m_file_first_track, dirent->m_file_first_sector);
+ while (iter.next())
+ result.insert(result.end(), (const u8 *)iter.data(), (const u8 *)iter.data() + iter.size());
+
+ return std::make_pair(std::error_condition(), std::move(result));
+}
+
+
+std::error_condition impl::file_create(const std::vector<std::string> &path, const meta_data &meta)
+{
+ std::string filename = meta.get_string(meta_name::name, "");
+ if (!is_valid_filename(filename))
+ return error::invalid_name;
+
+ std::optional<cbmdos_dirent> result;
+ u8 track = 0;
+ u8 sector = 0;
+ u8 file_index = 0;
+ auto callback = [&result, &track, &sector, &file_index](u8 t, u8 s, u8 i, const cbmdos_dirent &dirent)
+ {
+ bool found = dirent.m_file_type == 0x00;
+ if (found)
+ {
+ result = dirent;
+ track = t;
+ sector = s;
+ file_index = i;
+ }
+ return found;
+ };
+ iterate_all_directory_entries(callback);
+
+ if (!result)
+ {
+ // Claim a next directory sector
+ auto const [err, new_sector] = claim_track_sector(DIRECTORY_TRACK);
+ if (err)
+ return err;
+ auto new_block = read_sector(DIRECTORY_TRACK, new_sector);
+ for (int i = 2; i < BLOCK_SIZE; i++)
+ new_block->w8(i, 0);
+ // Find last directory sector
+ u8 last_sector = 0;
+ block_iterator iter(*this, DIRECTORY_TRACK, FIRST_DIRECTORY_SECTOR);
+ while (iter.next())
+ last_sector = iter.sector();
+ // Update chain on last directory sector
+ auto last_dir_block = read_sector(DIRECTORY_TRACK, last_sector);
+ last_dir_block->w8(OFFSET_CHAIN_TRACK, DIRECTORY_TRACK);
+ last_dir_block->w8(OFFSET_CHAIN_SECTOR, new_sector);
+ track = DIRECTORY_TRACK;
+ sector = new_sector;
+ }
+
+ auto const [err, file_track, file_sector] = claim_sector();
+ if (err)
+ return err;
+
+ // Create the file
+ auto dirblk = read_sector(track, sector);
+ u32 offset = file_index * DIRECTORY_ENTRY_SIZE;
+ for (int i = 0; i < DIRECTORY_ENTRY_SIZE; i++)
+ dirblk->w8(offset + i, (i >= 5 && i < 5 + 16) ? 0xa0 : 0x00);
+ dirblk->w8(offset + OFFSET_FILE_TYPE, FILE_TYPE_PRG);
+ dirblk->w8(offset + OFFSET_FILE_FIRST_TRACK, file_track);
+ dirblk->w8(offset + OFFSET_FILE_FIRST_SECTOR, file_sector);
+ dirblk->wstr(offset + OFFSET_FILE_NAME, filename);
+ // TODO set first side sector block track (rel file)
+ // TODO set first side sector block sector (rel file)
+ // TODO set rel file record length
+ // sector count will be set while writing the data
+
+ return std::error_condition();
+}
+
+
+std::error_condition impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
+{
+ if (path.size() != 1)
+ return error::not_found;
+ std::string_view path_part = path[0];
+
+ std::optional<cbmdos_dirent> result;
+ u8 dir_track = 0;
+ u8 dir_sector = 0;
+ u8 dir_file_index = 0;
+ auto const callback = [&result, &dir_track, &dir_sector, &dir_file_index, path_part] (u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent)
+ {
+ bool found = strtrimright_cbm(dirent.m_file_name) == path_part;
+ if (found)
+ {
+ dir_track = track;
+ dir_sector = sector;
+ dir_file_index = file_index;
+ result = dirent;
+ }
+ return found;
+ };
+ iterate_directory_entries(callback);
+
+ if (!result)
+ return error::not_found;
+
+ u8 data_track = result->m_file_first_track;
+ u8 data_sector = result->m_file_first_sector;
+
+ const size_t data_length = data.size();
+ size_t offset = 0;
+ u32 sector_count = 0;
+ while (offset < data_length)
+ {
+ auto datablk = read_sector(data_track, data_sector);
+ u8 bytes = (data_length - offset) > SECTOR_DATA_BYTES ? SECTOR_DATA_BYTES : data_length - offset;
+ datablk->write(2, data.data() + offset, bytes);
+ offset += SECTOR_DATA_BYTES;
+ sector_count++;
+ if (datablk->r8(OFFSET_CHAIN_TRACK) == CHAIN_END)
+ {
+ if (offset < data_length)
+ {
+ auto [err, next_track, next_sector] = claim_sector();
+ if (err)
+ return err;
+ datablk->w8(OFFSET_CHAIN_TRACK, next_track);
+ datablk->w8(OFFSET_CHAIN_SECTOR, next_sector);
+ data_track = next_track;
+ data_sector = next_sector;
+ }
+ }
+ else
+ {
+ if (offset < data_length)
+ {
+ data_track = datablk->r8(OFFSET_CHAIN_TRACK);
+ data_sector = datablk->r8(OFFSET_CHAIN_SECTOR);
+ }
+ else
+ {
+ // Free the rest of the chain
+ u8 track_to_free = datablk->r8(OFFSET_CHAIN_TRACK);
+ u8 sector_to_free = datablk->r8(OFFSET_CHAIN_SECTOR);
+
+ while (track_to_free != CHAIN_END)
+ {
+ std::error_condition const err = free_sector(track_to_free, sector_to_free);
+ if (err)
+ return err;
+ datablk = read_sector(track_to_free, sector_to_free);
+ track_to_free = datablk->r8(OFFSET_CHAIN_TRACK);
+ sector_to_free = datablk->r8(OFFSET_CHAIN_SECTOR);
+ }
+ }
+ }
+ if (offset >= data_length)
+ {
+ datablk->w8(OFFSET_CHAIN_TRACK, CHAIN_END);
+ datablk->w8(OFFSET_CHAIN_SECTOR, bytes + 1);
+ }
+ }
+
+ // Write sector count and file type to directory entry
+ auto dirblk = read_sector(dir_track, dir_sector);
+ u8 file_type = determine_file_type(data);
+ if (file_type == FILE_TYPE_DEL)
+ {
+ // Free sector, update first file sector to 00
+ u8 file_track = dirblk->r8(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_FILE_FIRST_TRACK);
+ u8 file_sector = dirblk->r8(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_FILE_FIRST_SECTOR);
+ std::error_condition err = free_sector(file_track, file_sector);
+ if (err)
+ return err;
+ dirblk->w8(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_FILE_FIRST_TRACK, 0);
+ dirblk->w8(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_FILE_FIRST_SECTOR, 0);
+ sector_count = 0;
+ }
+ dirblk->w8(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_FILE_TYPE, file_type);
+ dirblk->w16l(DIRECTORY_ENTRY_SIZE * dir_file_index + OFFSET_SECTOR_COUNT, sector_count);
+
+ return std::error_condition();
+}
+
+
+u8 impl::determine_file_type(const std::vector<u8> &data) const
+{
+ // TODO: Determine/set the actual file type, defaulting to PRG for now.
+ const size_t data_length = data.size();
+ if (data_length == 0)
+ return FILE_TYPE_DEL;
+ return FILE_TYPE_PRG;
+}
+
+
+bool impl::is_valid_filename(const std::string &filename) const
+{
+ // We only support a subset of the character set supported by Commodore for filenames.
+ std::regex filename_regex("[A-Z0-9!\"#\\$%&'\\(\\)*+,-./:;<=>?]{1,16}");
+ return std::regex_match(filename, filename_regex);
+}
+
+
+std::pair<std::error_condition, u8> impl::claim_track_sector(u8 track) const
+{
+ if (track == 0 || track > m_max_track)
+ return std::make_pair(error::invalid_block, 0);
+
+ u8 map_index;
+ for (map_index = 0; map_index < TRACK_VARIANTS && !(s_track_sector_map[map_index].first_track <= track && track <= s_track_sector_map[map_index].last_track) ; map_index++);
+ if (map_index >= TRACK_VARIANTS)
+ return std::make_pair(error::invalid_block, 0);
+
+ auto bamblk = read_sector(DIRECTORY_TRACK, BAM_SECTOR);
+ u8 free_count = bamblk->r8(4 * track);
+ u32 free_bitmap = bamblk->r24l(4 * track + 1);
+
+ for (int s = 0; s < MAX_SECTORS; s++)
+ {
+ int sector = s_track_sector_map[map_index].sector[s];
+ if (sector >= 0 && free_count > 0 && util::BIT(free_bitmap, sector))
+ {
+ free_bitmap &= ~(1 << sector);
+ free_count--;
+ bamblk->w8(4 * track, free_count);
+ bamblk->w24l(4 * track + 1, free_bitmap);
+ // Write chain end marker in new sector
+ auto claimedlk = read_sector(track, sector);
+ claimedlk->w8(OFFSET_CHAIN_TRACK, CHAIN_END);
+ claimedlk->w8(OFFSET_CHAIN_SECTOR, 0xff);
+ return std::make_pair(std::error_condition(), sector);
+ }
+ }
+ return std::make_pair(error::no_space, 0);
+}
+
+
+std::tuple<std::error_condition, u8, u8> impl::claim_sector() const
+{
+ for (int track = 0; track < m_max_track - 1; track++)
+ {
+ auto const [err, sector] = claim_track_sector(s_data_track_order[track]);
+ if (!err)
+ return std::make_tuple(std::error_condition(), s_data_track_order[track], sector);
+ if (err != error::no_space)
+ return std::make_tuple(err, 0, 0);
+ }
+ return std::make_tuple(error::no_space, 0, 0);
+}
+
+
+std::error_condition impl::free_sector(u8 track, u8 sector) const
+{
+ if (track == 0 || track > m_max_track)
+ return error::invalid_block;
+
+ auto bamblk = read_sector(DIRECTORY_TRACK, BAM_SECTOR);
+ u8 free_count = bamblk->r8(4 * track);
+ u32 free_bitmap = bamblk->r24l(4 * track + 1);
+ free_bitmap |= (1 << sector);
+ free_count++;
+ bamblk->w8(4 * track, free_count);
+ bamblk->w24l(4 * track + 1, free_bitmap);
+ return std::error_condition();
+}
+
+
+//-------------------------------------------------
+// impl::read_sector
+//-------------------------------------------------
+
+fsblk_t::block_t::ptr impl::read_sector(int track, int sector) const
+{
+ // CBM thinks in terms of tracks/sectors, but we have a block device abstraction
+ u32 block = 0;
+ block += std::max(std::min(track, 18) - 1, 0) * 21;
+ block += std::max(std::min(track, 25) - 18, 0) * 19;
+ block += std::max(std::min(track, 31) - 25, 0) * 18;
+ block += std::max(std::min(track, 41) - 31, 0) * 17;
+ block += sector;
+ return m_blockdev.get(block);
+}
+
+
+//-------------------------------------------------
+// impl::dirent_from_path
+//-------------------------------------------------
+
+std::optional<impl::cbmdos_dirent> impl::dirent_from_path(const std::vector<std::string> &path) const
+{
+ if (path.size() != 1)
+ return std::nullopt;
+ std::string_view path_part = path[0];
+
+ std::optional<cbmdos_dirent> result;
+ auto const callback = [&result, path_part] (u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent)
+ {
+ bool const found = strtrimright_cbm(dirent.m_file_name) == path_part;
+ if (found)
+ result = dirent;
+ return found;
+ };
+ iterate_directory_entries(callback);
+ return result;
+}
+
+
+//-------------------------------------------------
+// impl::iterate_directory_entries
+//-------------------------------------------------
+
+void impl::iterate_directory_entries(const std::function<bool(u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent)> &callback) const
+{
+ block_iterator iter(*this, DIRECTORY_TRACK, FIRST_DIRECTORY_SECTOR);
+ while (iter.next())
+ {
+ auto entries = iter.dirent_data();
+
+ for (int file_index = 0; file_index < SECTOR_DIRECTORY_COUNT; file_index++)
+ {
+ if (entries[file_index].m_file_type != 0x00)
+ {
+ if (callback(iter.track(), iter.sector(), file_index, entries[file_index]))
+ return;
+ }
+ }
+ }
+}
+
+void impl::iterate_all_directory_entries(const std::function<bool(u8 track, u8 sector, u8 file_index, const cbmdos_dirent &dirent)> &callback) const
+{
+ block_iterator iter(*this, DIRECTORY_TRACK, FIRST_DIRECTORY_SECTOR);
+ while (iter.next())
+ {
+ auto entries = iter.dirent_data();
+
+ for (int file_index = 0; file_index < SECTOR_DIRECTORY_COUNT; file_index++)
+ {
+ if (callback(iter.track(), iter.sector(), file_index, entries[file_index]))
+ return;
+ }
+ }
+}
+
+//-------------------------------------------------
+// impl::metadata_from_dirent
+//-------------------------------------------------
+
+meta_data impl::metadata_from_dirent(const cbmdos_dirent &dirent) const
+{
+ std::string file_type;
+ switch (dirent.m_file_type)
+ {
+ case FILE_TYPE_DEL:
+ file_type = "DEL";
+ break;
+ case FILE_TYPE_SEQ:
+ file_type = "SEQ";
+ break;
+ case FILE_TYPE_PRG:
+ file_type = "PRG";
+ break;
+ case FILE_TYPE_USR:
+ file_type = "USR";
+ break;
+ case FILE_TYPE_REL:
+ file_type = "REL";
+ break;
+ default:
+ file_type = util::string_format("$0x02X", (int)dirent.m_file_type);
+ break;
+ }
+
+ // compute the file size
+ u32 file_size = 0;
+ block_iterator iter(*this, dirent.m_file_first_track, dirent.m_file_first_sector);
+ while (iter.next())
+ file_size += iter.size();
+
+ // build the metadata and return it
+ meta_data result;
+ result.set(meta_name::name, strtrimright_cbm(dirent.m_file_name));
+ result.set(meta_name::file_type, std::move(file_type));
+ result.set(meta_name::length, file_size);
+ return result;
+}
+
+
+//-------------------------------------------------
+// impl::block_iterator ctor
+//-------------------------------------------------
+
+impl::block_iterator::block_iterator(const impl &fs, u8 first_track, u8 first_sector)
+ : m_fs(fs)
+ , m_track(first_track)
+ , m_sector(first_sector)
+ , m_next_track(first_track)
+ , m_next_sector(first_sector)
+{
+}
+
+
+//-------------------------------------------------
+// impl::block_iterator::next
+//-------------------------------------------------
+
+bool impl::block_iterator::next()
+{
+ bool result;
+ m_track = m_next_track;
+ m_sector = m_next_sector;
+ if (m_track != CHAIN_END)
+ {
+ // check for the degenerate scenario where we have a cycle (this should not happen
+ // on a non-corrupt disk)
+ auto visited_tuple = std::make_tuple(m_next_track, m_next_sector);
+ if (m_visited_set.find(visited_tuple) != m_visited_set.end())
+ return false;
+ m_visited_set.insert(visited_tuple);
+
+ // with that out of the way, proceed
+ m_block = m_fs.read_sector(m_next_track, m_next_sector);
+ m_next_track = m_block->r8(OFFSET_CHAIN_TRACK);
+ m_next_sector = m_block->r8(OFFSET_CHAIN_SECTOR);
+ result = true;
+ }
+ else
+ {
+ // the iterator has already completed
+ result = false;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// impl::block_iterator::data
+//-------------------------------------------------
+
+const void *impl::block_iterator::data() const
+{
+ return m_block->rodata() + 2;
+}
+
+
+//-------------------------------------------------
+// impl::block_iterator::dirent_data
+//-------------------------------------------------
+
+const std::array<impl::cbmdos_dirent, impl::SECTOR_DIRECTORY_COUNT> &impl::block_iterator::dirent_data() const
+{
+ return *reinterpret_cast<const std::array<impl::cbmdos_dirent, SECTOR_DIRECTORY_COUNT> *>(m_block->rodata());
+}
+
+
+//-------------------------------------------------
+// impl::block_iterator::size
+//-------------------------------------------------
+
+u8 impl::block_iterator::size() const
+{
+ return (m_track != CHAIN_END) ? SECTOR_DATA_BYTES : (m_sector - 1);
+}
+
+} // anonymous namespace
diff --git a/src/lib/formats/fs_cbmdos.h b/src/lib/formats/fs_cbmdos.h
new file mode 100644
index 00000000000..f98d3ed0f41
--- /dev/null
+++ b/src/lib/formats/fs_cbmdos.h
@@ -0,0 +1,44 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ fs_cbmdos.h
+
+ Management of CBM (Commodore) DOS disk images
+
+***************************************************************************/
+
+#ifndef MAME_FORMATS_FS_CBMDOS_H
+#define MAME_FORMATS_FS_CBMDOS_H
+
+#pragma once
+
+#include "fsmgr.h"
+
+namespace fs {
+
+ class cbmdos_image : public manager_t {
+ public:
+ cbmdos_image() = default;
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+
+ virtual std::vector<meta_description> volume_meta_description() const override;
+ virtual std::vector<meta_description> file_meta_description() const override;
+ };
+
+
+ extern const cbmdos_image CBMDOS;
+
+} // namespace fs
+
+#endif // #define MAME_FORMATS_FS_CBMDOS_H
diff --git a/src/lib/formats/fs_coco_os9.cpp b/src/lib/formats/fs_coco_os9.cpp
new file mode 100644
index 00000000000..8e9d9e8c8d0
--- /dev/null
+++ b/src/lib/formats/fs_coco_os9.cpp
@@ -0,0 +1,750 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ fs_coco_os9.cpp
+
+ Management of CoCo OS-9 floppy images
+
+ OS-9 Level 2 Technical Reference, Chapter 5, Random Block File Manager,
+ page 2
+
+ https://colorcomputerarchive.com/repo/Documents/Manuals/Operating%20Systems/OS-9%20Level%202%20Manual%20(Tandy).pdf
+
+***************************************************************************/
+
+#include "fs_coco_os9.h"
+#include "coco_rawdsk.h"
+#include "fsblk.h"
+
+#include "multibyte.h"
+#include "strformat.h"
+
+#include <optional>
+
+
+using namespace fs;
+
+namespace fs { const coco_os9_image COCO_OS9; }
+
+namespace {
+
+// ======================> coco_os9_impl
+
+class coco_os9_impl : public filesystem_t
+{
+public:
+ // ======================> volume_header
+
+ class volume_header
+ {
+ public:
+ volume_header(fsblk_t::block_t::ptr &&block);
+ volume_header(const volume_header &) = delete;
+ volume_header(volume_header &&) = default;
+
+ u32 total_sectors() const { return m_block->r24b(0); }
+ u8 track_size_in_sectors() const { return m_block->r8(3); }
+ u16 allocation_bitmap_bytes() const { return m_block->r16b(4); }
+ u16 cluster_size() const { return m_block->r16b(6); }
+ u32 root_dir_lsn() const { return m_block->r24b(8); }
+ u16 owner_id() const { return m_block->r16b(11); }
+ u16 disk_id() const { return m_block->r16b(14); }
+ u8 format_flags() const { return m_block->r8(16); }
+ u16 sectors_per_track() const { return m_block->r16b(17); }
+ u32 bootstrap_lsn() const { return m_block->r24b(21); }
+ u16 bootstrap_size() const { return m_block->r16b(24); }
+ util::arbitrary_datetime creation_date() const { return from_os9_date(m_block->r24b(26), m_block->r16b(29)); }
+ u16 sector_size() const { u16 result = m_block->r16b(104); return result != 0 ? result : 256; }
+ u8 sides() const { return (format_flags() & 0x01) ? 2 : 1; }
+ bool double_density() const { return (format_flags() & 0x02) != 0; }
+ bool double_track() const { return (format_flags() & 0x04) != 0; }
+ bool quad_track_density() const { return (format_flags() & 0x08) != 0; }
+ bool octal_track_density() const { return (format_flags() & 0x10) != 0; }
+
+ std::string name() const;
+
+ private:
+ fsblk_t::block_t::ptr m_block;
+ };
+
+
+ // ======================> file_header
+
+ class file_header
+ {
+ public:
+ file_header(fsblk_t::block_t::ptr &&block, std::string &&filename);
+ file_header(const file_header &) = delete;
+ file_header(file_header &&) = default;
+
+ file_header &operator=(const file_header &) = delete;
+ file_header &operator=(file_header &&) = default;
+
+ u8 attributes() const { return m_block->r8(0); }
+ u16 owner_id() const { return m_block->r16b(1); }
+ u8 link_count() const { return m_block->r8(8); }
+ u32 file_size() const { return m_block->r32b(9); }
+ util::arbitrary_datetime creation_date() const;
+ bool is_directory() const { return (attributes() & 0x80) != 0; }
+ bool is_non_sharable() const { return (attributes() & 0x40) != 0; }
+ bool is_public_execute() const { return (attributes() & 0x20) != 0; }
+ bool is_public_write() const { return (attributes() & 0x10) != 0; }
+ bool is_public_read() const { return (attributes() & 0x08) != 0; }
+ bool is_user_execute() const { return (attributes() & 0x04) != 0; }
+ bool is_user_write() const { return (attributes() & 0x02) != 0; }
+ bool is_user_read() const { return (attributes() & 0x01) != 0; }
+
+ meta_data metadata() const;
+ int get_sector_map_entry_count() const;
+ void get_sector_map_entry(int entry_number, u32 &start_lsn, u16 &count) const;
+
+ private:
+ fsblk_t::block_t::ptr m_block;
+ std::string m_filename;
+ };
+
+ // ctor/dtor
+ coco_os9_impl(fsblk_t &blockdev, volume_header &&header);
+ virtual ~coco_os9_impl() = default;
+
+ virtual meta_data volume_metadata() override;
+ virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override;
+ virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+ virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+ virtual std::error_condition format(const meta_data &meta) override;
+
+ std::optional<file_header> find(const std::vector<std::string> &path, std::optional<dir_entry_type> expected_entry_type) const;
+ void iterate_directory_entries(const file_header &header, const std::function<bool(std::string &&, u32)> callback) const;
+ std::vector<u8> read_file_data(const file_header &header) const;
+
+ static std::string pick_os9_string(std::string_view raw_string);
+ static std::string to_os9_string(std::string_view s, size_t length);
+ static util::arbitrary_datetime from_os9_date(u32 os9_date, u16 os9_time = 0);
+ static std::tuple<u32, u16> to_os9_date(const util::arbitrary_datetime &datetime);
+ static bool is_ignored_filename(std::string_view name);
+ static bool validate_filename(std::string_view name);
+
+private:
+ volume_header m_volume_header;
+};
+
+} // anonymous namespace
+
+//**************************************************************************
+// IMPLEMENTATION
+//**************************************************************************
+
+//-------------------------------------------------
+// name
+//-------------------------------------------------
+
+const char *coco_os9_image::name() const
+{
+ return "coco_os9";
+}
+
+
+//-------------------------------------------------
+// description
+//-------------------------------------------------
+
+const char *coco_os9_image::description() const
+{
+ return "CoCo OS-9";
+}
+
+
+//-------------------------------------------------
+// enumerate_f
+//-------------------------------------------------
+
+void coco_os9_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_COCO_RAWDSK_FORMAT, floppy_image::FF_525, floppy_image::SSSD, 161280, "coco_rawdsk_os9_35", "CoCo Raw Disk OS-9 single-sided 35 tracks");
+ fe.add(FLOPPY_COCO_RAWDSK_FORMAT, floppy_image::FF_525, floppy_image::SSSD, 184320, "coco_rawdsk_os9_40", "CoCo Raw Disk OS-9 single-sided 40 tracks");
+}
+
+
+//-------------------------------------------------
+// can_format
+//-------------------------------------------------
+
+bool coco_os9_image::can_format() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// can_read
+//-------------------------------------------------
+
+bool coco_os9_image::can_read() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// can_write
+//-------------------------------------------------
+
+bool coco_os9_image::can_write() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// has_rsrc
+//-------------------------------------------------
+
+bool coco_os9_image::has_rsrc() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// directory_separator
+//-------------------------------------------------
+
+char coco_os9_image::directory_separator() const
+{
+ return '/';
+}
+
+
+//-------------------------------------------------
+// volume_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> coco_os9_image::volume_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_description(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 32; }, "Volume name, up to 32 characters"));
+ results.emplace_back(meta_description(meta_name::creation_date, util::arbitrary_datetime::now(), false, nullptr, "Creation time"));
+ return results;
+}
+
+
+//-------------------------------------------------
+// file_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> coco_os9_image::file_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return coco_os9_impl::validate_filename(m.as_string()); }, "File name"));
+ results.emplace_back(meta_description(meta_name::creation_date, util::arbitrary_datetime::now(), false, nullptr, "Creation time"));
+ results.emplace_back(meta_description(meta_name::owner_id, 0, true, nullptr, "Owner ID"));
+ results.emplace_back(meta_description(meta_name::attributes, "", true, nullptr, "File attributes"));
+ results.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "Size of the file in bytes"));
+ return results;
+}
+
+
+//-------------------------------------------------
+// directory_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> coco_os9_image::directory_meta_description() const
+{
+ return file_meta_description();
+}
+
+//-------------------------------------------------
+// mount
+//-------------------------------------------------
+
+std::unique_ptr<filesystem_t> coco_os9_image::mount(fsblk_t &blockdev) const
+{
+ // read the header block
+ blockdev.set_block_size(256);
+ coco_os9_impl::volume_header header(blockdev.get(0));
+
+ // sanity checks
+ if (header.sectors_per_track() != header.track_size_in_sectors())
+ return { };
+
+ // create the implementation
+ return std::make_unique<coco_os9_impl>(blockdev, std::move(header));
+}
+
+
+//-------------------------------------------------
+// impl ctor
+//-------------------------------------------------
+
+coco_os9_impl::coco_os9_impl(fsblk_t &blockdev, volume_header &&header)
+ : filesystem_t(blockdev, 256)
+ , m_volume_header(std::move(header))
+{
+}
+
+
+//-------------------------------------------------
+// coco_os9_impl::volume_metadata
+//-------------------------------------------------
+
+meta_data coco_os9_impl::volume_metadata()
+{
+ meta_data results;
+ results.set(meta_name::name, m_volume_header.name());
+ results.set(meta_name::creation_date, m_volume_header.creation_date());
+ return results;
+}
+
+
+//-------------------------------------------------
+// coco_os9_impl::metadata
+//-------------------------------------------------
+
+std::pair<std::error_condition, meta_data> coco_os9_impl::metadata(const std::vector<std::string> &path)
+{
+ // look up the path
+ std::optional<file_header> header = find(path, { });
+ if (!header)
+ return std::make_pair(error::not_found, meta_data());
+
+ return std::make_pair(std::error_condition(), header->metadata());
+}
+
+
+//-------------------------------------------------
+// coco_os9_impl::directory_contents
+//-------------------------------------------------
+
+std::pair<std::error_condition, std::vector<dir_entry>> coco_os9_impl::directory_contents(const std::vector<std::string> &path)
+{
+ // look up the path
+ std::optional<file_header> header = find(path, dir_entry_type::dir);
+ if (!header)
+ return std::make_pair(error::not_found, std::vector<dir_entry>());
+
+ // iterate through the directory
+ std::vector<dir_entry> results;
+ auto callback = [this, &results](std::string &&filename, u32 lsn)
+ {
+ file_header header(m_blockdev.get(lsn), std::move(filename));
+ dir_entry_type entry_type = header.is_directory()
+ ? dir_entry_type::dir
+ : dir_entry_type::file;
+ results.emplace_back(entry_type, header.metadata());
+ return false;
+ };
+ iterate_directory_entries(*header, callback);
+
+ // and we're done
+ return std::make_pair(std::error_condition(), std::move(results));
+}
+
+
+//-------------------------------------------------
+// coco_os9_impl::file_read
+//-------------------------------------------------
+
+std::pair<std::error_condition, std::vector<u8>> coco_os9_impl::file_read(const std::vector<std::string> &path)
+{
+ // look up the path
+ std::optional<file_header> header = find(path, dir_entry_type::file);
+ if (!header)
+ return std::make_pair(error::not_found, std::vector<u8>());
+
+ std::vector<u8> data = read_file_data(*header);
+ return std::make_pair(std::error_condition(), std::move(data));
+}
+
+
+//-------------------------------------------------
+// coco_os9_impl::format
+//-------------------------------------------------
+
+std::error_condition coco_os9_impl::format(const meta_data &meta)
+{
+ // for some reason, the OS-9 world favored filling with 0xE5
+ m_blockdev.fill_all(0xe5);
+
+ // identify geometry info
+ u8 sectors = 18; // TODO - we need a definitive technique to get the floppy geometry
+ u8 heads = 1; // TODO - we need a definitive technique to get the floppy geometry
+ u16 sector_bytes = 256; // TODO - we need a definitive technique to get the floppy geometry
+ bool is_double_density = true; // TODO - we need a definitive technique to get the floppy geometry
+ u32 tracks = m_blockdev.block_count() / sectors / heads;
+
+ // get attributes from metadata
+ std::string volume_title = meta.get_string(meta_name::name, "UNTITLED");
+ util::arbitrary_datetime creation_datetime = meta.get_date(meta_name::creation_date);
+ auto [creation_os9date, creation_os9time] = to_os9_date(creation_datetime);
+
+ u32 lsn_count = m_blockdev.block_count();
+ u16 cluster_size = 1;
+ u16 owner_id = 1;
+ u16 disk_id = 1;
+ u8 attributes = 0;
+ u32 allocation_bitmap_bits = lsn_count / cluster_size;
+ u32 allocation_bitmap_lsns = (allocation_bitmap_bits / 8 + sector_bytes - 1) / sector_bytes;
+ u8 format_flags = ((heads > 1) ? 0x01 : 0x00)
+ | (is_double_density ? 0x02 : 0x00);
+
+ // volume header
+ auto volume_header = m_blockdev.get(0);
+ volume_header->fill(0x00);
+ volume_header->w24b(0, lsn_count); // DD.TOT - total secctors
+ volume_header->w8(3, sectors); // DD.TKS - track size in sectors
+ volume_header->w16b(4, (allocation_bitmap_bits + 7) / 8); // DD.MAP - allocation bitmap in bytes
+ volume_header->w16b(6, cluster_size); // DD.BIT - cluster size
+ volume_header->w24b(8, 1 + allocation_bitmap_lsns); // DD.DIR - root directory LSN
+ volume_header->w16b(11, owner_id); // DD.OWN - owner ID
+ volume_header->w8(13, attributes); // DD.ATT - Dattributes
+ volume_header->w16b(14, disk_id); // DD.DSK - disk ID
+ volume_header->w8(16, format_flags); // DD.FMT - format flags
+ volume_header->w16b(17, sectors); // DD.SPT - sectors per track
+ volume_header->w24b(26, creation_os9date); // DD.DAT - date of creation
+ volume_header->w16b(29, creation_os9time); // DD.DAT - time of creation
+ volume_header->wstr(31, to_os9_string(volume_title, 32)); // DD.NAM - title
+ volume_header->w16b(103, sector_bytes / 256); // sector bytes
+
+ // path descriptor options
+ volume_header->w8(0x3f + 0x00, 1); // device class
+ volume_header->w8(0x3f + 0x01, 1); // drive number
+ volume_header->w8(0x3f + 0x03, 0x20); // device type
+ volume_header->w8(0x3f + 0x04, 1); // density capability
+ volume_header->w16b(0x3f + 0x05, tracks); // number of tracks
+ volume_header->w8(0x3f + 0x07, heads); // number of sides
+ volume_header->w16b(0x3f + 0x09, sectors); // sectors per track
+ volume_header->w16b(0x3f + 0x0b, sectors); // sectors on track zero
+ volume_header->w8(0x3f + 0x0d, 3); // sector interleave factor
+ volume_header->w8(0x3f + 0x0e, 8); // default sectors per allocation
+
+ // allocation bitmap
+ u32 total_allocated_sectors = 1 + allocation_bitmap_lsns + 1 + 8;
+ std::vector<u8> abblk_bytes;
+ abblk_bytes.resize(sector_bytes);
+ for (u32 i = 0; i < allocation_bitmap_lsns; i++)
+ {
+ for (u32 j = 0; j < sector_bytes; j++)
+ {
+ u32 pos = (i * sector_bytes + j) * 8;
+ if (pos + 8 < total_allocated_sectors)
+ abblk_bytes[j] = 0xff;
+ else if (pos >= total_allocated_sectors)
+ abblk_bytes[j] = 0x00;
+ else
+ abblk_bytes[j] = ~((1 << (8 - total_allocated_sectors + pos)) - 1);
+ }
+
+ auto abblk = m_blockdev.get(1 + i);
+ abblk->write(0, abblk_bytes.data(), sector_bytes);
+ }
+
+ // root directory header
+ auto roothdr_blk = m_blockdev.get(1 + allocation_bitmap_lsns);
+ roothdr_blk->fill(0x00);
+ roothdr_blk->w8(0x00, 0xbf);
+ roothdr_blk->w8(0x01, 0x00);
+ roothdr_blk->w8(0x02, 0x00);
+ roothdr_blk->w24b(0x03, creation_os9date);
+ roothdr_blk->w16b(0x06, creation_os9time);
+ roothdr_blk->w8(0x08, 0x01);
+ roothdr_blk->w8(0x09, 0x00);
+ roothdr_blk->w8(0x0a, 0x00);
+ roothdr_blk->w8(0x0b, 0x00);
+ roothdr_blk->w8(0x0c, 0x40);
+ roothdr_blk->w24b(0x0d, creation_os9date);
+ roothdr_blk->w24b(0x10, 1 + allocation_bitmap_lsns + 1);
+ roothdr_blk->w16b(0x13, 8);
+
+ // root directory data
+ auto rootdata_blk = m_blockdev.get(1 + allocation_bitmap_lsns + 1);
+ rootdata_blk->fill(0x00);
+ rootdata_blk->w8(0x00, 0x2e);
+ rootdata_blk->w8(0x01, 0xae);
+ rootdata_blk->w8(0x1f, 1 + allocation_bitmap_lsns);
+ rootdata_blk->w8(0x20, 0xae);
+ rootdata_blk->w8(0x3f, 1 + allocation_bitmap_lsns);
+ return std::error_condition();
+}
+
+
+//-------------------------------------------------
+// coco_os9_impl::find
+//-------------------------------------------------
+
+std::optional<coco_os9_impl::file_header> coco_os9_impl::find(const std::vector<std::string> &path, std::optional<dir_entry_type> expected_entry_type) const
+{
+ u32 lsn = m_volume_header.root_dir_lsn();
+ file_header current(m_blockdev.get(lsn), "");
+
+ // traverse the directory
+ for (const std::string &path_part : path)
+ {
+ // use iterate_directory_entries() to find this file part
+ std::optional<u32> child_lsn;
+ auto callback = [&child_lsn, &path_part](std::string &&child_filename, u32 this_child_lsn)
+ {
+ bool found = child_filename == path_part;
+ if (found)
+ child_lsn = this_child_lsn;
+ return found;
+ };
+ iterate_directory_entries(current, callback);
+
+ // did we find the child?
+ if (!child_lsn)
+ return { };
+ current = file_header(m_blockdev.get(*child_lsn), std::string(path_part));
+ }
+
+ // ensure that we found the entry type we expect
+ dir_entry_type current_entry_type = current.is_directory() ? dir_entry_type::dir : dir_entry_type::file;
+ return !expected_entry_type || current_entry_type == *expected_entry_type
+ ? std::move(current)
+ : std::optional<file_header>();
+}
+
+
+//-------------------------------------------------
+// coco_os9_impl::iterate_directory_entries
+//-------------------------------------------------
+
+void coco_os9_impl::iterate_directory_entries(const file_header &header, const std::function<bool(std::string &&, u32)> callback) const
+{
+ // read the directory data
+ std::vector<u8> directory_data = read_file_data(header);
+
+ // and assemble results
+ bool done = false;
+ int directory_count = directory_data.size() / 32;
+ for (int i = 0; !done && i < directory_count; i++)
+ {
+ // determine the filename
+ std::string_view raw_filename((const char *)&directory_data[i * 32], 29);
+ std::string filename = pick_os9_string(raw_filename);
+ if (is_ignored_filename(filename))
+ continue;
+
+ // set up the child header
+ u32 lsn = get_u24be(&directory_data[i * 32] + 29);
+
+ // invoke the callback
+ done = callback(std::move(filename), lsn);
+ }
+}
+
+
+//-------------------------------------------------
+// coco_os9_impl::read_file_data
+//-------------------------------------------------
+
+std::vector<u8> coco_os9_impl::read_file_data(const file_header &header) const
+{
+ // prep the vector to return the data from
+ std::vector<u8> data;
+ data.reserve(header.file_size());
+
+ int entry_count = header.get_sector_map_entry_count();
+ for (int i = 0; i < entry_count; i++)
+ {
+ u32 start_lsn;
+ u16 count;
+ header.get_sector_map_entry(i, start_lsn, count);
+
+ for (u32 lsn = start_lsn; lsn < start_lsn + count; lsn++)
+ {
+ auto block = m_blockdev.get(lsn);
+ size_t block_size = std::min(std::min(u32(m_volume_header.sector_size()), block->size()), header.file_size() - u32(data.size()));
+ for (auto i = 0; i < block_size; i++)
+ data.push_back(block->rodata()[i]);
+ }
+ }
+ return data;
+}
+
+
+//-------------------------------------------------
+// pick_os9_string
+//-------------------------------------------------
+
+std::string coco_os9_impl::pick_os9_string(std::string_view raw_string)
+{
+ // find the last NUL or high bit character
+ auto iter = std::find_if(raw_string.begin(), raw_string.end(), [](char ch)
+ {
+ return ch == '\0' || ch & 0x80;
+ });
+
+ // get the preliminary result
+ std::string result(raw_string.begin(), iter);
+
+ // and add the final character if we have to
+ if (iter < raw_string.end() && *iter & 0x80)
+ result.append(1, *iter & 0x7f);
+ return result;
+
+}
+
+
+//-------------------------------------------------
+// to_os9_string
+//-------------------------------------------------
+
+std::string coco_os9_impl::to_os9_string(std::string_view s, size_t length)
+{
+ std::string result(length, '\0');
+ for (auto i = 0; i < std::min(length, s.size()); i++)
+ {
+ result[i] = (s[i] & 0x7f)
+ | (i == s.size() ? 0x80 : 0x00);
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// from_os9_date
+//-------------------------------------------------
+
+util::arbitrary_datetime coco_os9_impl::from_os9_date(u32 os9_date, u16 os9_time)
+{
+ util::arbitrary_datetime dt;
+ memset(&dt, 0, sizeof(dt));
+ dt.year = ((os9_date >> 16) & 0xff) + 1900;
+ dt.month = (os9_date >> 8) & 0xff;
+ dt.day_of_month = (os9_date >> 0) & 0xff;
+ dt.hour = (os9_time >> 8) & 0xff;
+ dt.minute = (os9_time >> 0) & 0xff;
+ return dt;
+}
+
+
+//-------------------------------------------------
+// to_os9_date
+//-------------------------------------------------
+
+std::tuple<u32, u16> coco_os9_impl::to_os9_date(const util::arbitrary_datetime &datetime)
+{
+ u32 os9_date = ((datetime.year - 1900) & 0xff) << 16
+ | (datetime.month & 0xff) << 8
+ | (datetime.day_of_month & 0xff) << 0;
+ u16 os9_time = (datetime.hour & 0xff) << 8
+ | (datetime.minute & 0xff) << 0;
+ return std::make_tuple(os9_date, os9_time);
+}
+
+
+//-------------------------------------------------
+// validate_filename
+//-------------------------------------------------
+
+bool coco_os9_impl::validate_filename(std::string_view name)
+{
+ return !is_ignored_filename(name)
+ && name.size() <= 29
+ && std::find_if(name.begin(), name.end(), [](const char ch) { return ch == '\0' || ch == '/' || (ch & 0x80); }) == name.end();
+}
+
+
+//-------------------------------------------------
+// is_ignored_filename - should this file name be
+// ignored if it is in the file system?
+//-------------------------------------------------
+
+bool coco_os9_impl::is_ignored_filename(std::string_view name)
+{
+ return name.empty()
+ || name[0] == '\0'
+ || name == "."
+ || name == "..";
+}
+
+
+//-------------------------------------------------
+// volume_header ctor
+//-------------------------------------------------
+
+coco_os9_impl::volume_header::volume_header(fsblk_t::block_t::ptr &&block)
+ : m_block(std::move(block))
+{
+}
+
+
+//-------------------------------------------------
+// volume_header::name
+//-------------------------------------------------
+
+std::string coco_os9_impl::volume_header::name() const
+{
+ std::string_view raw_name(m_block->rstr(31, 32));
+ return pick_os9_string(raw_name);
+}
+
+
+//-------------------------------------------------
+// file_header ctor
+//-------------------------------------------------
+
+coco_os9_impl::file_header::file_header(fsblk_t::block_t::ptr &&block, std::string &&filename)
+ : m_block(std::move(block))
+ , m_filename(std::move(filename))
+{
+}
+
+
+//-------------------------------------------------
+// file_header::creation_date
+//-------------------------------------------------
+
+util::arbitrary_datetime coco_os9_impl::file_header::creation_date() const
+{
+ return from_os9_date(m_block->r24b(13));
+}
+
+
+//-------------------------------------------------
+// file_header::metadata
+//-------------------------------------------------
+
+meta_data coco_os9_impl::file_header::metadata() const
+{
+ // format the attributes
+ std::string attributes = util::string_format("%c%c%c%c%c%c%c%c",
+ is_directory() ? 'd' : '-',
+ is_non_sharable() ? 's' : '-',
+ is_public_execute() ? 'x' : '-',
+ is_public_write() ? 'w' : '-',
+ is_public_read() ? 'r' : '-',
+ is_user_execute() ? 'x' : '-',
+ is_user_write() ? 'w' : '-',
+ is_user_read() ? 'r' : '-');
+
+ meta_data result;
+ result.set(meta_name::name, m_filename);
+ result.set(meta_name::creation_date, creation_date());
+ result.set(meta_name::owner_id, owner_id());
+ result.set(meta_name::attributes, std::move(attributes));
+ result.set(meta_name::length, file_size());
+ return result;
+}
+
+
+//-------------------------------------------------
+// file_header::get_sector_map_entry_count
+//-------------------------------------------------
+
+int coco_os9_impl::file_header::get_sector_map_entry_count() const
+{
+ return (m_block->size() - 16) / 5;
+}
+
+
+//-------------------------------------------------
+// file_header::get_sector_map_entry
+//-------------------------------------------------
+
+void coco_os9_impl::file_header::get_sector_map_entry(int entry_number, u32 &start_lsn, u16 &count) const
+{
+ start_lsn = m_block->r24b(16 + (entry_number * 5) + 0);
+ count = m_block->r16b(16 + (entry_number * 5) + 3);
+}
diff --git a/src/lib/formats/fs_coco_os9.h b/src/lib/formats/fs_coco_os9.h
new file mode 100644
index 00000000000..15644c11080
--- /dev/null
+++ b/src/lib/formats/fs_coco_os9.h
@@ -0,0 +1,47 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ fs_coco_os9.h
+
+ Management of CoCo OS-9 floppy images
+
+***************************************************************************/
+
+#ifndef MAME_FORMATS_FS_COCO_OS9_H
+#define MAME_FORMATS_FS_COCO_OS9_H
+
+#pragma once
+
+#include "fsmgr.h"
+
+namespace fs {
+
+// ======================> coco_os9_image
+
+class coco_os9_image : public manager_t {
+public:
+ coco_os9_image() = default;
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+ virtual char directory_separator() const override;
+
+ virtual std::vector<meta_description> volume_meta_description() const override;
+ virtual std::vector<meta_description> file_meta_description() const override;
+ virtual std::vector<meta_description> directory_meta_description() const override;
+};
+
+extern const coco_os9_image COCO_OS9;
+
+} // namespace fs
+
+#endif // MAME_FORMATS_FS_COCO_OS9_H
diff --git a/src/lib/formats/fs_coco_rsdos.cpp b/src/lib/formats/fs_coco_rsdos.cpp
new file mode 100644
index 00000000000..0ce88cf01df
--- /dev/null
+++ b/src/lib/formats/fs_coco_rsdos.cpp
@@ -0,0 +1,743 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods, Wilbert Pol
+/***************************************************************************
+
+ fs_coco_rsdos.cpp
+
+ Management of CoCo "RS-DOS" floppy images
+
+Limitation:
+- The determination of the file type is very limited.
+
+***************************************************************************/
+
+#include "fs_coco_rsdos.h"
+#include "coco_rawdsk.h"
+#include "fsblk.h"
+
+#include "util/strformat.h"
+
+#include <bitset>
+#include <optional>
+#include <regex>
+#include <string_view>
+
+using namespace fs;
+
+namespace fs { const coco_rsdos_image COCO_RSDOS; }
+
+namespace {
+
+class coco_rsdos_impl : public filesystem_t
+{
+public:
+ coco_rsdos_impl(fsblk_t &blockdev);
+ virtual ~coco_rsdos_impl() = default;
+
+ static constexpr int SECTOR_DIRECTORY_ENTRY_COUNT = 8;
+
+ struct rsdos_dirent
+ {
+ char m_filename[11];
+ u8 m_filetype;
+ u8 m_asciiflag;
+ u8 m_first_granule;
+ u8 m_last_sector_bytes_msb;
+ u8 m_last_sector_bytes_lsb;
+ };
+
+ struct rsdos_dirent_sector
+ {
+ struct
+ {
+ rsdos_dirent m_dirent;
+ u8 m_unused[16];
+ } m_entries[SECTOR_DIRECTORY_ENTRY_COUNT];
+ };
+
+ class granule_iterator
+ {
+ public:
+ granule_iterator(coco_rsdos_impl &fs, const rsdos_dirent &dirent);
+ bool next(u8 &granule, u16 &byte_count);
+
+ private:
+ fsblk_t::block_t::ptr m_granule_map;
+ std::optional<u8> m_current_granule;
+ u8 m_maximum_granules;
+ u16 m_last_sector_bytes;
+ std::bitset<256> m_visited_granules;
+ };
+
+ virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override;
+ virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+ virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+ virtual std::error_condition format(const meta_data &meta) override;
+ virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override;
+ virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
+
+ static bool validate_filename(std::string_view name);
+
+private:
+ static constexpr u8 TRACK_GRANULE_COUNT = 2;
+ static constexpr u8 GRANULE_SECTOR_COUNT = 9;
+ static constexpr u8 TRACK_SECTOR_COUNT = TRACK_GRANULE_COUNT * GRANULE_SECTOR_COUNT;
+ static constexpr u8 DIRECTORY_TRACK = 17;
+ static constexpr u8 DIRECTORY_ENTRY_SIZE = 0x20;
+ static constexpr u8 FNAME_LENGTH = 11;
+ static constexpr u16 SECTOR_SIZE = 0x100;
+ static constexpr u8 OFFSET_FILE_TYPE = 0x0b;
+ static constexpr u8 OFFSET_ASCII_FLAG = 0x0c;
+ static constexpr u8 OFFSET_FIRST_GRANULE = 0x0d;
+ static constexpr u8 OFFSET_LAST_SECTOR_BYTES = 0x0e;
+ static constexpr u8 FILE_TYPE_BASIC = 0x00;
+ static constexpr u8 FILE_TYPE_BASIC_DATA = 0x01;
+ static constexpr u8 FILE_TYPE_MACHINE_CODE = 0x02;
+ static constexpr u8 FILE_TYPE_TEXT_EDITOR = 0x03;
+ static constexpr u8 FILE_LAST_GRANULE_INDICATOR = 0xc0;
+
+ fsblk_t::block_t::ptr read_sector(int track, int sector) const;
+ fsblk_t::block_t::ptr read_granule_sector(u8 granule, u8 sector) const;
+ u8 maximum_granules() const;
+ std::optional<rsdos_dirent> dirent_from_path(const std::vector<std::string> &path);
+ template <typename T>
+ void iterate_directory_entries(T &&callback);
+ meta_data get_metadata_from_dirent(const rsdos_dirent &dirent);
+ static std::string get_filename_from_dirent(const rsdos_dirent &dirent);
+ std::pair<std::error_condition, std::string> build_direntry_filename(const std::string &filename);
+ std::pair<std::error_condition, u8> claim_granule();
+ void write_granule_map(u8 granule, u8 map_data);
+ u8 read_granule_map(u8 granule) const;
+ bool is_ascii(const std::vector<u8> &data) const;
+ u8 determine_file_type(const std::vector<u8> &data) const;
+};
+
+} // anonymous namespace
+
+
+//-------------------------------------------------
+// name
+//-------------------------------------------------
+
+const char *coco_rsdos_image::name() const
+{
+ return "coco_rsdos";
+}
+
+
+//-------------------------------------------------
+// description
+//-------------------------------------------------
+
+const char *coco_rsdos_image::description() const
+{
+ return "CoCo RS-DOS";
+}
+
+
+//-------------------------------------------------
+// enumerate_f
+//-------------------------------------------------
+
+void coco_rsdos_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_COCO_RAWDSK_FORMAT, floppy_image::FF_525, floppy_image::SSSD, 161280, "coco_rawdsk_rsdos_35", "CoCo Raw Disk RS-DOS single-sided 35 tracks");
+ fe.add(FLOPPY_COCO_RAWDSK_FORMAT, floppy_image::FF_525, floppy_image::SSSD, 184320, "coco_rawdsk_rsdos_40", "CoCo Raw Disk RS-DOS single-sided 40 tracks");
+}
+
+
+//-------------------------------------------------
+// can_format
+//-------------------------------------------------
+
+bool coco_rsdos_image::can_format() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// can_read
+//-------------------------------------------------
+
+bool coco_rsdos_image::can_read() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// can_write
+//-------------------------------------------------
+
+bool coco_rsdos_image::can_write() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// has_rsrc
+//-------------------------------------------------
+
+bool coco_rsdos_image::has_rsrc() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// file_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> coco_rsdos_image::file_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return coco_rsdos_impl::validate_filename(m.as_string()); }, "File name, 8.3"));
+ results.emplace_back(meta_description(meta_name::file_type, 0, true, nullptr, "Type of the file"));
+ results.emplace_back(meta_description(meta_name::ascii_flag, "B", true, nullptr, "Ascii or binary flag"));
+ results.emplace_back(meta_description(meta_name::size_in_blocks, 0, true, nullptr, "Number of granules used by the file"));
+ results.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "Size of the file in bytes"));
+ return results;
+}
+
+
+//-------------------------------------------------
+// mount
+//-------------------------------------------------
+
+std::unique_ptr<filesystem_t> coco_rsdos_image::mount(fsblk_t &blockdev) const
+{
+ return std::make_unique<coco_rsdos_impl>(blockdev);
+}
+
+
+//-------------------------------------------------
+// validate_filename
+//-------------------------------------------------
+
+bool coco_rsdos_impl::validate_filename(std::string_view name)
+{
+ auto pos = name.find('.');
+ auto stem_length = pos != std::string::npos ? pos : name.size();
+ auto ext_length = pos != std::string::npos ? name.size() - pos - 1 : 0;
+ return stem_length > 0 && stem_length <= 8 && ext_length <= 3;
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl ctor
+//-------------------------------------------------
+
+coco_rsdos_impl::coco_rsdos_impl(fsblk_t &blockdev)
+ : filesystem_t(blockdev, SECTOR_SIZE)
+{
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::metadata
+//-------------------------------------------------
+
+std::pair<std::error_condition, meta_data> coco_rsdos_impl::metadata(const std::vector<std::string> &path)
+{
+ // attempt to find the file
+ const std::optional<rsdos_dirent> dirent = dirent_from_path(path);
+ if (!dirent)
+ return std::make_pair(error::not_found, meta_data());
+
+ return std::make_pair(std::error_condition(), get_metadata_from_dirent(*dirent));
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::directory_contents
+//-------------------------------------------------
+
+std::pair<std::error_condition, std::vector<dir_entry>> coco_rsdos_impl::directory_contents(const std::vector<std::string> &path)
+{
+ std::vector<dir_entry> results;
+ auto const callback = [this, &results](u8 s, u8 i, const rsdos_dirent &dirent)
+ {
+ results.emplace_back(dir_entry_type::file, get_metadata_from_dirent(dirent));
+ return false;
+ };
+ iterate_directory_entries(callback);
+ return std::make_pair(std::error_condition(), std::move(results));
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::file_read
+//-------------------------------------------------
+
+std::pair<std::error_condition, std::vector<u8>> coco_rsdos_impl::file_read(const std::vector<std::string> &path)
+{
+ // attempt to find the file
+ const std::optional<rsdos_dirent> dirent = dirent_from_path(path);
+ if (!dirent)
+ return std::make_pair(error::not_found, std::vector<u8>());
+
+ std::vector<u8> result;
+ u8 granule;
+ u16 byte_count;
+ granule_iterator iter(*this, *dirent);
+ while (iter.next(granule, byte_count))
+ {
+ // resize the results
+ size_t current_size = result.size();
+ result.resize(current_size + byte_count);
+
+ // determine which track and sector this granule starts at
+ int track = granule / 2 + (granule >= 34 ? 1 : 0);
+ int sector = granule % 2 * 9 + 1;
+
+ // and read all the sectors
+ while (byte_count > 0)
+ {
+ // read this sector
+ auto block = read_sector(track, sector);
+ u16 data_length = std::min(byte_count, u16(SECTOR_SIZE));
+
+ // and append it to the results
+ block->read(0, result.data() + current_size, data_length);
+
+ // and advance
+ current_size += data_length;
+ byte_count -= data_length;
+ sector++;
+ }
+ }
+ return std::make_pair(std::error_condition(), std::move(result));
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::format
+//-------------------------------------------------
+
+std::error_condition coco_rsdos_impl::format(const meta_data &meta)
+{
+ // formatting RS-DOS is easy - just fill everything with 0xFF
+ m_blockdev.fill_all(0xFF);
+ return std::error_condition();
+}
+
+
+std::pair<std::error_condition, std::string> coco_rsdos_impl::build_direntry_filename(const std::string &filename)
+{
+ // The manual does not say anything about valid characters for a file name.
+ const std::regex filename_regex("([^.]{0,8})(\\.([^.]{0,3}))?");
+ std::smatch smatch;
+ if (!std::regex_match(filename, smatch, filename_regex))
+ return std::make_pair(error::invalid_name, std::string());
+ if (smatch.size() != 4)
+ return std::make_pair(error::invalid_name, std::string());
+
+ std::string fname;
+ fname.resize(FNAME_LENGTH, ' ');
+
+ for (int i = 0; i < 8 && i < smatch.str(1).size(); i++)
+ fname[i] = smatch.str(1)[i];
+
+ for (int j = 0; j < 3 && j < smatch.str(3).size(); j++)
+ fname[8 + j] = smatch.str(3)[j];
+
+ return std::make_pair(std::error_condition(), std::move(fname));
+}
+
+
+std::error_condition coco_rsdos_impl::file_create(const std::vector<std::string> &path, const meta_data &meta)
+{
+ if (!path.empty())
+ return error::unsupported;
+
+ const std::string filename = meta.get_string(meta_name::name, "");
+ auto [err, fname] = build_direntry_filename(filename);
+ if (err)
+ return err;
+
+ bool found_entry = false;
+ u8 dir_sector = 0;
+ u8 file_index = 0;
+ for (dir_sector = 3; !found_entry && dir_sector <= TRACK_SECTOR_COUNT; dir_sector++)
+ {
+ auto dir_block = read_sector(DIRECTORY_TRACK, dir_sector);
+
+ for (file_index = 0; !found_entry && file_index < SECTOR_DIRECTORY_ENTRY_COUNT; file_index++)
+ {
+ const u8 first_byte = dir_block->r8(file_index * DIRECTORY_ENTRY_SIZE);
+ // 0xff marks the end of the directory, 0x00 marks a deleted file
+ found_entry = (first_byte == 0xff || first_byte == 0x00);
+ if (found_entry)
+ {
+ for (int i = 0; i < DIRECTORY_ENTRY_SIZE; i++)
+ dir_block->w8(file_index * DIRECTORY_ENTRY_SIZE + i, 0);
+
+ auto [cerr, granule] = claim_granule();
+ if (cerr != std::error_condition())
+ return cerr;
+
+ dir_block->wstr(file_index * DIRECTORY_ENTRY_SIZE + 0, fname);
+ dir_block->w8(file_index * DIRECTORY_ENTRY_SIZE + OFFSET_FIRST_GRANULE, granule);
+ // The file type, ASCII flag, and number of bytes in last sector of file will be set during writing.
+ }
+ }
+ }
+ if (!found_entry)
+ return error::no_space;
+
+ return std::error_condition();
+}
+
+
+std::error_condition coco_rsdos_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
+{
+ if (path.size() != 1)
+ return error::not_found;
+ const std::string &target = path[0];
+
+ std::optional<rsdos_dirent> result;
+ u8 dir_sector = 0;
+ u8 dir_file_index = 0;
+ auto const callback = [&result, &target, &dir_sector, &dir_file_index](u8 s, u8 i, const rsdos_dirent &dirent)
+ {
+ const bool found = get_filename_from_dirent(dirent) == target;
+ if (found)
+ {
+ result = dirent;
+ dir_sector = s;
+ dir_file_index = i;
+ }
+ return found;
+ };
+ iterate_directory_entries(callback);
+
+ if (!result)
+ return error::not_found;
+
+ const size_t data_length = data.size();
+ const u8 max_granule = maximum_granules();
+ u8 granule = result->m_first_granule;
+ u8 granule_sector = 1;
+ size_t offset = 0;
+ u16 bytes_in_last_sector = 0;
+ u8 linked_granule = read_granule_map(granule);
+
+ while (offset < data_length)
+ {
+ auto data_block = read_granule_sector(granule, granule_sector);
+ bytes_in_last_sector = (data_length - offset) > SECTOR_SIZE ? SECTOR_SIZE : data_length - offset;
+ data_block->write(0, data.data() + offset, bytes_in_last_sector);
+ offset += SECTOR_SIZE;
+ write_granule_map(granule, FILE_LAST_GRANULE_INDICATOR + granule_sector);
+ if (offset < data_length)
+ {
+ granule_sector++;
+ if (granule_sector > GRANULE_SECTOR_COUNT)
+ {
+ // Re-use linked granule or claim a new granule
+ if (linked_granule < max_granule)
+ {
+ granule = linked_granule;
+ linked_granule = read_granule_map(linked_granule);
+ }
+ else
+ {
+ auto [err, next_granule] = claim_granule();
+ if (err)
+ return err;
+ write_granule_map(granule, next_granule);
+ granule = next_granule;
+ }
+ granule_sector = 1;
+ }
+ }
+ }
+
+ // Free unused space
+ while (linked_granule < max_granule)
+ {
+ const u8 granule_to_free = linked_granule;
+ linked_granule = read_granule_map(granule_to_free);
+ write_granule_map(granule_to_free, 0xff);
+ }
+
+ // Update directory entry
+ auto dir_block = read_sector(DIRECTORY_TRACK, dir_sector);
+ dir_block->w8(dir_file_index * DIRECTORY_ENTRY_SIZE + OFFSET_FILE_TYPE, determine_file_type(data));
+ dir_block->w8(dir_file_index * DIRECTORY_ENTRY_SIZE + OFFSET_ASCII_FLAG, is_ascii(data) ? 0xff : 0x00);
+ dir_block->w16b(dir_file_index * DIRECTORY_ENTRY_SIZE + OFFSET_LAST_SECTOR_BYTES, bytes_in_last_sector);
+
+ return std::error_condition();
+}
+
+
+bool coco_rsdos_impl::is_ascii(const std::vector<u8> &data) const
+{
+ const size_t data_length = data.size();
+
+ if (data_length == 0)
+ return false;
+
+ for (int i = 0; i < data_length; i++)
+ {
+ if (!(data[i] == 0x0d || data[i] == 0x0a || (data[i] >= 0x20 && data[i] < 0x60)))
+ return false;
+ }
+
+ return true;
+}
+
+
+u8 coco_rsdos_impl::determine_file_type(const std::vector<u8> &data) const
+{
+ if (is_ascii(data))
+ {
+ // TODO: Distinguish between BASIC code and text editor data
+ return FILE_TYPE_BASIC;
+ }
+
+ const size_t data_length = data.size();
+ // Binary BASIC code seems to begin with ff <16bit size> 26
+ if (data_length > 4 && data[0] == 0xff && data[3] == 0x26 && ((data[1] << 8) | data[2]) == data_length - 3)
+ return FILE_TYPE_BASIC;
+
+ // TODO: Distinguish between Machine Code and Basic Data
+
+ return FILE_TYPE_MACHINE_CODE;
+}
+
+
+std::pair<std::error_condition, u8> coco_rsdos_impl::claim_granule()
+{
+ // Granules are likely not assigned in this order on hardware.
+ auto granule_block = read_sector(DIRECTORY_TRACK, 2);
+ for (int g = 0; g < maximum_granules(); g++)
+ {
+ if (granule_block->r8(g) == 0xff)
+ {
+ granule_block->w8(g, FILE_LAST_GRANULE_INDICATOR);
+ return std::make_pair(std::error_condition(), g);
+ }
+ }
+ return std::make_pair(error::no_space, 0);
+}
+
+
+void coco_rsdos_impl::write_granule_map(u8 granule, u8 map_data)
+{
+ auto granule_block = read_sector(DIRECTORY_TRACK, 2);
+ granule_block->w8(granule, map_data);
+}
+
+
+u8 coco_rsdos_impl::read_granule_map(u8 granule) const
+{
+ auto granule_block = read_sector(DIRECTORY_TRACK, 2);
+ return granule_block->r8(granule);
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::read_sector
+//-------------------------------------------------
+
+fsblk_t::block_t::ptr coco_rsdos_impl::read_sector(int track, int sector) const
+{
+ // the CoCo RS-DOS world thinks in terms of tracks/sectors, but we have a block device
+ // abstraction
+ return m_blockdev.get(track * TRACK_SECTOR_COUNT + sector - 1);
+}
+
+
+fsblk_t::block_t::ptr coco_rsdos_impl::read_granule_sector(u8 granule, u8 sector) const
+{
+ // Track 17 does not hold granules
+ if (granule < (DIRECTORY_TRACK * TRACK_GRANULE_COUNT))
+ return m_blockdev.get(granule * GRANULE_SECTOR_COUNT + sector - 1);
+ else
+ return m_blockdev.get((granule + TRACK_GRANULE_COUNT) * GRANULE_SECTOR_COUNT + sector - 1);
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::maximum_granules
+//-------------------------------------------------
+
+u8 coco_rsdos_impl::maximum_granules() const
+{
+ u32 sector_count = m_blockdev.block_count();
+ u32 granule_count = (sector_count / GRANULE_SECTOR_COUNT) - 2;
+ return granule_count <= 0xFF ? u8(granule_count) : 0xFF;
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::dirent_from_path
+//-------------------------------------------------
+
+std::optional<coco_rsdos_impl::rsdos_dirent> coco_rsdos_impl::dirent_from_path(const std::vector<std::string> &path)
+{
+ if (path.size() != 1)
+ return { };
+ const std::string &target = path[0];
+
+ std::optional<rsdos_dirent> result;
+ auto const callback = [&result, &target](u8 s, u8 i, const rsdos_dirent &dirent)
+ {
+ bool found = get_filename_from_dirent(dirent) == target;
+ if (found)
+ result = dirent;
+ return found;
+ };
+ iterate_directory_entries(callback);
+ return result;
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::iterate_directory_entries
+//-------------------------------------------------
+
+template <typename T>
+void coco_rsdos_impl::iterate_directory_entries(T &&callback)
+{
+ bool done = false;
+ for (int dir_sector = 3; !done && dir_sector <= 18; dir_sector++)
+ {
+ // read this directory sector
+ auto dir_block = read_sector(DIRECTORY_TRACK, dir_sector);
+ const rsdos_dirent_sector &sector = *reinterpret_cast<const rsdos_dirent_sector *>(dir_block->rodata());
+
+ // and loop through all entries
+ for (int file_index = 0; file_index < 8; file_index++)
+ {
+ // 0xFF marks the end of the directory
+ if (sector.m_entries[file_index].m_dirent.m_filename[0] == '\xFF')
+ {
+ done = true;
+ }
+ else
+ {
+ // 0x00 marks a deleted file
+ if (sector.m_entries[file_index].m_dirent.m_filename[0] != '\0')
+ done = callback(dir_sector, 0, sector.m_entries[file_index].m_dirent);
+ }
+
+ if (done)
+ break;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::get_metadata_from_dirent
+//-------------------------------------------------
+
+meta_data coco_rsdos_impl::get_metadata_from_dirent(const rsdos_dirent &dirent)
+{
+ u32 file_size = 0;
+ int granule_count = 0;
+
+ // we need to iterate on the file to determine the size and granule/block count
+ u8 granule;
+ u16 byte_count;
+ granule_iterator iter(*this, dirent);
+ while (iter.next(granule, byte_count))
+ {
+ granule_count++;
+ file_size += byte_count;
+ }
+
+ // determine the file name
+ std::string name = get_filename_from_dirent(dirent);
+
+ // turn the ASCII flag to a single character (this reflects what the user sees when doing a directory listing on a real CoCo)
+ char file_type_char = 'B' + dirent.m_asciiflag;
+
+ // build the metadata and return it
+ meta_data result;
+ result.set(meta_name::name, std::move(name));
+ result.set(meta_name::file_type, dirent.m_filetype);
+ result.set(meta_name::ascii_flag, std::string(1, file_type_char));
+ result.set(meta_name::size_in_blocks, granule_count);
+ result.set(meta_name::length, file_size);
+ return result;
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::get_filename_from_dirent
+//-------------------------------------------------
+
+std::string coco_rsdos_impl::get_filename_from_dirent(const rsdos_dirent &dirent)
+{
+ std::string_view stem = trim_end_spaces(std::string_view(&dirent.m_filename[0], 8));
+ std::string_view ext = trim_end_spaces(std::string_view(&dirent.m_filename[8], 3));
+ return util::string_format("%s.%s", stem, ext);
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::granule_iterator ctor
+//-------------------------------------------------
+
+coco_rsdos_impl::granule_iterator::granule_iterator(coco_rsdos_impl &fs, const rsdos_dirent &dirent)
+ : m_granule_map(fs.read_sector(DIRECTORY_TRACK, 2))
+ , m_current_granule(dirent.m_first_granule)
+ , m_maximum_granules(fs.maximum_granules())
+ , m_last_sector_bytes((u16(dirent.m_last_sector_bytes_msb) << 8) | dirent.m_last_sector_bytes_lsb)
+{
+ // visit the first granule
+ m_visited_granules.set(dirent.m_first_granule);
+}
+
+
+//-------------------------------------------------
+// coco_rsdos_impl::granule_iterator::next
+//-------------------------------------------------
+
+bool coco_rsdos_impl::granule_iterator::next(u8 &granule, u16 &byte_count)
+{
+ bool success = false;
+ granule = ~0;
+ byte_count = 0;
+
+ if (m_current_granule)
+ {
+ std::optional<u8> next_granule;
+ const u8 *granule_map_data = m_granule_map->rodata();
+ if (granule_map_data[*m_current_granule] < m_maximum_granules)
+ {
+ // this entry points to the next granule
+ success = true;
+ granule = *m_current_granule;
+ byte_count = GRANULE_SECTOR_COUNT * SECTOR_SIZE;
+ next_granule = granule_map_data[*m_current_granule];
+
+ // check for cycles, which should only happen if the disk is corrupt (or not in RS-DOS format)
+ if (m_visited_granules[*next_granule])
+ next_granule = std::nullopt; // this is corrupt!
+ else
+ m_visited_granules.set(*next_granule);
+ }
+ else if (granule_map_data[*m_current_granule] >= FILE_LAST_GRANULE_INDICATOR && granule_map_data[*m_current_granule] <= FILE_LAST_GRANULE_INDICATOR + GRANULE_SECTOR_COUNT)
+ {
+ // this is the last granule in the file
+ success = true;
+ granule = *m_current_granule;
+ u16 sector_count = std::max(granule_map_data[*m_current_granule], u8(0xC1)) - 0xC1;
+ byte_count = sector_count * SECTOR_SIZE + m_last_sector_bytes;
+ next_granule = std::nullopt;
+ }
+ else
+ {
+ // should not happen; but we'll treat this as an EOF
+ next_granule = std::nullopt;
+ }
+ m_current_granule = next_granule;
+ }
+ return success;
+}
diff --git a/src/lib/formats/fs_coco_rsdos.h b/src/lib/formats/fs_coco_rsdos.h
new file mode 100644
index 00000000000..43723972964
--- /dev/null
+++ b/src/lib/formats/fs_coco_rsdos.h
@@ -0,0 +1,43 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ fs_coco_rsdos.h
+
+ Management of CoCo "RS-DOS" floppy images
+
+***************************************************************************/
+
+#ifndef MAME_FORMATS_FS_COCO_RSDOS_H
+#define MAME_FORMATS_FS_COCO_RSDOS_H
+
+#pragma once
+
+#include "fsmgr.h"
+
+namespace fs {
+
+class coco_rsdos_image : public manager_t {
+public:
+ coco_rsdos_image() = default;
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+
+ virtual std::vector<meta_description> file_meta_description() const override;
+};
+
+
+extern const coco_rsdos_image COCO_RSDOS;
+
+} // namespace fs
+
+#endif // MAME_FORMATS_FS_COCO_RSDOS_H
diff --git a/src/lib/formats/fs_fat.cpp b/src/lib/formats/fs_fat.cpp
new file mode 100644
index 00000000000..8e47bbc5abe
--- /dev/null
+++ b/src/lib/formats/fs_fat.cpp
@@ -0,0 +1,1353 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods,Wilbert Pol
+/***************************************************************************
+
+ fs_fat.cpp
+
+ PC FAT disk images
+
+ Current Limitations:
+ - Only supports floppy disks
+ - No FAT32 support
+ - No Long Filenames Support
+
+ Removal of files is untested; floptool does not have a command to delete
+ a file.
+
+*****************************************************************************
+
+ Master boot record format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 446 Boot machine code
+ 446 16 Partion #1 info
+ 462 16 Partion #2 info
+ 478 16 Partion #3 info
+ 494 16 Partion #4 info
+ 510 2 Magic bytes (0x55 0xAA)
+
+
+ Partition info format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 1 Active byte (0x80=active 0x00=inactive)
+ 1 1 Starting head
+ 2 1 Starting sector (bits 5-0) and high bits of starting track (bits 6-5)
+ 3 1 Low bits of starting track
+ 4 1 Partition type:
+ 0x00 Unused
+ 0x?1 FAT12 (0-15 MB)
+ 0x?2 XENIX
+ 0x?4 FAT16 (16-32 MB)
+ 0x?6 FAT16` (32 MB-2 GB)
+ 0x?7 HPFS or NTFS
+ 0x?A Boot Manager
+ 0x?B FAT32 (512 MB-2 TB)
+ 0x?C FAT32 (512 MB-2 TB LBA)
+ 0x1? OS/2 Boot manager/Win95 hidden
+ 0xC? DR-DOS secured partition
+ 0xD? Multiuser DOS secured partition
+ 0xE? SpeedStor extended partition
+ 5 1 Ending head
+ 6 1 Ending sector (bits 5-0) and high bits of ending track (bits 6-5)
+ 7 1 Low bits of ending track
+ 8 4 Sector index of beginning of partition
+ 12 4 Total sectors in partition
+
+
+ Boot sector format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 3 Jump instruction (to skip over header on boot)
+ 3 8 OEM Name
+ 11 2 Bytes per sector
+ 13 1 Sectors per cluster
+ 14 2 Reserved sector count (including boot sector)
+ 16 1 Number of FATs (file allocation tables)
+ 17 2 Number of root directory entries
+ 19 2 Total sectors (0 if 0x10000 or more)
+ 21 1 Media descriptor
+ 22 2 Sectors per FAT
+ 24 2 Sectors per track
+ 26 2 Number of heads
+ 28 4 Hidden sectors
+ 32 4 Total sectors (0 if less than 0x10000)
+ 36 1 Physical drive number
+ 37 1 Current head
+ 38 1 Signature
+ 39 4 ID
+ 43 11 Volume Label
+ 54 8 FAT file system type
+ 62 448 Boot machine code
+ 510 2 Magic bytes (0x55 0xAA)
+
+ For more information:
+ http://support.microsoft.com/kb/q140418/
+
+
+ Directory Entry Format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 8 DOS File Name (padded with spaces)
+ 8 3 DOS File Extension (padded with spaces)
+ 11 1 File Attributes
+ 12 2 Unknown
+ 14 4 Time of Creation
+ 18 2 Last Access Time
+ 20 2 EA-Index (OS/2 stuff)
+ 22 4 Last Modified Time
+ 26 2 First Cluster
+ 28 4 File Size
+
+
+ Dates and times are stored in separate words; when together, the time is
+ first and the date is second.
+
+ Time:
+ bits 15-11 Hour
+ bits 10- 5 Minute
+ bits 4- 0 Second / 2
+
+ Date:
+ bits 15- 9 Year - 1980
+ bits 8- 5 Month
+ bits 4- 0 Day
+
+ LFN Entry Format:
+
+ Offset Length Description
+ ------ ------ -----------
+ 0 1 Sequence Number (bit 6 is set on highest sequence)
+ 1 10 Name characters (five UTF-16LE chars)
+ 11 1 Attributes (always 0x0F)
+ 12 1 Reserved (always 0x00)
+ 13 1 Checksum of short filename entry
+ 14 12 Name characters (six UTF-16LE chars)
+ 26 2 Entry Cluster (always 0x00)
+ 28 4 Name characters (two UTF-16LE chars)
+
+ Valid characters in DOS file names:
+ - Upper case letters A-Z
+ - Numbers 0-9
+ - Space (though there is no way to identify a trailing space)
+ - ! # $ % & ( ) - @ ^ _ ` { } ~
+ - Characters 128-255 (though the code page is indeterminate)
+
+ For more information:
+ http://en.wikipedia.org/wiki/File_Allocation_Table
+
+****************************************************************************/
+
+#include "fs_fat.h"
+#include "fsblk.h"
+#include "pc_dsk.h"
+
+#include "strformat.h"
+
+#include <optional>
+#include <regex>
+#include <string_view>
+
+
+using namespace fs;
+
+const fs::pc_fat_image fs::PC_FAT;
+
+//**************************************************************************
+// TYPE DECLARATIONS
+//**************************************************************************
+
+namespace {
+
+// ======================> directory_entry
+
+class directory_entry
+{
+public:
+ static constexpr int SIZE = 32;
+ static constexpr int OFFSET_FNAME = 0;
+ static constexpr int FNAME_LENGTH = 11;
+ static constexpr int OFFSET_ATTRIBUTES = 11;
+ static constexpr int OFFSET_CREATE_DATETIME = 14;
+ static constexpr int OFFSET_START_CLUSTER_HI = 20;
+ static constexpr int OFFSET_MODIFIED_DATETIME = 22;
+ static constexpr int OFFSET_START_CLUSTER = 26;
+ static constexpr int OFFSET_FILE_SIZE = 28;
+ static constexpr u8 DELETED_FILE_MARKER = 0xe5;
+ static constexpr u8 ATTR_READ_ONLY = 0x01;
+ static constexpr u8 ATTR_HIDDEN = 0x02;
+ static constexpr u8 ATTR_SYSTEM = 0x04;
+ static constexpr u8 ATTR_VOLUME_LABEL = 0x08;
+ static constexpr u8 ATTR_DIRECTORY = 0x10;
+ static constexpr u8 ATTR_ARCHIVE = 0x20;
+
+ directory_entry(fsblk_t::block_t::ptr block, u32 offset)
+ : m_block(block)
+ , m_offset(offset)
+ {
+ }
+
+ std::string_view raw_stem() const { return m_block->rstr(m_offset + OFFSET_FNAME, 8); }
+ std::string_view raw_ext() const { return m_block->rstr(m_offset + OFFSET_FNAME + 8, 3); }
+ u8 attributes() const { return m_block->r8(m_offset + OFFSET_ATTRIBUTES); }
+ u32 raw_create_datetime() const { return m_block->r32l(m_offset + OFFSET_CREATE_DATETIME); }
+ u32 raw_modified_datetime() const { return m_block->r32l(m_offset + OFFSET_MODIFIED_DATETIME); }
+ u32 start_cluster() const { return ((u32)m_block->r16l(m_offset + OFFSET_START_CLUSTER_HI)) << 16 | m_block->r16l(m_offset + OFFSET_START_CLUSTER); }
+ u32 file_size() const { return m_block->r32l(m_offset + OFFSET_FILE_SIZE); }
+
+ bool is_read_only() const { return (attributes() & 0x01) != 0x00; }
+ bool is_hidden() const { return (attributes() & 0x02) != 0x00; }
+ bool is_system() const { return (attributes() & 0x04) != 0x00; }
+ bool is_volume_label() const { return (attributes() & 0x08) != 0x00; }
+ bool is_long_file_name() const { return attributes() == 0x0f; }
+ bool is_subdirectory() const { return (attributes() & 0x10) != 0x00; }
+ bool is_archive() const { return (attributes() & 0x20) != 0x00; }
+ bool is_deleted() const { return m_block->r8(m_offset) == DELETED_FILE_MARKER; }
+
+ std::string name() const;
+ meta_data metadata() const;
+
+ void set_file_size(u32 file_size) { m_block->w32l(m_offset + OFFSET_FILE_SIZE, file_size); }
+ void set_raw_modified_datetime(u32 datetime) { m_block->w32l(m_offset + OFFSET_MODIFIED_DATETIME, datetime); }
+ void mark_deleted() { m_block->w8(m_offset + OFFSET_FNAME, DELETED_FILE_MARKER); }
+
+private:
+ fsblk_t::block_t::ptr m_block;
+ u32 m_offset;
+};
+
+// ======================> directory_span
+
+class directory_span
+{
+public:
+ typedef std::unique_ptr<directory_span> ptr;
+
+ directory_span() = default;
+ virtual ~directory_span() = default;
+
+ virtual std::vector<u32> get_directory_sectors() const = 0;
+};
+
+
+// ======================> directory_entry
+
+class impl : public filesystem_t
+{
+public:
+ // ctor/dtor
+ impl(fsblk_t &blockdev, fsblk_t::block_t::ptr &&boot_sector_block, std::vector<u8> &&file_allocation_table, u32 starting_sector, u32 sector_count, u16 reserved_sector_count, u8 bits_per_fat_entry);
+ virtual ~impl() = default;
+
+ // accessors
+ fsblk_t &blockdev() { return m_blockdev; }
+ u16 bytes_per_sector() const { return m_bytes_per_sector; }
+ u32 dirents_per_sector() const { return bytes_per_sector() / directory_entry::SIZE; }
+
+ // virtuals
+ virtual meta_data volume_metadata() override;
+ virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override;
+ virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+ virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+ virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override;
+ virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
+ virtual std::error_condition remove(const std::vector<std::string> &path) override;
+
+ // methods
+ std::vector<u32> get_sectors_from_fat(const directory_entry &dirent) const;
+
+ // Boot sector settings
+ static constexpr u32 OFFSET_BYTES_PER_SECTOR = 0x0b;
+ static constexpr u32 OFFSET_CLUSTER_SECTOR_COUNT = 0x0d;
+ static constexpr u32 OFFSET_RESERVED_SECTOR_COUNT = 0x0e;
+ static constexpr u32 OFFSET_FAT_COUNT = 0x10;
+ static constexpr u32 OFFSET_DIRECTORY_ENTRY_COUNT = 0x11;
+ static constexpr u32 OFFSET_FAT_SECTOR_COUNT = 0x16;
+
+private:
+ static constexpr u32 FIRST_VALID_CLUSTER = 2;
+
+ fsblk_t::block_t::ptr m_boot_sector_block;
+ std::vector<u8> m_file_allocation_table;
+ u32 m_starting_sector;
+ u32 m_sector_count;
+ u16 m_reserved_sector_count;
+ u16 m_bytes_per_sector;
+ u16 m_root_directory_size;
+ u16 m_sectors_per_cluster;
+ u8 m_fat_count;
+ u16 m_fat_sector_count;
+ u8 m_bits_per_fat_entry;
+ u32 m_last_cluster_indicator;
+ u32 m_last_valid_cluster;
+
+ // methods
+ std::optional<directory_entry> find_entity(const std::vector<std::string> &path) const;
+ directory_span::ptr find_directory(std::vector<std::string>::const_iterator path_begin, std::vector<std::string>::const_iterator path_end) const;
+ std::optional<directory_entry> find_child(const directory_span &current_dir, std::string_view target) const;
+ template <typename T> void iterate_directory_entries(const directory_span &dir, T &&callback) const;
+ bool is_valid_short_filename(std::string const &filename);
+ std::error_condition build_direntry_filename(std::string const &filename, std::string &fname);
+ std::error_condition file_create_root(std::string &fname, u8 attributes = 0);
+ std::error_condition file_create_directory(directory_entry &dirent, std::string &fname, u8 attributes = 0);
+ std::error_condition file_create_sector(u32 sector, std::string &fname, u8 attributes);
+ std::error_condition initialize_directory(u32 directory_cluster, u32 parent_cluster);
+ std::error_condition initialize_directory_entry(fsblk_t::block_t &dirblk, u32 offset, std::string_view fname, u8 attributes, u32 start_cluster);
+ std::error_condition free_clusters(u32 start_cluster);
+ void clear_cluster_sectors(u32 cluster, u8 fill_byte);
+ u32 first_cluster_sector(u32 cluster);
+ u32 get_next_cluster(u32 cluster);
+ void set_next_cluster(u32 cluster, u32 next_cluster);
+ u32 find_free_cluster();
+
+};
+
+
+// ======================> root_directory_span
+
+class root_directory_span : public directory_span
+{
+public:
+ root_directory_span(const impl &fs, u32 first_sector, u16 directory_entry_count);
+
+ virtual std::vector<u32> get_directory_sectors() const override;
+
+private:
+ const impl & m_fs;
+ u32 m_first_sector;
+ u16 m_directory_entry_count;
+};
+
+
+// ======================> subdirectory_span
+
+class subdirectory_span : public directory_span
+{
+public:
+ subdirectory_span(const impl &fs, directory_entry &&dirent);
+
+ virtual std::vector<u32> get_directory_sectors() const override;
+
+private:
+ const impl & m_fs;
+ directory_entry m_dirent;
+};
+
+
+}
+
+
+//**************************************************************************
+// IMPLEMENTATION
+//**************************************************************************
+
+//-------------------------------------------------
+// validate_filename
+//-------------------------------------------------
+
+namespace {
+bool validate_filename(std::string_view name)
+{
+ auto const is_invalid_filename_char = [] (char ch)
+ {
+ return ch == '\0' || strchr("\\/:*?\"<>|", ch);
+ };
+
+ return !name.empty()
+ && std::find_if(name.begin(), name.end(), is_invalid_filename_char) == name.end();
+}
+
+
+//-------------------------------------------------
+// decode_fat_datetime
+//-------------------------------------------------
+
+util::arbitrary_datetime decode_fat_datetime(u32 dt)
+{
+ util::arbitrary_datetime result;
+ memset(&result, 0, sizeof(result));
+
+ result.year = ((dt >> 25) & 0x7f) + 1980;
+ result.month = (dt >> 21) & 0x0f;
+ result.day_of_month = (dt >> 16) & 0x1f;
+ result.hour = (dt >> 11) & 0x1f;
+ result.minute = (dt >> 5) & 0x3f;
+ result.second = ((dt >> 0) & 0x1f) * 2;
+ return result;
+}
+
+u32 encode_now_fat_datetime()
+{
+ auto now = util::arbitrary_datetime::now();
+
+ return u32((((now.year - 1980) & 0x7f) << 25) |
+ ((now.month & 0x0f) << 21) |
+ ((now.day_of_month & 0x1f) << 16) |
+ ((now.hour & 0x1f) << 11) |
+ ((now.minute & 0x3f) << 5) |
+ ((now.second >> 1) & 0x1f));
+}
+
+
+}
+
+//-------------------------------------------------
+// fat_image::can_format
+//-------------------------------------------------
+
+bool fs::fat_image::can_format() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// fat_image::can_read
+//-------------------------------------------------
+
+bool fs::fat_image::can_read() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// fat_image::can_write
+//-------------------------------------------------
+
+bool fs::fat_image::can_write() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// fat_image::has_rsrc
+//-------------------------------------------------
+
+bool fs::fat_image::has_rsrc() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// fat_image::directory_separator
+//-------------------------------------------------
+
+char fs::fat_image::directory_separator() const
+{
+ return '\\';
+}
+
+
+//-------------------------------------------------
+// fat_image::volume_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> fs::fat_image::volume_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_name::name, "UNTITLED", false, [] (const meta_value &m) { return validate_filename(m.as_string()); }, "Volume name");
+ results.emplace_back(meta_name::oem_name, "", false, [] (const meta_value &m) { return m.as_string().size() <= 8; }, "OEM name, up to 8 characters");
+ return results;
+}
+
+
+//-------------------------------------------------
+// fat_image::file_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> fs::fat_image::file_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_name::name, "", false, [] (const meta_value &m) { return validate_filename(m.as_string()); }, "File name");
+ results.emplace_back(meta_name::creation_date, util::arbitrary_datetime::now(), false, nullptr, "Creation time");
+ results.emplace_back(meta_name::modification_date, util::arbitrary_datetime::now(), false, nullptr, "Modification time");
+ results.emplace_back(meta_name::length, 0, true, nullptr, "Size of the file in bytes");
+ return results;
+}
+
+
+//-------------------------------------------------
+// fat_image::directory_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> fs::fat_image::directory_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_name::name, "", false, [] (const meta_value &m) { return validate_filename(m.as_string()); }, "File name");
+ results.emplace_back(meta_name::creation_date, util::arbitrary_datetime::now(), false, nullptr, "Creation time");
+ results.emplace_back(meta_name::modification_date, util::arbitrary_datetime::now(), false, nullptr, "Modification time");
+ return results;
+}
+
+
+//-------------------------------------------------
+// fat_image::mount_partition
+//-------------------------------------------------
+
+std::unique_ptr<filesystem_t> fs::fat_image::mount_partition(fsblk_t &blockdev, u32 starting_sector, u32 sector_count, u8 bits_per_fat_entry)
+{
+ // load the boot sector block and get some basic info
+ fsblk_t::block_t::ptr boot_sector_block = blockdev.get(starting_sector);
+ u16 reserved_sector_count = boot_sector_block->r16l(impl::OFFSET_RESERVED_SECTOR_COUNT);
+
+ // load all file allocation table sectors
+ u32 fat_count = boot_sector_block->r8(impl::OFFSET_FAT_COUNT);
+ u32 sectors_per_fat = boot_sector_block->r16l(impl::OFFSET_FAT_SECTOR_COUNT);
+ u16 bytes_per_sector = boot_sector_block->r16l(impl::OFFSET_BYTES_PER_SECTOR);
+ std::vector<u8> file_allocation_table;
+ file_allocation_table.reserve(fat_count * sectors_per_fat * bytes_per_sector);
+ for (auto i = 0; i < fat_count * sectors_per_fat; i++)
+ {
+ fsblk_t::block_t::ptr fatblk = blockdev.get(starting_sector + reserved_sector_count + i);
+ file_allocation_table.insert(file_allocation_table.end(), fatblk->rodata(), fatblk->rodata() + bytes_per_sector);
+ }
+
+ // and return the implementation
+ return std::make_unique<impl>(blockdev, std::move(boot_sector_block), std::move(file_allocation_table),
+ starting_sector, sector_count, reserved_sector_count, bits_per_fat_entry);
+}
+
+
+//-------------------------------------------------
+// directory_entry::name
+//-------------------------------------------------
+
+std::string directory_entry::name() const
+{
+ std::string_view stem = filesystem_t::trim_end_spaces(raw_stem());
+ std::string_view ext = filesystem_t::trim_end_spaces(raw_ext());
+ return !ext.empty()
+ ? util::string_format("%s.%s", stem, ext)
+ : std::string(stem);
+}
+
+
+//-------------------------------------------------
+// directory_entry::metadata
+//-------------------------------------------------
+
+meta_data directory_entry::metadata() const
+{
+ meta_data result;
+ result.set(meta_name::name, name());
+ result.set(meta_name::creation_date, decode_fat_datetime(raw_create_datetime()));
+ result.set(meta_name::modification_date, decode_fat_datetime(raw_modified_datetime()));
+ result.set(meta_name::length, file_size());
+ return result;
+}
+
+
+//-------------------------------------------------
+// impl ctor
+//-------------------------------------------------
+
+impl::impl(fsblk_t &blockdev, fsblk_t::block_t::ptr &&boot_sector_block, std::vector<u8> &&file_allocation_table, u32 starting_sector, u32 sector_count, u16 reserved_sector_count, u8 bits_per_fat_entry)
+ : filesystem_t(blockdev, 512)
+ , m_boot_sector_block(std::move(boot_sector_block))
+ , m_file_allocation_table(std::move(file_allocation_table))
+ , m_starting_sector(starting_sector)
+ , m_sector_count(sector_count)
+ , m_reserved_sector_count(reserved_sector_count)
+ , m_bytes_per_sector(m_boot_sector_block->r16l(OFFSET_BYTES_PER_SECTOR))
+ , m_root_directory_size(m_boot_sector_block->r16l(OFFSET_DIRECTORY_ENTRY_COUNT))
+ , m_sectors_per_cluster(m_boot_sector_block->r8(OFFSET_CLUSTER_SECTOR_COUNT))
+ , m_fat_count(m_boot_sector_block->r8(OFFSET_FAT_COUNT))
+ , m_fat_sector_count(m_boot_sector_block->r16l(OFFSET_FAT_SECTOR_COUNT))
+ , m_bits_per_fat_entry(bits_per_fat_entry)
+ , m_last_cluster_indicator(((u64)1 << bits_per_fat_entry) - 1)
+ , m_last_valid_cluster(m_last_cluster_indicator - 0x10)
+{
+ if (m_bytes_per_sector == 0)
+ m_bytes_per_sector = 512;
+}
+
+
+//-------------------------------------------------
+// impl::volume_metadata
+//-------------------------------------------------
+
+meta_data impl::volume_metadata()
+{
+ std::vector<std::string> root_path;
+ directory_span::ptr root_dir = find_directory(root_path.begin(), root_path.end());
+ assert(root_dir);
+
+ // Get the volume label from the root directory, not the extended BPB (whose name field may not be kept up-to-date even when it exists)
+ meta_data results;
+ auto const callback = [&results] (const directory_entry &dirent)
+ {
+ if (dirent.is_volume_label())
+ {
+ results.set(meta_name::name, dirent.name());
+ return true;
+ }
+ return false;
+ };
+ iterate_directory_entries(*root_dir, callback);
+ if (!results.has(meta_name::name))
+ results.set(meta_name::name, "UNTITLED");
+
+ results.set(meta_name::oem_name, m_boot_sector_block->rstr(3, 8));
+ return results;
+}
+
+
+//-------------------------------------------------
+// impl::metadata
+//-------------------------------------------------
+
+std::pair<std::error_condition, meta_data> impl::metadata(const std::vector<std::string> &path)
+{
+ std::optional<directory_entry> dirent = find_entity(path);
+ if (!dirent)
+ return std::make_pair(error::not_found, meta_data());
+
+ return std::make_pair(std::error_condition(), dirent->metadata());
+}
+
+
+//-------------------------------------------------
+// impl::directory_contents
+//-------------------------------------------------
+
+std::pair<std::error_condition, std::vector<dir_entry>> impl::directory_contents(const std::vector<std::string> &path)
+{
+ directory_span::ptr dir = find_directory(path.begin(), path.end());
+ if (!dir)
+ return std::make_pair(error::not_found, std::vector<dir_entry>());
+
+ std::vector<dir_entry> results;
+ auto const callback = [&results] (const directory_entry &dirent)
+ {
+ if (!dirent.is_volume_label())
+ {
+ dir_entry_type entry_type = dirent.is_subdirectory() ? dir_entry_type::dir : dir_entry_type::file;
+ results.emplace_back(entry_type, dirent.metadata());
+ }
+ return false;
+ };
+ iterate_directory_entries(*dir, callback);
+ return std::make_pair(std::error_condition(), std::move(results));
+}
+
+
+//-------------------------------------------------
+// impl::file_read
+//-------------------------------------------------
+
+std::pair<std::error_condition, std::vector<u8>> impl::file_read(const std::vector<std::string> &path)
+{
+ // find the file
+ std::optional<directory_entry> dirent = find_entity(path);
+ if (!dirent)
+ return std::make_pair(error::not_found, std::vector<u8>());
+ if (dirent->is_subdirectory())
+ return std::make_pair(error::invalid_name, std::vector<u8>());
+
+ // get the list of sectors for this file
+ std::vector<u32> sectors = get_sectors_from_fat(*dirent);
+
+ // prepare the results
+ std::vector<u8> result;
+ result.reserve(dirent->file_size());
+
+ // and add data from all sectors
+ for (u32 sector : sectors)
+ {
+ fsblk_t::block_t::ptr block = m_blockdev.get(sector);
+ const u8 *data = block->rodata();
+ size_t length = std::min((size_t)dirent->file_size() - result.size(), (size_t)block->size());
+ result.insert(result.end(), data, data + length);
+ }
+ return std::make_pair(std::error_condition(), std::move(result));
+}
+
+
+bool impl::is_valid_short_filename(std::string const &filename)
+{
+ /*
+ Valid characters in DOS file names:
+ - Upper case letters A-Z
+ - Numbers 0-9
+ - Space (though there is no way to identify a trailing space)
+ - ! # $ % & ( ) - @ ^ _ ` { } ~
+ - Characters 128-255, except e5 (though the code page is indeterminate)
+ We currently do not check for characters 128-255.
+ */
+ std::regex filename_regex("([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,8})(\\.([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,3}))?");
+ return std::regex_match(filename, filename_regex);
+}
+
+
+std::error_condition impl::build_direntry_filename(std::string const &filename, std::string &fname)
+{
+ std::regex filename_regex("([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,8})(\\.([A-Z0-9!#\\$%&\\(\\)\\-@^_`\\{\\}~]{0,3}))?");
+ std::smatch smatch;
+ if (!std::regex_match(filename, smatch, filename_regex))
+ return error::invalid_name;
+ if (smatch.size() != 4)
+ return error::invalid_name;
+
+ fname.resize(directory_entry::FNAME_LENGTH, ' ');
+
+ for (int i = 0; i < 8 && i < smatch.str(1).size(); i++)
+ fname[i] = smatch.str(1)[i];
+
+ for (int j = 0; j < 3 && j < smatch.str(3).size(); j++)
+ fname[8 + j] = smatch.str(3)[j];
+
+ return std::error_condition();
+}
+
+
+std::error_condition impl::file_create(const std::vector<std::string> &path, const meta_data &meta)
+{
+ std::string filename = meta.get_string(meta_name::name, "");
+ std::string fname;
+ std::error_condition err = build_direntry_filename(filename, fname);
+ if (err)
+ return err;
+
+ if (path.empty())
+ {
+ return file_create_root(fname);
+ }
+ else
+ {
+ // Make sure that all parts of the path exist, creating the path parts as needed.
+ std::optional<directory_entry> dirent = find_entity(path);
+ if (!dirent)
+ {
+ std::vector<std::string> partial_path;
+ std::optional<directory_entry> parent_entry;
+ for (auto const &path_part : path)
+ {
+ partial_path.emplace_back(path_part);
+ std::optional<directory_entry> dir_entry = find_entity(partial_path);
+ if (!dir_entry)
+ {
+ if (!is_valid_short_filename(path_part))
+ return error::invalid_name;
+
+ std::string part_fname;
+ std::error_condition err = build_direntry_filename(path_part, part_fname);
+ if (err)
+ return err;
+ err = !parent_entry ?
+ file_create_root(part_fname, directory_entry::ATTR_DIRECTORY) :
+ file_create_directory(parent_entry.value(), part_fname, directory_entry::ATTR_DIRECTORY);
+ if (err)
+ return err;
+
+ dir_entry = find_entity(partial_path);
+ if (!dir_entry)
+ return error::invalid_name;
+
+ err = initialize_directory(dir_entry->start_cluster(), parent_entry ? parent_entry->start_cluster() : 0);
+ if (err)
+ return err;
+ }
+ else
+ {
+ if (!dir_entry->is_subdirectory())
+ return error::invalid_name;
+ }
+ parent_entry = dir_entry;
+ }
+
+ dirent = find_entity(path);
+ if (!dirent)
+ return error::invalid_name;
+ }
+
+ return file_create_directory(*dirent, fname);
+ }
+}
+
+
+std::error_condition impl::initialize_directory(u32 directory_cluster, u32 parent_cluster)
+{
+ clear_cluster_sectors(directory_cluster, 0x00);
+
+ auto dirblk = m_blockdev.get(first_cluster_sector(directory_cluster));
+
+ // Add special directory entries for . and ..
+ std::string dir_fname;
+ dir_fname.resize(directory_entry::FNAME_LENGTH, ' ');
+ dir_fname[0] = '.';
+ std::error_condition err = initialize_directory_entry(*dirblk, 0, dir_fname, directory_entry::ATTR_DIRECTORY, directory_cluster);
+ if (err)
+ return err;
+
+ dir_fname[1] = '.';
+ err = initialize_directory_entry(*dirblk, directory_entry::SIZE, dir_fname, directory_entry::ATTR_DIRECTORY, parent_cluster);
+ if (err)
+ return err;
+
+ return std::error_condition();
+}
+
+
+std::error_condition impl::initialize_directory_entry(fsblk_t::block_t &dirblk, u32 offset, std::string_view fname, u8 attributes, u32 start_cluster)
+{
+ if (fname.size() != directory_entry::FNAME_LENGTH)
+ return error::invalid_name;
+
+ for (int i = 0; i < directory_entry::SIZE; i += 4)
+ dirblk.w32l(offset + i, 0);
+
+ dirblk.wstr(offset + directory_entry::OFFSET_FNAME, fname);
+ dirblk.w8(offset + directory_entry::OFFSET_ATTRIBUTES, attributes);
+ dirblk.w32l(offset + directory_entry::OFFSET_CREATE_DATETIME, encode_now_fat_datetime());
+ dirblk.w32l(offset + directory_entry::OFFSET_MODIFIED_DATETIME, encode_now_fat_datetime());
+ dirblk.w16l(offset + directory_entry::OFFSET_START_CLUSTER_HI, u16(start_cluster >> 16));
+ dirblk.w16l(offset + directory_entry::OFFSET_START_CLUSTER, u16(start_cluster & 0xffff));
+
+ return std::error_condition();
+}
+
+
+std::error_condition impl::file_create_root(std::string &fname, u8 attributes)
+{
+ const u32 first_directory_sector = m_starting_sector + m_reserved_sector_count + ((u32)m_file_allocation_table.size() / m_bytes_per_sector);
+ const u32 directory_sector_count = (m_root_directory_size * directory_entry::SIZE) / m_bytes_per_sector;
+ for (u32 sector = first_directory_sector; sector < first_directory_sector + directory_sector_count; sector++)
+ {
+ std::error_condition err = file_create_sector(sector, fname, attributes);
+ if (err != error::not_found)
+ return err;
+ }
+ return error::no_space;
+}
+
+
+std::error_condition impl::file_create_directory(directory_entry &dirent, std::string &fname, u8 attributes)
+{
+ u32 current_cluster = dirent.start_cluster();
+ do {
+ const u32 first_sector = first_cluster_sector(current_cluster);
+ for (int i = 0; i < m_sectors_per_cluster; i++)
+ {
+ std::error_condition err = file_create_sector(first_sector + i, fname, attributes);
+ if (err != error::not_found)
+ return err;
+ }
+
+ // File could not be created yet. Move to next cluster, allocating a new cluster when needed.
+ u32 next_cluster = get_next_cluster(current_cluster);
+ if (next_cluster >= m_last_valid_cluster)
+ {
+ next_cluster = find_free_cluster();
+ if (next_cluster == 0)
+ return error::no_space;
+
+ set_next_cluster(current_cluster, next_cluster);
+ set_next_cluster(next_cluster, m_last_cluster_indicator);
+
+ clear_cluster_sectors(next_cluster, 0x00);
+ }
+ current_cluster = next_cluster;
+ } while (current_cluster > FIRST_VALID_CLUSTER && current_cluster < m_last_valid_cluster);
+ return error::no_space;
+}
+
+
+u32 impl::first_cluster_sector(u32 cluster)
+{
+ return m_starting_sector + m_reserved_sector_count +
+ ((u32)m_file_allocation_table.size() / m_bytes_per_sector) +
+ ((m_root_directory_size + 1) / dirents_per_sector()) +
+ ((cluster - FIRST_VALID_CLUSTER) * m_sectors_per_cluster);
+}
+
+
+void impl::clear_cluster_sectors(u32 cluster, u8 fill_byte)
+{
+ const u32 sector = first_cluster_sector(cluster);
+ for (int i = 0; i < m_sectors_per_cluster; i++)
+ {
+ auto dirblk = m_blockdev.get(sector + i);
+ for (int offset = 0; offset < m_bytes_per_sector; offset++)
+ dirblk->w8(offset, fill_byte);
+ }
+}
+
+
+// Returns error::not_found when no room could be found to create the file in the sector.
+std::error_condition impl::file_create_sector(u32 sector, std::string &fname, u8 attributes)
+{
+ auto dirblk = m_blockdev.get(sector);
+ for (u32 blkoffset = 0; blkoffset < m_bytes_per_sector; blkoffset += directory_entry::SIZE)
+ {
+ u8 first_byte = dirblk->r8(blkoffset);
+ if (first_byte == 0x00 || first_byte == directory_entry::DELETED_FILE_MARKER)
+ {
+ u32 start_cluster = find_free_cluster();
+ if (start_cluster == 0)
+ return error::no_space;
+ set_next_cluster(start_cluster, m_last_cluster_indicator);
+
+ std::error_condition err = initialize_directory_entry(*dirblk, blkoffset, fname, attributes, start_cluster);
+ if (err)
+ return err;
+
+ return std::error_condition();
+ }
+ }
+ return error::not_found;
+}
+
+
+std::error_condition impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
+{
+ std::optional<directory_entry> dirent = find_entity(path);
+ if (!dirent)
+ return error::not_found;
+
+ if (dirent->is_subdirectory())
+ return error::invalid_name;
+
+ u32 current_length = dirent->file_size();
+ const size_t data_length = data.size();
+ const u32 bytes_per_cluster = m_sectors_per_cluster * bytes_per_sector();
+ const u32 current_clusters = (current_length + bytes_per_cluster - 1) / bytes_per_cluster;
+ const u32 required_clusters = (data_length + bytes_per_cluster - 1) / bytes_per_cluster;
+
+ if (required_clusters > current_clusters)
+ {
+ u32 current_cluster = dirent->start_cluster();
+ u32 next_cluster = 0;
+ do {
+ next_cluster = get_next_cluster(current_cluster);
+ if (next_cluster < FIRST_VALID_CLUSTER)
+ return error::invalid_block;
+ } while (next_cluster < m_last_valid_cluster);
+ for (int i = current_clusters; i < required_clusters; i++)
+ {
+ u32 free_cluster = find_free_cluster();
+ if (free_cluster < FIRST_VALID_CLUSTER)
+ return error::no_space;
+
+ set_next_cluster(current_cluster, free_cluster);
+ set_next_cluster(free_cluster, m_last_cluster_indicator);
+ current_cluster = free_cluster;
+ }
+ }
+ if (required_clusters < current_clusters)
+ {
+ u32 current_cluster = dirent->start_cluster();
+ for (int i = 0; i < required_clusters; i++)
+ {
+ current_cluster = get_next_cluster(current_cluster);
+ }
+ u32 next_cluster = get_next_cluster(current_cluster);
+ set_next_cluster(current_cluster, m_last_cluster_indicator);
+
+ std::error_condition err = free_clusters(next_cluster);
+ if (err)
+ return err;
+ }
+
+ auto sectors = get_sectors_from_fat(*dirent);
+ size_t offset = 0;
+ for (auto sector : sectors)
+ {
+ if (offset < data_length)
+ {
+ auto datablk = m_blockdev.get(sector);
+ u32 bytes = (data_length - offset > m_bytes_per_sector) ? m_bytes_per_sector : data_length - offset;
+ datablk->write(0, data.data() + offset, bytes);
+ offset += m_bytes_per_sector;
+ }
+ }
+
+ dirent->set_raw_modified_datetime(encode_now_fat_datetime());
+ dirent->set_file_size(data_length);
+
+ return std::error_condition();
+}
+
+
+std::error_condition impl::remove(const std::vector<std::string> &path)
+{
+ if (path.size() != 0)
+ return error::unsupported;
+
+ std::optional<directory_entry> dirent = find_entity(path);
+ if (!dirent)
+ return std::error_condition();
+
+ // Removing directories is not supported yet
+ if (dirent->is_subdirectory())
+ return error::unsupported;
+
+ dirent->mark_deleted();
+
+ return std::error_condition();
+}
+
+
+std::error_condition impl::free_clusters(u32 start_cluster)
+{
+ while (start_cluster < m_last_valid_cluster)
+ {
+ if (start_cluster < FIRST_VALID_CLUSTER)
+ return error::invalid_block;
+
+ u32 next_cluster = get_next_cluster(start_cluster);
+ set_next_cluster(start_cluster, 0);
+ start_cluster = next_cluster;
+ }
+ return std::error_condition();
+}
+
+//-------------------------------------------------
+// impl::get_sectors_from_fat
+//-------------------------------------------------
+
+std::vector<u32> impl::get_sectors_from_fat(const directory_entry &dirent) const
+{
+ // prepare results
+ std::vector<u32> results;
+ results.reserve(dirent.file_size() / bytes_per_sector());
+
+ // get critical information
+ u16 root_directory_sector_count = (m_root_directory_size + 1) / dirents_per_sector();
+ u32 fat_sector_count = m_fat_count * m_fat_sector_count;
+ u32 data_starting_sector = m_starting_sector + m_reserved_sector_count + fat_sector_count + root_directory_sector_count;
+ u32 data_cluster_count = (m_sector_count - data_starting_sector) / m_sectors_per_cluster;
+
+ // find all clusters
+ u32 start_cluster_mask = ((u64)1 << m_bits_per_fat_entry) - 1;
+ u32 cluster = dirent.start_cluster() & start_cluster_mask;
+
+ while (cluster >= FIRST_VALID_CLUSTER && cluster < (data_cluster_count + 2))
+ {
+ // add the sectors for this cluster
+ for (auto i = 0; i < m_sectors_per_cluster; i++)
+ results.push_back(data_starting_sector + (cluster - FIRST_VALID_CLUSTER) * m_sectors_per_cluster + i);
+
+ // determine the bit position of this entry
+ u32 entry_bit_position = cluster * m_bits_per_fat_entry;
+
+ // sanity check; check for overflows
+ u32 new_cluster = 0;
+ if (entry_bit_position + m_bits_per_fat_entry <= m_file_allocation_table.size() * 8)
+ {
+ // this awkward logic is here because we cannot rely on FAT entries all being in one
+ // sector (thank you FAT12)
+ u32 current_bit = 0;
+ while (current_bit < m_bits_per_fat_entry)
+ {
+ u32 pos = entry_bit_position + current_bit;
+ u32 shift = pos % 8;
+ u32 bit_count = std::min(8 - shift, m_bits_per_fat_entry - current_bit);
+ u32 bits = (m_file_allocation_table[pos / 8] >> shift) & ((1 << bit_count) - 1);
+
+ new_cluster |= (bits << current_bit);
+ current_bit += bit_count;
+ }
+
+ // normalize special cluster IDs
+ if (new_cluster > m_last_valid_cluster)
+ new_cluster |= ~m_last_cluster_indicator;
+ }
+ cluster = new_cluster;
+ }
+
+ return results;
+}
+
+
+u32 impl::get_next_cluster(u32 cluster)
+{
+ u32 entry_bit_position = cluster * m_bits_per_fat_entry;
+ u32 new_cluster = 0;
+ if (entry_bit_position + m_bits_per_fat_entry <= m_file_allocation_table.size() * 8)
+ {
+ u32 current_bit = 0;
+ while (current_bit < m_bits_per_fat_entry)
+ {
+ u32 pos = entry_bit_position + current_bit;
+ u32 shift = pos % 8;
+ u32 bit_count = std::min(8 - shift, m_bits_per_fat_entry - current_bit);
+ u32 bits = (m_file_allocation_table[pos / 8] >> shift) & ((1 << bit_count) - 1);
+
+ new_cluster |= (bits << current_bit);
+ current_bit += bit_count;
+ }
+ }
+ return new_cluster;
+}
+
+
+void impl::set_next_cluster(u32 cluster, u32 next_cluster)
+{
+ const u32 m_fat_start_sector = m_starting_sector + m_reserved_sector_count;
+ const u32 entry_bit_position = cluster * m_bits_per_fat_entry;
+ if (entry_bit_position + m_bits_per_fat_entry <= m_file_allocation_table.size() * 8)
+ {
+ u32 current_bit = 0;
+ while (current_bit < m_bits_per_fat_entry)
+ {
+ u32 pos = entry_bit_position + current_bit;
+ u32 shift = pos % 8;
+ u32 bit_count = std::min(8 - shift, m_bits_per_fat_entry - current_bit);
+ u32 byte_pos = pos / 8;
+ u32 mask = ((1 << bit_count) - 1);
+ m_file_allocation_table[byte_pos] = (m_file_allocation_table[byte_pos] & ~(mask << shift)) | ((next_cluster & mask) << shift);
+ next_cluster = next_cluster >> bit_count;
+ current_bit += bit_count;
+ // Write back to backing blocks
+ for (int i = 0; i < m_fat_count; i++)
+ {
+ u32 fat_sector = m_fat_start_sector + (i * m_fat_sector_count) + (byte_pos / m_bytes_per_sector);
+ auto fatblk = m_blockdev.get(fat_sector);
+ fatblk->w8(byte_pos % m_bytes_per_sector, m_file_allocation_table[byte_pos]);
+ }
+ }
+ }
+}
+
+
+// Returns 0 if no free cluster could be found
+u32 impl::find_free_cluster()
+{
+ u16 root_directory_sector_count = (m_root_directory_size + 1) / dirents_per_sector();
+ u32 fat_sector_count = m_fat_count * m_fat_sector_count;
+ u32 data_starting_sector = m_starting_sector + m_reserved_sector_count + fat_sector_count + root_directory_sector_count;
+ u32 data_cluster_count = (m_sector_count - data_starting_sector) / m_sectors_per_cluster;
+
+ for (u32 cluster = FIRST_VALID_CLUSTER; cluster < (data_cluster_count + 2); cluster++)
+ {
+ u32 entry_bit_position = cluster * m_bits_per_fat_entry;
+
+ if (entry_bit_position + m_bits_per_fat_entry <= m_file_allocation_table.size() * 8)
+ {
+ u32 new_cluster = 0;
+ u32 current_bit = 0;
+ while (current_bit < m_bits_per_fat_entry)
+ {
+ u32 pos = entry_bit_position + current_bit;
+ u32 shift = pos % 8;
+ u32 bit_count = std::min(8 - shift, m_bits_per_fat_entry - current_bit);
+ u32 bits = (m_file_allocation_table[pos / 8] >> shift) & ((1 << bit_count) - 1);
+
+ new_cluster |= (bits << current_bit);
+ current_bit += bit_count;
+ }
+
+ if (new_cluster == 0)
+ return cluster;
+ }
+ }
+ return 0;
+}
+
+//-------------------------------------------------
+// impl::find_entity
+//-------------------------------------------------
+
+std::optional<directory_entry> impl::find_entity(const std::vector<std::string> &path) const
+{
+ // special case; reject empty paths
+ if (path.empty())
+ return { };
+
+ // find the containing directory
+ directory_span::ptr dir = find_directory(path.begin(), path.end() - 1);
+ if (!dir)
+ return { };
+
+ // find the last child
+ return find_child(*dir, path[path.size() - 1]);
+}
+
+
+//-------------------------------------------------
+// impl::find_directory
+//-------------------------------------------------
+
+directory_span::ptr impl::find_directory(std::vector<std::string>::const_iterator path_begin, std::vector<std::string>::const_iterator path_end) const
+{
+ // the root directory is treated differently
+ u32 first_sector = m_starting_sector + m_reserved_sector_count + (u32)m_file_allocation_table.size() / m_bytes_per_sector;
+ directory_span::ptr current_dir = std::make_unique<root_directory_span>(*this, first_sector, m_root_directory_size);
+
+ // traverse the directory
+ for (auto iter = path_begin; iter != path_end; iter++)
+ {
+ // find the child file
+ std::optional<directory_entry> child_directory = find_child(*current_dir, *iter);
+ if (!child_directory)
+ return { };
+
+ // advance into the child directory
+ current_dir = std::make_unique<subdirectory_span>(*this, std::move(*child_directory));
+ }
+
+ return current_dir;
+}
+
+
+//-------------------------------------------------
+// impl::find_child
+//-------------------------------------------------
+
+std::optional<directory_entry> impl::find_child(const directory_span &current_dir, std::string_view target) const
+{
+ std::optional<directory_entry> result;
+ auto const callback = [&result, target] (const directory_entry &dirent)
+ {
+ bool found = dirent.name() == target;
+ if (found)
+ result = dirent;
+ return found;
+ };
+ iterate_directory_entries(current_dir, callback);
+ return result;
+}
+
+
+//-------------------------------------------------
+// impl::iterate_directory_entries
+//-------------------------------------------------
+
+template <typename T>
+void impl::iterate_directory_entries(const directory_span &dir, T &&callback) const
+{
+ std::vector<u32> sectors = dir.get_directory_sectors();
+ for (u32 sector : sectors)
+ {
+ bool done = false;
+ fsblk_t::block_t::ptr block = m_blockdev.get(sector);
+ for (u32 index = 0; !done && (index < dirents_per_sector()); index++)
+ {
+ directory_entry dirent(block, index * 32);
+ if (dirent.raw_stem()[0] != 0x00 && !dirent.is_deleted() && !dirent.is_long_file_name())
+ {
+ // get the filename
+ std::string_view stem = trim_end_spaces(dirent.raw_stem());
+ std::string_view ext = trim_end_spaces(dirent.raw_ext());
+ if (ext.empty() && (stem == "." || stem == ".."))
+ continue;
+
+ // invoke the callback
+ done = callback(dirent);
+ }
+ }
+
+ if (done)
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// root_directory_span ctor
+//-------------------------------------------------
+
+root_directory_span::root_directory_span(const impl &fs, u32 first_sector, u16 directory_entry_count)
+ : m_fs(fs)
+ , m_first_sector(first_sector)
+ , m_directory_entry_count(directory_entry_count)
+{
+}
+
+
+//-------------------------------------------------
+// root_directory_span::get_directory_sectors
+//-------------------------------------------------
+
+std::vector<u32> root_directory_span::get_directory_sectors() const
+{
+ u32 directory_sector_count = (m_directory_entry_count + m_fs.dirents_per_sector() - 1) / m_fs.dirents_per_sector();
+
+ std::vector<u32> result;
+ result.reserve(directory_sector_count);
+ for (auto i = 0; i < directory_sector_count; i++)
+ result.push_back(m_first_sector + i);
+ return result;
+}
+
+
+//-------------------------------------------------
+// subdirectory_span ctor
+//-------------------------------------------------
+
+subdirectory_span::subdirectory_span(const impl &fs, directory_entry &&dirent)
+ : m_fs(fs)
+ , m_dirent(std::move(dirent))
+{
+}
+
+
+//-------------------------------------------------
+// subdirectory_span::get_directory_sectors
+//-------------------------------------------------
+
+std::vector<u32> subdirectory_span::get_directory_sectors() const
+{
+ return m_fs.get_sectors_from_fat(m_dirent);
+}
+
+
+//**************************************************************************
+// PC FAT SPECIFIC
+//**************************************************************************
+
+//-------------------------------------------------
+// pc_fat_image::name
+//-------------------------------------------------
+
+const char *fs::pc_fat_image::name() const
+{
+ return "pc_fat";
+}
+
+
+//-------------------------------------------------
+// pc_fat_image::description
+//-------------------------------------------------
+
+const char *fs::pc_fat_image::description() const
+{
+ return "PC FAT";
+}
+
+
+//-------------------------------------------------
+// pc_fat_image::enumerate_f
+//-------------------------------------------------
+
+void pc_fat_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_35, floppy_image::DSSD, 368640, "pc_fat_dssd", "PC FAT 3.5\" dual-sided single density");
+ fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 737280, "pc_fat_dsdd", "PC FAT 3.5\" dual-sided double density");
+ fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_35, floppy_image::DSHD, 1474560, "pc_fat_dshd", "PC FAT 3.5\" dual-sided high density");
+ fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_35, floppy_image::DSED, 2949120, "pc_fat_dsed", "PC FAT 3.5\" dual-sided extra density");
+ fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::SSDD, 163840, "pc_fat_525ssdd_8", "PC FAT 5.25\" single-sided double density, 8 sectors/track");
+ fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::SSDD, 184320, "pc_fat_525ssdd", "PC FAT 5.25\" single-sided double density, 9 sectors/track");
+ fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::DSDD, 327680, "pc_fat_525dsdd_8", "PC FAT 5.25\" dual-sided double density, 8 sectors/track");
+ fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::DSDD, 368640, "pc_fat_525dsdd", "PC FAT 5.25\" dual-sided double density, 9 sectors/track");
+ fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::DSQD, 737280, "pc_fat_525dsqd", "PC FAT 5.25\" dual-sided quad density");
+ fe.add(FLOPPY_PC_FORMAT, floppy_image::FF_525, floppy_image::DSHD, 1228800, "pc_fat_525dshd", "PC FAT 5.25\" dual-sided high density");
+}
+
+
+//-------------------------------------------------
+// pc_fat_image::mount
+//-------------------------------------------------
+
+std::unique_ptr<filesystem_t> pc_fat_image::mount(fsblk_t &blockdev) const
+{
+ blockdev.set_block_size(512);
+ return mount_partition(blockdev, 0, blockdev.block_count(), 12);
+}
diff --git a/src/lib/formats/fs_fat.h b/src/lib/formats/fs_fat.h
new file mode 100644
index 00000000000..0466cf0ffaf
--- /dev/null
+++ b/src/lib/formats/fs_fat.h
@@ -0,0 +1,60 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ fs_fat.h
+
+ PC FAT disk images
+
+***************************************************************************/
+
+#ifndef MAME_FORMATS_FS_FAT_H
+#define MAME_FORMATS_FS_FAT_H
+
+#pragma once
+
+#include "fsmgr.h"
+
+namespace fs {
+
+using u8 = uint8_t;
+
+// ======================> fat_image
+
+class fat_image : public manager_t {
+public:
+ fat_image() = default;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+ virtual char directory_separator() const override;
+
+ virtual std::vector<meta_description> volume_meta_description() const override;
+ virtual std::vector<meta_description> file_meta_description() const override;
+ virtual std::vector<meta_description> directory_meta_description() const override;
+
+protected:
+ static std::unique_ptr<filesystem_t> mount_partition(fsblk_t &blockdev, u32 starting_sector, u32 sector_count, u8 bits_per_fat_entry);
+};
+
+
+// ======================> fat_image
+
+class pc_fat_image : public fat_image {
+public:
+ pc_fat_image() = default;
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+};
+
+
+extern const pc_fat_image PC_FAT;
+
+} // namespace fs
+
+#endif // MAME_FORMATS_FS_FAT_H
diff --git a/src/lib/formats/fs_hp98x5.cpp b/src/lib/formats/fs_hp98x5.cpp
new file mode 100644
index 00000000000..f070d01b30f
--- /dev/null
+++ b/src/lib/formats/fs_hp98x5.cpp
@@ -0,0 +1,1552 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ fs_hp98x5.cpp
+
+ HP 9825, 9831 & 9845 filesystem handling
+
+ HP9825,HP9831 and HP9845 have very similar filesystems for disk
+ storage (storage on tape is an entirely different story, each
+ system has its own format).
+ The main reference for 9845 FS comes from A. Kueckes page at
+ https://www.hp9845.net/9845/projects/hpdir
+ For what concerns the 9825 FS, I mainly reverse engineered it
+ from assembly sources of mass storage ROMs.
+ File names in 98x5 filesystems have no extension. In this driver
+ I'm using a virtual extensions to provide a more user-friendly
+ encoding of the numeric file type,
+ I've added the "TXT" extension to handle the very common string-only
+ DATA files. This kind of DATA files emulates text files on
+ 9825/45 systems. When reading or writing TXT files, ASCII text is
+ automatically translated to/from DATA files (see also
+ src/tools/imgtool/modules/hp9845_tape.cpp for the same function
+ on DC100 tapes).
+ The general form of file name is:
+ name[[.&bpr].ext]
+ Where "name" is the filename itself (1 to 6 characters), "bpr" is
+ the optional record size and "ext" is the virtual extension.
+ BPR applies to 9845 only. It encodes the record size attribute
+ of files (in bytes, 4 <= bpr <= 32766, even values only).
+ BPR and ext are optional when reading a file. If specified they
+ have to match file metadata, though.
+ Extension is required when file is being created. It's also
+ required for DATA<->TXT translation (see above).
+ BPR is optional when creating a file, it defaults to 256 bytes
+ when missing.
+ This form of file names comes from hpdir tool (see URL above).
+ Examples:
+ - Create a text file having 1024 bytes-per-record:
+ "FILE.&1024.TXT"
+ - Read a data file as text:
+ "FILE.TXT"
+ - Read/write an existing program file:
+ "FILE[.PROG]"
+
+ This driver generates the spare track when formatting and updates
+ it when writing. If the main directory or availability table
+ are corrupted, though, no attempt is made to use their 2nd copy
+ from spare track.
+
+ NB: This driver doesn't add the so-called "bootstraps" when
+ formatting a HP9825 disk. For this reason the created image
+ cannot be used with HP98217 mass storage ROM which relies on
+ bootstraps. Pre-formatted images (such as the one available at
+ http://www.hpmuseum.net/software/9825_discs.zip) should be
+ used with this ROM. Bootstraps, when present, are completely
+ ignored by this driver.
+
+*********************************************************************/
+
+#include "fs_hp98x5.h"
+#include "fsblk.h"
+#include "hpi_dsk.h"
+
+#include "strformat.h"
+
+#include <bitset>
+#include <map>
+
+using namespace fs;
+using namespace std::literals;
+
+// Constants
+constexpr unsigned SECTORS = hpi_format::HPI_SECTORS;
+constexpr unsigned SECTOR_SIZE = hpi_format::HPI_SECTOR_SIZE;
+constexpr unsigned DS_SECTORS = hpi_format::HPI_TRACKS * hpi_format::HPI_HEADS * SECTORS;
+constexpr unsigned DS_IMAGE_SIZE = DS_SECTORS * SECTOR_SIZE;
+constexpr unsigned SS_SECTORS = hpi_format::HPI_9885_TRACKS * SECTORS;
+constexpr unsigned SS_IMAGE_SIZE = SS_SECTORS * SECTOR_SIZE;
+
+// +--------+
+// | HP9825 |
+// | HP9831 |
+// | HP9845 |
+// +--------+
+namespace fs {
+ const hp9825_image HP9825;
+ const hp9831_image HP9831;
+ const hp9845_image HP9845;
+}
+
+// +-------------+
+// | hp98x5_impl |
+// +-------------+
+namespace {
+ class hp98x5_impl : public filesystem_t {
+ public:
+ enum fs_type {
+ FS_TYPE_9825,
+ FS_TYPE_9831,
+ FS_TYPE_9845,
+ FS_COUNT
+ };
+
+ hp98x5_impl(fsblk_t& blockdev, fs_type type);
+ virtual ~hp98x5_impl() = default;
+
+ virtual meta_data volume_metadata() override;
+
+ virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override;
+
+ virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+
+ virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override;
+
+ virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+
+ virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
+
+ virtual std::error_condition format(const meta_data &meta) override;
+
+ private:
+ // Map of used/free sectors
+ using sect_map = std::bitset<DS_SECTORS>;
+ // 0-based LBAs
+ using lba_t = int;
+
+ // Position of system record
+ static constexpr lba_t SYS_REC_LBA = 0;
+ // Size of dir. entries
+ static constexpr unsigned DIR_ENTRY_SIZE = 16;
+ // Unspecified file type
+ static constexpr u8 NO_FILE_TYPE = 0xff;
+ // Special file type: DATA file as a text file
+ static constexpr u8 TEXT_TYPE = 0xfe;
+ // DATA file type on 9825/9831
+ static constexpr u8 DATA_TYPE_9825 = 1;
+ // DATA file type on 9845
+ static constexpr u8 DATA_TYPE_9845 = 8;
+ // Record size of DATA files on 9825
+ static constexpr unsigned BPR_9825 = 256;
+ // Default record size
+ static constexpr unsigned DEF_BPR = 256;
+ // Special extension for text files
+ inline static const std::string_view TEXT_EXT = "TXT"sv;
+ // DATA record types
+ static constexpr u16 REC_TYPE_EOR = 0x1e; // End-of-record
+ static constexpr u16 REC_TYPE_EOF = 0x3e; // End-of-file
+ static constexpr u16 REC_TYPE_FULLSTR = 0x3c; // A whole (un-split) string
+ static constexpr u16 REC_TYPE_1STSTR = 0x1c; // First part of a string
+ static constexpr u16 REC_TYPE_MIDSTR = 0x0c; // Middle part(s) of a string
+ static constexpr u16 REC_TYPE_ENDSTR = 0x2c; // Last part of a string
+ // Sys flag value for DATA files (HP9845)
+ static constexpr u8 SYS_FLAG_DATA = 1;
+ // Sys flag value for non-DATA files (HP9845)
+ static constexpr u8 SYS_FLAG_NON_DATA = 6;
+ // Spare track when formatting
+ static constexpr unsigned FMT_SPARE_TRACK = 1;
+ // Directory size when formatting (sectors)
+ static constexpr unsigned FMT_DIR_SIZE = 22;
+ // Av. table size when formatting (sectors)
+ static constexpr unsigned FMT_MAP_SIZE = 6;
+ // # of system tracks when formatting
+ static constexpr unsigned FMT_SYS_TRACKS = 2;
+ // # of data tracks when formatting
+ static constexpr unsigned FMT_DATA_TRACKS_SS = 65;
+ static constexpr unsigned FMT_DATA_TRACKS_DS = 148;
+
+ // Directory entries
+ struct entry {
+ entry()
+ : m_in_use(false)
+ , m_name{}
+ , m_type_code{0}
+ , m_size{0}
+ , m_sectors{0}
+ , m_bpr{0}
+ , m_1st_sect{0}
+ , m_extra_w{0}
+ , m_attrs{0}
+ { }
+
+ // In use?
+ bool m_in_use;
+ // Filename
+ std::string m_name;
+ // Type code
+ u8 m_type_code;
+ // Size (bytes)
+ unsigned m_size;
+ // Size in sectors
+ unsigned m_sectors;
+ // Record size (bytes-per-record)
+ unsigned m_bpr;
+ // First sector of data
+ lba_t m_1st_sect;
+ // Extra word
+ u16 m_extra_w;
+ // Various attributes
+ u8 m_attrs;
+ };
+
+ using dir_t = std::vector<entry>;
+
+ std::error_condition parse_filename(const std::string& name, std::string& basename, int& bpr, u8& file_type) const;
+ bool validate_filename(const std::string& s) const;
+ void ensure_dir_loaded();
+ std::pair<dir_t, sect_map> decode_dir() const;
+ std::vector<u8> encode_dir() const;
+ bool is_data_file(u8 type_code) const;
+ void store_dir_map();
+ static dir_t::iterator scan_dir(dir_t& dir, const std::string& name);
+ dir_t::iterator find_file(const std::string& basename, int bpr, u8 file_type);
+ meta_data get_metadata(const entry& e);
+ sect_map no_data_map() const;
+ sect_map decode_map() const;
+ std::vector<u8> encode_map() const;
+ void set_not_in_use(lba_t first, unsigned sectors);
+ bool allocate(unsigned sectors, lba_t& first);
+ bool is_valid_char(u8 c) const;
+ std::string dec_name(std::vector<u8>::const_iterator begin, std::vector<u8>::const_iterator end) const;
+ static u16 r16b(std::vector<u8>::const_iterator it);
+ template<class T> static void w16b(T it, u16 w);
+ std::vector<u8> get_sector_range(lba_t first, unsigned size) const;
+ void store_sector_range(lba_t first, const std::vector<u8>& data);
+ static void check_av_data(const std::vector<u8>& in, const std::vector<u8>::const_iterator& it, int min_av);
+ static u16 get_u16(const std::vector<u8>& in, std::vector<u8>::const_iterator& it);
+ static std::vector<u8> convert_data_2_txt(const std::vector<u8>& in, unsigned bpr);
+ static void split_string_n_dump(const std::vector<u8>& in, std::vector<u8>& out, unsigned bpr);
+ static std::vector<u8> convert_txt_2_data(const std::vector<u8>& in, unsigned bpr);
+
+ // FS type
+ const fs_type m_fs_type;
+ // Number of tracks in image
+ unsigned m_img_tracks;
+ // Total tracks
+ unsigned m_tot_tracks;
+ // System tracks
+ unsigned m_sys_tracks;
+ // Data tracks
+ unsigned m_data_tracks;
+ // Directory start
+ lba_t m_dir_start;
+ // Size of directory
+ unsigned m_dir_size;
+ // Start of av. table
+ lba_t m_av_start;
+ // Size of av. table
+ unsigned m_av_size;
+ // Start of data part
+ lba_t m_data_start;
+ // End of data part
+ lba_t m_data_end;
+ // Start of spare part
+ lba_t m_spare_start;
+ // Has dir. been loaded?
+ bool m_dir_loaded;
+ // Directory
+ dir_t m_dir;
+ // Sectors in use
+ sect_map m_in_use;
+
+ // Interleave factor when formatting
+ // Index into array is fs_type
+ static const std::array<u16, FS_COUNT> m_fmt_interleave;
+
+ // Mapping of file types in each FS type
+ // Index into array is fs_type
+ // Key is type code
+ // Value is virtual file extension
+ const std::array<std::map<u8, std::string_view>, FS_COUNT> m_fs_type_maps;
+ };
+}
+
+hp98x5_impl::hp98x5_impl(fsblk_t& blockdev, fs_type type)
+ : filesystem_t(blockdev , SECTOR_SIZE)
+ , m_fs_type(type)
+ , m_dir_loaded(false)
+ , m_fs_type_maps({
+ // ********
+ // * 9825 *
+ // ********
+ std::map<u8, std::string_view>{
+ { 1, "DATA"sv },
+ { 2, "PROG"sv },
+ { 3, "KEYS"sv },
+ { 5, "ALL"sv },
+ { 6, "BPRG"sv }
+ },
+ // ********
+ // * 9831 *
+ // ********
+ std::map<u8, std::string_view>{
+ { 1, "DATA"sv },
+ { 9, "DATA"sv },
+ { 18, "PROG"sv },
+ { 19, "KEYS"sv },
+ { 20, "NULL"sv },
+ { 21, "MEMORY"sv },
+ { 22, "BINARY"sv },
+ { 23, "86DATA"sv }
+ },
+ // ********
+ // * 9845 *
+ // ********
+ std::map<u8, std::string_view>{
+ { 0, "BKUP"sv },
+ { 8, "DATA"sv },
+ { 16,"PROG"sv },
+ { 24,"KEYS"sv },
+ { 32,"BDAT"sv },
+ { 40,"ALL"sv },
+ { 48,"BPRG"sv },
+ { 56,"OPRM"sv },
+ { 60,"ASMB"sv }
+ }})
+{
+}
+
+meta_data hp98x5_impl::volume_metadata()
+{
+ meta_data res;
+ return res;
+}
+
+std::pair<std::error_condition, meta_data> hp98x5_impl::metadata(const std::vector<std::string> &path)
+{
+ if (path.size() != 1) {
+ return std::make_pair(error::not_found, meta_data{});
+ }
+
+ std::string basename;
+ int bpr;
+ u8 file_type;
+
+ auto err = parse_filename(path.front(), basename, bpr, file_type);
+ if (err) {
+ return std::make_pair(error::not_found, meta_data{});
+ }
+
+ ensure_dir_loaded();
+ auto it = scan_dir(m_dir, basename);
+ if (it == m_dir.end()) {
+ return std::make_pair(error::not_found, meta_data{});
+ }
+
+ auto meta = get_metadata(*it);
+ return std::make_pair(std::error_condition(), std::move(meta));
+}
+
+std::pair<std::error_condition, std::vector<dir_entry>> hp98x5_impl::directory_contents(const std::vector<std::string> &path)
+{
+ if (path.empty()) {
+ ensure_dir_loaded();
+ // Copy all dir_entry(s) out of m_dir
+ std::vector<dir_entry> dir_entries;
+ for (const auto& e : m_dir) {
+ if (e.m_in_use) {
+ auto meta = get_metadata(e);
+ dir_entries.emplace_back(dir_entry_type::file, std::move(meta));
+ }
+ }
+ return std::make_pair(std::error_condition(), dir_entries);
+ } else {
+ return std::make_pair(error::not_found, std::vector<dir_entry>{});
+ }
+}
+
+std::error_condition hp98x5_impl::file_create(const std::vector<std::string> &path, const meta_data &meta)
+{
+ if (!path.empty()) {
+ return error::invalid_name;
+ }
+
+ auto name = meta.get_string(meta_name::name);
+
+ std::string basename;
+ int bpr;
+ u8 file_type;
+
+ // Parse & validate filename
+ // Extension must be specified
+ auto err = parse_filename(name, basename, bpr, file_type);
+ if (err) {
+ return err;
+ }
+ if (file_type == NO_FILE_TYPE) {
+ return error::invalid_name;
+ }
+
+ if (bpr < 0) {
+ // Use default bpr when not specified
+ bpr = DEF_BPR;
+ }
+
+ // Check that file doesn't exist
+ ensure_dir_loaded();
+ auto it = scan_dir(m_dir, basename);
+ if (it != m_dir.end()) {
+ return error::already_exists;
+ }
+
+ // Find a free entry in directory
+ auto it2 = std::find_if(m_dir.begin(), m_dir.end(), [](const entry& e) { return !e.m_in_use; });
+ if (it2 == m_dir.end()) {
+ // Add a new entry at the end of directory
+ if (m_dir.size() < m_dir_size * SECTOR_SIZE / DIR_ENTRY_SIZE) {
+ m_dir.emplace_back(entry());
+ it2 = m_dir.end() - 1;
+ } else {
+ return error::no_space;
+ }
+ }
+
+ // Fill dir entry
+ it2->m_in_use = true;
+ it2->m_name = basename;
+ it2->m_type_code = (file_type != TEXT_TYPE) ? file_type : (m_fs_type != FS_TYPE_9845 ? DATA_TYPE_9825 : DATA_TYPE_9845);
+ it2->m_size = 0;
+ it2->m_sectors = 0;
+ it2->m_bpr = bpr > 0 ? unsigned(bpr) : DEF_BPR;
+ it2->m_1st_sect = 0;
+ it2->m_extra_w = 0;
+ it2->m_attrs = m_fs_type != FS_TYPE_9845 ? 0 : (it2->m_type_code == DATA_TYPE_9845 ? SYS_FLAG_DATA : SYS_FLAG_NON_DATA);
+
+ store_dir_map();
+
+ return std::error_condition();
+}
+
+std::pair<std::error_condition, std::vector<u8>> hp98x5_impl::file_read(const std::vector<std::string> &path)
+{
+ if (path.size() != 1) {
+ return std::make_pair(error::not_found, std::vector<u8>{});
+ }
+ std::string basename;
+ int bpr;
+ u8 file_type;
+
+ auto err = parse_filename(path.front(), basename, bpr, file_type);
+
+ if (!err) {
+ auto it = find_file(basename, bpr, file_type);
+ if (it == m_dir.end()) {
+ err = error::not_found;
+ } else {
+ auto file_data = get_sector_range(it->m_1st_sect, it->m_sectors);
+ if (file_type == TEXT_TYPE) {
+ file_data = convert_data_2_txt(file_data, it->m_bpr);
+ } else {
+ file_data.resize(it->m_size);
+ }
+ return std::make_pair(std::error_condition(), std::move(file_data));
+ }
+ }
+ return std::make_pair(err, std::vector<u8>{});
+}
+
+std::error_condition hp98x5_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
+{
+ if (path.size() != 1) {
+ return error::not_found;
+ }
+
+ std::string basename;
+ int bpr;
+ u8 file_type;
+
+ auto err = parse_filename(path.front(), basename, bpr, file_type);
+ if (err) {
+ return err;
+ }
+ // Check that file already exists
+ auto it = find_file(basename, bpr, file_type);
+ if (it == m_dir.end()) {
+ return error::not_found;
+ }
+
+ // De-allocate current blocks
+ if (it->m_sectors) {
+ set_not_in_use(it->m_1st_sect, it->m_sectors);
+ }
+
+ const std::vector<u8> *data_ptr = &data;
+ std::vector<u8> new_data;
+
+ if (file_type == TEXT_TYPE) {
+ new_data = convert_txt_2_data(data, it->m_bpr);
+ data_ptr = &new_data;
+ }
+
+ // Allocate space
+ it->m_sectors = (data_ptr->size() + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ if (!allocate(it->m_sectors, it->m_1st_sect)) {
+ return error::no_space;
+ }
+
+ // Store file content
+ store_sector_range(it->m_1st_sect, *data_ptr);
+ it->m_size = data_ptr->size();
+
+ // Update directory & map
+ store_dir_map();
+
+ return std::error_condition();
+}
+
+std::error_condition hp98x5_impl::format(const meta_data &meta)
+{
+ bool is_ds = m_blockdev.block_count() == DS_SECTORS;
+
+ // Compose system record
+ std::vector<u8> sys_rec;
+ auto out_ins = std::back_inserter(sys_rec);
+
+ // System ID
+ w16b(out_ins, 0);
+ // Sectors per track
+ w16b(out_ins, SECTORS);
+ // Total tracks
+ m_data_tracks = is_ds ? FMT_DATA_TRACKS_DS : FMT_DATA_TRACKS_SS;
+ m_sys_tracks = FMT_SYS_TRACKS;
+ m_tot_tracks = m_data_tracks + m_sys_tracks;
+ m_data_start = m_sys_tracks * SECTORS;
+ m_data_end = m_tot_tracks * SECTORS;
+ w16b(out_ins, m_tot_tracks);
+ // Spare track
+ m_spare_start = FMT_SPARE_TRACK * SECTORS;
+ w16b(out_ins, FMT_SPARE_TRACK);
+ // First sector of directory
+ m_dir_start = 1;
+ m_dir_size = FMT_DIR_SIZE;
+ w16b(out_ins, m_dir_start);
+ // First sector of availability table
+ m_av_start = m_dir_start + m_dir_size;
+ m_av_size = FMT_MAP_SIZE;
+ w16b(out_ins, m_av_start);
+ // First sector after availability table
+ w16b(out_ins, m_av_start + m_av_size);
+ // Count of system tracks
+ w16b(out_ins, m_sys_tracks);
+ // Count of data tracks
+ w16b(out_ins, m_data_tracks);
+ // Interleave factor
+ w16b(out_ins, m_fmt_interleave[ m_fs_type ]);
+
+ m_blockdev.get(0)->write(0, sys_rec.data(), sys_rec.size());
+
+ // 9825 & 9831 have a backup copy of system record in spare track but 9845 hasn't, go figure
+ if (m_fs_type != FS_TYPE_9845) {
+ m_blockdev.get(m_spare_start)->write(0, sys_rec.data(), sys_rec.size());
+ }
+
+ m_dir.clear();
+ m_dir_loaded = true;
+ m_in_use = no_data_map();
+
+ store_dir_map();
+
+ return std::error_condition();
+}
+
+std::error_condition hp98x5_impl::parse_filename(const std::string& name, std::string& basename, int& bpr, u8& file_type) const
+{
+ // General form of filenames:
+ // basename[[.&bpr].ext]
+ // where "bpr" is the optional byte-per-record attribute of file (this part can only be
+ // specified for 9845 filesystems)
+ // and "ext" is the virtual file extension
+
+ file_type = NO_FILE_TYPE;
+ bpr = -1;
+
+ auto p1 = name.find_first_of('.');
+
+ if (p1 != std::string::npos) {
+ std::string::size_type p2 = std::string::npos;
+
+ if (m_fs_type == FS_TYPE_9845) {
+ p2 = name.find_first_of('.', p1 + 1);
+ }
+
+ if (p2 != std::string::npos) {
+ // [p1+1 p2) should have "&bpr"
+ if (name[ p1 + 1 ] != '&') {
+ return error::invalid_name;
+ }
+ if (!std::all_of(name.cbegin() + p1 + 2, name.cbegin() + p2, [](char c) { return c >= '0' && c <= '9'; })) {
+ return error::invalid_name;
+ }
+ try {
+ bpr = std::stoul(std::string{name, p1 + 2, p2 - p1 - 2});
+ } catch (...) {
+ return error::invalid_name;
+ }
+ if (bpr < 4 || bpr > 32767 || (bpr & 1) != 0) {
+ return error::invalid_name;
+ }
+ } else {
+ p2 = p1;
+ }
+
+ std::string ext{ name, p2 + 1, std::string::npos };
+ if (ext == TEXT_EXT) {
+ file_type = TEXT_TYPE;
+ } else {
+ for (const auto& e : m_fs_type_maps[ m_fs_type ]) {
+ if (e.second == ext) {
+ file_type = e.first;
+ break;
+ }
+ }
+ if (file_type == NO_FILE_TYPE) {
+ return error::invalid_name;
+ }
+ }
+ }
+
+ // [0 p1) has "basename"
+ basename = std::string(name, 0, p1);
+ if (validate_filename(basename)) {
+ return std::error_condition();
+ } else {
+ return error::invalid_name;
+ }
+}
+
+bool hp98x5_impl::validate_filename(const std::string& s) const
+{
+ if (s.size() < 1 || s.size() > 6) {
+ return false;
+ }
+
+ // 9831 files must not have '*' at beginning
+ if (m_fs_type == FS_TYPE_9831 && s.front() == '*') {
+ return false;
+ }
+
+ auto it = std::find_if_not(s.cbegin(), s.cend(), [this](char c) { return is_valid_char(u8(c)); });
+ return it == s.cend();
+}
+
+void hp98x5_impl::ensure_dir_loaded()
+{
+ if (m_dir_loaded) {
+ return;
+ }
+
+ // Get system record
+ auto sys_rec = m_blockdev.get(SYS_REC_LBA);
+
+ // Layout of system record
+ // Word # Content
+ // ================
+ // 0 System ID
+ // 1 Sectors per track
+ // 2 Total tracks
+ // 3 Track of spare system info
+ // 4 First sector of directory
+ // 5 First sector of availability table
+ // 6 First sector after availability table
+ // 7 Count of system tracks
+ // 8 Count of data tracks
+ // 9 Interleave factor
+ // 10..127 N/U
+ u16 sects_per_track = sys_rec->r16b(2);
+ u16 tot_tracks = sys_rec->r16b(4);
+ u16 spare_track = sys_rec->r16b(6);
+ u16 dir_start = sys_rec->r16b(8);
+ u16 av_start = sys_rec->r16b(10);
+ u16 av_end = sys_rec->r16b(12);
+ u16 sys_tracks = sys_rec->r16b(14);
+ u16 data_tracks = sys_rec->r16b(16);
+
+ m_img_tracks = m_blockdev.block_count() / SECTORS;
+
+ m_tot_tracks = tot_tracks;
+ m_sys_tracks = sys_tracks;
+ m_data_tracks = data_tracks;
+
+ m_dir_start = lba_t(dir_start);
+ m_dir_size = av_start - dir_start;
+ m_av_start = lba_t(av_start);
+ m_av_size = av_end - av_start;
+
+ m_data_start = sys_tracks * SECTORS;
+ m_data_end = (sys_tracks + data_tracks) * SECTORS;
+
+ m_spare_start = spare_track * SECTORS;
+
+ // Consistency checks
+ if (sects_per_track != SECTORS) {
+ throw std::runtime_error(util::string_format("Invalid sectors per track (%u)", sects_per_track));
+ }
+ if (dir_start == 0 ||
+ av_start <= dir_start ||
+ av_end <= av_start) {
+ throw std::runtime_error(util::string_format("Invalid dir/av. table pointers (%u,%u,%u)", dir_start, av_start, av_end));
+ }
+ lba_t spare_end = m_spare_start + av_end;
+ if (m_spare_start < av_end ||
+ spare_end > m_data_start) {
+ throw std::runtime_error(util::string_format("Invalid spare info position (%d,%d)", m_spare_start, spare_end));
+ }
+ if (tot_tracks > m_img_tracks) {
+ throw std::runtime_error(util::string_format("Invalid total track number (%u,%u)", tot_tracks, m_img_tracks));
+ }
+ if (tot_tracks < (data_tracks + sys_tracks) ||
+ data_tracks < 1 ||
+ sys_tracks < 2) {
+ throw std::runtime_error(util::string_format("Invalid sys/data tracks (%u,%u,%u)", sys_tracks, data_tracks, tot_tracks));
+ }
+
+ auto&& [decoded_dir, decoded_map] = decode_dir();
+ m_in_use = decode_map();
+
+ if (m_in_use != decoded_map) {
+ fprintf(stderr, "WARNING: Incorrect map\n");
+ }
+
+ m_dir = std::move(decoded_dir);
+
+ m_dir_loaded = true;
+}
+
+std::pair<hp98x5_impl::dir_t, hp98x5_impl::sect_map> hp98x5_impl::decode_dir() const
+{
+ dir_t dir;
+ sect_map in_use = no_data_map();
+
+ auto enc_dir = get_sector_range(m_dir_start, m_dir_size);
+
+ for (auto it = enc_dir.cbegin(); it != enc_dir.cend(); it += DIR_ENTRY_SIZE) {
+ auto w1 = r16b(it + 2);
+ if (w1 == 0xffff) {
+ // Directory ends
+ break;
+ }
+
+ entry e;
+
+ auto w0 = r16b(it);
+ if (w0 != 0) {
+ e.m_in_use = true;
+ // +0..+5: File name
+ e.m_name = dec_name(it, it + 6);
+ if (scan_dir(dir, e.m_name) != dir.end()) {
+ throw std::runtime_error(util::string_format("Duplicated file name (%s)", e.m_name));
+ }
+ // +6..+7: 1st sector
+ lba_t first_sect = r16b(it + 6);
+ // +8..+9: Number of sectors
+ unsigned sectors = r16b(it + 8);
+ if (first_sect < m_data_start || (first_sect + sectors) >= m_data_end || sectors == 0) {
+ throw std::runtime_error(util::string_format("Invalid file allocation (%d,%u)", first_sect, sectors));
+ }
+ e.m_1st_sect = first_sect;
+ e.m_sectors = sectors;
+ // Mark used sectors
+ for (lba_t i = first_sect; i < (first_sect + sectors); i++) {
+ in_use.set(i);
+ }
+ if (m_fs_type == FS_TYPE_9825 || m_fs_type == FS_TYPE_9831) {
+ // ******** ********
+ // * 9825 * * 9831 *
+ // ******** ********
+
+ // +12..+13: File type & secure flag
+ auto w6 = r16b(it + 12);
+ // b15..b10 = File type
+ e.m_type_code = u8(w6 >> 10);
+ // b9 = secured file (9825 only)
+ if (m_fs_type == FS_TYPE_9825) {
+ e.m_attrs = u8((w6 >> 9) & 1);
+ } else {
+ e.m_attrs = 0;
+ }
+ // +14..+15: Extra word
+ e.m_extra_w = r16b(it + 14);
+
+ // DATA files have a fixed record size
+ e.m_bpr = BPR_9825;
+
+ if (!is_data_file(e.m_type_code)) {
+ // File type != DATA
+ // +10..+11: Size of file in words - 1
+ auto w5 = r16b(it + 10);
+ e.m_size = (w5 + 1) * 2;
+ unsigned exp_sectors = (e.m_size + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ if (exp_sectors != sectors) {
+ throw std::runtime_error(util::string_format("Mismatching number of sectors (%u,%u)", exp_sectors, sectors));
+ }
+ } else {
+ // File type == DATA
+ e.m_size = e.m_sectors * SECTOR_SIZE;
+ }
+ } else {
+ // ********
+ // * 9845 *
+ // ********
+
+ // +10..+11: Words per record
+ auto w5 = r16b(it + 10);
+ if (w5 < 2 || w5 > 16383) {
+ throw std::runtime_error(util::string_format("Invalid size of records (%u)", w5));
+ }
+ e.m_bpr = w5 * 2;
+
+ // +12..+13: File type & sys. flags
+ auto w6 = r16b(it + 12);
+ // b12..b7 = File type
+ e.m_type_code = u8((w6 >> 7) & 0x3f);
+ // b15..b13 = System flags
+ e.m_attrs = u8(w6 >> 13);
+
+ // +14..+15: Protection
+ e.m_extra_w = r16b(it + 14);
+
+ e.m_size = e.m_sectors * SECTOR_SIZE;
+ }
+ }
+
+ dir.emplace_back(std::move(e));
+ }
+
+ return std::make_pair(std::move(dir), std::move(in_use));
+}
+
+std::vector<u8> hp98x5_impl::encode_dir() const
+{
+ std::vector<u8> res;
+ auto out_ins = std::back_inserter(res);
+
+ for (const auto& e : m_dir) {
+ if (e.m_in_use) {
+ // Filename
+ for (auto c : e.m_name) {
+ *out_ins = u8(c);
+ }
+ if (e.m_name.size() < 6) {
+ std::fill_n(out_ins, 6 - e.m_name.size(), 0);
+ }
+ // 1st sector
+ w16b(out_ins, e.m_1st_sect);
+ // Number of sectors
+ w16b(out_ins, e.m_sectors);
+ if (m_fs_type == FS_TYPE_9825 || m_fs_type == FS_TYPE_9831) {
+ // ******** ********
+ // * 9825 * * 9831 *
+ // ******** ********
+
+ // Size of file in words - 1 (any file type but DATA)
+ if (!is_data_file(e.m_type_code)) {
+ w16b(out_ins, e.m_size / 2 - 1);
+ } else {
+ w16b(out_ins, 0);
+ }
+
+ // File type & secure flag
+ u16 w6 = u16(e.m_type_code) << 10;
+ if (e.m_attrs) {
+ w6 |= 1U << 9;
+ }
+ w16b(out_ins, w6);
+
+ // Extra word
+ w16b(out_ins, e.m_extra_w);
+ } else {
+ // ********
+ // * 9845 *
+ // ********
+
+ // Words per record
+ w16b(out_ins, e.m_bpr / 2);
+
+ // File type & sys. flags
+ u16 w6 = (u16(e.m_attrs) << 13) | (u16(e.m_type_code) << 7);
+ w16b(out_ins, w6);
+
+ // Protection
+ w16b(out_ins, e.m_extra_w);
+ }
+ } else {
+ // Empty entry
+ std::fill_n(out_ins, DIR_ENTRY_SIZE, 0);
+ }
+ }
+
+ // Insert dir. terminator (9845 only)
+ unsigned dir_len = m_dir_size * SECTOR_SIZE;
+ if (m_fs_type == FS_TYPE_9845 && dir_len - res.size() >= DIR_ENTRY_SIZE) {
+ w16b(out_ins, 0);
+ w16b(out_ins, 0xffff);
+ w16b(out_ins, 0);
+ w16b(out_ins, 0);
+ w16b(out_ins, 0);
+ w16b(out_ins, 0);
+ w16b(out_ins, 0);
+ w16b(out_ins, 0);
+ }
+
+ // Pad to end
+ if (res.size() < dir_len) {
+ std::fill_n(out_ins, dir_len - res.size(), 0);
+ }
+
+ return res;
+}
+
+bool hp98x5_impl::is_data_file(u8 type_code) const
+{
+ auto mp = m_fs_type_maps[ m_fs_type ];
+ auto it = mp.find(type_code);
+ return it != mp.end() && it->second == "DATA"sv;
+}
+
+void hp98x5_impl::store_dir_map()
+{
+ // Store directory (2 copies)
+ auto dir_content = encode_dir();
+ store_sector_range(m_dir_start, dir_content);
+ store_sector_range(m_dir_start + m_spare_start, dir_content);
+ // Store map (2 copies)
+ auto map_content = encode_map();
+ store_sector_range(m_av_start, map_content);
+ store_sector_range(m_av_start + m_spare_start, map_content);
+}
+
+hp98x5_impl::dir_t::iterator hp98x5_impl::scan_dir(dir_t& dir, const std::string& name)
+{
+ return std::find_if(dir.begin(), dir.end(), [&name](const entry& e){ return e.m_in_use && name == e.m_name; });
+}
+
+hp98x5_impl::dir_t::iterator hp98x5_impl::find_file(const std::string& basename, int bpr, u8 file_type)
+{
+ ensure_dir_loaded();
+ auto it = scan_dir(m_dir, basename);
+ if (it != m_dir.end() &&
+ (file_type == NO_FILE_TYPE ||
+ (file_type == TEXT_TYPE && is_data_file(it->m_type_code)) ||
+ file_type == it->m_type_code) &&
+ (bpr < 0 || bpr == it->m_bpr)) {
+ return it;
+ } else {
+ return m_dir.end();
+ }
+}
+
+meta_data hp98x5_impl::get_metadata(const entry& e)
+{
+ meta_data meta;
+ meta.set(meta_name::name, e.m_name);
+
+ auto mp = m_fs_type_maps[ m_fs_type ];
+ auto it = mp.find(e.m_type_code);
+ if (it != mp.end()) {
+ meta.set(meta_name::file_type , it->second);
+ } else {
+ meta.set(meta_name::file_type , util::string_format("?%02x", e.m_type_code));
+ }
+
+ if (m_fs_type == FS_TYPE_9825) {
+ // ********
+ // * 9825 *
+ // ********
+ if (e.m_attrs) {
+ // "Secured" program
+ meta.set(meta_name::attributes, "S");
+ }
+ } else if (m_fs_type == FS_TYPE_9845) {
+ // ********
+ // * 9845 *
+ // ********
+ if (e.m_bpr != DEF_BPR) {
+ // System flags & record size
+ meta.set(meta_name::attributes, util::string_format("S=%u R=%u", e.m_attrs, e.m_bpr));
+ } else {
+ // System flags
+ meta.set(meta_name::attributes, util::string_format("S=%u", e.m_attrs));
+ }
+ }
+
+ meta.set(meta_name::length, e.m_size);
+
+ return meta;
+}
+
+hp98x5_impl::sect_map hp98x5_impl::no_data_map() const
+{
+ sect_map res;
+
+ // First mark every sector as used...
+ res.set();
+
+ // ...then carve out the data part of disk
+ for (auto i = m_data_start; i < m_data_end; i++) {
+ res.reset(i);
+ }
+
+ return res;
+}
+
+hp98x5_impl::sect_map hp98x5_impl::decode_map() const
+{
+ sect_map res;
+
+ // First mark every sector as used...
+ res.set();
+
+ // ..then scan availability table and mark free sectors
+ auto enc_av = get_sector_range(m_av_start, m_av_size);
+ for (auto it = enc_av.cbegin(); it != enc_av.cend(); it += 4) {
+ lba_t start = r16b(it);
+ unsigned size = r16b(it + 2);
+ if (start == 0xffff) {
+ // table ends
+ break;
+ } else if (size == 0) {
+ // Null entry, skip
+ continue;
+ }
+ if (start < m_data_start ||
+ start + size > m_data_end) {
+ throw std::runtime_error(util::string_format("Invalid entry in av. table (%d,%u)", start, size));
+ }
+ for (lba_t k = start; k < start + size; k++) {
+ if (!res[ k ]) {
+ throw std::runtime_error(util::string_format("Sector %d marked free twice", k));
+ } else {
+ res.reset(k);
+ }
+ }
+ }
+
+ return res;
+}
+
+std::vector<u8> hp98x5_impl::encode_map() const
+{
+ std::vector<u8> res(m_av_size * SECTOR_SIZE);
+
+ auto it = res.begin();
+
+ bool in_free_block = false;
+
+ for (auto i = m_data_start; i < m_data_end; i++) {
+ if (m_in_use[ i ]) {
+ in_free_block = false;
+ } else if (in_free_block) {
+ it -= 2;
+ w16b(it, r16b(it) + 1);
+ it += 2;
+ } else {
+ in_free_block = true;
+ if (res.end() - it < 4) {
+ throw std::runtime_error("Availability table too fragmented");
+ }
+ w16b(it, u16(i));
+ it += 2;
+ w16b(it, 1);
+ it += 2;
+ }
+ }
+
+ if (m_fs_type == FS_TYPE_9845 && res.end() - it >= 4) {
+ w16b(it, 0xffffU);
+ }
+
+ return res;
+}
+
+void hp98x5_impl::set_not_in_use(lba_t first, unsigned sectors)
+{
+ for (lba_t i = first; i < (first + sectors); i++) {
+ m_in_use.reset(i);
+ }
+}
+
+bool hp98x5_impl::allocate(unsigned sectors, lba_t& first)
+{
+ if (sectors > (m_data_end - m_data_start)) {
+ return false;
+ }
+
+ sect_map scanner;
+ for (lba_t i = m_data_start; i < (m_data_start + sectors); i++) {
+ scanner.set(i);
+ }
+
+ for (lba_t i = m_data_start; i <= (m_data_end - sectors); i++) {
+ sect_map tmp = m_in_use & scanner;
+ if (tmp.none()) {
+ m_in_use |= scanner;
+ first = i;
+ return true;
+ }
+ scanner <<= 1;
+ }
+
+ return false;
+}
+
+bool hp98x5_impl::is_valid_char(u8 c) const
+{
+ if (c <= 0x20 || c >= 0x7f || c == '"' || c == ':') {
+ return false;
+ } else if (m_fs_type == FS_TYPE_9825) {
+ return c != ',' && c != ';';
+ } else if (m_fs_type == FS_TYPE_9831) {
+ return c != ',';
+ } else {
+ return true;
+ }
+}
+
+std::string hp98x5_impl::dec_name(std::vector<u8>::const_iterator begin, std::vector<u8>::const_iterator end) const
+{
+ std::string res;
+ bool ended = false;
+ for (auto i = begin; i < end; i++) {
+ auto c = *i;
+ if (ended) {
+ if (c != 0) {
+ throw std::runtime_error("Invalid name in dir. entry");
+ }
+ } else {
+ if (c == 0) {
+ ended = true;
+ } else if (is_valid_char(c)) {
+ res.push_back(char(c));
+ } else {
+ throw std::runtime_error("Invalid name in dir. entry");
+ }
+ }
+ }
+
+ return res;
+}
+
+u16 hp98x5_impl::r16b(std::vector<u8>::const_iterator it)
+{
+ u16 w = (u16(it[ 0 ]) << 8) | it[ 1 ];
+ return w;
+}
+
+template<class T> void hp98x5_impl::w16b(T it, u16 w)
+{
+ *it++ = u8(w >> 8);
+ *it = u8(w & 0xff);
+}
+
+std::vector<u8> hp98x5_impl::get_sector_range(lba_t first, unsigned size) const
+{
+ std::vector<u8> res;
+
+ res.resize(size * SECTOR_SIZE);
+
+ u8 *ptr = res.data();
+
+ for (lba_t idx = first; idx < first + size; idx++) {
+ auto data_sect = m_blockdev.get(u32(idx));
+ data_sect->read(0, ptr, SECTOR_SIZE);
+ ptr += SECTOR_SIZE;
+ }
+ return res;
+}
+
+void hp98x5_impl::store_sector_range(lba_t first, const std::vector<u8>& data)
+{
+ const u8 *ptr = data.data();
+ u32 to_go = u32(data.size());
+ unsigned sects = (data.size() + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ for (lba_t idx = first; idx < first + sects; idx++) {
+ u32 count = std::min<u32>(to_go, SECTOR_SIZE);
+ auto blk = m_blockdev.get(u32(idx));
+ blk->write(0, ptr, count);
+ ptr += count;
+ to_go -= count;
+ }
+}
+
+void hp98x5_impl::check_av_data(const std::vector<u8>& in, const std::vector<u8>::const_iterator& it, int min_av)
+{
+ if (in.cend() - it < min_av) {
+ throw std::runtime_error("DATA file too short");
+ }
+}
+
+u16 hp98x5_impl::get_u16(const std::vector<u8>& in, std::vector<u8>::const_iterator& it)
+{
+ check_av_data(in, it, 2);
+ auto tmp = r16b(it);
+ it += 2;
+ return tmp;
+}
+
+std::vector<u8> hp98x5_impl::convert_data_2_txt(const std::vector<u8>& in, unsigned bpr)
+{
+ auto it = in.cbegin();
+ std::vector<u8> accum;
+
+ u16 rec_type;
+ unsigned accum_len = 0;
+
+ do {
+ // Get record type
+ rec_type = get_u16(in, it);
+ switch (rec_type) {
+ case REC_TYPE_EOR:
+ // End of record: just skip it
+ break;
+
+ case REC_TYPE_FULLSTR:
+ // A string in a single piece
+ case REC_TYPE_1STSTR:
+ // First piece of a split string
+ case REC_TYPE_MIDSTR:
+ // Mid piece(s) of a split string
+ case REC_TYPE_ENDSTR: {
+ // Closing piece of a split string
+ if (((rec_type == REC_TYPE_FULLSTR || rec_type == REC_TYPE_1STSTR) && accum_len > 0) ||
+ ((rec_type == REC_TYPE_MIDSTR || rec_type == REC_TYPE_ENDSTR) && accum_len == 0)) {
+ throw std::runtime_error("Wrong sequence of string pieces");
+ }
+
+ unsigned tmp_len = (unsigned)get_u16(in, it);
+
+ if (rec_type == REC_TYPE_FULLSTR || rec_type == REC_TYPE_1STSTR) {
+ accum_len = tmp_len;
+ } else if (tmp_len != accum_len) {
+ throw std::runtime_error("Wrong length of string piece");
+ }
+
+ unsigned len_to_eor = bpr - (it - in.cbegin()) % bpr;
+ unsigned rec_len;
+ if (rec_type == REC_TYPE_FULLSTR || rec_type == REC_TYPE_ENDSTR) {
+ if (accum_len > len_to_eor) {
+ throw std::runtime_error("Wrong length of string piece");
+ }
+ rec_len = accum_len;
+ } else {
+ if (accum_len <= len_to_eor) {
+ throw std::runtime_error("Wrong length of string piece");
+ }
+ rec_len = len_to_eor;
+ }
+ check_av_data(in, it, rec_len);
+ // Sanitize and copy string
+ for (unsigned i = 0; i < rec_len; i++) {
+ u8 c = *it++;
+ if (c < 0x20 || c >= 0x7f) {
+ c = ' ';
+ }
+ accum.push_back(c);
+ }
+ if (rec_type == REC_TYPE_FULLSTR || rec_type == REC_TYPE_ENDSTR) {
+ // Record ended, add a \n to output
+ accum.push_back('\n');
+ }
+ if (rec_len & 1) {
+ // Keep length of string pieces even
+ it++;
+ }
+ accum_len -= rec_len;
+ break;
+ }
+
+ case REC_TYPE_EOF:
+ // End of file
+ break;
+
+ default:
+ throw std::runtime_error(util::string_format("Unknown record type (%04x)", rec_type));
+ }
+ } while (rec_type != REC_TYPE_EOF);
+
+ return accum;
+}
+
+void hp98x5_impl::split_string_n_dump(const std::vector<u8>& in, std::vector<u8>& out, unsigned bpr)
+{
+ auto it = in.cbegin();
+ auto out_ins = std::back_inserter(out);
+ u16 rec_type = REC_TYPE_1STSTR;
+
+ while (1) {
+ unsigned free_len = bpr - (out.size() % bpr);
+ if (free_len <= 4) {
+ // Not enough free space at end of current record: fill with EORs
+ while (free_len) {
+ w16b(out_ins, REC_TYPE_EOR);
+ free_len -= 2;
+ }
+ } else {
+ unsigned s_len = in.cend() - it;
+ unsigned s_part_len = std::min(free_len - 4 , s_len);
+ if (s_part_len == s_len) {
+ // Free space to EOR enough for what's left of string
+ break;
+ }
+ w16b(out_ins, rec_type);
+ w16b(out_ins, u16(s_len));
+ std::copy(it, it + s_part_len, out_ins);
+ it += s_part_len;
+ rec_type = REC_TYPE_MIDSTR;
+ }
+ }
+
+ w16b(out_ins, rec_type == REC_TYPE_MIDSTR ? REC_TYPE_ENDSTR : REC_TYPE_FULLSTR);
+ w16b(out_ins, u16(in.cend() - it));
+ std::copy(it, in.cend(), out_ins);
+ if (in.size() & 1) {
+ *out_ins = 0;
+ }
+}
+
+std::vector<u8> hp98x5_impl::convert_txt_2_data(const std::vector<u8>& in, unsigned bpr)
+{
+ std::vector<u8> res;
+
+ std::vector<u8> accum;
+ std::vector<u8> trailer;
+
+ // Split "in" into lines of text discarding trailing whitespace on each one
+ for (auto c : in) {
+ if (c == '\n') {
+ if (!accum.empty()) {
+ split_string_n_dump(accum, res, bpr);
+ }
+ accum.clear();
+ trailer.clear();
+ } else if (c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\f' || c == '\v') {
+ trailer.push_back(c);
+ } else {
+ if (!trailer.empty()) {
+ accum.insert(accum.end(), trailer.cbegin(), trailer.cend());
+ trailer.clear();
+ }
+ accum.push_back(c);
+ }
+ }
+
+ // Fill free space of last record with EOFs
+ auto out_ins = std::back_inserter(res);
+ for (unsigned pad_len = bpr - (res.size() % bpr); pad_len; pad_len -= 2) {
+ w16b(out_ins, REC_TYPE_EOF);
+ }
+
+ return res;
+}
+
+const std::array<u16, hp98x5_impl::FS_COUNT> hp98x5_impl::m_fmt_interleave = {
+ // ********
+ // * 9825 *
+ // ********
+ 2,
+ // ********
+ // * 9831 *
+ // ********
+ 0,
+ // ********
+ // * 9845 *
+ // ********
+ 7
+};
+
+// +--------------+
+// | hp9825_image |
+// +--------------+
+const char *hp9825_image::name() const
+{
+ return "hp9825";
+}
+
+const char *hp9825_image::description() const
+{
+ return "HP9825 FS";
+}
+
+void hp9825_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_HPI_FORMAT, floppy_image::FF_8, floppy_image::SSDD, SS_IMAGE_SIZE, "hp9825_ss", "HP9825 SS");
+ fe.add(FLOPPY_HPI_FORMAT, floppy_image::FF_8, floppy_image::DSDD, DS_IMAGE_SIZE, "hp9825_ds", "HP9825 DS");
+}
+
+bool hp9825_image::can_format() const
+{
+ return true;
+}
+
+bool hp9825_image::can_read() const
+{
+ return true;
+}
+
+bool hp9825_image::can_write() const
+{
+ return true;
+}
+
+bool hp9825_image::has_rsrc() const
+{
+ return false;
+}
+
+std::vector<meta_description> hp9825_image::volume_meta_description() const
+{
+ std::vector<meta_description> res;
+
+ return res;
+}
+
+std::vector<meta_description> hp9825_image::file_meta_description() const
+{
+ std::vector<meta_description> res;
+
+ res.emplace_back(meta_description(meta_name::name, "Empty", false, [](const meta_value &m) { return m.as_string().size() <= 6; }, "File name, up to 6 characters"));
+ res.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "File size"));
+ res.emplace_back(meta_description(meta_name::file_type, "", true, nullptr, "File type"));
+ res.emplace_back(meta_description(meta_name::attributes, "", true, nullptr, "File attributes"));
+
+ return res;
+}
+
+std::unique_ptr<filesystem_t> hp9825_image::mount(fsblk_t &blockdev) const
+{
+ return std::make_unique<hp98x5_impl>(blockdev, hp98x5_impl::FS_TYPE_9825);
+}
+
+// +--------------+
+// | hp9831_image |
+// +--------------+
+const char *hp9831_image::name() const
+{
+ return "hp9831";
+}
+
+const char *hp9831_image::description() const
+{
+ return "HP9831 FS";
+}
+
+void hp9831_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_HPI_FORMAT, floppy_image::FF_8, floppy_image::SSDD, SS_IMAGE_SIZE, "hp9831_ss", "HP9831 SS");
+}
+
+bool hp9831_image::can_format() const
+{
+ return true;
+}
+
+bool hp9831_image::can_read() const
+{
+ return true;
+}
+
+bool hp9831_image::can_write() const
+{
+ return true;
+}
+
+bool hp9831_image::has_rsrc() const
+{
+ return false;
+}
+
+std::vector<meta_description> hp9831_image::volume_meta_description() const
+{
+ std::vector<meta_description> res;
+
+ return res;
+}
+
+std::vector<meta_description> hp9831_image::file_meta_description() const
+{
+ std::vector<meta_description> res;
+
+ res.emplace_back(meta_description(meta_name::name, "Empty", false, [](const meta_value &m) { return m.as_string().size() <= 6; }, "File name, up to 6 characters"));
+ res.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "File size"));
+ res.emplace_back(meta_description(meta_name::file_type, "", true, nullptr, "File type"));
+
+ return res;
+}
+
+std::unique_ptr<filesystem_t> hp9831_image::mount(fsblk_t &blockdev) const
+{
+ return std::make_unique<hp98x5_impl>(blockdev, hp98x5_impl::FS_TYPE_9831);
+}
+
+// +--------------+
+// | hp9845_image |
+// +--------------+
+const char *hp9845_image::name() const
+{
+ return "hp9845";
+}
+
+const char *hp9845_image::description() const
+{
+ return "HP9845 FS";
+}
+
+void hp9845_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_HPI_FORMAT, floppy_image::FF_8, floppy_image::SSDD, SS_IMAGE_SIZE, "hp9845_ss", "HP9845 SS");
+ fe.add(FLOPPY_HPI_FORMAT, floppy_image::FF_8, floppy_image::DSDD, DS_IMAGE_SIZE, "hp9845_ds", "HP9845 DS");
+}
+
+bool hp9845_image::can_format() const
+{
+ return true;
+}
+
+bool hp9845_image::can_read() const
+{
+ return true;
+}
+
+bool hp9845_image::can_write() const
+{
+ return true;
+}
+
+bool hp9845_image::has_rsrc() const
+{
+ return false;
+}
+
+std::vector<meta_description> hp9845_image::volume_meta_description() const
+{
+ std::vector<meta_description> res;
+
+ return res;
+}
+
+std::vector<meta_description> hp9845_image::file_meta_description() const
+{
+ std::vector<meta_description> res;
+
+ res.emplace_back(meta_description(meta_name::name, "Empty", false, [](const meta_value &m) { return m.as_string().size() <= 6; }, "File name, up to 6 characters"));
+ res.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "File size"));
+ res.emplace_back(meta_description(meta_name::file_type, "", true, nullptr, "File type"));
+ res.emplace_back(meta_description(meta_name::attributes, "", true, nullptr, "File attributes"));
+
+ return res;
+}
+
+std::unique_ptr<filesystem_t> hp9845_image::mount(fsblk_t &blockdev) const
+{
+ return std::make_unique<hp98x5_impl>(blockdev, hp98x5_impl::FS_TYPE_9845);
+}
diff --git a/src/lib/formats/fs_hp98x5.h b/src/lib/formats/fs_hp98x5.h
new file mode 100644
index 00000000000..e011a5cee48
--- /dev/null
+++ b/src/lib/formats/fs_hp98x5.h
@@ -0,0 +1,88 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ fs_hp98x5.h
+
+ HP 9825, 9831 & 9845 filesystem handling
+
+*********************************************************************/
+
+#ifndef MAME_FORMATS_FS_HP98X5_H
+#define MAME_FORMATS_FS_HP98X5_H
+
+#pragma once
+
+#include "fsmgr.h"
+
+namespace fs {
+
+ class hp9825_image : public manager_t {
+ public:
+ hp9825_image() : manager_t() {}
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+
+ virtual std::vector<meta_description> volume_meta_description() const override;
+ virtual std::vector<meta_description> file_meta_description() const override;
+
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ };
+
+ class hp9831_image : public manager_t {
+ public:
+ hp9831_image() : manager_t() {}
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+
+ virtual std::vector<meta_description> volume_meta_description() const override;
+ virtual std::vector<meta_description> file_meta_description() const override;
+
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ };
+
+ class hp9845_image : public manager_t {
+ public:
+ hp9845_image() : manager_t() {}
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+
+ virtual std::vector<meta_description> volume_meta_description() const override;
+ virtual std::vector<meta_description> file_meta_description() const override;
+
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ };
+
+ extern const hp9825_image HP9825;
+ extern const hp9831_image HP9831;
+ extern const hp9845_image HP9845;
+} // namespace fs
+
+#endif /* MAME_FORMATS_FS_HP98X5_H */
diff --git a/src/lib/formats/fs_hplif.cpp b/src/lib/formats/fs_hplif.cpp
new file mode 100644
index 00000000000..8a3d2c52da4
--- /dev/null
+++ b/src/lib/formats/fs_hplif.cpp
@@ -0,0 +1,499 @@
+// license:BSD-3-Clause
+// copyright-holders:Sven Schnelle
+/***************************************************************************
+
+ fs_hplif.cpp
+
+ Management of HP LIF images
+
+***************************************************************************/
+
+#include "fs_hplif.h"
+#include "fsblk.h"
+#include "hp300_dsk.h"
+
+#include "corestr.h"
+#include "osdcomm.h"
+#include "strformat.h"
+
+#include <array>
+#include <optional>
+#include <set>
+#include <string_view>
+#include <tuple>
+#include <iostream>
+
+namespace fs {
+ const hplif_image HPLIF;
+};
+
+using namespace fs;
+
+namespace {
+
+class impl : public filesystem_t {
+public:
+
+ struct hplif_time {
+ u8 year;
+ u8 month;
+ u8 day;
+ u8 hour;
+ u8 minute;
+ u8 second;
+ };
+
+ struct hplif_dirent
+ {
+ char m_file_name[10];
+ u16 m_file_type;
+ u32 m_starting_sector;
+ u32 m_sector_count;
+ hplif_time m_time;
+ u16 m_volume_number;
+ u32 m_general_purpose;
+ };
+
+ class block_iterator
+ {
+ public:
+ block_iterator(const impl &fs, u32 first_sector, u32 sector_count);
+ bool next();
+ const void *data() const;
+ const std::array<hplif_dirent, 8> &dirent_data() const;
+ u8 size() const;
+
+ private:
+ const impl & m_fs;
+ fsblk_t::block_t::ptr m_block;
+ u8 m_sector;
+ u32 m_sector_count;
+ };
+
+ impl(fsblk_t &blockdev);
+ virtual ~impl() = default;
+
+ virtual meta_data volume_metadata() override;
+ virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override;
+ virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+ virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+
+private:
+ fsblk_t::block_t::ptr read_sector(u32 starting_sector) const;
+ std::optional<hplif_dirent> dirent_from_path(const std::vector<std::string> &path) const;
+ void iterate_directory_entries(const std::function<bool(const hplif_dirent &dirent)> &callback) const;
+ util::arbitrary_datetime decode_datetime(const hplif_time *time) const;
+ meta_data metadata_from_dirent(const hplif_dirent &dirent) const;
+ std::error_condition format(const meta_data &meta) override;
+};
+
+// methods
+std::string_view strtrimright_hplif(std::string_view str);
+template<size_t N> std::string_view strtrimright_hplif(const char (&str)[N]);
+
+} // anonymous namespace
+
+
+//-------------------------------------------------
+// name
+//-------------------------------------------------
+
+const char *fs::hplif_image::name() const
+{
+ return "hplif";
+}
+
+
+//-------------------------------------------------
+// description
+//-------------------------------------------------
+
+const char *fs::hplif_image::description() const
+{
+ return "HPLIF";
+}
+
+
+//-------------------------------------------------
+// enumerate_f
+//-------------------------------------------------
+
+void fs::hplif_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 630784, "hp_lif_9121_format_1", "HP 9121 LIF 3.5\" dual-sided double density Format 1");
+ fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 709632, "hp_lif_9121_format_2", "HP 9121 LIF 3.5\" dual-sided double density Format 2");
+ fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 788480, "hp_lif_9121_format_3", "HP 9121 LIF 3.5\" dual-sided double density Format 3");
+ fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::SSDD, 270336, "hp_lif_9121_format_4", "HP 9121 LIF 3.5\" single-sided double density Format 4");
+ fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 737280, "hp_lif_9121_format_16", "HP 9121 LIF 3.5\" dual-sided double density Format 16");
+
+ fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSHD, 1261568, "hp_lif_9122_format_014", "HP 9122 LIF 3.5\" dual-sided high density Format 0, 1, 4");
+ fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSHD, 1419264, "hp_lif_9122_format_2", "HP 9122 LIF 3.5\" dual-sided high density Format 2");
+ fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSHD, 1576960, "hp_lif_9122_format_3", "HP 9122 LIF 3.5\" dual-sided high density Format 3");
+ fe.add(FLOPPY_HP300_FORMAT, floppy_image::FF_35, floppy_image::DSHD, 1474560, "hp_lif_9122_format_16" , "HP 9122 LIF 3.5\" dual-sided high density Format 16");
+}
+
+
+//-------------------------------------------------
+// can_format
+//-------------------------------------------------
+
+bool fs::hplif_image::can_format() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// can_read
+//-------------------------------------------------
+
+bool fs::hplif_image::can_read() const
+{
+ return true;
+}
+
+
+//-------------------------------------------------
+// can_write
+//-------------------------------------------------
+
+bool fs::hplif_image::can_write() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// has_rsrc
+//-------------------------------------------------
+
+bool fs::hplif_image::has_rsrc() const
+{
+ return false;
+}
+
+
+//-------------------------------------------------
+// volume_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> fs::hplif_image::volume_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_name::name, "", false, [](const meta_value &m) { return m.as_string().size() <= 6; }, "Volume name, up to 6 characters");
+ results.emplace_back(meta_name::creation_date, 0, true, nullptr, "Time of creation");
+ return results;
+}
+
+
+//-------------------------------------------------
+// file_meta_description
+//-------------------------------------------------
+
+std::vector<meta_description> fs::hplif_image::file_meta_description() const
+{
+ std::vector<meta_description> results;
+ results.emplace_back(meta_name::name, "", false, [](const meta_value &m) { return m.as_string().size() <= 10; }, "File name, up to 10 characters");
+ results.emplace_back(meta_name::file_type, "", true, nullptr, "Type of the file");
+ results.emplace_back(meta_name::length, 0, true, nullptr, "Size of the file in bytes");
+ results.emplace_back(meta_name::modification_date, 0, true, nullptr, "Time of last modification");
+ return results;
+}
+
+
+//-------------------------------------------------
+// mount
+//-------------------------------------------------
+
+std::unique_ptr<filesystem_t> fs::hplif_image::mount(fsblk_t &blockdev) const
+{
+ return std::make_unique<impl>(blockdev);
+}
+
+
+//-------------------------------------------------
+// strtrimright_cbm
+//-------------------------------------------------
+
+namespace {
+
+std::string_view strtrimright_hplif(std::string_view str)
+{
+ return strtrimright(str, [](char c) { return c != (char)0x20; });
+}
+
+
+//-------------------------------------------------
+// strtrimright_cbm
+//-------------------------------------------------
+
+template<size_t N>
+std::string_view strtrimright_hplif(const char (&str)[N])
+{
+ std::string_view sv(str, std::size(str));
+ return strtrimright_hplif(sv);
+}
+
+
+//-------------------------------------------------
+// impl ctor
+//-------------------------------------------------
+
+impl::impl(fsblk_t &blockdev)
+ : filesystem_t(blockdev, 256)
+{
+}
+
+
+//-------------------------------------------------
+// impl::volume_metadata
+//-------------------------------------------------
+
+meta_data impl::volume_metadata()
+{
+ auto block = read_sector(0);
+ std::string_view disk_name = block->rstr(2, 6);
+
+ meta_data results;
+ results.set(meta_name::name, strtrimright_hplif(disk_name));
+ results.set(meta_name::creation_date, decode_datetime(reinterpret_cast<const hplif_time *>(block->rodata() + 36)));
+ return results;
+}
+
+
+//-------------------------------------------------
+// impl::metadata
+//-------------------------------------------------
+
+std::pair<std::error_condition, meta_data> impl::metadata(const std::vector<std::string> &path)
+{
+ std::optional<hplif_dirent> dirent = dirent_from_path(path);
+ if (!dirent)
+ return std::make_pair(error::not_found, meta_data());
+
+ return std::make_pair(std::error_condition(), metadata_from_dirent(*dirent));
+}
+
+
+//-------------------------------------------------
+// impl::directory_contents
+//-------------------------------------------------
+
+std::pair<std::error_condition, std::vector<dir_entry>> impl::directory_contents(const std::vector<std::string> &path)
+{
+ std::vector<dir_entry> results;
+ auto callback = [this, &results](const hplif_dirent &ent)
+ {
+ results.emplace_back(dir_entry_type::file, metadata_from_dirent(ent));
+ return false;
+ };
+ iterate_directory_entries(callback);
+ return std::make_pair(std::error_condition(), std::move(results));
+}
+
+
+//-------------------------------------------------
+// impl::file_read
+//-------------------------------------------------
+
+std::pair<std::error_condition, std::vector<u8>> impl::file_read(const std::vector<std::string> &path)
+{
+ // find the file
+ std::optional<hplif_dirent> dirent = dirent_from_path(path);
+ if (!dirent)
+ return std::make_pair(error::not_found, std::vector<u8>());
+
+ // and get the data
+ u32 sector_count = big_endianize_int32(dirent->m_sector_count);
+ hplif_dirent hdr = dirent.value();
+ std::vector<u8> result;
+ result.insert(
+ result.end(),
+ reinterpret_cast<const u8 *>(&hdr),
+ reinterpret_cast<const u8 *>(&hdr) + 32);
+ result.reserve(sector_count * 256 + 32);
+ block_iterator iter(*this, big_endianize_int32(dirent->m_starting_sector), sector_count);
+ while (iter.next())
+ result.insert(result.end(), (const u8 *)iter.data(), (const u8 *)iter.data() + 256);
+
+ return std::make_pair(std::error_condition(), std::move(result));
+}
+
+
+//-------------------------------------------------
+// impl::read_sector
+//-------------------------------------------------
+
+fsblk_t::block_t::ptr impl::read_sector(u32 sector) const
+{
+ return m_blockdev.get(sector);
+}
+
+
+//-------------------------------------------------
+// impl::dirent_from_path
+//-------------------------------------------------
+
+std::optional<impl::hplif_dirent> impl::dirent_from_path(const std::vector<std::string> &path) const
+{
+ if (path.size() != 1)
+ return { };
+ std::string_view path_part = path[0];
+
+ std::optional<hplif_dirent> result;
+ auto callback = [&result, path_part](const hplif_dirent &dirent)
+ {
+ bool found = strtrimright_hplif(dirent.m_file_name) == path_part;
+ if (found)
+ result = dirent;
+ return found;
+ };
+ iterate_directory_entries(callback);
+ return result;
+}
+
+
+//-------------------------------------------------
+// impl::iterate_directory_entries
+//-------------------------------------------------
+
+void impl::iterate_directory_entries(const std::function<bool(const hplif_dirent &dirent)> &callback) const
+{
+ fsblk_t::block_t::ptr block = m_blockdev.get(0);
+ block_iterator iter(*this, block->r32b(8), block->r32b(16));
+
+ if (block->r16b(0) != 0x8000)
+ {
+ return;
+ }
+
+ while (iter.next())
+ {
+ for (const hplif_dirent &ent : iter.dirent_data())
+ {
+ if (ent.m_file_type == 0xffff)
+ {
+ return;
+ }
+
+ if (ent.m_file_type != 0x0000)
+ {
+ if (callback(ent))
+ return;
+ }
+ }
+ }
+}
+
+//-------------------------------------------------
+// impl::decode_datetime
+//-------------------------------------------------
+
+util::arbitrary_datetime impl::decode_datetime(const hplif_time *time) const
+{
+ util::arbitrary_datetime result;
+ memset(&result, 0, sizeof(result));
+
+ result.year = bcd_2_dec(time->year) + 1900;
+ result.month = bcd_2_dec(time->month);
+ result.day_of_month = bcd_2_dec(time->day);
+ result.hour = bcd_2_dec(time->hour);
+ result.minute = bcd_2_dec(time->minute);
+ result.second = bcd_2_dec(time->second);
+ return result;
+}
+
+//-------------------------------------------------
+// impl::metadata_from_dirent
+//-------------------------------------------------
+
+meta_data impl::metadata_from_dirent(const hplif_dirent &dirent) const
+{
+ std::string file_type = util::string_format("0x%04X", big_endianize_int16(dirent.m_file_type));
+
+ // build the metadata and return it
+ meta_data result;
+ result.set(meta_name::name, strtrimright_hplif(dirent.m_file_name));
+ result.set(meta_name::file_type, std::move(file_type));
+ result.set(meta_name::length, big_endianize_int32(dirent.m_sector_count) * 256);
+ result.set(meta_name::modification_date, decode_datetime(&dirent.m_time));
+ return result;
+}
+
+
+//-------------------------------------------------
+// impl::block_iterator ctor
+//-------------------------------------------------
+
+impl::block_iterator::block_iterator(const impl &fs, u32 starting_sector, u32 sector_count)
+ : m_fs(fs)
+ , m_sector(starting_sector)
+ , m_sector_count(sector_count)
+{
+}
+
+
+//-------------------------------------------------
+// impl::block_iterator::next
+//-------------------------------------------------
+
+bool impl::block_iterator::next()
+{
+ bool result;
+ if (m_sector_count != 0x00)
+ {
+ m_block = m_fs.read_sector(m_sector++);
+ m_sector_count--;
+ result = true;
+ }
+ else
+ {
+ // the iterator has already completed
+ result = false;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// impl::block_iterator::data
+//-------------------------------------------------
+
+const void *impl::block_iterator::data() const
+{
+ return m_block->rodata();
+}
+
+//-------------------------------------------------
+// impl::format
+//-------------------------------------------------
+
+std::error_condition impl::format(const meta_data &meta)
+{
+ std::string volume_name = meta.get_string(meta_name::name, "B9826 ");
+ fsblk_t::block_t::ptr block = m_blockdev.get(0);
+
+ if (volume_name.size() < 6)
+ volume_name.insert(volume_name.end(), 6 - volume_name.size(), ' ');
+ if (volume_name.size() > 6)
+ volume_name.resize(6);
+
+ block->w16b(0, 0x8000); // LIF magic
+ block->wstr(2, volume_name);
+ block->w32b(8, 2); // directory start
+ block->w16b(12, 0x1000); // LIF identifier
+ block->w32b(16, 14); // directory size
+ block->w16b(20, 1); // LIF version
+ return std::error_condition();
+}
+
+//-------------------------------------------------
+// impl::block_iterator::dirent_data
+//-------------------------------------------------
+
+const std::array<impl::hplif_dirent, 8> &impl::block_iterator::dirent_data() const
+{
+ return *reinterpret_cast<const std::array<impl::hplif_dirent, 8> *>(m_block->rodata());
+}
+
+} // anonymous namespace
diff --git a/src/lib/formats/fs_hplif.h b/src/lib/formats/fs_hplif.h
new file mode 100644
index 00000000000..fa0ec8cf6e5
--- /dev/null
+++ b/src/lib/formats/fs_hplif.h
@@ -0,0 +1,43 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/***************************************************************************
+
+ fs_hplif.h
+
+ Management of CBM (Commodore) DOS disk images
+
+***************************************************************************/
+
+#ifndef MAME_FORMATS_FS_HPLIF_H
+#define MAME_FORMATS_FS_HPLIF_H
+
+#pragma once
+
+#include "fsmgr.h"
+
+namespace fs {
+
+ class hplif_image : public manager_t {
+ public:
+ hplif_image() = default;
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+
+ virtual std::vector<meta_description> volume_meta_description() const override;
+ virtual std::vector<meta_description> file_meta_description() const override;
+ };
+
+ extern const hplif_image HPLIF;
+
+} // namespace fs
+
+#endif // #define MAME_FORMATS_FS_HPLIF_H
diff --git a/src/lib/formats/fs_isis.cpp b/src/lib/formats/fs_isis.cpp
new file mode 100644
index 00000000000..fb79314a360
--- /dev/null
+++ b/src/lib/formats/fs_isis.cpp
@@ -0,0 +1,1031 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ fs_isis.cpp
+
+ Intel ISIS-II filesystem handling
+
+*********************************************************************/
+
+#include "fs_isis.h"
+#include "fsblk.h"
+#include "img_dsk.h"
+
+#include "multibyte.h"
+#include "strformat.h"
+
+#include <bitset>
+
+using namespace fs;
+using namespace std::literals;
+
+// Constants
+constexpr unsigned SECTOR_SIZE = img_format::SECTOR_SIZE;
+constexpr unsigned DD_SECTORS = img_format::TRACKS * img_format::HEADS * img_format::SECTORS;
+constexpr unsigned DD_IMAGE_SIZE = DD_SECTORS * SECTOR_SIZE;
+constexpr unsigned SD_SECTORS = DD_SECTORS / 2;
+constexpr unsigned SD_IMAGE_SIZE = DD_IMAGE_SIZE / 2;
+
+// +------+
+// | ISIS |
+// +------+
+namespace fs {
+ const isis_image ISIS;
+}
+
+// +-----------+
+// | isis_impl |
+// +-----------+
+namespace {
+ class isis_impl : public filesystem_t {
+ public:
+ isis_impl(fsblk_t& blockdev);
+ virtual ~isis_impl() = default;
+
+ virtual meta_data volume_metadata() override;
+
+ virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override;
+
+ virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+
+ virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override;
+
+ virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+
+ virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
+
+ virtual std::error_condition format(const meta_data &meta) override;
+
+ private:
+ using sect_map = std::bitset<DD_SECTORS>;
+ // first: Track [0..76]
+ // second: Sector [1..52]
+ // (0,0) is a special case: a null pointer in linkage blocks
+ using track_sect = std::pair<u8 , u8>;
+ // 0-based LBAs
+ using lba_t = int;
+ // List of LBAs
+ using lba_list = std::vector<lba_t>;
+
+ // Position of ISIS.DIR (directory): (1,1)
+ static constexpr track_sect DIR_TS{ 1 , 1 };
+ // Size of ISIS.DIR
+ static constexpr unsigned DIR_SECTS = 25;
+ static constexpr unsigned DIR_SIZE = DIR_SECTS * SECTOR_SIZE;
+ // Size of dir. entries
+ static constexpr unsigned DIR_ENTRY_SIZE = 16;
+ // Count of directory entries
+ static constexpr unsigned DIR_ENTRIES = DIR_SIZE / DIR_ENTRY_SIZE;
+ // Dir entry allocation states
+ static constexpr u8 DIR_IN_USE = 0x00;
+ static constexpr u8 DIR_NEVER_USED = 0x7f;
+ static constexpr u8 DIR_DELETED = 0xff;
+ // Default attributes for system files (format & invisible)
+ static constexpr u8 DEF_SYS_ATTRS = 0x81;
+ // Position of ISIS.MAP (map): (2,1)
+ static constexpr track_sect MAP_TS{ 2 , 1 };
+ // ISIS.MAP size in SD & DD formats
+ static constexpr unsigned ISIS_MAP_SD_SIZE = 256;
+ static constexpr unsigned ISIS_MAP_DD_SIZE = 512;
+ // Position of ISIS.LAB (disk label): (0,25)
+ static constexpr track_sect LAB_TS{ 0 , 25 };
+ // ISIS.LAB size in SD & DD formats
+ static constexpr unsigned ISIS_LAB_SD_SIZE = 128;
+ static constexpr unsigned ISIS_LAB_DD_SIZE = 6784;
+ // Position and number of block sets in ISIS.LAB (SD format)
+ static constexpr unsigned ISIS_LAB_SD_BLOCKS = 1;
+ // Position and number of block sets in ISIS.LAB (DD format)
+ static constexpr unsigned ISIS_LAB_DD_1ST_BLOCKS = 27;
+ static constexpr track_sect LAB_DD_2ND_TS{ 1 , 27 };
+ static constexpr unsigned ISIS_LAB_DD_2ND_BLOCKS = 26;
+ // Position of ISIS.T0 (boot): (0,1)
+ static constexpr track_sect T0_TS{ 0 , 1 };
+ // Position of ISIS.T0 1st linkage block: (0,24)
+ static constexpr track_sect T0_1ST_LINK_TS{ 0 , 24 };
+ // ISIS.T0 size
+ static constexpr unsigned T0_SECTS = 23;
+ static constexpr unsigned ISIS_T0_SIZE = T0_SECTS * SECTOR_SIZE;
+ // First position that can be allocated for user files
+ static constexpr track_sect ALLOC_START_SD{ 2 , 4 };
+ static constexpr track_sect ALLOC_START_DD{ 2 , 6 };
+ // Pointers per linkage block (excluding backward/forward pointers)
+ static constexpr unsigned PTRS_PER_BLOCK = 62;
+
+ // ISIS.DIR file (directory)
+ inline static const std::string_view ISIS_DIR = "ISIS.DIR"sv;
+ // ISIS.MAP file (map of free/used sectors)
+ inline static const std::string_view ISIS_MAP = "ISIS.MAP"sv;
+ // ISIS.T0 file (boot code)
+ inline static const std::string_view ISIS_T0 = "ISIS.T0"sv;
+ // ISIS.LAB file (it holds volume label & OS version)
+ inline static const std::string_view ISIS_LAB = "ISIS.LAB"sv;
+
+ // Valid characters in file names (digits & uppercase letters)
+ inline static const std::string_view VALID_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"sv;
+
+ // Directory entries
+ struct entry {
+ entry()
+ : m_alloc_state(DIR_NEVER_USED)
+ , m_name{}
+ , m_attrs{0}
+ , m_size{0}
+ , m_1st_link{-1}
+ , m_lbas()
+ { }
+
+ // Allocation state, one of 00, 7f, ff
+ u8 m_alloc_state;
+ // Filename
+ std::string m_name;
+ // Attributes
+ u8 m_attrs;
+ // Size
+ unsigned m_size;
+ // First linkage block
+ lba_t m_1st_link;
+ // List of file blocks
+ // Blocks @ idx = 63 * N are linkage blocks, all others are data blocks
+ lba_list m_lbas;
+ };
+
+ using dir_t = std::array<entry, DIR_ENTRIES>;
+
+ // Convert between TS pair & 0-based LBA
+ // TS=(0,0) is converted to -1
+ lba_t ts_2_lba(const track_sect& ts) const;
+ track_sect lba_2_ts(lba_t lba) const;
+
+ lba_t lba_from_2b(const fsblk_t::block_t& blk, u32 offset) const;
+ void lba_to_2b(lba_t lba, fsblk_t::block_t& blk, u32 offset) const;
+
+ bool is_dd() const;
+ unsigned sectors_per_track() const;
+ unsigned tot_sectors() const { return is_dd() ? DD_SECTORS : SD_SECTORS; }
+
+ static std::string dec_name(std::vector<u8>::const_iterator begin, std::vector<u8>::const_iterator end);
+ static bool validate_filename(const std::string& s);
+ static bool user_can_create(const std::string& name);
+ static bool user_can_write(const std::string& name);
+ void ensure_dir_loaded();
+ void check_map();
+ std::vector<u8> encode_dir() const;
+ void store_dir_map();
+ dir_t::iterator scan_dir(const std::string& name);
+ static meta_data get_metadata(const entry& e);
+ std::pair<const lba_list*, unsigned> find_file(const std::string& name);
+ std::pair<const lba_list*, unsigned> find_file(const std::vector<std::string> &path);
+ lba_list get_file_allocation(lba_t first_link, unsigned size, sect_map& in_use);
+ std::vector<u8> get_file_content(const lba_list& sects, unsigned size);
+ void store_file_content(const lba_list& sects, const std::vector<u8>& data);
+ unsigned map_size() const;
+ sect_map decode_map(const std::vector<u8>& data) const;
+ std::vector<u8> encode_map(const sect_map& map) const;
+ void set_not_in_use(const lba_list& lbas);
+ bool allocate(unsigned file_size, lba_list& list);
+ void allocate_seq(lba_t first_lba, unsigned blocks, lba_list& list);
+ void allocate_t0(lba_list& list);
+
+ // Has dir. been loaded?
+ bool m_dir_loaded;
+ // Directory
+ dir_t m_dir;
+ // Sectors in use
+ sect_map m_in_use;
+ };
+}
+
+isis_impl::isis_impl(fsblk_t& blockdev)
+ : filesystem_t(blockdev , SECTOR_SIZE)
+ , m_dir_loaded{false}
+ , m_dir()
+ , m_in_use{}
+{
+}
+
+meta_data isis_impl::volume_metadata()
+{
+ meta_data res;
+
+ const auto& [ lbas, size ] = find_file(std::string(ISIS_LAB));
+ if (lbas != nullptr && size >= 11) {
+ auto file_data = get_file_content(*lbas, size);
+ auto vol_name = dec_name(file_data.cbegin(), file_data.cbegin() + 9);
+ if (vol_name.empty()) {
+ vol_name = "UNTITLED";
+ }
+ res.set(meta_name::name, vol_name);
+ try {
+ res.set(meta_name::os_version, u64(std::stoul(std::string(file_data.cbegin() + 9, file_data.cbegin() + 11))));
+ } catch (...) {
+ res.set(meta_name::os_version, 0);
+ }
+ }
+ return res;
+}
+
+std::pair<std::error_condition, meta_data> isis_impl::metadata(const std::vector<std::string> &path)
+{
+ if (path.size() != 1) {
+ return std::make_pair(error::not_found, meta_data{});
+ }
+
+ ensure_dir_loaded();
+
+ auto it = scan_dir(path.front());
+ if (it == m_dir.end()) {
+ return std::make_pair(error::not_found, meta_data{});
+ }
+
+ auto meta = get_metadata(*it);
+ return std::make_pair(std::error_condition(), std::move(meta));
+}
+
+std::pair<std::error_condition, std::vector<dir_entry>> isis_impl::directory_contents(const std::vector<std::string> &path)
+{
+ if (path.empty()) {
+ ensure_dir_loaded();
+ // Copy all dir_entry(s) out of m_dir
+ std::vector<dir_entry> dir_entries;
+ for (const auto& e : m_dir) {
+ if (e.m_alloc_state == DIR_IN_USE) {
+ auto meta = get_metadata(e);
+ dir_entries.emplace_back(dir_entry_type::file, std::move(meta));
+ }
+ }
+ return std::make_pair(std::error_condition(), dir_entries);
+ } else {
+ return std::make_pair(error::not_found, std::vector<dir_entry>{});
+ }
+}
+
+std::error_condition isis_impl::file_create(const std::vector<std::string> &path, const meta_data &meta)
+{
+ if (!path.empty()) {
+ return error::invalid_name;
+ }
+
+ auto name = meta.get_string(meta_name::name);
+
+ // Validate filename
+ auto pt_pos = name.find('.');
+
+ std::string filename = name.substr(0, pt_pos);
+
+ if (filename.size() < 1 || filename.size() > 6 || !validate_filename(filename)) {
+ return error::invalid_name;
+ }
+
+ if (pt_pos != std::string::npos) {
+ auto ext = name.substr(pt_pos + 1);
+ if (ext.size() < 1 || ext.size() > 3 || !validate_filename(ext)) {
+ return error::invalid_name;
+ }
+ filename.push_back('.');
+ filename += ext;
+ }
+
+ // Check that file can be created by user
+ if (!user_can_create(filename)) {
+ return error::unsupported;
+ }
+
+ // Check that file doesn't exist
+ ensure_dir_loaded();
+ auto it = scan_dir(filename);
+ if (it != m_dir.end()) {
+ return error::already_exists;
+ }
+
+ // Find a free entry in directory
+ for (it = m_dir.begin(); it != m_dir.end(); it++) {
+ if (it->m_alloc_state != DIR_IN_USE) {
+ break;
+ }
+ }
+ if (it == m_dir.end()) {
+ return error::no_space;
+ }
+
+ // Allocate space
+ lba_list lbas;
+ if (!allocate(0, lbas)) {
+ return error::no_space;
+ }
+
+ // Fill dir entry
+ // TODO: get attributes from metadata
+ it->m_alloc_state = DIR_IN_USE;
+ it->m_name = filename;
+ it->m_attrs = 0;
+ it->m_size = 0;
+ it->m_1st_link = lbas.front();
+ it->m_lbas = std::move(lbas);
+
+ // Update directory & map
+ store_dir_map();
+
+ return std::error_condition();
+}
+
+std::pair<std::error_condition, std::vector<u8>> isis_impl::file_read(const std::vector<std::string> &path)
+{
+ const auto& [ lbas, size ] = find_file(path);
+ if (lbas != nullptr) {
+ auto file_data = get_file_content(*lbas, size);
+ return std::make_pair(std::error_condition(), std::move(file_data));
+ }
+ return std::make_pair(error::not_found, std::vector<u8>{});
+}
+
+std::error_condition isis_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
+{
+ if (path.size() != 1) {
+ return error::not_found;
+ }
+
+ // Check that file is user-writeable
+ const auto& filename = path.front();
+ if (!user_can_write(filename)) {
+ return error::unsupported;
+ }
+
+ // ISIS.T0 can only be 2944 bytes long
+ bool is_t0 = filename == ISIS_T0;
+ if (is_t0 && data.size() != ISIS_T0_SIZE) {
+ return error::incorrect_size;
+ }
+
+ // Check that file already exists
+ ensure_dir_loaded();
+ auto it = scan_dir(filename);
+ if (it == m_dir.end()) {
+ return error::not_found;
+ }
+
+ // De-allocate current blocks
+ set_not_in_use(it->m_lbas);
+
+ // Allocate blocks for new file size
+ lba_list lbas;
+ if (is_t0) {
+ allocate_t0(lbas);
+ } else if (!allocate(data.size(), lbas)) {
+ return error::no_space;
+ }
+
+ // Update dir entry
+ it->m_size = data.size();
+ it->m_1st_link = lbas.front();
+ it->m_lbas = lbas;
+
+ // Store file content
+ store_file_content(lbas, data);
+
+ // Update directory & map
+ store_dir_map();
+
+ return std::error_condition();
+}
+
+std::error_condition isis_impl::format(const meta_data &meta)
+{
+ entry dir_e;
+ dir_e.m_alloc_state = DIR_IN_USE;
+ dir_e.m_name = ISIS_DIR;
+ dir_e.m_attrs = DEF_SYS_ATTRS;
+ dir_e.m_size = DIR_SIZE;
+ dir_e.m_1st_link = ts_2_lba(DIR_TS);
+ m_dir[ 0 ] = dir_e;
+ entry map_e;
+ map_e.m_alloc_state = DIR_IN_USE;
+ map_e.m_name = ISIS_MAP;
+ map_e.m_attrs = DEF_SYS_ATTRS;
+ map_e.m_size = map_size();
+ map_e.m_1st_link = ts_2_lba(MAP_TS);
+ m_dir[ 1 ] = map_e;
+
+ // Empty T0
+ entry t0_e;
+ t0_e.m_alloc_state = DIR_IN_USE;
+ t0_e.m_name = ISIS_T0;
+ t0_e.m_attrs = DEF_SYS_ATTRS;
+ t0_e.m_size = ISIS_T0_SIZE;
+ t0_e.m_1st_link = ts_2_lba(T0_1ST_LINK_TS);
+ m_dir[ 2 ] = t0_e;
+ std::vector<u8> t0_content(ISIS_T0_SIZE);
+ lba_list t0_lbas;
+ allocate_t0(t0_lbas);
+ store_file_content(t0_lbas, t0_content);
+
+ // Empty ISIS.LAB
+ // TODO: fill with volume metadata
+ entry lab_e;
+ lab_e.m_alloc_state = DIR_IN_USE;
+ lab_e.m_name = ISIS_LAB;
+ lab_e.m_attrs = DEF_SYS_ATTRS;
+ lab_e.m_size = is_dd() ? ISIS_LAB_DD_SIZE : ISIS_LAB_SD_SIZE;
+ lab_e.m_1st_link = ts_2_lba(LAB_TS);
+ m_dir[ 3 ] = lab_e;
+ if (is_dd()) {
+ std::vector<u8> lab_content(ISIS_LAB_DD_SIZE);
+ lba_list lab_lbas;
+ allocate_seq(ts_2_lba(LAB_TS), ISIS_LAB_DD_1ST_BLOCKS + 1, lab_lbas);
+ allocate_seq(ts_2_lba(LAB_DD_2ND_TS), ISIS_LAB_DD_2ND_BLOCKS, lab_lbas);
+ store_file_content(lab_lbas, lab_content);
+ } else {
+ std::vector<u8> lab_content(ISIS_LAB_SD_SIZE);
+ lba_list lab_lbas;
+ allocate_seq(ts_2_lba(LAB_TS), ISIS_LAB_SD_BLOCKS + 1, lab_lbas);
+ store_file_content(lab_lbas, lab_content);
+ }
+
+ store_dir_map();
+ return std::error_condition();
+}
+
+isis_impl::lba_t isis_impl::ts_2_lba(const track_sect &ts) const
+{
+ unsigned secs_per_track = sectors_per_track();
+
+ if (ts.first == 0 && ts.second == 0) {
+ return -1;
+ } else if (ts.first >= img_format::TRACKS || ts.second < 1 ||
+ ts.second > secs_per_track) {
+ throw std::out_of_range(util::string_format("Invalid TS=(%u,%u)", ts.first, ts.second));
+ } else {
+ return ts.first * secs_per_track + ts.second - 1;
+ }
+}
+
+isis_impl::track_sect isis_impl::lba_2_ts(lba_t lba) const
+{
+ if (lba < 0) {
+ return track_sect{ 0 , 0 };
+ } else {
+ unsigned secs_per_track = sectors_per_track();
+
+ unsigned track = unsigned(lba) / secs_per_track;
+ unsigned sect = unsigned(lba) % secs_per_track;
+ return track_sect{ u8(track), u8(sect) + 1 };
+ }
+}
+
+isis_impl::lba_t isis_impl::lba_from_2b(const fsblk_t::block_t& blk, u32 offset) const
+{
+ return ts_2_lba(track_sect{ blk.r8(offset + 1), blk.r8(offset) });
+}
+
+void isis_impl::lba_to_2b(lba_t lba, fsblk_t::block_t& blk, u32 offset) const
+{
+ auto ts = lba_2_ts(lba);
+
+ blk.w8(offset, ts.second);
+ blk.w8(offset + 1, ts.first);
+}
+
+bool isis_impl::is_dd() const
+{
+ return m_blockdev.block_count() == DD_SECTORS;
+}
+
+unsigned isis_impl::sectors_per_track() const
+{
+ return is_dd() ? img_format::SECTORS : img_format::SECTORS / 2;
+}
+
+std::string isis_impl::dec_name(std::vector<u8>::const_iterator begin, std::vector<u8>::const_iterator end)
+{
+ std::string res;
+ bool ended = false;
+ for (auto i = begin; i < end; i++) {
+ auto c = *i;
+ if (ended) {
+ if (c != 0) {
+ throw std::runtime_error("Invalid name in dir. entry");
+ }
+ } else {
+ if (c == 0) {
+ ended = true;
+ } else if (VALID_CHARS.find(char(c)) != std::string::npos) {
+ res.push_back(char(c));
+ } else {
+ throw std::runtime_error("Invalid name in dir. entry");
+ }
+ }
+ }
+
+ return res;
+}
+
+bool isis_impl::validate_filename(const std::string& s)
+{
+ auto pos = s.find_first_not_of(VALID_CHARS);
+ return pos == std::string::npos;
+}
+
+bool isis_impl::user_can_create(const std::string& name)
+{
+ return name != ISIS_DIR &&
+ name != ISIS_MAP &&
+ name != ISIS_LAB &&
+ name != ISIS_T0;
+}
+
+bool isis_impl::user_can_write(const std::string& name)
+{
+ return name != ISIS_DIR &&
+ name != ISIS_MAP &&
+ name != ISIS_LAB;
+}
+
+void isis_impl::ensure_dir_loaded()
+{
+ if (m_dir_loaded) {
+ return;
+ }
+
+ m_in_use.reset();
+
+ // Fetch dir
+ auto dir_lba_list = get_file_allocation(ts_2_lba(DIR_TS), DIR_SIZE, m_in_use);
+ auto enc_dir = get_file_content(dir_lba_list, DIR_SIZE);
+
+ m_in_use.reset();
+
+ // Scan the whole dir.
+ auto dir_it = m_dir.begin();
+ for (auto i = enc_dir.cbegin(); i < enc_dir.cend(); i += DIR_ENTRY_SIZE, dir_it++) {
+ entry new_entry;
+ // +0: Entry state
+ // 00 Entry in use
+ // 7f Entry empty/never used
+ // ff Entry deleted
+ u8 entry_state = *i;
+ new_entry.m_alloc_state = entry_state;
+ if (entry_state == DIR_IN_USE) {
+ new_entry.m_alloc_state = entry_state;
+ // +1..+6: File name
+ // +7..+9: File extension
+ auto file_name = dec_name(i + 1, i + 7);
+ if (file_name.empty()) {
+ throw std::runtime_error("Null filename");
+ }
+ auto file_ext = dec_name(i + 7, i + 10);
+ if (!file_ext.empty()) {
+ file_name.push_back('.');
+ file_name += file_ext;
+ }
+ auto other_entry = scan_dir(file_name);
+ if (other_entry != m_dir.end()) {
+ throw std::runtime_error(util::string_format("Duplicated file name (%s)", file_name));
+ }
+ new_entry.m_name = file_name;
+ // +10: Attributes
+ new_entry.m_attrs = i[ 10 ];
+ // +11: bytes in last sector
+ auto remainder = i[ 11 ];
+ // +12..+13: size in sectors
+ unsigned sectors = get_u16le(&i[ 12 ]);
+ if (remainder > SECTOR_SIZE) {
+ throw std::runtime_error("Invalid dir. entry");
+ }
+ unsigned size;
+ if (sectors == 0) {
+ size = 0;
+ } else {
+ size = (sectors - 1) * SECTOR_SIZE + remainder;
+ }
+ new_entry.m_size = size;
+ // +14..+15: Sector/Track of first linkage block
+ new_entry.m_1st_link = ts_2_lba(track_sect{ i[ 15 ], i[ 14 ] });
+ auto file_lba_list = get_file_allocation(new_entry.m_1st_link, size, m_in_use);
+ new_entry.m_lbas = std::move(file_lba_list);
+ } else if (entry_state != DIR_NEVER_USED && entry_state != DIR_DELETED) {
+ throw std::runtime_error(util::string_format("Invalid dir. entry type (%02x)", entry_state));
+ }
+ *dir_it = std::move(new_entry);
+ }
+
+ m_dir_loaded = true;
+
+ // Consistency checks
+ // ISIS.DIR position and size
+ if (m_dir[ 0 ].m_alloc_state != DIR_IN_USE ||
+ m_dir[ 0 ].m_name != ISIS_DIR ||
+ m_dir[ 0 ].m_size != DIR_SIZE ||
+ m_dir[ 0 ].m_1st_link != ts_2_lba(DIR_TS)) {
+ throw std::runtime_error(util::string_format("Invalid %s", ISIS_DIR));
+ }
+ // ISIS.MAP position
+ if (m_dir[ 1 ].m_alloc_state != DIR_IN_USE ||
+ m_dir[ 1 ].m_name != ISIS_MAP ||
+ m_dir[ 1 ].m_1st_link != ts_2_lba(MAP_TS)) {
+ throw std::runtime_error(util::string_format("Invalid %s", ISIS_MAP));
+ }
+ // ISIS.T0 position and size
+ if (m_dir[ 2 ].m_alloc_state != DIR_IN_USE ||
+ m_dir[ 2 ].m_name != ISIS_T0 ||
+ m_dir[ 2 ].m_size != ISIS_T0_SIZE ||
+ m_dir[ 2 ].m_1st_link != ts_2_lba(T0_1ST_LINK_TS)) {
+ throw std::runtime_error(util::string_format("Invalid %s", ISIS_T0));
+ }
+ // ISIS.LAB position and size
+ if (m_dir[ 3 ].m_alloc_state != DIR_IN_USE ||
+ m_dir[ 3 ].m_name != ISIS_LAB ||
+ m_dir[ 3 ].m_size != (is_dd() ? ISIS_LAB_DD_SIZE : ISIS_LAB_SD_SIZE) ||
+ m_dir[ 3 ].m_1st_link != ts_2_lba(LAB_TS)) {
+ throw std::runtime_error(util::string_format("Invalid %s", ISIS_LAB));
+ }
+
+ check_map();
+}
+
+void isis_impl::check_map()
+{
+ const auto& [ lbas, size ] = find_file(std::string(ISIS_MAP));
+ if (lbas == nullptr) {
+ fprintf(stderr, "WARNING: map file missing\n");
+ return;
+ }
+ auto exp_size = map_size();
+ if (size != exp_size) {
+ fprintf(stderr, "WARNING: map file has wrong size (%u vs %u)\n", size, exp_size);
+ return;
+ }
+ auto map_file_data = get_file_content(*lbas, size);
+ auto dec_map = decode_map(map_file_data);
+ if (dec_map != m_in_use) {
+ fprintf(stderr, "WARNING: Incorrect map file\n");
+ }
+}
+
+std::vector<u8> isis_impl::encode_dir() const
+{
+ std::vector<u8> res(DIR_SIZE);
+
+ auto i = res.begin();
+ for (const auto& e : m_dir) {
+ // +0: Entry state
+ i[ 0 ] = e.m_alloc_state;
+ if (e.m_alloc_state == DIR_IN_USE) {
+ // +1..+6: File name
+ // +7..+9: File extension
+ auto pos = e.m_name.find('.');
+ if (pos != std::string::npos) {
+ for (std::string::size_type j = 0; j < pos; j++) {
+ i[ 1 + j ] = u8(e.m_name[ j ]);
+ }
+ for (std::string::size_type j = pos + 1; j < e.m_name.size(); j++) {
+ i[ 6 + j - pos ] = u8(e.m_name[ j ]);
+ }
+ } else {
+ for (std::string::size_type j = 0; j < e.m_name.size(); j++) {
+ i[ 1 + j ] = u8(e.m_name[ j ]);
+ }
+ }
+ // +10: Attributes
+ i[ 10 ] = e.m_attrs;
+ // +11: bytes in last sector
+ // +12..+13: size in sectors
+ unsigned sects = e.m_size / SECTOR_SIZE;
+ unsigned rem = e.m_size % SECTOR_SIZE;
+ i[ 11 ] = rem != 0 ? rem : SECTOR_SIZE;
+ if (rem) {
+ sects++;
+ }
+ i[ 12 ] = u8(sects & 0xff);
+ i[ 13 ] = u8(sects >> 8);
+ // +14..+15: Sector/Track of first linkage block
+ auto ts = lba_2_ts(e.m_1st_link);
+ i[ 14 ] = ts.second;
+ i[ 15 ] = ts.first;
+ }
+ i += DIR_ENTRY_SIZE;
+ }
+
+ return res;
+}
+
+void isis_impl::store_dir_map()
+{
+ // Store directory
+ auto dir_content = encode_dir();
+ lba_list dir_lbas;
+ allocate_seq(ts_2_lba(DIR_TS), DIR_SECTS + 1, dir_lbas);
+ store_file_content(dir_lbas, dir_content);
+ // Store map
+ unsigned map_sects = map_size() / SECTOR_SIZE;
+ lba_list map_lbas;
+ allocate_seq(ts_2_lba(MAP_TS), map_sects + 1, map_lbas);
+ auto map_content = encode_map(m_in_use);
+ store_file_content(map_lbas, map_content);
+}
+
+isis_impl::dir_t::iterator isis_impl::scan_dir(const std::string& name)
+{
+ for (auto it = m_dir.begin(); it != m_dir.end(); it++) {
+ if (it->m_alloc_state == DIR_IN_USE && it->m_name == name) {
+ return it;
+ }
+ }
+ return m_dir.end();
+}
+
+meta_data isis_impl::get_metadata(const entry& e)
+{
+ meta_data meta;
+ meta.set(meta_name::name, e.m_name);
+ std::string attributes;
+ // Format
+ attributes.push_back(e.m_attrs & 0x80 ? 'F' : ' ');
+ // Write protected
+ attributes.push_back(e.m_attrs & 0x04 ? 'W' : ' ');
+ // System
+ attributes.push_back(e.m_attrs & 0x02 ? 'S' : ' ');
+ // Invisible
+ attributes.push_back(e.m_attrs & 0x01 ? 'I' : ' ');
+ meta.set(meta_name::attributes, std::move(attributes));
+ meta.set(meta_name::length, e.m_size);
+
+ return meta;
+}
+
+std::pair<const isis_impl::lba_list*, unsigned> isis_impl::find_file(const std::string& name)
+{
+ ensure_dir_loaded();
+
+ auto it = scan_dir(name);
+
+ if (it != m_dir.end()) {
+ return std::make_pair(&it->m_lbas, it->m_size);
+ } else {
+ return std::make_pair(nullptr, 0);
+ }
+}
+
+std::pair<const isis_impl::lba_list*, unsigned> isis_impl::find_file(const std::vector<std::string> &path)
+{
+ if (path.size() == 1) {
+ return find_file(path[ 0 ]);
+ }
+ return std::make_pair(nullptr, 0);
+}
+
+isis_impl::lba_list isis_impl::get_file_allocation(lba_t first_link, unsigned size, sect_map& in_use)
+{
+ lba_t prev_lba = -1;
+ lba_t curr_map = first_link;
+ lba_list res;
+ while (true) {
+ if (in_use[ curr_map ]) {
+ throw std::runtime_error(util::string_format("Sector %d used more than once", curr_map));
+ }
+ in_use.set(curr_map);
+ res.push_back(curr_map);
+ // Get a linkage block
+ auto map_sect = m_blockdev.get(curr_map);
+ auto prev_ptr = lba_from_2b(*map_sect , 0);
+ auto next_ptr = lba_from_2b(*map_sect , 2);
+ if (prev_lba != prev_ptr) {
+ throw std::runtime_error(util::string_format("Incorrect backward linking in sector %d", curr_map));
+ }
+ // Scan all pointers in linkage block
+ for (u32 i = 4; i < SECTOR_SIZE; i += 2) {
+ auto ptr = lba_from_2b(*map_sect , i);
+ if (size > 0) {
+ if (ptr < 0) {
+ throw std::runtime_error(util::string_format("Unexpected end of pointer list in sector %d", curr_map));
+ } else if (in_use[ ptr ]) {
+ throw std::runtime_error(util::string_format("Sector %d used more than once", ptr));
+ } else {
+ in_use.set(ptr);
+ res.push_back(ptr);
+ unsigned to_copy = std::min(size , SECTOR_SIZE);
+ size -= to_copy;
+ }
+ } else if (ptr >= 0) {
+ throw std::runtime_error(util::string_format("Unexpected continuation of pointer list in sector %d", curr_map));
+ }
+ }
+ // More linkage blocks?
+ if (size > 0) {
+ if (next_ptr < 0) {
+ throw std::runtime_error("Pointer list too short");
+ } else {
+ prev_lba = curr_map;
+ curr_map = next_ptr;
+ }
+ } else if (next_ptr >= 0) {
+ throw std::runtime_error("Pointer list too long");
+ } else {
+ break;
+ }
+ }
+ return res;
+}
+
+std::vector<u8> isis_impl::get_file_content(const lba_list& sects, unsigned size)
+{
+ std::vector<u8> res;
+
+ res.resize(size);
+
+ unsigned pos = 0;
+
+ for (unsigned idx = 0; idx < sects.size(); idx++) {
+ // Skip over linkage blocks
+ if ((idx % (PTRS_PER_BLOCK + 1)) != 0) {
+ unsigned to_copy = std::min(size, SECTOR_SIZE);
+ auto data_sect = m_blockdev.get(sects[ idx ]);
+ data_sect->read(0, res.data() + pos, to_copy);
+ size -= to_copy;
+ pos += to_copy;
+ }
+ }
+ return res;
+}
+
+void isis_impl::store_file_content(const lba_list& sects, const std::vector<u8>& data)
+{
+ lba_t prev_ptr = -1;
+ // Write linkage blocks
+ // A linkage block has room for 62 pointers to data blocks
+ for (unsigned idx = 0; idx < sects.size(); idx += (PTRS_PER_BLOCK + 1)) {
+ lba_t link_lba = sects[ idx ];
+ auto blk = m_blockdev.get(link_lba);
+ // Pointer to previous block
+ lba_to_2b(prev_ptr, *blk, 0);
+ prev_ptr = link_lba;
+ lba_t next_ptr;
+ if (sects.size() - idx > (PTRS_PER_BLOCK + 1)) {
+ // Points to next linkage block
+ next_ptr = sects[ idx + PTRS_PER_BLOCK + 1 ];
+ } else {
+ // No more linkage blocks
+ next_ptr = -1;
+ }
+ // Pointer to next block
+ lba_to_2b(next_ptr, *blk, 2);
+ // Pointers to data blocks
+ for (unsigned j = 0; j < PTRS_PER_BLOCK; j++) {
+ lba_to_2b((j + idx + 1 < sects.size()) ? sects[ j + idx + 1 ] : -1, *blk, 4 + 2 * j);
+ }
+ }
+ // Write data blocks
+ auto ptr = data.data();
+ u32 to_go = u32(data.size());
+ for (unsigned idx = 0; idx < sects.size(); idx++) {
+ if ((idx % (PTRS_PER_BLOCK + 1)) == 0) {
+ // Skip over linkage blocks
+ continue;
+ }
+ u32 count = std::min<u32>(to_go, SECTOR_SIZE);
+ auto blk = m_blockdev.get(sects[ idx ]);
+ blk->write(0, ptr, count);
+ ptr += count;
+ to_go -= count;
+ }
+}
+
+unsigned isis_impl::map_size() const
+{
+ return is_dd() ? ISIS_MAP_DD_SIZE : ISIS_MAP_SD_SIZE;
+}
+
+isis_impl::sect_map isis_impl::decode_map(const std::vector<u8>& data) const
+{
+ sect_map res;
+ auto sects = tot_sectors();
+ for (unsigned i = 0; i < sects; i++) {
+ if ((data[ i / 8 ] & (0x80 >> (i % 8))) != 0) {
+ res.set(i);
+ }
+ }
+ return res;
+}
+
+std::vector<u8> isis_impl::encode_map(const sect_map& map) const
+{
+ size_t sz = map_size();
+ std::vector<u8> res(sz);
+ auto sects = tot_sectors();
+ for (unsigned i = 0; i < sects; i++) {
+ if (map[ i ]) {
+ res[ i / 8 ] |= (0x80 >> (i % 8));
+ }
+ }
+ return res;
+}
+
+void isis_impl::set_not_in_use(const lba_list& lbas)
+{
+ for (const auto lba : lbas) {
+ m_in_use.reset(lba);
+ }
+}
+
+bool isis_impl::allocate(unsigned file_size, lba_list& list)
+{
+ unsigned data_blocks = (file_size + SECTOR_SIZE - 1) / SECTOR_SIZE;
+ unsigned linkage_blocks = (data_blocks + PTRS_PER_BLOCK - 1) / PTRS_PER_BLOCK;
+ if (data_blocks == 0) {
+ // At least one linkage block is always allocated, even for null files
+ linkage_blocks = 1;
+ }
+ unsigned to_do = data_blocks + linkage_blocks;
+ lba_t first_blk = is_dd() ? ts_2_lba(ALLOC_START_DD) : ts_2_lba(ALLOC_START_SD);
+ auto sects = tot_sectors();
+ for (lba_t i = first_blk; i < sects; i++) {
+ if (!m_in_use[ i ]) {
+ list.push_back(i);
+ m_in_use.set(i);
+ to_do--;
+ if (to_do == 0) {
+ break;
+ }
+ }
+ }
+ return to_do == 0;
+}
+
+void isis_impl::allocate_seq(lba_t first_lba, unsigned blocks, lba_list& list)
+{
+ for (lba_t i = first_lba; i < first_lba + blocks; i++) {
+ list.push_back(i);
+ m_in_use.set(i);
+ }
+}
+
+void isis_impl::allocate_t0(lba_list& list)
+{
+ // T0 keeps the linkage block at the end of data blocks
+ allocate_seq(ts_2_lba(T0_1ST_LINK_TS), 1, list);
+ allocate_seq(ts_2_lba(T0_TS), T0_SECTS, list);
+}
+
+// +------------+
+// | isis_image |
+// +------------+
+const char *isis_image::name() const
+{
+ return "isis";
+}
+
+const char *isis_image::description() const
+{
+ return "Intel ISIS-II";
+}
+
+void isis_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_IMG_FORMAT, floppy_image::FF_8, floppy_image::SSSD, SD_IMAGE_SIZE, "isis_sd", "Intel ISIS-II SD");
+ fe.add(FLOPPY_IMG_FORMAT, floppy_image::FF_8, floppy_image::SSDD, DD_IMAGE_SIZE, "isis_dd", "Intel ISIS-II DD");
+}
+
+bool isis_image::can_format() const
+{
+ return true;
+}
+
+bool isis_image::can_read() const
+{
+ return true;
+}
+
+bool isis_image::can_write() const
+{
+ return true;
+}
+
+bool isis_image::has_rsrc() const
+{
+ return false;
+}
+
+std::vector<meta_description> isis_image::volume_meta_description() const
+{
+ std::vector<meta_description> res;
+
+ res.emplace_back(meta_description(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 9; }, "Volume name, up to 9 characters"));
+ res.emplace_back(meta_description(meta_name::os_version, 41, false, [](const meta_value &m) { return m.as_number() <= 99; }, "O/S version, 0..99"));
+
+ return res;
+}
+
+std::vector<meta_description> isis_image::file_meta_description() const
+{
+ std::vector<meta_description> res;
+
+ res.emplace_back(meta_description(meta_name::name, "Empty", false, [](const meta_value &m) { return m.as_string().size() <= 9; }, "File name, up to 9 characters"));
+ res.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "File size"));
+ res.emplace_back(meta_description(meta_name::attributes, "", true, nullptr, "File attributes"));
+
+ return res;
+}
+
+std::unique_ptr<filesystem_t> isis_image::mount(fsblk_t &blockdev) const
+{
+ return std::make_unique<isis_impl>(blockdev);
+}
diff --git a/src/lib/formats/fs_isis.h b/src/lib/formats/fs_isis.h
new file mode 100644
index 00000000000..00868809ae6
--- /dev/null
+++ b/src/lib/formats/fs_isis.h
@@ -0,0 +1,44 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ fs_isis.h
+
+ Intel ISIS-II filesystem handling
+
+*********************************************************************/
+
+#ifndef MAME_FORMATS_FS_ISIS_H
+#define MAME_FORMATS_FS_ISIS_H
+
+#pragma once
+
+#include "fsmgr.h"
+
+namespace fs {
+
+ class isis_image : public manager_t {
+ public:
+ isis_image() : manager_t() {}
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+
+ virtual std::vector<meta_description> volume_meta_description() const override;
+ virtual std::vector<meta_description> file_meta_description() const override;
+
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ };
+
+ extern const isis_image ISIS;
+} // namespace fs
+
+#endif /* MAME_FORMATS_FS_ISIS_H */
diff --git a/src/lib/formats/fs_oric_jasmin.cpp b/src/lib/formats/fs_oric_jasmin.cpp
new file mode 100644
index 00000000000..db24a4a93f8
--- /dev/null
+++ b/src/lib/formats/fs_oric_jasmin.cpp
@@ -0,0 +1,661 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Management of Oric Jasmin floppy images
+
+#include "fs_oric_jasmin.h"
+#include "fsblk.h"
+#include "oric_dsk.h"
+
+#include "multibyte.h"
+
+#include <stdexcept>
+
+using namespace fs;
+
+namespace fs { const oric_jasmin_image ORIC_JASMIN; }
+
+
+// Floppy only, format is 41 tracks, 1/2 heads, 17 sectors.
+// Filesystem has no subdirectories.
+//
+// References are pair of bytes (track + head*41, sector)
+//
+// Track 20 sector 1 has the free bitmap and the volume name
+//
+// offset 3*(track + head*41): bitmap from track, 24 bits LSB first,
+// sector 1 in bit 16, sector 17 in bit 0.
+// 0x01ffff is empty track. Special value
+// 0x800000 marks a full track.
+// offset f6-f7: 0x8080
+// offset f8-ff: volume name, padded with 0x20
+//
+//
+// Track 20 sector 2 has the first directory sector.
+// Directory sector format:
+// offset 00-01: reference to the sector, (0, 0) for the first one
+// offset 02-03: reference to the next sector, (0, 0) on the first sector if it's the end, (ff, 00) for the end of another sector
+// offset 04+ : 14 file entries, 18 bytes each
+// offset 00-01: reference to the first sector of the inode, (ff, xx) when no entry
+// offset 02 : U/L for (U)nlocked or (L)ocked file
+// offset 03-0e: filename.ext, space padding between filename and '.'
+// offset 0f : S/D for (S)equential (normal) or (D)irect-access files
+// offset 10-11: number of sectors used by the file, including inode, little-endian
+//
+// Inodes:
+// offset 00-01: reference to the next inode sector, (ff, 00) on the last one
+// offset 02-03: loading address for the file on the first sector, ffff otherwise
+// offset 04-05: length of the file in bytes on the first sector, ffff otherwise
+// offset 06+ : reference to data sectors, (ff, ff) when done
+
+namespace {
+
+class oric_jasmin_impl : public filesystem_t {
+public:
+ oric_jasmin_impl(fsblk_t &blockdev);
+ virtual ~oric_jasmin_impl() = default;
+
+ virtual meta_data volume_metadata() override;
+ virtual std::error_condition volume_metadata_change(const meta_data &info) override;
+ virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override;
+ virtual std::error_condition metadata_change(const std::vector<std::string> &path, const meta_data &meta) override;
+
+ virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+ virtual std::error_condition rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) override;
+ virtual std::error_condition remove(const std::vector<std::string> &path) override;
+
+ virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override;
+
+ virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+ virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
+
+ virtual std::error_condition format(const meta_data &meta) override;
+
+ static bool validate_filename(std::string name);
+
+private:
+ static u32 cs_to_block(u16 ref);
+ [[maybe_unused]] static u16 block_to_cs(u32 block);
+
+ bool ref_valid(u16 ref);
+ static std::string read_file_name(const u8 *p);
+
+ std::vector<u16> allocate_blocks(u32 count);
+ void free_blocks(const std::vector<u16> &blocks);
+ u32 free_block_count();
+
+ static std::string file_name_read(const u8 *p);
+ static std::string file_name_prepare(std::string name);
+ static bool file_is_system(const u8 *entry);
+ meta_data file_metadata(const u8 *entry);
+ std::tuple<fsblk_t::block_t::ptr, u32, bool> file_find(std::string name);
+};
+}
+
+
+const char *oric_jasmin_image::name() const
+{
+ return "oric_jasmin";
+}
+
+const char *oric_jasmin_image::description() const
+{
+ return "Oric Jasmin";
+}
+
+void oric_jasmin_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_ORIC_JASMIN_FORMAT, floppy_image::FF_3, floppy_image::DSDD, 356864, "oric_jasmin_ds", "Oric Jasmin dual-sided");
+ fe.add(FLOPPY_ORIC_JASMIN_FORMAT, floppy_image::FF_3, floppy_image::SSDD, 178432, "oric_jasmin_ss", "Oric Jasmin single-sided");
+}
+
+std::unique_ptr<filesystem_t> oric_jasmin_image::mount(fsblk_t &blockdev) const
+{
+ return std::make_unique<oric_jasmin_impl>(blockdev);
+}
+
+bool oric_jasmin_image::can_format() const
+{
+ return true;
+}
+
+bool oric_jasmin_image::can_read() const
+{
+ return true;
+}
+
+bool oric_jasmin_image::can_write() const
+{
+ return true;
+}
+
+bool oric_jasmin_image::has_rsrc() const
+{
+ return false;
+}
+
+std::vector<meta_description> oric_jasmin_image::volume_meta_description() const
+{
+ std::vector<meta_description> res;
+ res.emplace_back(meta_description(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 8; }, "Volume name, up to 8 characters"));
+
+ return res;
+}
+
+std::vector<meta_description> oric_jasmin_image::file_meta_description() const
+{
+ std::vector<meta_description> res;
+ res.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return oric_jasmin_impl::validate_filename(m.as_string()); }, "File name, 8.3"));
+ res.emplace_back(meta_description(meta_name::loading_address, 0x501, false, [](const meta_value &m) { return m.as_number() < 0x10000; }, "Loading address of the file"));
+ res.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "Size of the file in bytes"));
+ res.emplace_back(meta_description(meta_name::size_in_blocks, 0, true, nullptr, "Number of blocks used by the file"));
+ res.emplace_back(meta_description(meta_name::locked, false, false, nullptr, "File locked"));
+ res.emplace_back(meta_description(meta_name::sequential, true, false, nullptr, "File sequential"));
+ return res;
+}
+
+
+
+oric_jasmin_impl::oric_jasmin_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 256)
+{
+}
+
+bool oric_jasmin_impl::ref_valid(u16 ref)
+{
+ u8 track = ref >> 8;
+ u8 sector = ref & 0xff;
+ if(sector < 1 || sector > 17)
+ return false;
+ if(track >= m_blockdev.block_count()/17)
+ return false;
+ return true;
+}
+
+u32 oric_jasmin_impl::cs_to_block(u16 ref)
+{
+ u8 track = ref >> 8;
+ if(track == 0xff)
+ abort();
+ u8 sector = ref & 0xff;
+ return track * 17 + sector - 1;
+}
+
+u16 oric_jasmin_impl::block_to_cs(u32 block)
+{
+ u8 track = block / 17;
+ u8 sector = (block % 17) + 1;
+ return (track << 8) | sector;
+}
+
+bool oric_jasmin_impl::validate_filename(std::string name)
+{
+ auto pos = name.find('.');
+ if(pos != std::string::npos)
+ return pos <= 8 && pos > 0 && name.size()-pos-1 <= 3;
+ else
+ return name.size() > 0 && name.size() <= 8;
+}
+
+std::error_condition oric_jasmin_impl::format(const meta_data &meta)
+{
+ std::string volume_name = meta.get_string(meta_name::name, "UNTITLED");
+ u32 blocks = m_blockdev.block_count();
+
+ m_blockdev.fill_all(0x6c);
+
+ u32 bblk = 20*17;
+ auto fmap = m_blockdev.get(bblk);
+ u32 off = 0;
+ for(u32 blk = 0; blk != blocks; blk += 17) {
+ if(blk == bblk)
+ fmap->w24l(off, 0x07fff);
+ else
+ fmap->w24l(off, 0x1ffff);
+ off += 3;
+ }
+
+ for(u32 blk = blocks; blk != 17*42*2; blk += 17) {
+ fmap->w24l(off, 0x800000);
+ off += 3;
+ }
+
+ fmap->w8(0xf6, 0x80);
+ fmap->w8(0xf7, 0x80);
+ volume_name.resize(8, ' ');
+ fmap->wstr(0xf8, volume_name);
+
+ auto bdir = m_blockdev.get(20*17+1);
+ bdir->fill(0xff);
+ bdir->w16l(0, 0x0000);
+ bdir->w16l(2, 0x0000);
+
+ return std::error_condition();
+}
+
+meta_data oric_jasmin_impl::volume_metadata()
+{
+ meta_data res;
+ auto bdir = m_blockdev.get(20*17);
+ int len = 8;
+ while(len > 0 && bdir->rodata()[0xf8 + len - 1] == ' ')
+ len--;
+
+ res.set(meta_name::name, bdir->rstr(0xf8, len));
+ return res;
+}
+
+std::error_condition oric_jasmin_impl::volume_metadata_change(const meta_data &meta)
+{
+ if(meta.has(meta_name::name)) {
+ std::string volume_name = meta.get_string(meta_name::name);
+ volume_name.resize(8, ' ');
+ m_blockdev.get(20*17)->wstr(0xf8, volume_name);
+ }
+ return std::error_condition();
+}
+
+std::string oric_jasmin_impl::file_name_prepare(std::string fname)
+{
+ std::string nname;
+ size_t i;
+ for(i = 0; i != 8 && i != fname.size() && fname[i] != '.'; i++)
+ nname += fname[i];
+ if(nname.size() != 8)
+ nname.insert(nname.end(), 8 - nname.size(), ' ');
+ nname += '.';
+ while(i != fname.size() && fname[i] != '.')
+ i++;
+ if(i != fname.size())
+ i++;
+ while(i != fname.size() && nname.size() != 12)
+ nname += fname[i++];
+ if(nname.size() != 12)
+ nname.insert(nname.end(), 12 - nname.size(), ' ');
+ return nname;
+}
+
+std::string oric_jasmin_impl::file_name_read(const u8 *p)
+{
+ int main_len;
+ for(main_len = 8; main_len > 0; main_len--)
+ if(p[main_len - 1] != ' ')
+ break;
+ int ext_len;
+ for(ext_len = 4; ext_len > 0; ext_len--)
+ if(p[ext_len + 8 - 1] != ' ')
+ break;
+ std::string name;
+ for(int i=0; i != main_len; i++)
+ name += char(p[i]);
+ for(int i=0; i != ext_len; i++)
+ name += char(p[8 + i]);
+ return name;
+}
+
+bool oric_jasmin_impl::file_is_system(const u8 *entry)
+{
+ u16 ref = get_u16be(entry);
+ return ref == 0 && get_u32be(entry+0xb) == 0x2e535953;
+}
+
+
+meta_data oric_jasmin_impl::file_metadata(const u8 *entry)
+{
+ meta_data res;
+
+ res.set(meta_name::name, file_name_read(entry + 3));
+ res.set(meta_name::locked, entry[2] == 'L');
+ res.set(meta_name::sequential, entry[0xf] == 'S');
+ res.set(meta_name::size_in_blocks, get_u16le(entry + 0x10));
+
+ bool sys = file_is_system(entry);
+ if(sys)
+ res.set(meta_name::length, 0x3e00);
+
+ else {
+ u16 ref = get_u16be(entry);
+ auto dblk = m_blockdev.get(cs_to_block(ref));
+ res.set(meta_name::loading_address, dblk->r16l(2));
+ res.set(meta_name::length, dblk->r16l(4));
+ }
+ return res;
+}
+
+std::tuple<fsblk_t::block_t::ptr, u32, bool> oric_jasmin_impl::file_find(std::string name)
+{
+ name = file_name_prepare(name);
+ auto bdir = m_blockdev.get(20*17+1);
+ for(;;) {
+ for(u32 i = 0; i != 14; i ++) {
+ u32 off = 4 + i*18;
+ u16 fref = bdir->r16b(off);
+ if(ref_valid(fref) || file_is_system(bdir->rodata()+off)) {
+ if(memcmp(bdir->rodata() + off + 3, name.data(), 12)) {
+ bool sys = file_is_system(bdir->rodata() + off);
+ return std::make_tuple(bdir, off, sys);
+ }
+ }
+ }
+ u16 ref = bdir->r16b(2);
+ if(!ref || !ref_valid(ref))
+ return std::make_tuple(bdir, 0U, false);
+
+ bdir = m_blockdev.get(cs_to_block(ref));
+ }
+}
+
+std::pair<std::error_condition, meta_data> oric_jasmin_impl::metadata(const std::vector<std::string> &path)
+{
+ if(path.size() != 1)
+ return std::make_pair(error::not_found, meta_data());
+
+ auto [bdir, off, sys] = file_find(path[0]);
+ std::ignore = sys;
+ if(!off)
+ return std::make_pair(error::not_found, meta_data());
+
+ return std::make_pair(std::error_condition(), file_metadata(bdir->rodata() + off));
+}
+
+std::error_condition oric_jasmin_impl::metadata_change(const std::vector<std::string> &path, const meta_data &meta)
+{
+ if(path.size() != 1)
+ return error::not_found;
+
+ auto [bdir, off, sys] = file_find(path[0]);
+ if(!off)
+ return error::not_found;
+
+ u8 *entry = bdir->data() + off;
+ if(meta.has(meta_name::locked))
+ entry[0x02] = meta.get_flag(meta_name::locked) ? 'L' : 'U';
+ if(meta.has(meta_name::name))
+ wstr(entry+0x03, file_name_prepare(meta.get_string(meta_name::name)));
+ if(meta.has(meta_name::sequential))
+ entry[0x0f] = meta.get_flag(meta_name::sequential) ? 'D' : 'S';
+ if(!sys && meta.has(meta_name::loading_address))
+ m_blockdev.get(cs_to_block(get_u16be(entry)))->w16l(2, meta.get_number(meta_name::loading_address));
+
+ return std::error_condition();
+}
+
+std::pair<std::error_condition, std::vector<dir_entry>> oric_jasmin_impl::directory_contents(const std::vector<std::string> &path)
+{
+ std::pair<std::error_condition, std::vector<dir_entry>> res;
+
+ if(path.size() != 0) {
+ res.first = error::not_found;
+ return res;
+ }
+
+ res.first = std::error_condition();
+
+ auto bdir = m_blockdev.get(20*17+1);
+ for(;;) {
+ for(u32 i = 0; i != 14; i ++) {
+ u32 off = 4 + i*18;
+ u16 fref = bdir->r16b(off);
+ if(ref_valid(fref) || file_is_system(bdir->rodata()+off)) {
+ meta_data meta = file_metadata(bdir->rodata()+off);
+ res.second.emplace_back(dir_entry(dir_entry_type::file, meta));
+ }
+ }
+ u16 ref = bdir->r16b(2);
+ if(!ref || !ref_valid(ref))
+ break;
+ bdir = m_blockdev.get(cs_to_block(ref));
+ }
+ return res;
+}
+
+std::error_condition oric_jasmin_impl::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath)
+{
+ if(opath.size() != 1 || npath.size() != 1)
+ return error::not_found;
+
+ auto [bdir, off, sys] = file_find(opath[0]);
+ std::ignore = sys;
+ if(!off)
+ return error::not_found;
+
+ wstr(bdir->data() + off + 0x03, file_name_prepare(npath[0]));
+
+ return std::error_condition();
+}
+
+std::error_condition oric_jasmin_impl::remove(const std::vector<std::string> &path)
+{
+ return error::unsupported;
+}
+
+std::error_condition oric_jasmin_impl::file_create(const std::vector<std::string> &path, const meta_data &meta)
+{
+ if(path.size() != 0)
+ return error::not_found;
+
+ // One block of sector list, one block of data
+ u32 nb = 2;
+
+ // Find the key for the next entry, increase nb if needed
+ auto bdir = m_blockdev.get(20*17+1);
+ u64 id = 0;
+ for(;;) {
+ for(u32 i = 0; i != 14; i ++) {
+ u32 off = 4 + i*18;
+ u16 ref = bdir->r16b(off);
+ if(!ref_valid(ref))
+ goto found;
+ id++;
+ }
+ u16 ref = bdir->r16b(2);
+ if(!ref || !ref_valid(ref)) {
+ nb ++;
+ break;
+ }
+ bdir = m_blockdev.get(cs_to_block(ref));
+ }
+ found:
+ auto block = allocate_blocks(nb);
+ if(block.empty())
+ return error::no_space;
+
+ auto sblk = m_blockdev.get(cs_to_block(block[0]));
+ sblk->w16b(0, 0xff00); // Next sector
+ sblk->w16l(2, meta.get_number(meta_name::loading_address, 0x500));
+ sblk->w16l(4, 0); // Length
+ sblk->w16b(6, block[1]); // Data block
+
+ if(nb == 3) {
+ bdir->w16l(0, block[2]); // Link to the next directory sector
+ bdir = m_blockdev.get(cs_to_block(block[2]));
+ bdir->fill(0xff);
+ bdir->w16l(0, block[2]); // Reference to itself
+ bdir->w16l(2, 0xff00); // No next directory sector
+ }
+
+ u32 off = 4 + (id % 14) * 18;
+ bdir->w16b(off+0x00, block[0]); // First (and only) sector in the sector list
+ bdir->w8 (off+0x02, meta.get_flag(meta_name::locked, false) ? 'L' : 'U');
+ bdir->wstr(off+0x03, file_name_prepare(meta.get_string(meta_name::name, "")));
+ bdir->w8 (off+0x0f, meta.get_flag(meta_name::sequential, true) ? 'S' : 'D');
+ bdir->w16l(off+0x10, 2); // 2 sectors for an empty file
+
+ return std::error_condition();
+}
+
+std::pair<std::error_condition, std::vector<u8>> oric_jasmin_impl::file_read(const std::vector<std::string> &path)
+{
+ std::vector<u8> data;
+
+ if(path.size() != 1)
+ return std::make_pair(error::not_found, data);
+
+ auto [bdir, off, sys] = file_find(path[0]);
+ if(!off)
+ return std::make_pair(error::not_found, data);
+
+ if(sys) {
+ data.resize(0x3e00);
+ for(u32 i = 0; i != 62; i++) {
+ auto dblk = m_blockdev.get(i);
+ dblk->read(0, data.data() + 256 * i, 256);
+ }
+
+ } else {
+ const u8 *entry = bdir->rodata() + off;
+ u16 ref = get_u16be(entry);
+ auto iblk = m_blockdev.get(cs_to_block(ref));
+ u32 length = iblk->r16l(4);
+ while(ref_valid(ref)) {
+ for(u32 pos = 6; pos != 256 && data.size() < length; pos += 2) {
+ u16 dref = iblk->r16b(pos);
+ if(!ref_valid(dref))
+ goto done;
+ auto dblk = m_blockdev.get(cs_to_block(dref));
+ u32 dpos = data.size();
+ data.resize(dpos + 256);
+ dblk->read(0, data.data() + dpos, 256);
+ if(data.size() >= length)
+ goto done;
+ }
+ ref = iblk->r16b(2);
+ if(!ref_valid(ref))
+ break;
+ iblk = m_blockdev.get(cs_to_block(ref));
+ }
+ done:
+ data.resize(length);
+ }
+
+ return std::make_pair(std::error_condition(), data);
+}
+
+std::error_condition oric_jasmin_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
+{
+ if(path.size() != 1)
+ return error::not_found;
+
+ auto [bdir, off, sys] = file_find(path[0]);
+ if(!off)
+ return error::not_found;
+
+ if(sys) {
+ if(data.size() != 0x3e00)
+ return error::incorrect_size;
+
+ for(u32 i=0; i != 0x3e; i++)
+ m_blockdev.get(i)->write(0, data.data() + i * 256, 256);
+
+ } else {
+ u8 *entry = bdir->data() + off;
+ u32 cur_ns = get_u16le(entry + 0x10);
+ // Data sectors first
+ u32 need_ns = (data.size() + 255) / 256;
+ if(need_ns == 0)
+ need_ns = 1;
+ // Add the sector list sectors, 125 entries/sector
+ need_ns += (need_ns + 124)/125;
+
+ // Enough space?
+ if(cur_ns < need_ns && free_block_count() < need_ns - cur_ns)
+ return error::no_space;
+
+ u16 load_address = 0;
+ std::vector<u16> tofree;
+ u16 iref = get_u16be(entry);
+ for(u32 i=0; i < cur_ns; i += 125+1) {
+ auto iblk = m_blockdev.get(cs_to_block(iref));
+ if(!i)
+ load_address = iblk->r16l(2);
+ tofree.push_back(iref);
+ for(u32 j=0; j != 125 && i+j+1 != cur_ns; j++)
+ tofree.push_back(iblk->r16b(6+2*j));
+ iref = iblk->r16b(2);
+ }
+ free_blocks(tofree);
+
+ std::vector<u16> blocks = allocate_blocks(need_ns);
+ for(u32 i=0; i < need_ns; i += 125+1) {
+ auto iblk = m_blockdev.get(cs_to_block(blocks[i]));
+ iblk->fill(0xff);
+ if(!i) {
+ iblk->w16l(2, load_address);
+ iblk->w16l(4, data.size());
+ }
+ if(i + 126 < need_ns)
+ iblk->w16b(0, blocks[i+126]);
+ else
+ iblk->w16b(0, 0xff00);
+
+ for(u32 j=0; j != 125 && i+j+1 != need_ns; j++) {
+ u32 dpos = 256 * (j + i/126*125);
+ u32 size = data.size() - dpos;
+ iblk->w16b(6+j*2, blocks[i+j+1]);
+ auto dblk = m_blockdev.get(cs_to_block(blocks[i+j+1]));
+ if(size >= 256)
+ dblk->write(0, data.data() + dpos, 256);
+ else {
+ dblk->write(0, data.data() + dpos, size);
+ dblk->fill(size, 0x55, 256-size);
+ }
+ }
+ }
+ put_u16le(entry + 0x10, need_ns);
+ put_u16be(entry + 0x00, blocks[0]);
+ }
+ return std::error_condition();
+}
+
+std::vector<u16> oric_jasmin_impl::allocate_blocks(u32 count)
+{
+ std::vector<u16> blocks;
+ if(free_block_count() < count)
+ return blocks;
+
+ auto fmap = m_blockdev.get(20*17);
+ u32 nf = 0;
+ for(u32 track = 0; track != 2*41 && nf != count; track++) {
+ u32 map = fmap->r24l(track*3);
+ if(map != 0x800000) {
+ for(u32 sect = 1; sect <= 17 && nf != count; sect++)
+ if(map & (0x20000 >> sect)) {
+ blocks.push_back((track << 8) | sect);
+ map &= ~(0x20000 >> sect);
+ nf++;
+ }
+ if(!map)
+ map = 0x800000;
+ fmap->w24l(track*3, map);
+ }
+ }
+ return blocks;
+}
+
+void oric_jasmin_impl::free_blocks(const std::vector<u16> &blocks)
+{
+ auto fmap = m_blockdev.get(20*17);
+ for(u16 ref : blocks) {
+ u32 track = ref >> 8;
+ u32 sect = ref & 0xff;
+ u32 map = fmap->r24l(track*3);
+ if(map == 0x800000)
+ map = 0;
+ map |= 0x20000 >> sect;
+ fmap->w24l(track*3, map);
+ }
+}
+
+u32 oric_jasmin_impl::free_block_count()
+{
+ auto fmap = m_blockdev.get(20*17);
+ u32 nf = 0;
+ for(u32 track = 0; track != 2*41; track++) {
+ u32 map = fmap->r24l(track*3);
+ if(map != 0x800000) {
+ for(u32 sect = 1; sect <= 17; sect++)
+ if(map & (0x20000 >> sect))
+ nf++;
+ }
+ }
+ return nf;
+}
diff --git a/src/lib/formats/fs_oric_jasmin.h b/src/lib/formats/fs_oric_jasmin.h
new file mode 100644
index 00000000000..514227d9069
--- /dev/null
+++ b/src/lib/formats/fs_oric_jasmin.h
@@ -0,0 +1,38 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Management of Oric Jasmin floppy images
+
+#ifndef MAME_FORMATS_FS_ORIC_JASMIN_H
+#define MAME_FORMATS_FS_ORIC_JASMIN_H
+
+#pragma once
+
+#include "fsmgr.h"
+
+namespace fs {
+
+class oric_jasmin_image : public manager_t {
+public:
+ oric_jasmin_image() : manager_t() {}
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+
+ virtual std::vector<meta_description> volume_meta_description() const override;
+ virtual std::vector<meta_description> file_meta_description() const override;
+};
+
+extern const oric_jasmin_image ORIC_JASMIN;
+
+} // namespace fs
+
+#endif
diff --git a/src/lib/formats/fs_prodos.cpp b/src/lib/formats/fs_prodos.cpp
new file mode 100644
index 00000000000..f7c77d47ff8
--- /dev/null
+++ b/src/lib/formats/fs_prodos.cpp
@@ -0,0 +1,522 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Creation of Apple ProDOS floppy images
+
+#include "fs_prodos.h"
+#include "ap_dsk35.h"
+#include "ap2_dsk.h"
+#include "fsblk.h"
+
+#include "corestr.h"
+#include "multibyte.h"
+#include "strformat.h"
+
+#include <map>
+#include <stdexcept>
+
+using namespace fs;
+
+namespace fs { const prodos_image PRODOS; }
+
+namespace {
+class prodos_impl : public filesystem_t {
+public:
+ prodos_impl(fsblk_t &blockdev);
+ virtual ~prodos_impl() = default;
+
+ virtual meta_data volume_metadata() override;
+ virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override;
+
+ virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+
+ virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+ virtual std::pair<std::error_condition, std::vector<u8>> file_rsrc_read(const std::vector<std::string> &path) override;
+
+ virtual std::error_condition format(const meta_data &meta) override;
+
+private:
+ static const u8 boot[512];
+ static util::arbitrary_datetime prodos_to_dt(u32 date);
+
+ std::tuple<fsblk_t::block_t::ptr, u32> path_find_step(const std::string &name, u16 block);
+ std::tuple<fsblk_t::block_t::ptr, u32, bool> path_find(const std::vector<std::string> &path);
+ std::pair<std::error_condition, std::vector<u8>> any_read(u8 type, u16 block, u32 length);
+};
+}
+
+
+const char *prodos_image::name() const
+{
+ return "prodos";
+}
+
+const char *prodos_image::description() const
+{
+ return "Apple ProDOS";
+}
+
+const u8 prodos_impl::boot[512] = {
+ 0x01, 0x38, 0xb0, 0x03, 0x4c, 0x1c, 0x09, 0x78, 0x86, 0x43, 0xc9, 0x03, 0x08, 0x8a, 0x29, 0x70,
+ 0x4a, 0x4a, 0x4a, 0x4a, 0x09, 0xc0, 0x85, 0x49, 0xa0, 0xff, 0x84, 0x48, 0x28, 0xc8, 0xb1, 0x48,
+ 0xd0, 0x3a, 0xb0, 0x0e, 0xa9, 0x03, 0x8d, 0x00, 0x08, 0xe6, 0x3d, 0xa5, 0x49, 0x48, 0xa9, 0x5b,
+ 0x48, 0x60, 0x85, 0x40, 0x85, 0x48, 0xa0, 0x5e, 0xb1, 0x48, 0x99, 0x94, 0x09, 0xc8, 0xc0, 0xeb,
+ 0xd0, 0xf6, 0xa2, 0x06, 0xbc, 0x32, 0x09, 0xbd, 0x39, 0x09, 0x99, 0xf2, 0x09, 0xbd, 0x40, 0x09,
+ 0x9d, 0x7f, 0x0a, 0xca, 0x10, 0xee, 0xa9, 0x09, 0x85, 0x49, 0xa9, 0x86, 0xa0, 0x00, 0xc9, 0xf9,
+ 0xb0, 0x2f, 0x85, 0x48, 0x84, 0x60, 0x84, 0x4a, 0x84, 0x4c, 0x84, 0x4e, 0x84, 0x47, 0xc8, 0x84,
+ 0x42, 0xc8, 0x84, 0x46, 0xa9, 0x0c, 0x85, 0x61, 0x85, 0x4b, 0x20, 0x27, 0x09, 0xb0, 0x66, 0xe6,
+ 0x61, 0xe6, 0x61, 0xe6, 0x46, 0xa5, 0x46, 0xc9, 0x06, 0x90, 0xef, 0xad, 0x00, 0x0c, 0x0d, 0x01,
+ 0x0c, 0xd0, 0x52, 0xa9, 0x04, 0xd0, 0x02, 0xa5, 0x4a, 0x18, 0x6d, 0x23, 0x0c, 0xa8, 0x90, 0x0d,
+ 0xe6, 0x4b, 0xa5, 0x4b, 0x4a, 0xb0, 0x06, 0xc9, 0x0a, 0xf0, 0x71, 0xa0, 0x04, 0x84, 0x4a, 0xad,
+ 0x20, 0x09, 0x29, 0x0f, 0xa8, 0xb1, 0x4a, 0xd9, 0x20, 0x09, 0xd0, 0xdb, 0x88, 0x10, 0xf6, 0xa0,
+ 0x16, 0xb1, 0x4a, 0x4a, 0x6d, 0x1f, 0x09, 0x8d, 0x1f, 0x09, 0xa0, 0x11, 0xb1, 0x4a, 0x85, 0x46,
+ 0xc8, 0xb1, 0x4a, 0x85, 0x47, 0xa9, 0x00, 0x85, 0x4a, 0xa0, 0x1e, 0x84, 0x4b, 0x84, 0x61, 0xc8,
+ 0x84, 0x4d, 0x20, 0x27, 0x09, 0xb0, 0x35, 0xe6, 0x61, 0xe6, 0x61, 0xa4, 0x4e, 0xe6, 0x4e, 0xb1,
+ 0x4a, 0x85, 0x46, 0xb1, 0x4c, 0x85, 0x47, 0x11, 0x4a, 0xd0, 0x18, 0xa2, 0x01, 0xa9, 0x00, 0xa8,
+ 0x91, 0x60, 0xc8, 0xd0, 0xfb, 0xe6, 0x61, 0xea, 0xea, 0xca, 0x10, 0xf4, 0xce, 0x1f, 0x09, 0xf0,
+ 0x07, 0xd0, 0xd8, 0xce, 0x1f, 0x09, 0xd0, 0xca, 0x58, 0x4c, 0x00, 0x20, 0x4c, 0x47, 0x09, 0x02,
+ 0x26, 0x50, 0x52, 0x4f, 0x44, 0x4f, 0x53, 0xa5, 0x60, 0x85, 0x44, 0xa5, 0x61, 0x85, 0x45, 0x6c,
+ 0x48, 0x00, 0x08, 0x1e, 0x24, 0x3f, 0x45, 0x47, 0x76, 0xf4, 0xd7, 0xd1, 0xb6, 0x4b, 0xb4, 0xac,
+ 0xa6, 0x2b, 0x18, 0x60, 0x4c, 0xbc, 0x09, 0x20, 0x58, 0xfc, 0xa0, 0x14, 0xb9, 0x58, 0x09, 0x99,
+ 0xb1, 0x05, 0x88, 0x10, 0xf7, 0x4c, 0x55, 0x09, 0xd5, 0xce, 0xc1, 0xc2, 0xcc, 0xc5, 0xa0, 0xd4,
+ 0xcf, 0xa0, 0xcc, 0xcf, 0xc1, 0xc4, 0xa0, 0xd0, 0xd2, 0xcf, 0xc4, 0xcf, 0xd3, 0xa5, 0x53, 0x29,
+ 0x03, 0x2a, 0x05, 0x2b, 0xaa, 0xbd, 0x80, 0xc0, 0xa9, 0x2c, 0xa2, 0x11, 0xca, 0xd0, 0xfd, 0xe9,
+ 0x01, 0xd0, 0xf7, 0xa6, 0x2b, 0x60, 0xa5, 0x46, 0x29, 0x07, 0xc9, 0x04, 0x29, 0x03, 0x08, 0x0a,
+ 0x28, 0x2a, 0x85, 0x3d, 0xa5, 0x47, 0x4a, 0xa5, 0x46, 0x6a, 0x4a, 0x4a, 0x85, 0x41, 0x0a, 0x85,
+ 0x51, 0xa5, 0x45, 0x85, 0x27, 0xa6, 0x2b, 0xbd, 0x89, 0xc0, 0x20, 0xbc, 0x09, 0xe6, 0x27, 0xe6,
+ 0x3d, 0xe6, 0x3d, 0xb0, 0x03, 0x20, 0xbc, 0x09, 0xbc, 0x88, 0xc0, 0x60, 0xa5, 0x40, 0x0a, 0x85,
+ 0x53, 0xa9, 0x00, 0x85, 0x54, 0xa5, 0x53, 0x85, 0x50, 0x38, 0xe5, 0x51, 0xf0, 0x14, 0xb0, 0x04,
+ 0xe6, 0x53, 0x90, 0x02, 0xc6, 0x53, 0x38, 0x20, 0x6d, 0x09, 0xa5, 0x50, 0x18, 0x20, 0x6f, 0x09,
+ 0xd0, 0xe3, 0xa0, 0x7f, 0x84, 0x52, 0x08, 0x28, 0x38, 0xc6, 0x52, 0xf0, 0xce, 0x18, 0x08, 0x88,
+ 0xf0, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+void prodos_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_APPLE_GCR_FORMAT, floppy_image::FF_35, floppy_image::DSDD, 819200, "prodos_800k", "Apple ProDOS 3.5\" 800K");
+ fe.add(FLOPPY_APPLE_GCR_FORMAT, floppy_image::FF_35, floppy_image::SSDD, 409600, "prodos_400k", "Apple ProDOS 3.5\" 400K");
+ fe.add(FLOPPY_A216S_PRODOS_FORMAT, floppy_image::FF_525, floppy_image::SSSD, 143360, "prodos_140k", "Apple ProDOS 5.25\" 140K");
+}
+
+std::unique_ptr<filesystem_t> prodos_image::mount(fsblk_t &blockdev) const
+{
+ return std::make_unique<prodos_impl>(blockdev);
+}
+
+bool prodos_image::can_format() const
+{
+ return true;
+}
+
+bool prodos_image::can_read() const
+{
+ return true;
+}
+
+bool prodos_image::can_write() const
+{
+ return false;
+}
+
+bool prodos_image::has_rsrc() const
+{
+ return true;
+}
+
+char prodos_image::directory_separator() const
+{
+ return '/';
+}
+
+// TODO: this list is incomplete
+static const std::map<u8, const char *> s_file_types =
+{
+ { 0x00, "UNK" },
+ { 0x01, "BAD" },
+ { 0x04, "TXT" },
+ { 0x06, "BIN" },
+ { 0x0f, "DIR" },
+ { 0x19, "ADB" },
+ { 0x1a, "AWP" },
+ { 0x1b, "ASP" },
+ { 0xc9, "FND" },
+ { 0xca, "ICN" },
+ { 0xef, "PAS" },
+ { 0xf0, "CMD" },
+ { 0xfa, "INT" },
+ { 0xfb, "IVR" },
+ { 0xfc, "BAS" },
+ { 0xfd, "VAR" },
+ { 0xfe, "REL" },
+ { 0xff, "SYS" }
+};
+
+static std::string file_type_to_string(uint8_t type)
+{
+ auto res = s_file_types.find(type);
+ if (res != s_file_types.end())
+ return res->second;
+ else
+ return util::string_format("0x%02x", type);
+}
+
+static bool validate_file_type(std::string str)
+{
+ if ((str.length() == 3 || str.length() == 4) && str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+ return str.find_first_not_of("0123456789ABCDEFabcdef", 2) == std::string::npos;
+ else
+ return std::any_of(s_file_types.begin(), s_file_types.end(), [&str](const auto &pair) { return util::strequpper(str, pair.second); });
+}
+
+std::vector<meta_description> prodos_image::volume_meta_description() const
+{
+ std::vector<meta_description> res;
+ res.emplace_back(meta_description(meta_name::name, "UNTITLED", false, [](const meta_value &m) { return m.as_string().size() <= 15; }, "Volume name, up to 15 characters"));
+ res.emplace_back(meta_description(meta_name::os_version, 5, false, [](const meta_value &m) { return m.as_number() <= 255; }, "Creator OS version"));
+ res.emplace_back(meta_description(meta_name::os_minimum_version, 5, false, [](const meta_value &m) { return m.as_number() <= 255; }, "Minimum OS version"));
+
+ auto now = util::arbitrary_datetime::now();
+ res.emplace_back(meta_description(meta_name::creation_date, now, false, nullptr, "Creation time"));
+ res.emplace_back(meta_description(meta_name::modification_date, now, false, nullptr, "Modification time"));
+ return res;
+}
+
+std::vector<meta_description> prodos_image::file_meta_description() const
+{
+ std::vector<meta_description> res;
+ res.emplace_back(meta_description(meta_name::name, "Empty file", false, [](const meta_value &m) { return m.as_string().size() <= 15; }, "File name, up to 15 characters"));
+ res.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "Size of the file in bytes"));
+ res.emplace_back(meta_description(meta_name::rsrc_length, 0, true, nullptr, "Size of the resource fork in bytes"));
+ res.emplace_back(meta_description(meta_name::file_type, "UNK", false, [](const meta_value &m) { return validate_file_type(m.as_string()); }, "File type, 3 letters or hex code preceded by 0x"));
+ res.emplace_back(meta_description(meta_name::os_version, 5, false, [](const meta_value &m) { return m.as_number() <= 255; }, "Creator OS version"));
+ res.emplace_back(meta_description(meta_name::os_minimum_version, 5, false, [](const meta_value &m) { return m.as_number() <= 255; }, "Minimum OS version"));
+
+ auto now = util::arbitrary_datetime::now();
+ res.emplace_back(meta_description(meta_name::creation_date, now, false, nullptr, "Creation time"));
+ res.emplace_back(meta_description(meta_name::modification_date, now, false, nullptr, "Modification time"));
+ return res;
+}
+
+std::vector<meta_description> prodos_image::directory_meta_description() const
+{
+ std::vector<meta_description> res;
+ res.emplace_back(meta_description(meta_name::name, "Empty directory", false, [](const meta_value &m) { return m.as_string().size() <= 15; }, "Directory name, up to 15 characters"));
+ res.emplace_back(meta_description(meta_name::os_version, 5, false, [](const meta_value &m) { return m.as_number() <= 255; }, "Creator OS version"));
+ res.emplace_back(meta_description(meta_name::os_minimum_version, 5, false, [](const meta_value &m) { return m.as_number() <= 255; }, "Minimum OS version"));
+
+ auto now = util::arbitrary_datetime::now();
+ res.emplace_back(meta_description(meta_name::creation_date, now, false, nullptr, "Creation time"));
+ res.emplace_back(meta_description(meta_name::modification_date, now, false, nullptr, "Modification time"));
+ return res;
+}
+
+std::error_condition prodos_impl::format(const meta_data &meta)
+{
+ std::string volume_name = meta.get_string(meta_name::name, "UNTITLED");
+ u32 blocks = m_blockdev.block_count();
+
+ // Maximum usable partition size = 32M - 512 bytes (65535 blocks)
+ if(blocks >= 0x10000)
+ blocks = 0xffff;
+
+ m_blockdev.get(0)->write(0x000, boot, 0x200); // Standard ProDOS boot sector as written by a 2gs
+ m_blockdev.get(1)->fill(0x00); // No SOS boot sector
+
+ auto kblk1 = m_blockdev.get(2); // key block first block
+ auto kblk2 = m_blockdev.get(3); // key block second block
+ auto kblk3 = m_blockdev.get(4); // key block third block
+ auto kblk4 = m_blockdev.get(5); // key block fourth block
+
+ kblk1->w16l(0x00, 0x0000); // Backwards key block pointer (null)
+ kblk1->w16l(0x02, 0x0003); // Forwards key block pointer
+ kblk1->w8 (0x04, 0xf0 | volume_name.size()); // Block type (f, key block) and name size
+ kblk1->wstr(0x05, volume_name); // Volume name, up to 15 characters
+ kblk1->w32b(0x16, 0x642a250d); // ??? date & time
+ kblk1->w16b(0x1a, 0x80ff); // ???
+ kblk1->w32b(0x1c, 0x642a250d); // Creation date & time
+ kblk1->w8 (0x20, 0x05); // ProDOS version (2gs)
+ kblk1->w8 (0x21, 0x00); // ProDOS minimum version
+ kblk1->w8 (0x22, 0xc3); // Allowed access (destroy, rename, !backup, 3x0, write read)
+ kblk1->w8 (0x23, 0x27); // Directory entry length (fixed)
+ kblk1->w8 (0x24, 0x0d); // Entries per block (fixed)
+ kblk1->w16l(0x25, 0x0000); // Number of file entries in the directory
+ kblk1->w16l(0x27, 0x0006); // Bitmap block pointer
+ kblk1->w16l(0x29, blocks); // Number of blocks
+
+ kblk2->w16l(0x00, 0x0002); // Backwards block pointer of the second volume block
+ kblk2->w16l(0x02, 0x0004); // Forwards block pointer of the second volume block
+ kblk3->w16l(0x00, 0x0003); // Backwards block pointer of the third volume block
+ kblk3->w16l(0x02, 0x0005); // Forwards block pointer of the third volume block
+ kblk4->w16l(0x00, 0x0004); // Backwards block pointer of the fourth volume block
+ kblk4->w16l(0x02, 0x0000); // Forwards block pointer of the fourth volume block (null)
+
+ u32 fmap_block_count = (blocks + 4095) / 4096;
+ u32 first_free_block = 6 + fmap_block_count;
+
+ // Mark blocks from first_free_block to blocks-1 (the last one) as free
+ for(u32 i = 0; i != fmap_block_count; i++) {
+ auto fmap = m_blockdev.get(6 + i);
+ u8 *fdata = fmap->data();
+ u32 start = i ? 0 : first_free_block;
+ u32 end = i != fmap_block_count - 1 ? 4095 : (blocks - 1) & 4095;
+ end += 1;
+ u32 sb = start >> 3;
+ u32 si = start & 7;
+ u32 eb = end >> 3;
+ u32 ei = end & 7;
+ if(sb == eb)
+ fdata[sb] = (0xff >> si) & ~(0xff >> ei);
+ else {
+ fdata[sb] = 0xff >> si;
+ if(eb != 512)
+ fdata[eb] = ~(0xff >> ei);
+ if(eb - sb > 1)
+ memset(fdata+sb, 0xff, eb-sb-1);
+ }
+ }
+ return std::error_condition();
+}
+
+prodos_impl::prodos_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 512)
+{
+}
+
+util::arbitrary_datetime prodos_impl::prodos_to_dt(u32 date)
+{
+ util::arbitrary_datetime dt;
+ dt.second = 0;
+ dt.minute = ((date >> 16) & 0x3f);
+ dt.hour = ((date >> 24) & 0x1f);
+ dt.day_of_month = ((date >> 0) & 0x1f); // 1-based
+ dt.month = ((date >> 5) & 0x0f); // 1-based
+ dt.year = ((date >> 9) & 0x7f) + 1900;
+ if (dt.year <= 1949)
+ dt.year += 100;
+
+ return dt;
+}
+
+meta_data prodos_impl::volume_metadata()
+{
+ meta_data res;
+ auto bdir = m_blockdev.get(2);
+ int len = bdir->r8(0x04) & 0xf;
+ res.set(meta_name::name, bdir->rstr(0x05, len));
+ res.set(meta_name::os_version, bdir->r8(0x20));
+ res.set(meta_name::os_minimum_version, bdir->r8(0x21));
+ res.set(meta_name::creation_date, prodos_to_dt(bdir->r32l(0x1c)));
+ res.set(meta_name::modification_date, prodos_to_dt(bdir->r32l(0x16)));
+ return res;
+}
+
+std::pair<std::error_condition, std::vector<dir_entry>> prodos_impl::directory_contents(const std::vector<std::string> &path)
+{
+ u16 block;
+ if(path.empty())
+ block = 2;
+
+ else {
+ auto [blk, off, dir] = path_find(path);
+ if(!off || !dir)
+ return std::make_pair(error::not_found, std::vector<dir_entry>());
+ block = blk->r16l(off+0x11);
+ }
+
+ std::vector<dir_entry> res;
+
+ do {
+ auto blk = m_blockdev.get(block);
+ for(u32 off = 4; off < 511; off += 39) {
+ u8 type = blk->r8(off);
+ // skip inactive entries and subroutine/volume headers
+ if(type != 0 && type < 0xe0) {
+ meta_data meta;
+ meta.set(meta_name::name, blk->rstr(off+1, type & 0xf));
+ type >>= 4;
+
+ if(type == 5) {
+ auto rootblk = m_blockdev.get(blk->r16l(off+0x11));
+ meta.set(meta_name::length, rootblk->r24l(0x005));
+ meta.set(meta_name::rsrc_length, rootblk->r24l(0x105));
+
+ } else if(type >= 1 && type <= 3) {
+ meta.set(meta_name::length, blk->r24l(off + 0x15));
+ meta.set(meta_name::file_type, file_type_to_string(blk->r8(off + 0x10)));
+ }
+
+ meta.set(meta_name::os_version, blk->r8(off + 0x1c));
+ meta.set(meta_name::os_minimum_version, blk->r8(off + 0x1d));
+ meta.set(meta_name::creation_date, prodos_to_dt(blk->r32l(off + 0x18)));
+ meta.set(meta_name::modification_date, prodos_to_dt(blk->r32l(off + 0x21)));
+
+ res.emplace_back(dir_entry(type == 0xd ? dir_entry_type::dir : dir_entry_type::file, meta));
+ }
+ }
+ block = blk->r16l(2);
+ if(block >= m_blockdev.block_count())
+ break;
+ } while(block);
+ return std::make_pair(std::error_condition(), res);
+}
+
+std::tuple<fsblk_t::block_t::ptr, u32> prodos_impl::path_find_step(const std::string &name, u16 block)
+{
+ for(;;) {
+ auto blk = m_blockdev.get(block);
+ for(u32 off = 4; off < 511; off += 39) {
+ u8 type = blk->r8(off);
+ if(type != 0 && type < 0xe0 && name == blk->rstr(off+1, type & 0xf))
+ return std::make_tuple(blk, off);
+ }
+ block = blk->r16l(2);
+ if(!block || block >= m_blockdev.block_count())
+ return std::make_tuple(blk, 0U);
+ }
+}
+
+std::tuple<fsblk_t::block_t::ptr, u32, bool> prodos_impl::path_find(const std::vector<std::string> &path)
+{
+ if(path.size() == 0)
+ return std::tuple<fsblk_t::block_t::ptr, u32, bool>(fsblk_t::block_t::ptr(), 0, false);
+
+ u16 block = 2;
+ for(u32 pathc = 0;; pathc++) {
+ auto [blk, off] = path_find_step(path[pathc], block);
+ if(!off)
+ return std::make_tuple(blk, off, false);
+
+ if(pathc + 1 == path.size())
+ return std::make_tuple(blk, off, (blk->r8(off) & 0xf0) == 0xd0);
+
+ if((blk->r8(off) & 0xf0) != 0xd0)
+ return std::make_tuple(blk, 0U, false);
+
+ block = blk->r16l(off + 0x11);
+ }
+}
+
+
+std::pair<std::error_condition, meta_data> prodos_impl::metadata(const std::vector<std::string> &path)
+{
+ if(path.size() == 0)
+ return std::make_pair(std::error_condition(), meta_data());
+
+ auto [blk, off, dir] = path_find(path);
+
+ if(!off)
+ return std::make_pair(error::not_found, meta_data());
+
+ const u8 *entry = blk->rodata() + off;
+
+ meta_data res;
+ if(dir) {
+ u8 type = entry[0];
+ std::string_view name = rstr(entry+1, type & 0xf);
+ res.set(meta_name::name, name);
+ } else {
+ u8 type = entry[0];
+ std::string_view name = rstr(entry+1, type & 0xf);
+ type >>= 4;
+ res.set(meta_name::name, name);
+ if(type == 5) {
+ auto rootblk = m_blockdev.get(get_u16le(entry+0x11));
+ res.set(meta_name::length, rootblk->r24l(0x005));
+ res.set(meta_name::rsrc_length, rootblk->r24l(0x105));
+
+ } else if(type >= 1 && type <= 3)
+ res.set(meta_name::length, get_u24le(entry + 0x15));
+
+ else
+ return std::make_pair(error::unsupported, meta_data());
+ }
+
+ return std::make_pair(std::error_condition(), res);
+}
+
+std::pair<std::error_condition, std::vector<u8>> prodos_impl::any_read(u8 type, u16 block, u32 length)
+{
+ std::pair<std::error_condition, std::vector<u8>> data;
+ data.first = std::error_condition();
+ data.second.resize((length + 511) & ~511);
+ u32 nb = data.second.size()/512;
+ if(!nb)
+ return data;
+
+ u8 *dst = data.second.data();
+ u8 *end = dst + data.second.size();
+ switch(type) {
+ case 1:
+ m_blockdev.get(block)->read(0, dst, 512);
+ dst += 512;
+ break;
+
+ case 2: {
+ auto iblk = m_blockdev.get(block);
+ for(u32 i=0; i != 256 && dst != end; i++) {
+ u16 blk = iblk->r8(i) | (iblk->r8(i | 0x100) << 8);
+ m_blockdev.get(blk)->read(0, dst, 512);
+ dst += 512;
+ }
+ break;
+ }
+
+ case 3: {
+ auto mblk = m_blockdev.get(block);
+ for(u32 j=0; dst != end; j += 256) {
+ u32 idx = j/256;
+ auto iblk = m_blockdev.get(mblk->r8(idx) | (mblk->r8(idx | 0x100) << 8));
+ for(u32 i=0; i != 256 && dst != end; i++) {
+ u16 blk = iblk->r8(i) | (iblk->r8(i | 0x100) << 8);
+ m_blockdev.get(blk)->read(0, dst, 512);
+ dst += 512;
+ }
+ }
+ break;
+ }
+
+ default:
+ data.first = error::unsupported;
+ data.second.clear();
+ return data;
+ }
+
+ data.second.resize(length);
+ return data;
+}
+
+std::pair<std::error_condition, std::vector<u8>> prodos_impl::file_read(const std::vector<std::string> &path)
+{
+ auto [blk, off, dir] = path_find(path);
+ if(!off || dir)
+ return std::make_pair(error::not_found, std::vector<u8>());
+
+ const u8 *entry = blk->rodata() + off;
+ u8 type = entry[0] >> 4;
+
+ if(type >= 1 && type <= 3)
+ return any_read(type, get_u16le(entry+0x11), get_u24le(entry + 0x15));
+
+ else if(type == 5) {
+ auto kblk = m_blockdev.get(get_u16le(entry+0x11));
+ return any_read(kblk->r8(0x000), kblk->r16l(0x001), kblk->r24l(0x005));
+
+ } else
+ return std::make_pair(error::unsupported, std::vector<u8>());
+}
+
+std::pair<std::error_condition, std::vector<u8>> prodos_impl::file_rsrc_read(const std::vector<std::string> &path)
+{
+ auto [blk, off, dir] = path_find(path);
+ if(!off || dir)
+ return std::make_pair(error::not_found, std::vector<u8>());
+
+ const u8 *entry = blk->rodata() + off;
+ u8 type = entry[0] >> 4;
+
+ if(type == 5) {
+ auto kblk = m_blockdev.get(get_u16le(entry+0x11));
+ return any_read(kblk->r8(0x100), kblk->r16l(0x101), kblk->r24l(0x105));
+
+ } else
+ return std::make_pair(error::unsupported, std::vector<u8>());
+}
diff --git a/src/lib/formats/fs_prodos.h b/src/lib/formats/fs_prodos.h
new file mode 100644
index 00000000000..71ec4dce2e3
--- /dev/null
+++ b/src/lib/formats/fs_prodos.h
@@ -0,0 +1,40 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Creation of Apple ProDOS floppy images
+
+#ifndef MAME_FORMATS_FS_PRODOS_H
+#define MAME_FORMATS_FS_PRODOS_H
+
+#pragma once
+
+#include "fsmgr.h"
+
+namespace fs {
+
+class prodos_image : public manager_t {
+public:
+ prodos_image() : manager_t() {}
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+ virtual char directory_separator() const override;
+
+ virtual std::vector<meta_description> volume_meta_description() const override;
+ virtual std::vector<meta_description> file_meta_description() const override;
+ virtual std::vector<meta_description> directory_meta_description() const override;
+};
+
+extern const prodos_image PRODOS;
+
+} // namespace fs
+
+#endif
diff --git a/src/lib/formats/fs_unformatted.cpp b/src/lib/formats/fs_unformatted.cpp
new file mode 100644
index 00000000000..995c2b76148
--- /dev/null
+++ b/src/lib/formats/fs_unformatted.cpp
@@ -0,0 +1,158 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Creation of unformatted floppy images
+
+#include "fs_unformatted.h"
+#include "flopimg.h"
+#include "fsblk.h"
+
+namespace fs {
+
+const unformatted_image UNFORMATTED;
+
+const char *unformatted_image::name() const
+{
+ return "unformatted";
+}
+
+const char *unformatted_image::description() const
+{
+ return "Unformatted floppy image";
+}
+
+void unformatted_image::enumerate_f(floppy_enumerator &fe) const
+{
+ u32 form_factor = fe.form_factor();
+ const std::vector<u32> &variants = fe.variants();
+
+ bool all = form_factor == floppy_image::FF_UNKNOWN;
+ u32 best_8 =
+ form_factor == floppy_image::FF_8 ?
+ has_variant(variants, floppy_image::DSDD) ? FSI_8_DSDD :
+ has_variant(variants, floppy_image::DSSD) ? FSI_8_DSSD : FSI_8_SSSD
+ : FSI_NONE;
+
+ u32 best_525 =
+ form_factor == floppy_image::FF_525 ?
+ has_variant(variants, floppy_image::DSHD) ? FSI_525_DSHD :
+ has_variant(variants, floppy_image::DSQD16) ? FSI_525_DSQD16 :
+ has_variant(variants, floppy_image::DSQD) ? FSI_525_DSQD :
+ has_variant(variants, floppy_image::DSDD16) ? FSI_525_DSDD16 :
+ has_variant(variants, floppy_image::DSDD) ? FSI_525_DSDD :
+ has_variant(variants, floppy_image::SSQD16) ? FSI_525_SSQD16 :
+ has_variant(variants, floppy_image::SSQD) ? FSI_525_SSQD :
+ has_variant(variants, floppy_image::SSDD16) ? FSI_525_SSDD16 :
+ has_variant(variants, floppy_image::SSDD) ? FSI_525_SSDD : FSI_525_SSSD
+ : FSI_NONE;
+
+ u32 best_35 =
+ form_factor == floppy_image::FF_35 ?
+ has_variant(variants, floppy_image::DSDD) ? FSI_35_DSDD : FSI_35_SSDD
+ : FSI_NONE;
+
+ u32 best_3 =
+ form_factor == floppy_image::FF_3 ?
+ has_variant(variants, floppy_image::DSDD) ? FSI_3_DSDD : FSI_3_SSDD
+ : FSI_NONE;
+
+ if(all || best_8 == FSI_8_DSDD)
+ fe.add_raw("u8dsdd", FSI_8_DSDD, "Unformatted 8\" double-sided double-density");
+ if(all || best_8 == FSI_8_DSSD)
+ fe.add_raw("u8dssd", FSI_8_DSSD, "Unformatted 8\" double-sided single-density");
+ if(all || best_8 == FSI_8_SSSD)
+ fe.add_raw("u8sssd", FSI_8_SSSD, "Unformatted 8\" single-sided single-density");
+
+ if(all || best_525 == FSI_525_DSHD)
+ fe.add_raw("u525dshd", FSI_525_DSHD, "Unformatted 5\"25 double-sided high-density");
+ if(all || best_525 == FSI_525_DSQD16)
+ fe.add_raw("u525dsqd16", FSI_525_DSQD16, "Unformatted 5\"25 double-sided quad-density 16 hard sectors");
+ if(all || best_525 == FSI_525_DSQD)
+ fe.add_raw("u525dsqd", FSI_525_DSQD, "Unformatted 5\"25 double-sided quad-density");
+ if(all || best_525 == FSI_525_DSDD16)
+ fe.add_raw("u525dsdd16", FSI_525_DSDD16, "Unformatted 5\"25 double-sided double-density 16 hard sectors");
+ if(all || best_525 == FSI_525_DSDD)
+ fe.add_raw("u525dsdd", FSI_525_DSDD, "Unformatted 5\"25 double-sided double-density");
+ if(all)
+ fe.add_raw("u525dssd", FSI_525_DSSD, "Unformatted 5\"25 double-sided single-density");
+ if(all || best_525 == FSI_525_SSQD16)
+ fe.add_raw("u525ssqd16", FSI_525_SSQD16, "Unformatted 5\"25 single-sided quad-density 16 hard sectors");
+ if(all || best_525 == FSI_525_SSQD)
+ fe.add_raw("u525ssqd", FSI_525_SSQD, "Unformatted 5\"25 single-sided quad-density");
+ if(all || best_525 == FSI_525_SSDD16)
+ fe.add_raw("u525ssdd16", FSI_525_SSDD16, "Unformatted 5\"25 single-sided double-density 16 hard sectors");
+ if(all || best_525 == FSI_525_SSDD)
+ fe.add_raw("u525ssdd", FSI_525_SSDD, "Unformatted 5\"25 single-sided double-density");
+ if(all || best_525 == FSI_525_SSSD)
+ fe.add_raw("u525sssd", FSI_525_SSSD, "Unformatted 5\"25 single-sided single-density");
+
+ if(all || has_variant(variants, floppy_image::DSED))
+ fe.add_raw("u35dsed", FSI_35_DSED, "Unformatted 3\"5 double-sided extra-density");
+ if(all || has_variant(variants, floppy_image::DSHD))
+ fe.add_raw("u35dshd", FSI_35_DSHD, "Unformatted 3\"5 double-sided high-density");
+ if(all || best_35 == FSI_35_DSDD)
+ fe.add_raw("u35dsdd", FSI_35_DSDD, "Unformatted 3\"5 double-sided double-density");
+ if(all || best_35 == FSI_35_SSDD)
+ fe.add_raw("u35ssdd", FSI_35_SSDD, "Unformatted 3\"5 single-sided double-density");
+
+ if(all || best_3 == FSI_3_DSDD)
+ fe.add_raw("u3dsdd", FSI_35_DSDD, "Unformatted 3\" double-sided double-density");
+ if(all || best_3 == FSI_3_SSDD)
+ fe.add_raw("u3ssdd", FSI_35_SSDD, "Unformatted 3\" single-sided double-density");
+}
+
+void unformatted_image::format(u32 key, floppy_image *image)
+{
+ switch(key) {
+ case FSI_8_DSDD: image->set_form_variant(floppy_image::FF_8, floppy_image::DSDD); break;
+ case FSI_8_DSSD: image->set_form_variant(floppy_image::FF_8, floppy_image::DSSD); break;
+ case FSI_8_SSSD: image->set_form_variant(floppy_image::FF_8, floppy_image::SSSD); break;
+
+ case FSI_525_DSHD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSHD); break;
+ case FSI_525_DSQD16: image->set_form_variant(floppy_image::FF_525, floppy_image::DSQD16); break;
+ case FSI_525_DSQD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSQD); break;
+ case FSI_525_DSDD16: image->set_form_variant(floppy_image::FF_525, floppy_image::DSDD16); break;
+ case FSI_525_DSDD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSDD); break;
+ case FSI_525_DSSD: image->set_form_variant(floppy_image::FF_525, floppy_image::DSSD); break;
+ case FSI_525_SSQD16: image->set_form_variant(floppy_image::FF_525, floppy_image::SSQD16); break;
+ case FSI_525_SSQD: image->set_form_variant(floppy_image::FF_525, floppy_image::SSQD); break;
+ case FSI_525_SSDD16: image->set_form_variant(floppy_image::FF_525, floppy_image::SSDD16); break;
+ case FSI_525_SSDD: image->set_form_variant(floppy_image::FF_525, floppy_image::SSDD); break;
+ case FSI_525_SSSD: image->set_form_variant(floppy_image::FF_525, floppy_image::SSSD); break;
+
+ case FSI_35_DSED: image->set_form_variant(floppy_image::FF_35, floppy_image::DSED); break;
+ case FSI_35_DSHD: image->set_form_variant(floppy_image::FF_35, floppy_image::DSHD); break;
+ case FSI_35_DSDD: image->set_form_variant(floppy_image::FF_35, floppy_image::DSDD); break;
+ case FSI_35_SSDD: image->set_form_variant(floppy_image::FF_35, floppy_image::SSDD); break;
+
+ case FSI_3_DSDD: image->set_form_variant(floppy_image::FF_3, floppy_image::DSDD); break;
+ case FSI_3_SSDD: image->set_form_variant(floppy_image::FF_3, floppy_image::SSDD); break;
+ }
+}
+
+std::unique_ptr<filesystem_t> unformatted_image::mount(fsblk_t &blockdev) const
+{
+ return nullptr;
+}
+
+bool unformatted_image::can_format() const
+{
+ return false;
+}
+
+bool unformatted_image::can_read() const
+{
+ return false;
+}
+
+bool unformatted_image::can_write() const
+{
+ return false;
+}
+
+bool unformatted_image::has_rsrc() const
+{
+ return false;
+}
+
+} // namespace fs
diff --git a/src/lib/formats/fs_unformatted.h b/src/lib/formats/fs_unformatted.h
new file mode 100644
index 00000000000..fa51049f384
--- /dev/null
+++ b/src/lib/formats/fs_unformatted.h
@@ -0,0 +1,67 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Creation of unformatted floppy images
+
+#ifndef MAME_FORMATS_FS_UNFORMATTED_H
+#define MAME_FORMATS_FS_UNFORMATTED_H
+
+#pragma once
+
+#include "fsmgr.h"
+
+class floppy_image;
+
+namespace fs {
+
+class unformatted_image : public manager_t {
+public:
+ enum {
+ FSI_NONE,
+
+ FSI_8_SSSD,
+ FSI_8_DSSD,
+ FSI_8_DSDD,
+
+ FSI_525_SSSD,
+ FSI_525_SSDD,
+ FSI_525_SSDD16,
+ FSI_525_SSQD,
+ FSI_525_SSQD16,
+ FSI_525_DSSD,
+ FSI_525_DSDD,
+ FSI_525_DSDD16,
+ FSI_525_DSQD,
+ FSI_525_DSQD16,
+ FSI_525_DSHD,
+
+ FSI_35_SSDD,
+ FSI_35_DSDD,
+ FSI_35_DSHD,
+ FSI_35_DSED,
+
+ FSI_3_DSDD,
+ FSI_3_SSDD,
+ };
+
+ unformatted_image() : manager_t() {}
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ static void format(u32 key, floppy_image *image);
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+};
+
+extern const unformatted_image UNFORMATTED;
+
+} // namespace fs
+
+#endif
diff --git a/src/lib/formats/fs_vtech.cpp b/src/lib/formats/fs_vtech.cpp
new file mode 100644
index 00000000000..163b2ae6d53
--- /dev/null
+++ b/src/lib/formats/fs_vtech.cpp
@@ -0,0 +1,432 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Management of VTech images
+
+#include "fs_vtech.h"
+#include "fsblk.h"
+#include "vt_dsk.h"
+
+#include "multibyte.h"
+
+#include <stdexcept>
+
+using namespace fs;
+
+namespace fs { const vtech_image VTECH; }
+
+// Floppy only, format is 40 tracks, 1 head, 16 sectors numbered 0-15, 256 bytes/sector.
+// Filesystem has no subdirectories.
+//
+// Track 0 sectors 0-14 have the file names. 16 bytes/entry
+// offset 0 : File type 'T' (basic), 'B' (binary), or some other letter (application-specific)
+// offset 1 : 0x3a
+// offset 2-9: File name
+// offset a : Track number of first file sector
+// offset b : Sector number of first file sector
+// offset c-d: Start address (little-endian)
+// offset e-f: End address (little-endian)
+//
+// Files are stored with 126 bytes/sector, and bytes 126 and 127 are
+// track/sector of the next file data sector.
+
+namespace {
+
+class vtech_impl : public filesystem_t {
+public:
+ vtech_impl(fsblk_t &blockdev);
+ virtual ~vtech_impl() = default;
+
+ virtual meta_data volume_metadata() override;
+ virtual std::error_condition volume_metadata_change(const meta_data &info) override;
+ virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path) override;
+ virtual std::error_condition metadata_change(const std::vector<std::string> &path, const meta_data &meta) override;
+
+ virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path) override;
+ virtual std::error_condition rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath) override;
+ virtual std::error_condition remove(const std::vector<std::string> &path) override;
+
+ virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta) override;
+
+ virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path) override;
+ virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data) override;
+
+ virtual std::error_condition format(const meta_data &meta) override;
+
+private:
+ meta_data file_metadata(const u8 *entry);
+ std::tuple<fsblk_t::block_t::ptr, u32> file_find(std::string_view name);
+ std::vector<std::pair<u8, u8>> allocate_blocks(u32 count);
+ void free_blocks(const std::vector<std::pair<u8, u8>> &blocks);
+ u32 free_block_count();
+};
+}
+
+const char *vtech_image::name() const
+{
+ return "vtech";
+}
+
+const char *vtech_image::description() const
+{
+ return "VTech (Laser 200/300)";
+}
+
+void vtech_image::enumerate_f(floppy_enumerator &fe) const
+{
+ fe.add(FLOPPY_VTECH_BIN_FORMAT, floppy_image::FF_525, floppy_image::SSSD, 163840, "vtech", "VTech");
+}
+
+std::unique_ptr<filesystem_t> vtech_image::mount(fsblk_t &blockdev) const
+{
+ return std::make_unique<vtech_impl>(blockdev);
+}
+
+bool vtech_image::can_format() const
+{
+ return true;
+}
+
+bool vtech_image::can_read() const
+{
+ return true;
+}
+
+bool vtech_image::can_write() const
+{
+ return true;
+}
+
+bool vtech_image::has_rsrc() const
+{
+ return false;
+}
+
+std::vector<meta_description> vtech_image::volume_meta_description() const
+{
+ std::vector<meta_description> res;
+ return res;
+}
+
+std::vector<meta_description> vtech_image::file_meta_description() const
+{
+ std::vector<meta_description> res;
+ res.emplace_back(meta_description(meta_name::name, "", false, [](const meta_value &m) { return m.as_string().size() <= 8; }, "File name, 8 chars"));
+ res.emplace_back(meta_description(meta_name::loading_address, 0x7ae9, false, [](const meta_value &m) { return m.as_number() < 0x10000; }, "Loading address of the file"));
+ res.emplace_back(meta_description(meta_name::length, 0, true, nullptr, "Size of the file in bytes"));
+ res.emplace_back(meta_description(meta_name::file_type, "T", true,
+ [](const meta_value &m) { return m.as_string().size() == 1 && m.as_string()[0] >= 'A' && m.as_string()[0] <= 'Z'; },
+ "File type (e.g. T = text, B = binary)"));
+ return res;
+}
+
+vtech_impl::vtech_impl(fsblk_t &blockdev) : filesystem_t(blockdev, 128)
+{
+}
+
+std::error_condition vtech_impl::format(const meta_data &meta)
+{
+ m_blockdev.fill_all(0);
+ return std::error_condition();
+}
+
+meta_data vtech_impl::volume_metadata()
+{
+ return meta_data();
+}
+
+std::error_condition vtech_impl::volume_metadata_change(const meta_data &meta)
+{
+ return std::error_condition();
+}
+
+meta_data vtech_impl::file_metadata(const u8 *entry)
+{
+ meta_data res;
+
+ res.set(meta_name::name, trim_end_spaces(rstr(entry+2, 8)));
+ res.set(meta_name::file_type, std::string{ char(entry[0]) });
+ res.set(meta_name::loading_address, get_u16le(entry + 0xc));
+ res.set(meta_name::length, (get_u16le(entry + 0xe) - get_u16le(entry + 0xc)) & 0xffff);
+
+ return res;
+}
+
+std::tuple<fsblk_t::block_t::ptr, u32> vtech_impl::file_find(std::string_view name)
+{
+ for(int sect = 0; sect != 14; sect++) {
+ auto bdir = m_blockdev.get(sect);
+ for(u32 i = 0; i != 8; i ++) {
+ u32 off = i*16;
+ u8 type = bdir->r8(off);
+ if(type < 'A' || type > 'Z')
+ continue;
+ if(bdir->r8(off+1) != ':')
+ continue;
+ if(trim_end_spaces(bdir->rstr(off+2, 8)) == name) {
+ return std::make_tuple(bdir, off);
+ }
+ }
+ }
+ return std::make_tuple(fsblk_t::block_t::ptr(), 0xffffffff);
+}
+
+std::pair<std::error_condition, meta_data> vtech_impl::metadata(const std::vector<std::string> &path)
+{
+ if(path.size() != 1)
+ return std::make_pair(error::not_found, meta_data());
+
+ auto [bdir, off] = file_find(path[0]);
+ if(off == 0xffffffff)
+ return std::make_pair(error::not_found, meta_data());
+
+ return std::make_pair(std::error_condition(), file_metadata(bdir->rodata() + off));
+}
+
+std::error_condition vtech_impl::metadata_change(const std::vector<std::string> &path, const meta_data &meta)
+{
+ if(path.size() != 1)
+ return error::not_found;
+
+ auto [bdir, off] = file_find(path[0]);
+ if(off == 0xffffffff)
+ return error::not_found;
+
+ u8 *entry = bdir->data() + off;
+ if(meta.has(meta_name::file_type))
+ entry[0x0] = meta.get_string(meta_name::file_type)[0];
+ if(meta.has(meta_name::name)) {
+ std::string name = meta.get_string(meta_name::name);
+ name.resize(8, ' ');
+ wstr(entry+0x2, name);
+ }
+ if(meta.has(meta_name::loading_address)) {
+ u16 new_loading = meta.get_number(meta_name::loading_address);
+ u16 new_end = get_u16le(entry + 0xe) - get_u16le(entry + 0xc) + new_loading;
+ put_u16le(entry + 0xc, new_loading);
+ put_u16le(entry + 0xe, new_end);
+ }
+
+ return std::error_condition();
+}
+
+std::pair<std::error_condition, std::vector<dir_entry>> vtech_impl::directory_contents(const std::vector<std::string> &path)
+{
+ std::pair<std::error_condition, std::vector<dir_entry>> res;
+
+ if(path.size() != 0) {
+ res.first = error::not_found;
+ return res;
+ }
+
+ res.first = std::error_condition();
+
+ for(int sect = 0; sect != 14; sect++) {
+ auto bdir = m_blockdev.get(sect);
+ for(u32 i = 0; i != 8; i ++) {
+ u32 off = i*16;
+ u8 type = bdir->r8(off);
+ if(type < 'A' || type > 'Z')
+ continue;
+ if(bdir->r8(off+1) != ':')
+ continue;
+ meta_data meta = file_metadata(bdir->rodata()+off);
+ res.second.emplace_back(dir_entry(dir_entry_type::file, meta));
+ }
+ }
+ return res;
+}
+
+std::error_condition vtech_impl::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath)
+{
+ if(opath.size() != 1 || npath.size() != 1)
+ return error::not_found;
+
+ auto [bdir, off] = file_find(opath[0]);
+ if(off == 0xffffffff)
+ return error::not_found;
+
+ std::string name = npath[0];
+ name.resize(8, ' ');
+ wstr(bdir->data() + off + 2, name);
+
+ return std::error_condition();
+}
+
+std::error_condition vtech_impl::remove(const std::vector<std::string> &path)
+{
+ return error::unsupported;
+}
+
+
+std::error_condition vtech_impl::file_create(const std::vector<std::string> &path, const meta_data &meta)
+{
+ if(path.size() != 0)
+ return error::not_found;
+
+ // Find the key for the next unused entry
+ for(int sect = 0; sect != 14; sect++) {
+ auto bdir = m_blockdev.get(sect);
+ for(u32 i = 0; i != 16; i ++) {
+ u32 off = i*16;
+ u8 type = bdir->r8(off);
+ if(type != 'T' && type != 'B') {
+ std::string fname = meta.get_string(meta_name::name, "");
+ fname.resize(8, ' ');
+
+ bdir->w8 (off+0x0, meta.get_string(meta_name::file_type, "T")[0]);
+ bdir->w8 (off+0x1, ':');
+ bdir->wstr(off+0x2, fname);
+ bdir->w8 (off+0xa, 0x00);
+ bdir->w8 (off+0xb, 0x00);
+ bdir->w16l(off+0xc, meta.get_number(meta_name::loading_address, 0x7ae9));
+ bdir->w16l(off+0xe, bdir->r16l(off+0xc)); // Size 0 initially
+ return std::error_condition();
+ }
+ }
+ }
+ return error::no_space;
+}
+
+std::pair<std::error_condition, std::vector<u8>> vtech_impl::file_read(const std::vector<std::string> &path)
+{
+ std::vector<u8> data;
+
+ if(path.size() != 1)
+ return std::make_pair(error::not_found, data);
+
+ auto [bdir, off] = file_find(path[0]);
+ if(off == 0xffffffff)
+ return std::make_pair(error::not_found, data);
+
+ const u8 *entry = bdir->rodata() + off;
+
+ u8 track = entry[0xa];
+ u8 sector = entry[0xb];
+ int len = (get_u16le(entry + 0xe) - get_u16le(entry + 0xc)) & 0xffff;
+
+ data.resize(len, 0);
+ int pos = 0;
+ while(pos < len) {
+ if(track >= 40 || sector >= 16)
+ break;
+ auto dblk = m_blockdev.get(track*16 + sector);
+ int size = len - pos;
+ if(size > 126)
+ size = 126;
+ dblk->read(0, data.data() + pos, size);
+ pos += size;
+ track = dblk->r8(126);
+ sector = dblk->r8(127);
+ }
+ return std::make_pair(std::error_condition(), data);
+}
+
+std::error_condition vtech_impl::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
+{
+ if(path.size() != 1)
+ return error::not_found;
+
+ auto [bdir, off] = file_find(path[0]);
+ if(off == 0xffffffff)
+ return error::not_found;
+
+ u8 *entry = bdir->data() + off;
+
+ u32 cur_len = (get_u16le(entry + 0xe) - get_u16le(entry + 0xc)) & 0xffff;
+ u32 new_len = data.size();
+ if(new_len > 65535)
+ new_len = 65535;
+ u32 cur_ns = (cur_len + 125)/126;
+ u32 need_ns = (new_len + 125) / 126;
+
+ // Enough space?
+ if(cur_ns < need_ns && free_block_count() < need_ns - cur_ns)
+ return error::no_space;
+
+ u8 track = entry[0xa];
+ u8 sector = entry[0xb];
+ std::vector<std::pair<u8, u8>> tofree;
+ for(u32 i = 0; i != cur_ns; i++) {
+ tofree.emplace_back(std::make_pair(track, sector));
+ auto dblk = m_blockdev.get(track*16 + sector);
+ track = dblk->r8(126);
+ sector = dblk->r8(127);
+ }
+
+ free_blocks(tofree);
+
+ std::vector<std::pair<u8, u8>> blocks = allocate_blocks(need_ns);
+ for(u32 i=0; i != need_ns; i ++) {
+ auto dblk = m_blockdev.get(blocks[i].first * 16 + blocks[i].second);
+ u32 len = new_len - i*126;
+ if(len > 126)
+ len = 126;
+ else if(len < 126)
+ dblk->fill(0x00);
+ dblk->write(0, data.data() + 126*i, len);
+ if(i < need_ns) {
+ dblk->w8(126, blocks[i+1].first);
+ dblk->w8(127, blocks[i+1].second);
+ } else
+ dblk->w16l(126, 0);
+ }
+
+ u16 end_address = (get_u16le(entry + 0xc) + data.size()) & 0xffff;
+ put_u16le(entry + 0xe, end_address);
+ if(need_ns) {
+ entry[0xa] = blocks[0].first;
+ entry[0xb] = blocks[0].second;
+ } else
+ put_u16le(entry + 0xa, 0);
+
+ return std::error_condition();
+}
+
+std::vector<std::pair<u8, u8>> vtech_impl::allocate_blocks(u32 count)
+{
+ std::vector<std::pair<u8, u8>> blocks;
+ if(free_block_count() < count)
+ return blocks;
+
+ auto fmap = m_blockdev.get(15);
+ for(u8 track = 1; track != 40; track++)
+ for(u8 sector = 0; sector != 16; sector++) {
+ u32 off = (track-1)*2 + (sector / 8);
+ u32 bit = 1 << (sector & 7);
+ if(!(fmap->r8(off) & bit)) {
+ fmap->w8(off, fmap->r8(off) | bit);
+ blocks.emplace_back(std::make_pair(track, sector));
+ if(blocks.size() == count)
+ return blocks;
+ }
+ }
+ abort();
+}
+
+void vtech_impl::free_blocks(const std::vector<std::pair<u8, u8>> &blocks)
+{
+ auto fmap = m_blockdev.get(15);
+ for(auto ref : blocks) {
+ u8 track = ref.first;
+ u8 sector = ref.second;
+ u32 off = (track-1)*2 + (sector / 8);
+ u32 bit = 1 << (sector & 7);
+ fmap->w8(off, fmap->r8(off) & ~bit);
+ }
+}
+
+u32 vtech_impl::free_block_count()
+{
+ auto fmap = m_blockdev.get(15);
+ u32 nf = 0;
+ for(u32 off = 0; off != (40-1)*2; off++) {
+ u8 m = fmap->r8(off);
+ // Count 1 bits;
+ m = ((m & 0xaa) >> 1) | (m & 0x55);
+ m = ((m & 0xcc) >> 2) | (m & 0x33);
+ m = ((m & 0xf0) >> 4) | (m & 0x0f);
+ nf += 7-m;
+ }
+ return nf;
+}
diff --git a/src/lib/formats/fs_vtech.h b/src/lib/formats/fs_vtech.h
new file mode 100644
index 00000000000..efb591bcb12
--- /dev/null
+++ b/src/lib/formats/fs_vtech.h
@@ -0,0 +1,38 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Management of VTech images
+
+#ifndef MAME_FORMATS_FS_VTECH_H
+#define MAME_FORMATS_FS_VTECH_H
+
+#pragma once
+
+#include "fsmgr.h"
+
+namespace fs {
+
+class vtech_image : public manager_t {
+public:
+ vtech_image() : manager_t() {}
+
+ virtual const char *name() const override;
+ virtual const char *description() const override;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const override;
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const override;
+
+ virtual bool can_format() const override;
+ virtual bool can_read() const override;
+ virtual bool can_write() const override;
+ virtual bool has_rsrc() const override;
+
+ virtual std::vector<meta_description> volume_meta_description() const override;
+ virtual std::vector<meta_description> file_meta_description() const override;
+};
+
+extern const vtech_image VTECH;
+
+} // namespace fs
+
+#endif
diff --git a/src/lib/formats/fsblk.cpp b/src/lib/formats/fsblk.cpp
new file mode 100644
index 00000000000..5e8198d0ce8
--- /dev/null
+++ b/src/lib/formats/fsblk.cpp
@@ -0,0 +1,254 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Filesystem operations on mounted image blocks
+
+#include "fsblk.h"
+
+#include "multibyte.h"
+#include "strformat.h"
+
+#include <cstring>
+#include <stdexcept>
+
+namespace fs {
+
+void fsblk_t::set_block_size(u32 block_size)
+{
+ m_block_size = block_size;
+}
+
+const u8 *fsblk_t::block_t::roffs(const char *function, u32 off, u32 size) const
+{
+ if(off + size > m_size)
+ throw std::out_of_range(util::string_format("block_t::%s out-of-block read access, offset=%d, size=%d, block size=%d", function, off, size, m_size));
+ return rodata() + off;
+}
+
+u8 *fsblk_t::block_t::woffs(const char *function, u32 off, u32 size)
+{
+ if(off + size > m_size)
+ throw std::out_of_range(util::string_format("block_t::%s out-of-block access, offset=%d, size=%d, block size=%d", function, off, size, m_size));
+ return data() + off;
+}
+
+void fsblk_t::block_t::write(u32 offset, const u8 *src, u32 size)
+{
+ memcpy(woffs("write", offset, size), src, size);
+}
+
+void fsblk_t::block_t::fill(u32 offset, u8 data, u32 size)
+{
+ memset(woffs("fill", offset, size), data, size);
+}
+
+void fsblk_t::block_t::fill(u8 data)
+{
+ memset(this->data(), data, size());
+}
+
+void fsblk_t::block_t::wstr(u32 offset, std::string_view str)
+{
+ memcpy(woffs("wstr", offset, str.size()), str.data(), str.size());
+}
+
+void fsblk_t::block_t::w8(u32 offset, u8 data)
+{
+ woffs("w8", offset, 1)[0] = data;
+}
+
+void fsblk_t::block_t::w16b(u32 offset, u16 data)
+{
+ put_u16be(woffs("w16b", offset, 2), data);
+}
+
+void fsblk_t::block_t::w24b(u32 offset, u32 data)
+{
+ put_u24be(woffs("w24b", offset, 3), data);
+}
+
+void fsblk_t::block_t::w32b(u32 offset, u32 data)
+{
+ put_u32be(woffs("w32b", offset, 4), data);
+}
+
+void fsblk_t::block_t::w16l(u32 offset, u16 data)
+{
+ put_u16le(woffs("w16l", offset, 2), data);
+}
+
+void fsblk_t::block_t::w24l(u32 offset, u32 data)
+{
+ put_u24le(woffs("w24l", offset, 3), data);
+}
+
+void fsblk_t::block_t::w32l(u32 offset, u32 data)
+{
+ put_u32le(woffs("w32l", offset, 4), data);
+}
+
+void fsblk_t::block_t::read(u32 offset, u8 *dst, u32 size) const
+{
+ memcpy(dst, roffs("read", offset, size), size);
+}
+
+std::string_view fsblk_t::block_t::rstr(u32 offset, u32 size) const
+{
+ const u8 *d = roffs("rstr", offset, size);
+ return std::string_view(reinterpret_cast<const char *>(d), size);
+}
+
+u8 fsblk_t::block_t::r8(u32 offset) const
+{
+ return roffs("r8", offset, 1)[0];
+}
+
+u16 fsblk_t::block_t::r16b(u32 offset) const
+{
+ return get_u16be(roffs("r16b", offset, 2));
+}
+
+u32 fsblk_t::block_t::r24b(u32 offset) const
+{
+ return get_u24be(roffs("r24b", offset, 3));
+}
+
+u32 fsblk_t::block_t::r32b(u32 offset) const
+{
+ return get_u32be(roffs("r32b", offset, 4));
+}
+
+u16 fsblk_t::block_t::r16l(u32 offset) const
+{
+ return get_u16le(roffs("r16l", offset, 2));
+}
+
+u32 fsblk_t::block_t::r24l(u32 offset) const
+{
+ return get_u24le(roffs("r24l", offset, 3));
+}
+
+u32 fsblk_t::block_t::r32l(u32 offset) const
+{
+ return get_u32le(roffs("r32l", offset, 4));
+}
+
+
+
+void filesystem_t::wstr(u8 *p, std::string_view str)
+{
+ memcpy(p, str.data(), str.size());
+}
+
+std::string_view filesystem_t::rstr(const u8 *p, u32 size)
+{
+ return std::string_view(reinterpret_cast<const char *>(p), size);
+}
+
+std::string_view filesystem_t::trim_end_spaces(std::string_view str)
+{
+ const auto i = str.find_last_not_of(' ');
+ return str.substr(0, (std::string::npos != i) ? (i + 1) : 0);
+}
+
+meta_data filesystem_t::volume_metadata()
+{
+ return meta_data();
+}
+
+std::error_condition filesystem_t::volume_metadata_change(const meta_data &meta)
+{
+ return error::unsupported;
+}
+
+std::pair<std::error_condition, meta_data> filesystem_t::metadata(const std::vector<std::string> &path)
+{
+ return std::make_pair(error::unsupported, meta_data());
+}
+
+std::error_condition filesystem_t::metadata_change(const std::vector<std::string> &path, const meta_data &meta)
+{
+ return error::unsupported;
+}
+
+std::pair<std::error_condition, std::vector<dir_entry>> filesystem_t::directory_contents(const std::vector<std::string> &path)
+{
+ return std::make_pair(error::unsupported, std::vector<dir_entry>());
+}
+
+std::error_condition filesystem_t::rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath)
+{
+ return error::unsupported;
+}
+
+std::error_condition filesystem_t::remove(const std::vector<std::string> &path)
+{
+ return error::unsupported;
+}
+
+std::error_condition filesystem_t::dir_create(const std::vector<std::string> &path, const meta_data &meta)
+{
+ return error::unsupported;
+}
+
+std::error_condition filesystem_t::file_create(const std::vector<std::string> &path, const meta_data &meta)
+{
+ return error::unsupported;
+}
+
+std::pair<std::error_condition, std::vector<u8>> filesystem_t::file_read(const std::vector<std::string> &path)
+{
+ return std::make_pair(error::unsupported, std::vector<u8>());
+}
+
+std::error_condition filesystem_t::file_write(const std::vector<std::string> &path, const std::vector<u8> &data)
+{
+ return error::unsupported;
+}
+
+std::pair<std::error_condition, std::vector<u8>> filesystem_t::file_rsrc_read(const std::vector<std::string> &path)
+{
+ return std::make_pair(error::unsupported, std::vector<u8>());
+}
+
+std::error_condition filesystem_t::file_rsrc_write(const std::vector<std::string> &path, const std::vector<u8> &data)
+{
+ return error::unsupported;
+}
+
+std::error_condition filesystem_t::format(const meta_data &meta)
+{
+ return error::unsupported;
+}
+
+std::error_category const &fs_category() noexcept
+{
+ class fs_category_impl : public std::error_category
+ {
+ public:
+ virtual char const *name() const noexcept override { return "fs"; }
+
+ virtual std::string message(int condition) const override
+ {
+ using namespace std::literals;
+ static std::string_view const s_messages[] = {
+ "No error"sv,
+ "Unsupported operation"sv,
+ "File or directory not found"sv,
+ "No space on volume"sv,
+ "Invalid block number"sv,
+ "Invalid filename or path"sv,
+ "Incorrect file size"sv,
+ "File already exists"sv,
+ };
+ if ((0 <= condition) && (std::size(s_messages) > condition))
+ return std::string(s_messages[condition]);
+ else
+ return "Unknown error"s;
+ }
+ };
+ static fs_category_impl const s_fs_category_instance;
+ return s_fs_category_instance;
+}
+
+} // namespace fs
diff --git a/src/lib/formats/fsblk.h b/src/lib/formats/fsblk.h
new file mode 100644
index 00000000000..7f368a7307c
--- /dev/null
+++ b/src/lib/formats/fsblk.h
@@ -0,0 +1,181 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Filesystem operations on mounted image blocks
+
+#ifndef MAME_FORMATS_FSBLK_H
+#define MAME_FORMATS_FSBLK_H
+
+#pragma once
+
+#include "fsmeta.h"
+
+#include <memory>
+#include <string>
+#include <string_view>
+#include <system_error>
+#include <utility>
+#include <vector>
+
+namespace fs {
+
+using u8 = uint8_t;
+using u16 = uint16_t;
+using u32 = uint32_t;
+using u64 = uint64_t;
+
+enum class error : int {
+ unsupported = 1,
+ not_found,
+ no_space,
+ invalid_block,
+ invalid_name,
+ incorrect_size,
+ already_exists,
+};
+
+enum class dir_entry_type {
+ dir,
+ file,
+};
+
+struct dir_entry {
+ std::string m_name;
+ dir_entry_type m_type;
+ meta_data m_meta;
+
+ dir_entry(dir_entry_type type, const meta_data &meta) : m_name(meta.get_string(meta_name::name)), m_type(type), m_meta(std::move(meta)) {}
+};
+
+class fsblk_t {
+public:
+ class block_t {
+ public:
+ using ptr = std::shared_ptr<block_t>;
+
+ block_t(u32 size) : m_size(size) {}
+ virtual ~block_t() = default;
+
+ u32 size() const { return m_size; }
+
+ virtual const u8 *rodata() const = 0;
+ virtual u8 *data() = 0;
+
+ void write(u32 offset, const u8 *src, u32 size);
+ void fill( u8 data);
+ void fill( u32 offset, u8 data, u32 size);
+ void wstr( u32 offset, std::string_view str);
+ void w8( u32 offset, u8 data);
+ void w16b( u32 offset, u16 data);
+ void w24b( u32 offset, u32 data);
+ void w32b( u32 offset, u32 data);
+ void w16l( u32 offset, u16 data);
+ void w24l( u32 offset, u32 data);
+ void w32l( u32 offset, u32 data);
+
+ void read(u32 offset, u8 *dst, u32 size) const;
+ std::string_view rstr(u32 offset, u32 size) const;
+ u8 r8( u32 offset) const;
+ u16 r16b(u32 offset) const;
+ u32 r24b(u32 offset) const;
+ u32 r32b(u32 offset) const;
+ u16 r16l(u32 offset) const;
+ u32 r24l(u32 offset) const;
+ u32 r32l(u32 offset) const;
+
+ protected:
+ u32 m_size;
+
+ private:
+ const u8 *roffs(const char *function, u32 off, u32 size) const;
+ u8 *woffs(const char *function, u32 off, u32 size);
+ };
+
+ fsblk_t() : m_block_size(0) {}
+ virtual ~fsblk_t() = default;
+
+ virtual void set_block_size(u32 block_size);
+ virtual u32 block_count() const = 0;
+ virtual block_t::ptr get(u32 id) = 0;
+ virtual void fill_all(u8 data) = 0;
+
+protected:
+ u32 m_block_size;
+};
+
+
+class filesystem_t {
+public:
+ virtual ~filesystem_t() = default;
+
+ // Get the metadata for the volume
+ virtual meta_data volume_metadata();
+
+ // Change the metadata for the volume
+ virtual std::error_condition volume_metadata_change(const meta_data &meta);
+
+ // Get the metadata for a file or a directory. Empty path targets the root directory
+ virtual std::pair<std::error_condition, meta_data> metadata(const std::vector<std::string> &path);
+
+ // Change the metadata for a file or a directory. Empty path targets the root directory
+ virtual std::error_condition metadata_change(const std::vector<std::string> &path, const meta_data &meta);
+
+ // Get the contents of a directory, empty path targets the root directory
+ virtual std::pair<std::error_condition, std::vector<dir_entry>> directory_contents(const std::vector<std::string> &path);
+
+ // Rename a file or a directory. In contrast to metadata_change, this can move the object
+ // between directories
+ virtual std::error_condition rename(const std::vector<std::string> &opath, const std::vector<std::string> &npath);
+
+ // Remove a file or a directory. Directories must be empty (e.g. it's not recursive)
+ virtual std::error_condition remove(const std::vector<std::string> &path);
+
+ // Create a directory, path designates where the directory must be, directory name is in meta
+ virtual std::error_condition dir_create(const std::vector<std::string> &path, const meta_data &meta);
+
+ // Create an empty file, path designates where the file must be, file name is in meta
+ virtual std::error_condition file_create(const std::vector<std::string> &path, const meta_data &meta);
+
+ // Read the contents of a file
+ virtual std::pair<std::error_condition, std::vector<u8>> file_read(const std::vector<std::string> &path);
+
+ // Replace the contents of a file, the file must already exist
+ virtual std::error_condition file_write(const std::vector<std::string> &path, const std::vector<u8> &data);
+
+ // Read the resource fork of a file on systems that handle those
+ virtual std::pair<std::error_condition, std::vector<u8>> file_rsrc_read(const std::vector<std::string> &path);
+
+ // Replace the resource fork of a file, the file must already exist
+ virtual std::error_condition file_rsrc_write(const std::vector<std::string> &path, const std::vector<u8> &data);
+
+ // Format an image, provide the volume metadata
+ virtual std::error_condition format(const meta_data &meta);
+
+ static void wstr(u8 *p, std::string_view str);
+
+ static std::string_view rstr(const u8 *p, u32 size);
+
+ static std::string_view trim_end_spaces(std::string_view str);
+
+protected:
+ filesystem_t(fsblk_t &blockdev, u32 size) : m_blockdev(blockdev) {
+ m_blockdev.set_block_size(size);
+ }
+
+ fsblk_t &m_blockdev;
+};
+
+// error category for filesystem errors
+std::error_category const &fs_category() noexcept;
+inline std::error_condition make_error_condition(error err) noexcept { return std::error_condition(int(err), fs_category()); }
+
+} // namespace fs
+
+
+namespace std {
+
+template <> struct is_error_condition_enum<fs::error> : public std::true_type { };
+
+} // namespace std
+
+#endif
diff --git a/src/lib/formats/fsblk_vec.cpp b/src/lib/formats/fsblk_vec.cpp
new file mode 100644
index 00000000000..a04d60c263c
--- /dev/null
+++ b/src/lib/formats/fsblk_vec.cpp
@@ -0,0 +1,42 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Block device on vector<u8>
+
+#include "fsblk_vec.h"
+
+#include "strformat.h"
+
+#include <algorithm>
+#include <stdexcept>
+
+namespace fs {
+
+const u8 *fsblk_vec_t::blk_t::rodata() const
+{
+ return m_data;
+}
+
+u8 *fsblk_vec_t::blk_t::data()
+{
+ return m_data;
+}
+
+u32 fsblk_vec_t::block_count() const
+{
+ return m_data.size() / m_block_size;
+}
+
+fsblk_vec_t::block_t::ptr fsblk_vec_t::get(u32 id)
+{
+ if(id >= block_count())
+ throw std::out_of_range(util::string_format("Block number overflow: requiring block %d on device of size %d (%d bytes, block size %d)", id, block_count(), m_data.size(), m_block_size));
+ return std::make_shared<blk_t>(m_data.data() + m_block_size*id, m_block_size);
+}
+
+void fsblk_vec_t::fill_all(u8 data)
+{
+ std::fill(m_data.begin(), m_data.end(), data);
+}
+
+} // namespace fs
diff --git a/src/lib/formats/fsblk_vec.h b/src/lib/formats/fsblk_vec.h
new file mode 100644
index 00000000000..b029bc55c5e
--- /dev/null
+++ b/src/lib/formats/fsblk_vec.h
@@ -0,0 +1,36 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Block device on vector<uint8_t>
+
+#include "fsblk.h"
+
+namespace fs {
+
+class fsblk_vec_t : public fsblk_t {
+private:
+ class blk_t : public block_t {
+ public:
+ blk_t(u8 *data, u32 size) : block_t(size), m_data(data) {}
+ virtual ~blk_t() = default;
+
+ virtual const u8 *rodata() const override;
+ virtual u8 *data() override;
+
+ private:
+ u8 *m_data;
+ };
+
+public:
+ fsblk_vec_t(std::vector<u8> &data) : m_data(data) {}
+ virtual ~fsblk_vec_t() = default;
+
+ virtual u32 block_count() const override;
+ virtual block_t::ptr get(u32 id) override;
+ virtual void fill_all(u8 data) override;
+
+private:
+ std::vector<u8> &m_data;
+};
+
+} // namespace fs
diff --git a/src/lib/formats/fsd_dsk.cpp b/src/lib/formats/fsd_dsk.cpp
index 9cd4bff1f61..b386dc026c6 100644
--- a/src/lib/formats/fsd_dsk.cpp
+++ b/src/lib/formats/fsd_dsk.cpp
@@ -8,8 +8,12 @@
***************************************************************************/
-#include "emu.h"
#include "fsd_dsk.h"
+#include "imageutl.h"
+
+#include "ioprocs.h"
+
+#include <cstring>
/*********************************************************************
@@ -64,48 +68,54 @@ fsd_format::fsd_format()
{
}
-const char *fsd_format::name() const
+const char *fsd_format::name() const noexcept
{
return "fsd";
}
-const char *fsd_format::description() const
+const char *fsd_format::description() const noexcept
{
return "BBC Micro 8271 protected disk image";
}
-const char *fsd_format::extensions() const
+const char *fsd_format::extensions() const noexcept
{
return "fsd";
}
-bool fsd_format::supports_save() const
+bool fsd_format::supports_save() const noexcept
{
return false;
}
-int fsd_format::identify(io_generic *io, uint32_t form_factor)
+int fsd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t h[3];
-
- io_generic_read(io, h, 0, 3);
+ auto const [err, actual] = read_at(io, 0, h, 3);
+ if (err || (3 != actual)) {
+ LOG_FORMATS("fsd: read error\n");
+ return 0;
+ }
if (memcmp(h, "FSD", 3) == 0) {
- return 100;
+ return FIFID_SIGN;
}
LOG_FORMATS("fsd: no match\n");
return 0;
}
-bool fsd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool fsd_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
const char* result[255];
result[0x00] = "OK";
result[0x0e] = "Data CRC Error";
result[0x20] = "Deleted Data";
- uint64_t size = io_generic_size(io);
- std::vector<uint8_t> img(size);
- io_generic_read(io, &img[0], 0, size);
+ uint64_t size;
+ if(io.length(size))
+ return false;
+ auto const [err, img, actual] = read_at(io, 0, size);
+ if(err || (actual != size))
+ return false;
uint64_t pos;
std::string title;
@@ -182,4 +192,4 @@ bool fsd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-const floppy_format_type FLOPPY_FSD_FORMAT = &floppy_image_format_creator<fsd_format>;
+const fsd_format FLOPPY_FSD_FORMAT;
diff --git a/src/lib/formats/fsd_dsk.h b/src/lib/formats/fsd_dsk.h
index 0eddbb3f5d7..8b61e8d68c6 100644
--- a/src/lib/formats/fsd_dsk.h
+++ b/src/lib/formats/fsd_dsk.h
@@ -12,7 +12,7 @@
#pragma once
-#include "flopimg.h"
+#include "flopimg_legacy.h"
#include "wd177x_dsk.h"
/**************************************************************************/
@@ -28,15 +28,15 @@ class fsd_format : public floppy_image_format_t
public:
fsd_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
};
-extern const floppy_format_type FLOPPY_FSD_FORMAT;
+extern const fsd_format FLOPPY_FSD_FORMAT;
#endif // MAME_FORMATS_FSD_DSK_H
diff --git a/src/lib/formats/fsmeta.cpp b/src/lib/formats/fsmeta.cpp
new file mode 100644
index 00000000000..3c731f21939
--- /dev/null
+++ b/src/lib/formats/fsmeta.cpp
@@ -0,0 +1,130 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Filesystem metadata management
+
+#include "fsmeta.h"
+
+#include "strformat.h"
+
+#include <optional>
+
+namespace fs {
+
+const char *meta_data::entry_name(meta_name name)
+{
+ switch(name) {
+ case meta_name::creation_date: return "creation_date";
+ case meta_name::length: return "length";
+ case meta_name::loading_address: return "loading_address";
+ case meta_name::locked: return "locked";
+ case meta_name::modification_date: return "modification_date";
+ case meta_name::name: return "name";
+ case meta_name::os_minimum_version: return "os_minimum_version";
+ case meta_name::os_version: return "os_version";
+ case meta_name::rsrc_length: return "rsrc_length";
+ case meta_name::sequential: return "sequential";
+ case meta_name::size_in_blocks: return "size_in_blocks";
+ case meta_name::file_type: return "file_type";
+ case meta_name::ascii_flag: return "ascii_flag";
+ case meta_name::owner_id: return "owner_id";
+ case meta_name::attributes: return "attributes";
+ case meta_name::oem_name: return "oem_name";
+ }
+ return "";
+}
+
+std::optional<meta_name> meta_data::from_entry_name(const char *name)
+{
+ for (int i = 0; i <= (int)meta_name::max; i++)
+ {
+ if (!strcmp(name, entry_name((meta_name)i)))
+ return (meta_name)i;
+ }
+ return {};
+}
+
+template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
+template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
+
+meta_type meta_value::type() const
+{
+ std::optional<meta_type> result;
+ std::visit(
+ overloaded{
+ [&result] (const std::string &) { result = meta_type::string; },
+ [&result] (std::uint64_t) { result = meta_type::number; },
+ [&result] (bool) { result = meta_type::flag; },
+ [&result] (const util::arbitrary_datetime &) { result = meta_type::date; } },
+ value);
+ return *result;
+}
+
+util::arbitrary_datetime meta_value::as_date() const
+{
+ util::arbitrary_datetime result = { 0, };
+
+ std::visit(
+ overloaded
+ {
+ [&result](const std::string &s)
+ {
+ sscanf(s.c_str(), "%d-%d-%d %d:%d:%d", &result.year, &result.month, &result.day_of_month, &result.hour, &result.minute, &result.second);
+ },
+ [&result](const util::arbitrary_datetime &dt) { result = dt; },
+ [](std::uint64_t) { /* nonsensical */ },
+ [](bool) { /* nonsensical */ }
+ }, value);
+
+ return result;
+}
+
+bool meta_value::as_flag() const
+{
+ bool result = false;
+
+ std::visit(
+ overloaded
+ {
+ [&result](const std::string &s) { result = !s.empty() && s != "f"; },
+ [&result](bool b) { result = b; },
+ [](std::uint64_t) { /* nonsensical */ },
+ [](const util::arbitrary_datetime &) { /* nonsensical */ }
+ }, value);
+ return result;
+}
+
+std::string meta_value::as_string() const
+{
+ std::string result;
+ std::visit(
+ overloaded{
+ [&result] (const std::string &val) { result = val; },
+ [&result] (std::uint64_t val) { result = util::string_format("0x%x", val); },
+ [&result] (bool val) { result = val ? "t" : "f"; },
+ [&result] (const util::arbitrary_datetime &val)
+ {
+ result = util::string_format("%04d-%02d-%02d %02d:%02d:%02d",
+ val.year, val.month, val.day_of_month,
+ val.hour, val.minute, val.second);
+ } },
+ value);
+ return result;
+}
+
+uint64_t meta_value::as_number() const
+{
+ uint64_t result = 0;
+
+ std::visit(overloaded
+ {
+ [&result](const std::string &s) { result = std::stoull(s); },
+ [&result](uint64_t i) { result = i; },
+ [](const util::arbitrary_datetime &) { /* nonsensical */ },
+ [](bool) { /* nonsensical */ }
+ }, value);
+ return result;
+}
+
+
+} // namespace fs
diff --git a/src/lib/formats/fsmeta.h b/src/lib/formats/fsmeta.h
new file mode 100644
index 00000000000..ff0b505ab8b
--- /dev/null
+++ b/src/lib/formats/fsmeta.h
@@ -0,0 +1,124 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Filesystem metadata management
+
+#ifndef MAME_FORMATS_FSMETA_H
+#define MAME_FORMATS_FSMETA_H
+
+#pragma once
+
+#include "timeconv.h"
+
+#include <cassert>
+#include <functional>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <variant>
+
+namespace fs {
+
+enum class meta_name {
+ creation_date,
+ length,
+ loading_address,
+ locked,
+ modification_date,
+ name,
+ os_minimum_version,
+ os_version,
+ rsrc_length,
+ sequential,
+ size_in_blocks,
+ file_type,
+ ascii_flag,
+ owner_id,
+ attributes,
+ oem_name,
+ max = oem_name
+};
+
+enum class meta_type {
+ date,
+ flag,
+ number,
+ string,
+};
+
+class meta_value {
+public:
+ meta_type type() const;
+
+ meta_value() { value = false; }
+ meta_value(std::string &&str) { value = std::move(str); }
+ meta_value(std::string_view str) { value = std::string(str); }
+ meta_value(const char *str) { value = std::string(str); }
+ meta_value(bool b) { value = b; }
+ meta_value(int32_t num) { value = uint64_t(num); }
+ meta_value(uint32_t num) { value = uint64_t(num); }
+ meta_value(int64_t num) { value = uint64_t(num); }
+ meta_value(uint64_t num) { value = num; }
+ meta_value(util::arbitrary_datetime dt) { value = dt; }
+
+ util::arbitrary_datetime as_date() const;
+ bool as_flag() const;
+ uint64_t as_number() const;
+ std::string as_string() const;
+
+private:
+ std::variant<std::string, uint64_t, bool, util::arbitrary_datetime> value;
+};
+
+class meta_data {
+public:
+ std::unordered_map<meta_name, meta_value> meta;
+
+ static const char *entry_name(meta_name name);
+ static std::optional<meta_name> from_entry_name(const char *name);
+
+ bool has(meta_name name) const { return meta.find(name) != meta.end(); }
+ bool empty() const { return meta.empty(); }
+
+ void set(meta_name name, const meta_value &val) { meta[name] = val; }
+ void set(meta_name name, meta_value &&val) { meta[name] = std::move(val); }
+ void set(meta_name name, std::string &&str) { set(name, meta_value(std::move(str))); }
+ void set(meta_name name, std::string_view str) { set(name, meta_value(str)); }
+ void set(meta_name name, const char *str) { set(name, meta_value(str)); }
+ void set(meta_name name, bool b) { set(name, meta_value(b)); }
+ void set(meta_name name, int32_t num) { set(name, meta_value(num)); }
+ void set(meta_name name, uint32_t num) { set(name, meta_value(num)); }
+ void set(meta_name name, int64_t num) { set(name, meta_value(num)); }
+ void set(meta_name name, uint64_t num) { set(name, meta_value(num)); }
+ void set(meta_name name, util::arbitrary_datetime dt) { set(name, meta_value(dt)); }
+ void set_now(meta_name name) { set(name, meta_value(util::arbitrary_datetime::now())); }
+
+ meta_value get(meta_name name) const { auto i = meta.find(name); assert(i != meta.end()); return i->second; }
+ util::arbitrary_datetime get_date(meta_name name, util::arbitrary_datetime def = util::arbitrary_datetime::now()) const { auto i = meta.find(name); if(i == meta.end()) return def; else return i->second.as_date(); }
+ bool get_flag(meta_name name, bool def = false) const { auto i = meta.find(name); if(i == meta.end()) return def; else return i->second.as_flag(); }
+ uint64_t get_number(meta_name name, uint64_t def = 0) const { auto i = meta.find(name); if(i == meta.end()) return def; else return i->second.as_number(); }
+ std::string get_string(meta_name name, std::string def = "") const { auto i = meta.find(name); if(i == meta.end()) return def; else return i->second.as_string(); }
+};
+
+struct meta_description {
+ meta_name m_name;
+ meta_value m_default;
+ bool m_ro;
+ std::function<void (const meta_value &)> m_validator;
+ const char *m_tooltip;
+
+ template <typename T>
+ meta_description(meta_name name, T def, bool ro, std::function<void(meta_value)> validator, const char *tooltip) :
+ meta_description(name, meta_value(def), ro, std::move(validator), tooltip)
+ {}
+
+private:
+ meta_description(meta_name name, meta_value &&def, bool ro, std::function<void(meta_value)> &&validator, const char *tooltip) :
+ m_name(name), m_default(std::move(def)), m_ro(ro), m_validator(validator), m_tooltip(tooltip)
+ {}
+};
+
+} // namespace fs
+
+#endif // MAME_FORMATS_FSMETA_H
diff --git a/src/lib/formats/fsmgr.cpp b/src/lib/formats/fsmgr.cpp
new file mode 100644
index 00000000000..0b30cf0951b
--- /dev/null
+++ b/src/lib/formats/fsmgr.cpp
@@ -0,0 +1,80 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Filesystem management code for floppy and hd images
+
+// Currently limited to floppies and creation of preformatted images
+
+#include "fsmgr.h"
+#include "flopimg.h"
+
+namespace fs {
+
+void manager_t::enumerate_f(floppy_enumerator &fe) const
+{
+}
+
+void manager_t::enumerate_h(hd_enumerator &he) const
+{
+}
+
+void manager_t::enumerate_c(cdrom_enumerator &ce) const
+{
+}
+
+bool manager_t::has_variant(const std::vector<u32> &variants, u32 variant)
+{
+ for(u32 v : variants)
+ if(variant == v)
+ return true;
+ return false;
+}
+
+bool manager_t::has(u32 form_factor, const std::vector<u32> &variants, u32 ff, u32 variant)
+{
+ if(form_factor == floppy_image::FF_UNKNOWN)
+ return true;
+ if(form_factor != ff)
+ return false;
+ for(u32 v : variants)
+ if(variant == v)
+ return true;
+ return false;
+}
+
+std::vector<meta_description> manager_t::volume_meta_description() const
+{
+ std::vector<meta_description> res;
+ return res;
+}
+
+std::vector<meta_description> manager_t::file_meta_description() const
+{
+ std::vector<meta_description> res;
+ return res;
+}
+
+std::vector<meta_description> manager_t::directory_meta_description() const
+{
+ std::vector<meta_description> res;
+ return res;
+}
+
+char manager_t::directory_separator() const
+{
+ return 0; // Subdirectories not supported by default
+}
+
+manager_t::floppy_enumerator::floppy_enumerator(u32 form_factor, const std::vector<u32> &variants)
+ : m_form_factor(form_factor)
+ , m_variants(variants)
+{
+}
+
+void manager_t::floppy_enumerator::add(const floppy_image_format_t &type, u32 form_factor, u32 variant, u32 image_size, const char *name, const char *description)
+{
+ if (has(m_form_factor, m_variants, form_factor, variant))
+ add_format(type, image_size, name, description);
+}
+
+} // namespace fs
diff --git a/src/lib/formats/fsmgr.h b/src/lib/formats/fsmgr.h
new file mode 100644
index 00000000000..e3565956fe2
--- /dev/null
+++ b/src/lib/formats/fsmgr.h
@@ -0,0 +1,91 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+// Filesystem management code for floppy, hd and cdrom images
+
+#ifndef MAME_FORMATS_FSMGR_H
+#define MAME_FORMATS_FSMGR_H
+
+#pragma once
+
+#include "fsmeta.h"
+
+#include <memory>
+#include <vector>
+
+class floppy_image_format_t;
+
+namespace fs {
+
+// declared in fsblk.h
+class filesystem_t;
+class fsblk_t;
+
+using u32 = uint32_t;
+
+class manager_t {
+public:
+ struct floppy_enumerator {
+ floppy_enumerator(u32 form_factor, const std::vector<u32> &variants);
+ virtual ~floppy_enumerator() = default;
+
+ void add(const floppy_image_format_t &type, u32 form_factor, u32 variant, u32 image_size, const char *name, const char *description);
+ virtual void add_raw(const char *name, u32 key, const char *description) = 0;
+
+ u32 form_factor() const { return m_form_factor; }
+ const std::vector<u32> &variants() const { return m_variants; }
+
+ protected:
+ virtual void add_format(const floppy_image_format_t &type, u32 image_size, const char *name, const char *description) = 0;
+
+ private:
+ u32 m_form_factor;
+ const std::vector<u32> & m_variants;
+ };
+
+ struct hd_enumerator {
+ virtual ~hd_enumerator() = default;
+
+ virtual void add(const manager_t *manager, const char *name, const char *description) = 0;
+ };
+
+ struct cdrom_enumerator {
+ virtual ~cdrom_enumerator() = default;
+
+ virtual void add(const manager_t *manager, const char *name, const char *description) = 0;
+ };
+
+ virtual ~manager_t() = default;
+
+ virtual const char *name() const = 0;
+ virtual const char *description() const = 0;
+
+ virtual void enumerate_f(floppy_enumerator &fe) const;
+ virtual void enumerate_h(hd_enumerator &he) const;
+ virtual void enumerate_c(cdrom_enumerator &ce) const;
+
+ virtual bool can_format() const = 0;
+ virtual bool can_read() const = 0;
+ virtual bool can_write() const = 0;
+ virtual bool has_rsrc() const = 0;
+ virtual char directory_separator() const;
+
+ bool has_subdirectories() const { return directory_separator() != 0; }
+
+ virtual std::vector<meta_description> volume_meta_description() const;
+ virtual std::vector<meta_description> file_meta_description() const;
+ virtual std::vector<meta_description> directory_meta_description() const;
+
+ // Create a filesystem object from a block device
+ virtual std::unique_ptr<filesystem_t> mount(fsblk_t &blockdev) const = 0;
+
+ static bool has(u32 form_factor, const std::vector<u32> &variants, u32 ff, u32 variant);
+ static bool has_variant(const std::vector<u32> &variants, u32 variant);
+
+protected:
+ manager_t() = default;
+};
+
+} // namespace fs
+
+#endif
diff --git a/src/lib/formats/g64_dsk.cpp b/src/lib/formats/g64_dsk.cpp
index d0fc182835d..177438dc9a6 100644
--- a/src/lib/formats/g64_dsk.cpp
+++ b/src/lib/formats/g64_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/g64_dsk.c
+ formats/g64_dsk.cpp
Commodore 1541/1571 GCR disk image format
@@ -10,8 +10,14 @@
*********************************************************************/
-#include "emu.h" // emu_fatalerror
#include "formats/g64_dsk.h"
+#include "imageutl.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include "osdcore.h" // osd_printf_*
+
#define G64_FORMAT_HEADER "GCR-1541"
@@ -27,25 +33,33 @@ const uint32_t g64_format::c1541_cell_size[] =
3250 // 16MHz/13/4
};
-int g64_format::identify(io_generic *io, uint32_t form_factor)
+int g64_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
char h[8];
+ auto const [err, actual] = read_at(io, 0, h, 8);
+ if (err || (8 != actual))
+ return 0;
+
+ if (!memcmp(h, G64_FORMAT_HEADER, 8))
+ return FIFID_SIGN;
- io_generic_read(io, h, 0, 8);
- if (!memcmp(h, G64_FORMAT_HEADER, 8)) {
- return 100;
- }
return 0;
}
-bool g64_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool g64_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint64_t size = io_generic_size(io);
- std::vector<uint8_t> img(size);
- io_generic_read(io, &img[0], 0, size);
+ uint64_t size;
+ if (io.length(size))
+ return false;
+
+ auto const [err, img, actual] = read_at(io, 0, size);
+ if (err || (actual != size))
+ return false;
- if (img[POS_VERSION]) {
- throw emu_fatalerror("g64_format: Unsupported version %u", img[POS_VERSION]);
+ if (img[POS_VERSION])
+ {
+ osd_printf_error("g64_format: Unsupported version %u\n", img[POS_VERSION]);
+ return false;
}
int track_count = img[POS_TRACK_COUNT];
@@ -60,20 +74,26 @@ bool g64_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
uint32_t tpos = POS_TRACK_OFFSET + (track * 4);
uint32_t spos = tpos + (track_count * 4);
- uint32_t dpos = pick_integer_le(&img[0], tpos, 4);
+ uint32_t dpos = get_u32le(&img[tpos]);
if (!dpos)
continue;
if (dpos > size)
- throw emu_fatalerror("g64_format: Track %u offset %06x out of bounds", track, dpos);
+ {
+ osd_printf_error("g64_format: Track %u offset %06x out of bounds\n", track, dpos);
+ return false;
+ }
- uint32_t speed_zone = pick_integer_le(&img[0], spos, 4);
+ uint32_t speed_zone = get_u32le(&img[spos]);
if (speed_zone > 3)
- throw emu_fatalerror("g64_format: Unsupported variable speed zones on track %d", track);
+ {
+ osd_printf_error("g64_format: Unsupported variable speed zones on track %d\n", track);
+ return false;
+ }
- uint16_t track_bytes = pick_integer_le(&img[0], dpos, 2);
+ uint16_t track_bytes = get_u16le(&img[dpos]);
int track_size = track_bytes * 8;
LOG_FORMATS("head %u track %u offs %u size %u cell %ld\n", head, cylinder, dpos, track_bytes, 200000000L/track_size);
@@ -82,77 +102,87 @@ bool g64_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
}
if (!head)
- image->set_variant(floppy_image::SSSD);
+ image.set_variant(floppy_image::SSSD);
else
- image->set_variant(floppy_image::DSSD);
+ image.set_variant(floppy_image::DSSD);
return true;
}
-int g64_format::generate_bitstream(int track, int head, int speed_zone, uint8_t *trackbuf, int &track_size, floppy_image *image)
+int g64_format::generate_bitstream(int track, int head, int speed_zone, std::vector<bool> &trackbuf, const floppy_image &image)
{
int cell_size = c1541_cell_size[speed_zone];
- generate_bitstream_from_track(track, head, cell_size, trackbuf, track_size, image);
+ trackbuf = generate_bitstream_from_track(track, head, cell_size, image);
- int actual_cell_size = 200000000L/track_size;
+ int actual_cell_size = 200000000L/trackbuf.size();
// allow a tolerance of +- 10 us (3990..4010 -> 4000)
return ((actual_cell_size >= cell_size-10) && (actual_cell_size <= cell_size+10)) ? speed_zone : -1;
}
-bool g64_format::save(io_generic *io, floppy_image *image)
+bool g64_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
+ uint8_t const zerofill[] = { 0x00, 0x00, 0x00, 0x00 };
+ std::vector<uint8_t> const prefill(TRACK_LENGTH, 0xff);
+
int tracks, heads;
- image->get_actual_geometry(tracks, heads);
+ image.get_actual_geometry(tracks, heads);
tracks = TRACK_COUNT * heads;
// write header
uint8_t header[] = { 'G', 'C', 'R', '-', '1', '5', '4', '1', 0x00, static_cast<uint8_t>(tracks), TRACK_LENGTH & 0xff, TRACK_LENGTH >> 8 };
- io_generic_write(io, header, POS_SIGNATURE, sizeof(header));
+ write_at(io, POS_SIGNATURE, header, sizeof(header)); // FIXME: check for errors
// write tracks
for (int head = 0; head < heads; head++) {
int tracks_written = 0;
- std::vector<uint8_t> trackbuf(TRACK_LENGTH-2);
+ std::vector<bool> trackbuf;
for (int track = 0; track < TRACK_COUNT; track++) {
- uint32_t tpos = POS_TRACK_OFFSET + (track * 4);
- uint32_t spos = tpos + (tracks * 4);
- uint32_t dpos = POS_TRACK_OFFSET + (tracks * 4 * 2) + (tracks_written * TRACK_LENGTH);
+ uint32_t const tpos = POS_TRACK_OFFSET + (track * 4);
+ uint32_t const spos = tpos + (tracks * 4);
+ uint32_t const dpos = POS_TRACK_OFFSET + (tracks * 4 * 2) + (tracks_written * TRACK_LENGTH);
- io_generic_write_filler(io, 0x00, tpos, 4);
- io_generic_write_filler(io, 0x00, spos, 4);
+ write_at(io, tpos, zerofill, 4); // FIXME: check for errors
+ write_at(io, spos, zerofill, 4); // FIXME: check for errors
- if (image->get_buffer(track, head).size() <= 1)
+ if (image.get_buffer(track, head).size() <= 1)
continue;
int track_size;
int speed_zone;
// figure out the cell size and speed zone from the track data
- if ((speed_zone = generate_bitstream(track, head, 3, &trackbuf[0], track_size, image)) == -1)
- if ((speed_zone = generate_bitstream(track, head, 2, &trackbuf[0], track_size, image)) == -1)
- if ((speed_zone = generate_bitstream(track, head, 1, &trackbuf[0], track_size, image)) == -1)
- if ((speed_zone = generate_bitstream(track, head, 0, &trackbuf[0], track_size, image)) == -1)
- throw emu_fatalerror("g64_format: Cannot determine speed zone for track %u", track);
+ if ((speed_zone = generate_bitstream(track, head, 3, trackbuf, image)) == -1)
+ if ((speed_zone = generate_bitstream(track, head, 2, trackbuf, image)) == -1)
+ if ((speed_zone = generate_bitstream(track, head, 1, trackbuf, image)) == -1)
+ if ((speed_zone = generate_bitstream(track, head, 0, trackbuf, image)) == -1) {
+ osd_printf_error("g64_format: Cannot determine speed zone for track %u\n", track);
+ return false;
+ }
LOG_FORMATS("head %u track %u size %u cell %u\n", head, track, track_size, c1541_cell_size[speed_zone]);
+ std::vector<uint8_t> packed((trackbuf.size() + 7) >> 3, 0);
+ for(uint32_t i = 0; i != trackbuf.size(); i++)
+ if(trackbuf[i])
+ packed[i >> 3] |= 0x80 >> (i & 7);
+
uint8_t track_offset[4];
uint8_t speed_offset[4];
uint8_t track_length[2];
- place_integer_le(track_offset, 0, 4, dpos);
- place_integer_le(speed_offset, 0, 4, speed_zone);
- place_integer_le(track_length, 0, 2, track_size/8);
+ put_u32le(track_offset, dpos);
+ put_u32le(speed_offset, speed_zone);
+ put_u16le(track_length, packed.size());
- io_generic_write(io, track_offset, tpos, 4);
- io_generic_write(io, speed_offset, spos, 4);
- io_generic_write_filler(io, 0xff, dpos, TRACK_LENGTH);
- io_generic_write(io, track_length, dpos, 2);
- io_generic_write(io, &trackbuf[0], dpos + 2, track_size);
+ write_at(io, tpos, track_offset, 4); // FIXME: check for errors
+ write_at(io, spos, speed_offset, 4); // FIXME: check for errors
+ write_at(io, dpos, prefill.data(), TRACK_LENGTH); // FIXME: check for errors
+ write_at(io, dpos, track_length, 2); // FIXME: check for errors
+ write_at(io, dpos + 2, packed.data(), packed.size()); // FIXME: check for errors
tracks_written++;
}
@@ -161,19 +191,19 @@ bool g64_format::save(io_generic *io, floppy_image *image)
return true;
}
-const char *g64_format::name() const
+const char *g64_format::name() const noexcept
{
return "g64";
}
-const char *g64_format::description() const
+const char *g64_format::description() const noexcept
{
return "Commodore 1541/1571 GCR disk image";
}
-const char *g64_format::extensions() const
+const char *g64_format::extensions() const noexcept
{
return "g64,g41,g71";
}
-const floppy_format_type FLOPPY_G64_FORMAT = &floppy_image_format_creator<g64_format>;
+const g64_format FLOPPY_G64_FORMAT;
diff --git a/src/lib/formats/g64_dsk.h b/src/lib/formats/g64_dsk.h
index 1c032a09759..dc8aad20860 100644
--- a/src/lib/formats/g64_dsk.h
+++ b/src/lib/formats/g64_dsk.h
@@ -13,21 +13,20 @@
#pragma once
#include "flopimg.h"
-#include "imageutl.h"
class g64_format : public floppy_image_format_t
{
public:
g64_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override { return true; }
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override { return true; }
protected:
enum
@@ -47,9 +46,9 @@ protected:
static const uint32_t c1541_cell_size[];
- int generate_bitstream(int track, int head, int speed_zone, uint8_t *trackbuf, int &track_size, floppy_image *image);
+ static int generate_bitstream(int track, int head, int speed_zone, std::vector<bool> &trackbuf, const floppy_image &image);
};
-extern const floppy_format_type FLOPPY_G64_FORMAT;
+extern const g64_format FLOPPY_G64_FORMAT;
#endif // MAME_FORMATS_G64_DSK_H
diff --git a/src/lib/formats/gtp_cas.cpp b/src/lib/formats/gtp_cas.cpp
index fd3a16932b3..dc996aa89db 100644
--- a/src/lib/formats/gtp_cas.cpp
+++ b/src/lib/formats/gtp_cas.cpp
@@ -6,7 +6,6 @@
Miodrag Milanovic
*/
-#include <assert.h>
#include "gtp_cas.h"
@@ -20,7 +19,7 @@
#define GTP_BLOCK_TURBO 0x01
#define GTP_BLOCK_NAME 0x10
-static int16_t wave_data;
+static int16_t wave_data; // FIXME: global variable prevent multiple instances
static int16_t len;
#define PULSE_WIDTH 30
@@ -128,7 +127,7 @@ static int gtp_cas_to_wav_size( const uint8_t *casdata, int caslen ) {
return size;
}
-static int gtp_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
+static int gtp_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) {
int i,size,n;
size = 0;
n = 0;
@@ -164,7 +163,7 @@ static int gtp_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
-static const struct CassetteLegacyWaveFiller gtp_legacy_fill_wave = {
+static const cassette_image::LegacyWaveFiller gtp_legacy_fill_wave = {
gtp_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
0, /* chunk_samples */
@@ -176,19 +175,19 @@ static const struct CassetteLegacyWaveFiller gtp_legacy_fill_wave = {
-static cassette_image::error gtp_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) {
- return cassette_legacy_identify( cassette, opts, &gtp_legacy_fill_wave );
+static cassette_image::error gtp_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) {
+ return cassette->legacy_identify( opts, &gtp_legacy_fill_wave );
}
static cassette_image::error gtp_cassette_load( cassette_image *cassette ) {
- return cassette_legacy_construct( cassette, &gtp_legacy_fill_wave );
+ return cassette->legacy_construct( &gtp_legacy_fill_wave );
}
-static const struct CassetteFormat gtp_cassette_format = {
+static const cassette_image::Format gtp_cassette_format = {
"gtp",
gtp_cassette_identify,
gtp_cassette_load,
diff --git a/src/lib/formats/guab_dsk.cpp b/src/lib/formats/guab_dsk.cpp
index 4c92362251a..4eb0fa62060 100644
--- a/src/lib/formats/guab_dsk.cpp
+++ b/src/lib/formats/guab_dsk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -14,17 +14,17 @@ guab_format::guab_format() : wd177x_format(formats)
{
}
-const char *guab_format::name() const
+const char *guab_format::name() const noexcept
{
return "guab";
}
-const char *guab_format::description() const
+const char *guab_format::description() const noexcept
{
return "JPM Give us a Break disk image";
}
-const char *guab_format::extensions() const
+const char *guab_format::extensions() const noexcept
{
return "dsk";
}
@@ -39,4 +39,4 @@ const guab_format::format guab_format::formats[] =
{}
};
-const floppy_format_type FLOPPY_GUAB_FORMAT = &floppy_image_format_creator<guab_format>;
+const guab_format FLOPPY_GUAB_FORMAT;
diff --git a/src/lib/formats/guab_dsk.h b/src/lib/formats/guab_dsk.h
index 771baa2bf75..75660a70771 100644
--- a/src/lib/formats/guab_dsk.h
+++ b/src/lib/formats/guab_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -19,14 +19,14 @@ class guab_format : public wd177x_format
public:
guab_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_GUAB_FORMAT;
+extern const guab_format FLOPPY_GUAB_FORMAT;
#endif // MAME_FORMATS_GUAB_DSK_H
diff --git a/src/lib/formats/h17disk.cpp b/src/lib/formats/h17disk.cpp
new file mode 100644
index 00000000000..9fb211ddb0d
--- /dev/null
+++ b/src/lib/formats/h17disk.cpp
@@ -0,0 +1,253 @@
+// license:BSD-3-Clause
+// copyright-holders:Mark Garlanger
+/*********************************************************************
+
+Heath H17D disk image format (version 2.0.0)
+
+ Format for Heath hard-sectored 5.25" disk images.
+
+ See https://heathkit.garlanger.com/diskformats/ for more information
+
+ TODO - implement writing to H17D image
+
+*********************************************************************/
+
+#include "h17disk.h"
+
+#include "imageutl.h"
+
+#include "ioprocs.h"
+
+#include <cstring>
+
+
+namespace {
+
+constexpr int TRACK_SIZE = 50'000;
+constexpr int BITCELL_SIZE = 4000;
+
+constexpr int SECTOR_METADATA_SIZE = 16;
+
+constexpr int SECTOR_DATA_SIZE = 256;
+constexpr int SECTORS_PER_TRACK = 10;
+
+struct format {
+ int head_count;
+ int track_count;
+ uint32_t variant;
+};
+
+const format formats[] = {
+ { 1, 40, floppy_image::SSSD10 }, // H-17-1
+ { 2, 40, floppy_image::DSSD10 },
+ { 1, 80, floppy_image::SSQD10 },
+ { 2, 80, floppy_image::DSQD10 }, // H-17-4
+ {}
+};
+
+struct block_header {
+ uint32_t block_name;
+ uint32_t length;
+};
+
+enum {
+ DskF = 0x466b7344, //!< "DskF", Disk Format
+ Parm = 0x6b726150, //!< "Parm", Parameters
+ Date = 0x65746144, //!< "Date", Date
+ Imgr = 0x72676d49, //!< "Imgr", Imager
+
+ Prog = 0x676f7250, //!< "Prog", Program (creation)
+ Padd = 0x64646150, //!< "Padd", Padding
+ H8DB = 0x42443848, //!< "H8DB", H8D data block
+ SecM = 0x4d636553, //!< "SecM", Sector Metadata
+ Labl = 0x6c62614c, //!< "Labl", Label
+ Comm = 0x6d6d6f43, //!< "Comm", Comment
+};
+
+std::pair<std::uint64_t, std::size_t> find_block(util::random_read &io, uint32_t block_id)
+{
+ LOG_FORMATS("find_block: 0x%x\n", block_id);
+
+ // start of file
+ int pos = 0;
+ block_header header = { 0, 0 };
+
+ do
+ {
+ pos += header.length + 8;
+ auto const [err, actual] = read_at(io, pos, (void *) &header, 8);
+ if (err || actual !=8)
+ {
+ return std::make_pair(0, 0);
+ }
+ header.length = swapendian_int32(header.length);
+ }
+ while (header.block_name != block_id);
+
+ // update position to point to data portion of the block
+ return std::make_pair(pos + 8, header.length);
+}
+
+format find_format(util::random_read &io)
+{
+ auto const [pos, length] = find_block(io, DskF);
+ if ((pos == 0) || (length < 2) || (length > 3))
+ {
+ LOG_FORMATS("Can't find valid DskF block %d/%d\n", pos, length);
+
+ return {};
+ }
+
+ uint8_t buf[3];
+
+ auto const [err, actual] = read_at(io, pos, buf, length);
+ if (err || (actual != length))
+ {
+ LOG_FORMATS("read error\n");
+
+ return {};
+ }
+
+ int head_count = buf[0];
+ int track_count = buf[1];
+
+ for (int i = 0; formats[i].head_count; i++)
+ {
+ if ((formats[i].head_count == head_count) && (formats[i].track_count == track_count))
+ {
+ LOG_FORMATS("find_format format found: %d - variant: 0x%x\n", i, formats[i].variant);
+
+ return formats[i];
+ }
+ }
+
+ LOG_FORMATS("Invalid disk format - heads: %d, tracks: %d\n", head_count, track_count);
+ return {};
+}
+
+} // anonymous namespace
+
+
+heath_h17d_format::heath_h17d_format() : floppy_image_format_t()
+{
+}
+
+int heath_h17d_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint8_t h[4];
+ auto const [err, actual] = read_at(io, 0, h, 4);
+
+ if (err || (actual != 4))
+ {
+ return 0;
+ }
+
+ // Verify "H17D" Signature.
+ if ((h[0] == 0x48) && (h[1] == 0x31) && (h[2] == 0x37) && (h[3] == 0x44))
+ {
+ return FIFID_SIGN;
+ }
+
+ return 0;
+}
+
+bool heath_h17d_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ const format fmt = find_format(io);
+
+ if (!fmt.head_count)
+ {
+ LOG_FORMATS("invalid format\n");
+
+ return false;
+ }
+
+ image.set_variant(fmt.variant);
+
+ std::vector<uint32_t> buf;
+
+ auto const [secm_pos, secm_length] = find_block(io, SecM);
+
+ uint8_t sector_meta_data[SECTOR_METADATA_SIZE];
+ uint8_t sector_data[SECTOR_DATA_SIZE];
+
+ for (int head = 0; head < fmt.head_count; head++)
+ {
+ for (int track = 0; track < fmt.track_count; track++)
+ {
+ for (int sector = 0; sector < SECTORS_PER_TRACK; sector++)
+ {
+ int sect_meta_pos = (sector + (track * fmt.head_count + head) * SECTORS_PER_TRACK) * SECTOR_METADATA_SIZE + secm_pos;
+
+ auto const [err, actual] = read_at(io, sect_meta_pos, sector_meta_data, SECTOR_METADATA_SIZE);
+
+ if (err || (actual != SECTOR_METADATA_SIZE))
+ {
+ LOG_FORMATS("unable to read sect meta data %d/%d/%d\n", head, track, sector);
+
+ return false;
+ }
+ int data_offset = sector_meta_data[0] << 24 | sector_meta_data[1] << 16 | sector_meta_data[2] << 8 | sector_meta_data[3];
+
+ auto const [err2, actual2] = read_at(io, data_offset, sector_data, SECTOR_DATA_SIZE);
+
+ if (err2 || (actual2 != SECTOR_DATA_SIZE))
+ {
+ LOG_FORMATS("unable to read sect data %d/%d/%d\n", head, track, sector);
+
+ return false;
+ }
+
+ // Inital 15 zero bytes
+ for (int i = 0; i < 15; i++)
+ {
+ fm_reverse_byte_w(buf, 0);
+ }
+
+ // header (sync byte, volume, track, sector, checksum)
+ for (int i = 0; i < 5; i++)
+ {
+ fm_reverse_byte_w(buf, sector_meta_data[5 + i]);
+ }
+
+ // 12 zero bytes
+ for (int i = 0; i < 12; i++)
+ {
+ fm_reverse_byte_w(buf, 0);
+ }
+
+ // data sync byte
+ fm_reverse_byte_w(buf, sector_meta_data[10]);
+
+ // sector data
+ for (int i = 0; i < 256; i++)
+ {
+ fm_reverse_byte_w(buf, sector_data[i]);
+ }
+
+ // sector data checksum
+ fm_reverse_byte_w(buf, sector_meta_data[11]);
+
+ // trailing zero's until the next sector hole usually ~ 30 characters.
+ while (buf.size() < TRACK_SIZE / SECTORS_PER_TRACK * (sector + 1))
+ {
+ fm_reverse_byte_w(buf, 0);
+ }
+ }
+
+ generate_track_from_levels(track, head, buf, 0, image);
+ buf.clear();
+ }
+ }
+
+ return true;
+}
+
+void heath_h17d_format::fm_reverse_byte_w(std::vector<uint32_t> &buffer, uint8_t val) const
+{
+ constexpr unsigned char lookup[16] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf };
+
+ fm_w(buffer, 8, lookup[val & 0x0f] << 4 | lookup[val >> 4], BITCELL_SIZE);
+}
+
+const heath_h17d_format FLOPPY_H17D_FORMAT;
diff --git a/src/lib/formats/h17disk.h b/src/lib/formats/h17disk.h
new file mode 100644
index 00000000000..19ac0bac052
--- /dev/null
+++ b/src/lib/formats/h17disk.h
@@ -0,0 +1,38 @@
+// license:BSD-3-Clause
+// copyright-holders:Mark Garlanger
+/*********************************************************************
+
+Heath h17disk disk image format
+
+The Heath hard-sectored disk format for the H8 and H89 systems with the
+H17 controller on the H8 and the H-88-1 controller on the H89.
+
+*********************************************************************/
+#ifndef MAME_FORMATS_H17DISK_H
+#define MAME_FORMATS_H17DISK_H
+
+#pragma once
+
+#include "flopimg.h"
+
+class heath_h17d_format : public floppy_image_format_t
+{
+public:
+ heath_h17d_format();
+
+ int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+
+ const char *name() const noexcept override { return "h17disk"; }
+ const char *description() const noexcept override { return "Heath H17D disk image"; }
+ const char *extensions() const noexcept override { return "h17,h17d,h17disk"; }
+ bool supports_save() const noexcept override { return false; }
+
+protected:
+
+ void fm_reverse_byte_w(std::vector<uint32_t> &buffer, uint8_t val) const;
+};
+
+extern const heath_h17d_format FLOPPY_H17D_FORMAT;
+
+#endif // MAME_FORMATS_H17DISK_H
diff --git a/src/lib/formats/h8_cas.cpp b/src/lib/formats/h8_cas.cpp
new file mode 100644
index 00000000000..526e411d5ae
--- /dev/null
+++ b/src/lib/formats/h8_cas.cpp
@@ -0,0 +1,96 @@
+// license:BSD-3-Clause
+// copyright-holders:Robbbert,Mark Garlanger
+/********************************************************************
+
+Support for Heathkit H8/H88 H8T cassette images
+
+
+Standard Kansas City format (300 baud)
+
+TODO - investigate 1200 buad support, H8 should support it, but H88 does not.
+
+We output a leader, followed by the contents of the H8T file.
+
+********************************************************************/
+
+#include "h8_cas.h"
+
+#include "coretmpl.h" // BIT
+
+
+namespace {
+
+constexpr double ONE_FREQ = 1200.0;
+constexpr double ONE_FREQ_VARIANCE = 300.0;
+constexpr double ZERO_FREQ = 2400.0;
+constexpr double ZERO_FREQ_VARIANCE = 600.0;
+
+const cassette_image::Modulation heath_h8t_modulation =
+{
+ cassette_image::MODULATION_SINEWAVE,
+ ONE_FREQ - ONE_FREQ_VARIANCE, ONE_FREQ, ONE_FREQ + ONE_FREQ_VARIANCE,
+ ZERO_FREQ - ZERO_FREQ_VARIANCE, ZERO_FREQ, ZERO_FREQ + ZERO_FREQ_VARIANCE
+};
+
+cassette_image::error heath_h8t_identify(cassette_image *cassette, cassette_image::Options *opts)
+{
+ return cassette->modulation_identify(heath_h8t_modulation, opts);
+}
+
+
+cassette_image::error heath_h8t_load(cassette_image *cassette)
+{
+ cassette_image::error err = cassette_image::error::SUCCESS;
+ uint64_t image_size = cassette->image_size();
+ double time_index = 0.0;
+ double time_displacement;
+
+ auto const MODULATE =
+ [&cassette, &err, &time_index, &time_displacement] (unsigned value)
+ {
+ for (int i = 0; (i < (value ? 8 : 4)); i++)
+ {
+ err = cassette->put_modulated_data_bit(0, time_index, value, heath_h8t_modulation, &time_displacement);
+ if (cassette_image::error::SUCCESS == err)
+ time_index += time_displacement;
+ else
+ return;
+ }
+ };
+
+ // leader - 1 second
+ while ((cassette_image::error::SUCCESS == err) && (time_index < 1.0))
+ MODULATE(1);
+
+ for (uint64_t image_pos = 0; (cassette_image::error::SUCCESS == err) && (image_pos < image_size); image_pos++)
+ {
+ uint8_t data = cassette->image_read_byte(image_pos);
+
+ // start bit
+ MODULATE(0);
+
+ // data bits
+ for (int bit = 0; (cassette_image::error::SUCCESS == err) && (bit < 8); bit++)
+ MODULATE(util::BIT(data, bit));
+
+ // stop bit
+ if (cassette_image::error::SUCCESS == err)
+ MODULATE(1);
+ }
+
+ return err;
+}
+
+const cassette_image::Format heath_h8t_format =
+{
+ "h8t",
+ heath_h8t_identify,
+ heath_h8t_load,
+ nullptr
+};
+
+}
+
+CASSETTE_FORMATLIST_START( h8_cassette_formats )
+ CASSETTE_FORMAT( heath_h8t_format )
+CASSETTE_FORMATLIST_END
diff --git a/src/lib/formats/h8_cas.h b/src/lib/formats/h8_cas.h
new file mode 100644
index 00000000000..9c7ea02104a
--- /dev/null
+++ b/src/lib/formats/h8_cas.h
@@ -0,0 +1,19 @@
+// license:BSD-3-Clause
+// copyright-holders:Robbbert,Mark Garlanger
+/*********************************************************************
+
+ h8_cas.h
+
+ Format code for Heathkit H8/H88 H8T cassette images
+
+*********************************************************************/
+#ifndef MAME_FORMATS_H8_CAS_H
+#define MAME_FORMATS_H8_CAS_H
+
+#pragma once
+
+#include "cassimg.h"
+
+CASSETTE_FORMATLIST_EXTERN(h8_cassette_formats);
+
+#endif // MAME_FORMATS_H8_CAS_H
diff --git a/src/lib/formats/hect_dsk.cpp b/src/lib/formats/hect_dsk.cpp
index 4cd36008540..d1fa695ed57 100644
--- a/src/lib/formats/hect_dsk.cpp
+++ b/src/lib/formats/hect_dsk.cpp
@@ -8,10 +8,10 @@
*********************************************************************/
-#include <assert.h>
+#include "hect_dsk.h"
+#include "basicdsk.h"
-#include "formats/hect_dsk.h"
-#include "formats/basicdsk.h"
+#include <cstring>
/*****************************************************************************/
/****** Management of the floppy images 200Ko and 800Ko *********************/
diff --git a/src/lib/formats/hect_dsk.h b/src/lib/formats/hect_dsk.h
index c8c185d316b..7a373b6b1f3 100644
--- a/src/lib/formats/hect_dsk.h
+++ b/src/lib/formats/hect_dsk.h
@@ -12,7 +12,7 @@
#pragma once
-#include "flopimg.h"
+#include "flopimg_legacy.h"
/**************************************************************************/
diff --git a/src/lib/formats/hect_tap.cpp b/src/lib/formats/hect_tap.cpp
index 4f823fb24e5..cef1a730875 100644
--- a/src/lib/formats/hect_tap.cpp
+++ b/src/lib/formats/hect_tap.cpp
@@ -14,7 +14,6 @@ You can find some *.K7 file on serveral server in France
Updated 3/1/10 : use real value for timing.
********************************************************************/
-#include <assert.h>
#include "hect_tap.h"
@@ -24,7 +23,7 @@ Updated 3/1/10 : use real value for timing.
#define SMPHI 32767
-static int cas_size;
+static int cas_size; // FIXME: global variable prevents multiple instances
enum
@@ -122,7 +121,7 @@ static int hector_handle_tap(int16_t *buffer, const uint8_t *casdata)
if (data_pos>1)
previous_block = casdata[data_pos-1];
- /* Handle block lenght on tape data */
+ /* Handle block length on tape data */
block_size = casdata[data_pos] ;
if (block_size==0)
block_size=256;
@@ -174,8 +173,8 @@ static int hector_handle_forth_tap(int16_t *buffer, const uint8_t *casdata)
/* 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*/
+ /* Handle block length on tape data */
+ block_size = 822 ; /* Fixed size for the forth */
/*block_count=0;*/
@@ -204,7 +203,7 @@ static int hector_handle_forth_tap(int16_t *buffer, const uint8_t *casdata)
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int hector_tap_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int hector_tap_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return hector_handle_tap( buffer, bytes );
}
@@ -223,7 +222,7 @@ static int hector_tap_forth_to_wav_size(const uint8_t *casdata, int caslen)
/*******************************************************************
Generate samples for the tape image FORTH
********************************************************************/
-static int hector_tap_forth_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int hector_tap_forth_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return hector_handle_forth_tap( buffer, bytes ); //forth removed here !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
@@ -240,7 +239,7 @@ static int hector_tap_to_wav_size(const uint8_t *casdata, int caslen)
}
-static const struct CassetteLegacyWaveFiller hector_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller hector_legacy_fill_wave =
{
hector_tap_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -251,7 +250,7 @@ static const struct CassetteLegacyWaveFiller hector_legacy_fill_wave =
0 /* trailer_samples */
};
-static const struct CassetteLegacyWaveFiller hector_forth_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller hector_forth_legacy_fill_wave =
{
hector_tap_forth_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -263,30 +262,30 @@ static const struct CassetteLegacyWaveFiller hector_forth_legacy_fill_wave =
};
-static cassette_image::error hector_k7_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error hector_k7_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &hector_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &hector_legacy_fill_wave);
}
static cassette_image::error hector_k7_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &hector_legacy_fill_wave);
+ return cassette->legacy_construct(&hector_legacy_fill_wave);
}
-static cassette_image::error hector_k7forth_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error hector_k7forth_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &hector_forth_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &hector_forth_legacy_fill_wave);
}
static cassette_image::error hector_k7forth_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &hector_forth_legacy_fill_wave);
+ return cassette->legacy_construct(&hector_forth_legacy_fill_wave);
}
-static const struct CassetteFormat hector_k7_format =
+static const cassette_image::Format hector_k7_format =
{
"k7,cin",
hector_k7_identify,
@@ -294,7 +293,7 @@ static const struct CassetteFormat hector_k7_format =
nullptr
};
-static const struct CassetteFormat hector_k7Forth_format =
+static const cassette_image::Format hector_k7Forth_format =
{
"for",
hector_k7forth_identify,
diff --git a/src/lib/formats/hector_minidisc.cpp b/src/lib/formats/hector_minidisc.cpp
index fe4a7ee44b1..9c56c1d12ae 100644
--- a/src/lib/formats/hector_minidisc.cpp
+++ b/src/lib/formats/hector_minidisc.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -14,17 +14,17 @@ hmd_format::hmd_format() : upd765_format(formats)
{
}
-const char *hmd_format::name() const
+const char *hmd_format::name() const noexcept
{
return "hector_minidisc";
}
-const char *hmd_format::description() const
+const char *hmd_format::description() const noexcept
{
return "Hector Minidisc disk image";
}
-const char *hmd_format::extensions() const
+const char *hmd_format::extensions() const noexcept
{
return "hmd";
}
@@ -38,4 +38,4 @@ const hmd_format::format hmd_format::formats[] =
{}
};
-const floppy_format_type FLOPPY_HMD_FORMAT = &floppy_image_format_creator<hmd_format>;
+const hmd_format FLOPPY_HMD_FORMAT;
diff --git a/src/lib/formats/hector_minidisc.h b/src/lib/formats/hector_minidisc.h
index a47fbea49d8..dbae1f5eff7 100644
--- a/src/lib/formats/hector_minidisc.h
+++ b/src/lib/formats/hector_minidisc.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -19,14 +19,14 @@ class hmd_format : public upd765_format
public:
hmd_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_HMD_FORMAT;
+extern const hmd_format FLOPPY_HMD_FORMAT;
#endif // MAME_FORMATS_HECTOR_MINIDISC_H
diff --git a/src/lib/formats/hp300_dsk.cpp b/src/lib/formats/hp300_dsk.cpp
new file mode 100644
index 00000000000..99c3c20f283
--- /dev/null
+++ b/src/lib/formats/hp300_dsk.cpp
@@ -0,0 +1,48 @@
+// license:BSD-3-Clause
+// copyright-holders:Sven Schnelle
+/*********************************************************************
+
+ formats/hp300_dsk.cpp
+
+ HP 9000/300 disk format
+
+*********************************************************************/
+
+#include "formats/hp300_dsk.h"
+
+hp300_format::hp300_format() : wd177x_format(formats)
+{
+}
+
+const char *hp300_format::name() const noexcept
+{
+ return "hp300";
+}
+
+const char *hp300_format::description() const noexcept
+{
+ return "HP 9000/300 disk image";
+}
+
+const char *hp300_format::extensions() const noexcept
+{
+ return "img";
+}
+
+const hp300_format::format hp300_format::formats[] = {
+ { floppy_image::FF_35, floppy_image::SSDD, floppy_image::MFM, 2000, 16, 66, 1, 256, {}, 0, {}, 32, 22, 46 }, // FORMAT 4
+ // HP 9121S, 9121D or 9133A
+ { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 16, 77, 2, 256, {}, 1, {}, 50, 22, 54 }, // FORMAT 0,1
+ { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 9, 77, 2, 512, {}, 1, {}, 50, 22, 89 }, // FORMAT 2
+ { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 5, 77, 2, 1024, {}, 1, {}, 50, 22, 108 }, // FORMAT 3
+ { floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM, 2000, 9, 80, 2, 512, {}, 1, {}, 146, 22, 81 }, // FORMAT 16
+ // HP9122C/D, 9123D, 9133D/H/L or 9153A/B
+ { floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM, 2000, 32, 77, 2, 256, {}, 1, {}, 50, 22, 59 }, // FORMAT 0,1,4
+ { floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM, 2000, 18, 77, 2, 512, {}, 1, {}, 50, 22, 97 }, // FORMAT 2
+ { floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM, 2000, 10, 77, 2, 1024, {}, 1, {}, 50, 22, 123 }, // FORMAT 3
+ { floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM, 2000, 18, 80, 2, 512, {}, 1, {}, 146, 22, 98 }, // FORMAT 16
+ {}
+};
+
+const hp300_format FLOPPY_HP300_FORMAT;
+
diff --git a/src/lib/formats/hp300_dsk.h b/src/lib/formats/hp300_dsk.h
new file mode 100644
index 00000000000..eb51d6c4543
--- /dev/null
+++ b/src/lib/formats/hp300_dsk.h
@@ -0,0 +1,32 @@
+// license:BSD-3-Clause
+// copyright-holders:Sven Schnelle
+/*********************************************************************
+
+ formats/hp300_dsk.h
+
+ HP9000/300 disk format
+
+*********************************************************************/
+#ifndef MAME_FORMATS_HP300_DSK_H
+#define MAME_FORMATS_HP300_DSK_H
+
+#pragma once
+
+#include "wd177x_dsk.h"
+
+class hp300_format : public wd177x_format
+{
+public:
+ hp300_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const format formats[];
+};
+
+extern const hp300_format FLOPPY_HP300_FORMAT;
+
+#endif // MAME_FORMATS_HP300_DSK_H
diff --git a/src/lib/formats/hp_ipc_dsk.cpp b/src/lib/formats/hp_ipc_dsk.cpp
index 8b9efce0120..0d88a18d156 100644
--- a/src/lib/formats/hp_ipc_dsk.cpp
+++ b/src/lib/formats/hp_ipc_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Sergey Svishchev
/*********************************************************************
- formats/hp_ipc_dsk.c
+ formats/hp_ipc_dsk.cpp
HP Integral PC format
*********************************************************************/
-#include <assert.h>
-
#include "formats/hp_ipc_dsk.h"
hp_ipc_format::hp_ipc_format() : wd177x_format(formats)
{
}
-const char *hp_ipc_format::name() const
+const char *hp_ipc_format::name() const noexcept
{
return "hp_ipc";
}
-const char *hp_ipc_format::description() const
+const char *hp_ipc_format::description() const noexcept
{
return "HP Integral PC disk image";
}
-const char *hp_ipc_format::extensions() const
+const char *hp_ipc_format::extensions() const noexcept
{
return "img";
}
@@ -38,5 +36,5 @@ const hp_ipc_format::format hp_ipc_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_HP_IPC_FORMAT = &floppy_image_format_creator<hp_ipc_format>;
+const hp_ipc_format FLOPPY_HP_IPC_FORMAT;
diff --git a/src/lib/formats/hp_ipc_dsk.h b/src/lib/formats/hp_ipc_dsk.h
index 9643b853230..442150adce7 100644
--- a/src/lib/formats/hp_ipc_dsk.h
+++ b/src/lib/formats/hp_ipc_dsk.h
@@ -19,14 +19,14 @@ class hp_ipc_format : public wd177x_format
public:
hp_ipc_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_HP_IPC_FORMAT;
+extern const hp_ipc_format FLOPPY_HP_IPC_FORMAT;
#endif // MAME_FORMATS_HP_IPC_DSK_H
diff --git a/src/lib/formats/hpi_dsk.cpp b/src/lib/formats/hpi_dsk.cpp
index 50d2653280d..952ec0a2709 100644
--- a/src/lib/formats/hpi_dsk.cpp
+++ b/src/lib/formats/hpi_dsk.cpp
@@ -45,12 +45,17 @@
*********************************************************************/
-#include "emu.h"
#include "hpi_dsk.h"
+#include "coretmpl.h" // BIT
+#include "ioprocs.h"
+
+#include "osdcore.h" // osd_printf_*
+
+
// Debugging
#define VERBOSE 0
-#define LOG(...) do { if (VERBOSE) printf(__VA_ARGS__); } while (false)
+#define LOG(...) do { if (VERBOSE) osd_printf_info(__VA_ARGS__); } while (false)
constexpr unsigned IL_OFFSET = 0x12; // Position of interleave factor in HPI image (2 bytes, big-endian)
constexpr unsigned DEFAULT_IL = 7; // Default interleaving factor
@@ -63,22 +68,18 @@ constexpr uint32_t DATA_CD_PATTERN = 0x55552a44; // C/D pattern of 0xff + DAT
constexpr unsigned GAP1_SIZE = 17; // Size of gap 1 (+1)
constexpr unsigned GAP2_SIZE = 35; // Size of gap 2 (+1)
constexpr int ID_DATA_OFFSET = 30 * 16; // Nominal distance (in cells) between ID & DATA AM
-// Size of image file (holding 77 cylinders)
-constexpr unsigned HPI_IMAGE_SIZE = HPI_TRACKS * HPI_HEADS * HPI_SECTORS * HPI_SECTOR_SIZE;
constexpr unsigned HPI_RED_TRACKS = 75; // Reduced number of tracks
-constexpr unsigned HPI_9885_TRACKS = 67; // Tracks visible to HP9885 drives
-// Size of reduced image file (holding 75 cylinders)
-constexpr unsigned HPI_RED_IMAGE_SIZE = HPI_RED_TRACKS * HPI_HEADS * HPI_SECTORS * HPI_SECTOR_SIZE;
hpi_format::hpi_format()
{
- (void)HPI_IMAGE_SIZE;
- (void)HPI_RED_IMAGE_SIZE;
}
-int hpi_format::identify(io_generic *io, uint32_t form_factor)
+int hpi_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size)) {
+ return 0;
+ }
// we try to stay back and give only 50 points, since another image
// format may also have images of the same size (there is no header and no
@@ -87,7 +88,7 @@ int hpi_format::identify(io_generic *io, uint32_t form_factor)
unsigned dummy_tracks;
if (((form_factor == floppy_image::FF_8) || (form_factor == floppy_image::FF_UNKNOWN)) &&
geometry_from_size(size , dummy_heads , dummy_tracks)) {
- return 50;
+ return FIFID_SIZE;
} else {
return 0;
}
@@ -140,26 +141,31 @@ bool hpi_format::geometry_from_size(uint64_t image_size , unsigned& heads , unsi
}
}
-bool hpi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool hpi_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
unsigned heads;
unsigned cylinders;
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size)) {
+ return false;
+ }
if (!geometry_from_size(size, heads, cylinders)) {
return false;
}
int max_tracks;
int max_heads;
- image->get_maximal_geometry(max_tracks , max_heads);
+ image.get_maximal_geometry(max_tracks , max_heads);
if (cylinders > max_tracks || heads > max_heads) {
return false;
}
- image->set_variant(heads == 2 ? floppy_image::DSDD : floppy_image::SSDD);
+ image.set_variant(heads == 2 ? floppy_image::DSDD : floppy_image::SSDD);
// Suck in the whole image
- std::vector<uint8_t> image_data(size);
- io_generic_read(io, (void *)image_data.data(), 0, size);
+ auto const [err, image_data, actual] = read_at(io, 0, size);
+ if (err || (actual != size)) {
+ return false;
+ }
// Get interleave factor from image
unsigned il = (unsigned)image_data[ IL_OFFSET ] * 256 + image_data[ IL_OFFSET + 1 ];
@@ -189,24 +195,22 @@ bool hpi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-bool hpi_format::save(io_generic *io, floppy_image *image)
+bool hpi_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
int tracks;
int heads;
- image->get_actual_geometry(tracks, heads);
+ image.get_actual_geometry(tracks, heads);
for (int cyl = 0; cyl < tracks; cyl++) {
for (int head = 0; head < heads; head++) {
- uint8_t bitstream[ 21000 ];
- int bitstream_size;
- generate_bitstream_from_track(cyl , head , CELL_SIZE , bitstream , bitstream_size , image , 0);
+ auto bitstream = generate_bitstream_from_track(cyl , head , CELL_SIZE , image , 0);
int pos = 0;
unsigned track_no , head_no , sector_no;
uint8_t sector_data[ HPI_SECTOR_SIZE ];
- while (get_next_sector(bitstream , bitstream_size , pos , track_no , head_no , sector_no , sector_data)) {
+ while (get_next_sector(bitstream , pos , track_no , head_no , sector_no , sector_data)) {
if (track_no == cyl && head_no == head && sector_no < HPI_SECTORS) {
unsigned offset_in_image = chs_to_lba(cyl, head, sector_no, heads) * HPI_SECTOR_SIZE;
- io_generic_write(io, sector_data, offset_in_image, HPI_SECTOR_SIZE);
+ /*auto const [err, actual] =*/ write_at(io, offset_in_image, sector_data, HPI_SECTOR_SIZE); // FIXME: check for errors
}
}
}
@@ -214,22 +218,22 @@ bool hpi_format::save(io_generic *io, floppy_image *image)
return true;
}
-const char *hpi_format::name() const
+const char *hpi_format::name() const noexcept
{
return "hpi";
}
-const char *hpi_format::description() const
+const char *hpi_format::description() const noexcept
{
return "HP9895A floppy disk image";
}
-const char *hpi_format::extensions() const
+const char *hpi_format::extensions() const noexcept
{
return "hpi";
}
-bool hpi_format::supports_save() const
+bool hpi_format::supports_save() const noexcept
{
return true;
}
@@ -260,7 +264,7 @@ void hpi_format::write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit ,
void hpi_format::write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint8_t clock)
{
for (unsigned i = 0; i < 8; i++) {
- write_mmfm_bit(buffer , BIT(data , i) , BIT(clock , i));
+ write_mmfm_bit(buffer , util::BIT(data , i) , util::BIT(clock , i));
}
}
@@ -281,7 +285,7 @@ void hpi_format::write_crc(std::vector<uint32_t> &buffer , uint16_t crc)
{
// Note that CRC is stored with MSB (x^15) first
for (unsigned i = 0; i < 16; i++) {
- write_mmfm_bit(buffer , BIT(crc , 15 - i) , 0);
+ write_mmfm_bit(buffer , util::BIT(crc , 15 - i) , 0);
}
}
@@ -365,17 +369,16 @@ unsigned hpi_format::chs_to_lba(unsigned cylinder , unsigned head , unsigned sec
return sector + (head + cylinder * heads) * HPI_SECTORS;
}
-std::vector<uint8_t> hpi_format::get_next_id_n_block(const uint8_t *bitstream , int bitstream_size , int& pos , int& start_pos)
+std::vector<uint8_t> hpi_format::get_next_id_n_block(const std::vector<bool> &bitstream , int& pos , int& start_pos)
{
std::vector<uint8_t> res;
uint32_t sr = 0;
// Look for either sync + ID AM or sync + DATA AM
- while (pos < bitstream_size && sr != ID_CD_PATTERN && sr != DATA_CD_PATTERN) {
- bool bit = BIT(bitstream[ pos >> 3 ] , 7 - (pos & 7));
+ while (pos < bitstream.size() && sr != ID_CD_PATTERN && sr != DATA_CD_PATTERN) {
+ sr = (sr << 1) | bitstream[pos];
pos++;
- sr = (sr << 1) | bit;
}
- if (pos == bitstream_size) {
+ if (pos == bitstream.size()) {
// End of track reached
return res;
}
@@ -392,11 +395,11 @@ std::vector<uint8_t> hpi_format::get_next_id_n_block(const uint8_t *bitstream ,
}
// Align to data cells
pos++;
- for (unsigned i = 0; i < to_dump && pos < bitstream_size; i++) {
+ for (unsigned i = 0; i < to_dump && pos < bitstream.size(); i++) {
uint8_t byte = 0;
unsigned j;
- for (j = 0; j < 8 && pos < bitstream_size; j++) {
- bool bit = BIT(bitstream[ pos >> 3 ] , 7 - (pos & 7));
+ for (j = 0; j < 8 && pos < bitstream.size(); j++) {
+ bool bit = bitstream[pos];
pos += 2;
byte >>= 1;
if (bit) {
@@ -410,7 +413,7 @@ std::vector<uint8_t> hpi_format::get_next_id_n_block(const uint8_t *bitstream ,
return res;
}
-bool hpi_format::get_next_sector(const uint8_t *bitstream , int bitstream_size , int& pos , unsigned& track , unsigned& head , unsigned& sector , uint8_t *sector_data)
+bool hpi_format::get_next_sector(const std::vector<bool> &bitstream , int& pos , unsigned& track , unsigned& head , unsigned& sector , uint8_t *sector_data)
{
std::vector<uint8_t> block;
while (true) {
@@ -418,7 +421,7 @@ bool hpi_format::get_next_sector(const uint8_t *bitstream , int bitstream_size ,
int id_pos = 0;
while (true) {
if (block.size() == 0) {
- block = get_next_id_n_block(bitstream , bitstream_size , pos , id_pos);
+ block = get_next_id_n_block(bitstream , pos , id_pos);
if (block.size() == 0) {
return false;
}
@@ -434,7 +437,7 @@ bool hpi_format::get_next_sector(const uint8_t *bitstream , int bitstream_size ,
}
// Then for DATA block
int data_pos = 0;
- block = get_next_id_n_block(bitstream , bitstream_size , pos , data_pos);
+ block = get_next_id_n_block(bitstream , pos , data_pos);
if (block.size() == 0) {
return false;
}
@@ -487,4 +490,4 @@ const uint8_t hpi_format::m_track_skew[ HPI_SECTORS - 1 ][ HPI_HEADS ] = {
{ 0x00 , 0x00 } // Interleave = 29
};
-const floppy_format_type FLOPPY_HPI_FORMAT = &floppy_image_format_creator<hpi_format>;
+const hpi_format FLOPPY_HPI_FORMAT;
diff --git a/src/lib/formats/hpi_dsk.h b/src/lib/formats/hpi_dsk.h
index 565a22dea58..9b15b382a4d 100644
--- a/src/lib/formats/hpi_dsk.h
+++ b/src/lib/formats/hpi_dsk.h
@@ -17,42 +17,43 @@
#include <array>
#include <vector>
-// Geometry constants
-constexpr unsigned HPI_TRACKS = 77;
-constexpr unsigned HPI_HEADS = 2;
-constexpr unsigned HPI_SECTORS = 30;
-constexpr unsigned HPI_SECTOR_SIZE = 256;
-
class hpi_format : public floppy_image_format_t
{
public:
+ // Geometry constants
+ static constexpr unsigned HPI_TRACKS = 77;
+ static constexpr unsigned HPI_9885_TRACKS = 67; // Tracks visible to HP9885 drives
+ static constexpr unsigned HPI_HEADS = 2;
+ static constexpr unsigned HPI_SECTORS = 30;
+ static constexpr unsigned HPI_SECTOR_SIZE = 256;
+
hpi_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
private:
typedef std::array<uint8_t , HPI_SECTORS> sector_list_t;
static bool geometry_from_size(uint64_t image_size , unsigned& heads , unsigned& tracks);
static void interleaved_sectors(unsigned il_factor , sector_list_t& sector_list);
- void write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit);
- void write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint8_t clock = 0);
- void write_sync(std::vector<uint32_t> &buffer);
- void write_crc(std::vector<uint32_t> &buffer , uint16_t crc);
- void write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_head_no , const uint8_t *sect_data);
- void fill_with_gap3(std::vector<uint32_t> &buffer);
+ static void write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit);
+ static void write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint8_t clock = 0);
+ static void write_sync(std::vector<uint32_t> &buffer);
+ static void write_crc(std::vector<uint32_t> &buffer , uint16_t crc);
+ static void write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_head_no , const uint8_t *sect_data);
+ static void fill_with_gap3(std::vector<uint32_t> &buffer);
static unsigned chs_to_lba(unsigned cylinder , unsigned head , unsigned sector , unsigned heads);
- std::vector<uint8_t> get_next_id_n_block(const uint8_t *bitstream , int bitstream_size , int& pos , int& start_pos);
- bool get_next_sector(const uint8_t *bitstream , int bitstream_size , int& pos , unsigned& track , unsigned& head , unsigned& sector , uint8_t *sector_data);
+ static std::vector<uint8_t> get_next_id_n_block(const std::vector<bool> &bitstream , int& pos , int& start_pos);
+ static bool get_next_sector(const std::vector<bool> &bitstream , int& pos , unsigned& track , unsigned& head , unsigned& sector , uint8_t *sector_data);
static const uint8_t m_track_skew[ HPI_SECTORS - 1 ][ HPI_HEADS ];
};
-extern const floppy_format_type FLOPPY_HPI_FORMAT;
+extern const hpi_format FLOPPY_HPI_FORMAT;
#endif // MAME_FORMATS_HPI_DSK_H
diff --git a/src/lib/formats/hti_tape.cpp b/src/lib/formats/hti_tape.cpp
index 29bab6cd51c..7d6119860f1 100644
--- a/src/lib/formats/hti_tape.cpp
+++ b/src/lib/formats/hti_tape.cpp
@@ -8,11 +8,15 @@
#include "hti_tape.h"
-#include "imageutl.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <tuple>
static constexpr uint32_t OLD_FILE_MAGIC = 0x5441434f; // Magic value at start of old-format image file: "TACO"
-static constexpr uint32_t FILE_MAGIC = 0x48544930; // Magic value at start of image file: "HTI0"
+static constexpr uint32_t FILE_MAGIC_DELTA = 0x48544930; // Magic value at start of delta-modulation image file: "HTI0"
+static constexpr uint32_t FILE_MAGIC_MANCHESTER = 0x48544931; // Magic value at start of manchester-modulation image file: "HTI1"
// *** Position of tape holes ***
// At beginning of tape:
@@ -41,25 +45,32 @@ static const hti_format_t::tape_pos_t tape_holes[] = {
};
hti_format_t::hti_format_t()
- : m_bits_per_word(16)
+ : m_img_format(HTI_DELTA_MOD_16_BITS)
{
clear_tape();
}
-bool hti_format_t::load_tape(io_generic *io)
+bool hti_format_t::load_tape(util::random_read &io)
{
- uint8_t tmp[ 4 ];
+ if (io.seek(0, SEEK_SET)) {
+ return false;
+ }
- io_generic_read(io, tmp, 0, 4);
- auto magic = pick_integer_be(tmp , 0 , 4);
- if (magic != FILE_MAGIC && magic != OLD_FILE_MAGIC) {
+ uint8_t tmp[4];
+ auto const [err, actual] = read(io, tmp, 4);
+ if (err || (4 != actual)) {
return false;
}
- uint64_t offset = 4;
+ auto magic = get_u32be(tmp);
+ if (((m_img_format == HTI_DELTA_MOD_16_BITS || m_img_format == HTI_DELTA_MOD_17_BITS) && magic != FILE_MAGIC_DELTA && magic != OLD_FILE_MAGIC) ||
+ (m_img_format == HTI_MANCHESTER_MOD && magic != FILE_MAGIC_MANCHESTER)) {
+ return false;
+ }
- for (tape_track_t& track : m_tracks) {
- if (!load_track(io , offset , track , magic == OLD_FILE_MAGIC)) {
+ for (unsigned i = 0; i < no_of_tracks(); i++) {
+ tape_track_t &track = m_tracks[i];
+ if (!load_track(io, track, magic == OLD_FILE_MAGIC)) {
clear_tape();
return false;
}
@@ -68,46 +79,46 @@ bool hti_format_t::load_tape(io_generic *io)
return true;
}
-void hti_format_t::save_tape(io_generic *io)
+void hti_format_t::save_tape(util::random_read_write &io)
{
- uint8_t tmp[ 4 ];
+ io.seek(0, SEEK_SET); // FIXME: check for errors
- place_integer_be(tmp, 0, 4, FILE_MAGIC);
- io_generic_write(io, tmp, 0, 4);
+ uint8_t tmp[4];
- uint64_t offset = 4;
+ put_u32be(tmp, m_img_format == HTI_MANCHESTER_MOD ? FILE_MAGIC_MANCHESTER : FILE_MAGIC_DELTA);
+ write(io, tmp, 4); // FIXME: check for errors
- for (const tape_track_t& track : m_tracks) {
- tape_pos_t next_pos = (tape_pos_t)-1;
+ for (unsigned i = 0; i < no_of_tracks(); i++) {
+ const tape_track_t &track = m_tracks[i];
+ tape_pos_t next_pos = tape_pos_t(-1);
unsigned n_words = 0;
tape_track_t::const_iterator it_start;
for (tape_track_t::const_iterator it = track.cbegin(); it != track.cend(); ++it) {
if (it->first != next_pos) {
- dump_sequence(io , offset , it_start , n_words);
+ dump_sequence(io, it_start, n_words);
it_start = it;
n_words = 0;
}
next_pos = it->first + word_length(it->second);
n_words++;
}
- dump_sequence(io , offset , it_start , n_words);
+ dump_sequence(io, it_start, n_words);
// End of track
- place_integer_le(tmp, 0, 4, (uint32_t)-1);
- io_generic_write(io, tmp, offset, 4);
- offset += 4;
+ put_u32le(tmp, (uint32_t)-1);
+ write(io, tmp, 4); // FIXME: check for errors
}
}
void hti_format_t::clear_tape()
{
- for (tape_track_t& track : m_tracks) {
+ for (tape_track_t &track : m_tracks) {
track.clear();
}
}
-hti_format_t::tape_pos_t hti_format_t::word_length(tape_word_t w)
+hti_format_t::tape_pos_t hti_format_t::word_length(tape_word_t w) const
{
- unsigned zeros , ones;
+ unsigned zeros, ones;
// pop count of w
ones = (w & 0x5555) + ((w >> 1) & 0x5555);
@@ -117,10 +128,10 @@ hti_format_t::tape_pos_t hti_format_t::word_length(tape_word_t w)
zeros = 16 - ones;
- return zeros * ZERO_BIT_LEN + ones * ONE_BIT_LEN;
+ return zeros * bit_length(false) + ones * bit_length(true);
}
-hti_format_t::tape_pos_t hti_format_t::farthest_end(const track_iterator_t& it , bool forward)
+hti_format_t::tape_pos_t hti_format_t::farthest_end(const track_iterator_t &it, bool forward) const
{
if (forward) {
return word_end_pos(it);
@@ -129,7 +140,7 @@ hti_format_t::tape_pos_t hti_format_t::farthest_end(const track_iterator_t& it ,
}
}
-bool hti_format_t::pos_offset(tape_pos_t& pos , bool forward , tape_pos_t offset)
+bool hti_format_t::pos_offset(tape_pos_t &pos, bool forward, tape_pos_t offset)
{
if (offset == 0) {
return true;
@@ -153,7 +164,7 @@ bool hti_format_t::pos_offset(tape_pos_t& pos , bool forward , tape_pos_t offset
}
}
-hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos , bool forward)
+hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos, bool forward)
{
if (forward) {
for (tape_pos_t hole : tape_holes) {
@@ -164,9 +175,9 @@ hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos , bool forward)
// No more holes: will hit end of tape
return NULL_TAPE_POS;
} else {
- for (int i = (sizeof(tape_holes) / sizeof(tape_holes[ 0 ])) - 1; i >= 0; i--) {
- if (tape_holes[ i ] < pos) {
- return tape_holes[ i ];
+ for (int i = (sizeof(tape_holes) / sizeof(tape_holes[0])) - 1; i >= 0; i--) {
+ if (tape_holes[i] < pos) {
+ return tape_holes[i];
}
}
// No more holes: will hit start of tape
@@ -174,45 +185,45 @@ hti_format_t::tape_pos_t hti_format_t::next_hole(tape_pos_t pos , bool forward)
}
}
-void hti_format_t::write_word(unsigned track_no , tape_pos_t start , tape_word_t word , tape_pos_t& length , bool forward)
+void hti_format_t::write_word(unsigned track_no, tape_pos_t start, tape_word_t word, tape_pos_t &length, bool forward)
{
- tape_track_t& track = m_tracks[ track_no ];
+ tape_track_t &track = m_tracks[track_no];
track_iterator_t it_low = track.lower_bound(start);
- adjust_it(track , it_low , start);
+ adjust_it(track, it_low, start);
length = word_length(word);
tape_pos_t end_pos = start + length;
track_iterator_t it_high = track.lower_bound(end_pos);
- track.erase(it_low , it_high);
+ track.erase(it_low, it_high);
// A 0 word is inserted after the word being written, if space allows.
// This is meant to avoid fragmentation of the slack space at the end of a record
// as the record expands & contracts when re-written with different content.
// Without this fix, a gap could form in the slack big enough to cause
// false gap detections.
- if (forward && it_high != track.end() && (it_high->first - end_pos) >= (ZERO_BIT_LEN * 16)) {
+ if (forward && it_high != track.end() && (it_high->first - end_pos) >= (bit_length(false) * 16)) {
track.insert(it_high, std::make_pair(end_pos, 0));
it_high--;
}
- track.insert(it_high , std::make_pair(start, word));
+ track.insert(it_high, std::make_pair(start, word));
}
-void hti_format_t::write_gap(unsigned track_no , tape_pos_t a , tape_pos_t b)
+void hti_format_t::write_gap(unsigned track_no, tape_pos_t a, tape_pos_t b)
{
- ensure_a_lt_b(a , b);
- tape_track_t& track = m_tracks[ track_no ];
+ ensure_a_lt_b(a, b);
+ tape_track_t &track = m_tracks[track_no];
track_iterator_t it_low = track.lower_bound(a);
- adjust_it(track , it_low , a);
+ adjust_it(track, it_low, a);
track_iterator_t it_high = track.lower_bound(b);
track.erase(it_low, it_high);
}
-bool hti_format_t::just_gap(unsigned track_no , tape_pos_t a , tape_pos_t b)
+bool hti_format_t::just_gap(unsigned track_no, tape_pos_t a, tape_pos_t b)
{
- ensure_a_lt_b(a , b);
- tape_track_t& track = m_tracks[ track_no ];
+ ensure_a_lt_b(a, b);
+ tape_track_t &track = m_tracks[track_no];
track_iterator_t it_low = track.lower_bound(a);
track_iterator_t it_high = track.lower_bound(b);
@@ -221,9 +232,9 @@ bool hti_format_t::just_gap(unsigned track_no , tape_pos_t a , tape_pos_t b)
return it_low == it_high;
}
-bool hti_format_t::next_data(unsigned track_no , tape_pos_t pos , bool forward , bool inclusive , track_iterator_t& it)
+bool hti_format_t::next_data(unsigned track_no, tape_pos_t pos, bool forward, bool inclusive, track_iterator_t &it)
{
- tape_track_t& track = m_tracks[ track_no ];
+ tape_track_t &track = m_tracks[track_no];
it = track.lower_bound(pos);
if (forward) {
if (inclusive) {
@@ -243,9 +254,9 @@ bool hti_format_t::next_data(unsigned track_no , tape_pos_t pos , bool forward ,
}
}
-hti_format_t::adv_res_t hti_format_t::adv_it(unsigned track_no , bool forward , track_iterator_t& it)
+hti_format_t::adv_res_t hti_format_t::adv_it(unsigned track_no, bool forward, track_iterator_t &it)
{
- tape_track_t& track = m_tracks[ track_no ];
+ tape_track_t &track = m_tracks[track_no];
if (forward) {
tape_pos_t prev_pos = word_end_pos(it);
++it;
@@ -267,7 +278,7 @@ hti_format_t::adv_res_t hti_format_t::adv_it(unsigned track_no , bool forward ,
}
}
-bool hti_format_t::sync_with_record(unsigned track_no , track_iterator_t& it , unsigned& bit_idx)
+bool hti_format_t::sync_with_record(unsigned track_no, track_iterator_t &it, unsigned &bit_idx)
{
while ((it->second & (1U << bit_idx)) == 0) {
if (bit_idx) {
@@ -288,7 +299,7 @@ bool hti_format_t::sync_with_record(unsigned track_no , track_iterator_t& it , u
return true;
}
-hti_format_t::adv_res_t hti_format_t::next_word(unsigned track_no , track_iterator_t& it , unsigned& bit_idx , tape_word_t& word)
+hti_format_t::adv_res_t hti_format_t::next_word(unsigned track_no, track_iterator_t &it, unsigned &bit_idx, tape_word_t &word)
{
if (bit_idx == 15) {
auto res = adv_it(track_no, true, it);
@@ -311,13 +322,13 @@ hti_format_t::adv_res_t hti_format_t::next_word(unsigned track_no , track_iterat
}
}
-bool hti_format_t::next_gap(unsigned track_no , tape_pos_t& pos , bool forward , tape_pos_t min_gap)
+bool hti_format_t::next_gap(unsigned track_no, tape_pos_t &pos, bool forward, tape_pos_t min_gap)
{
tape_track_t::iterator it;
// First align with next data
- next_data(track_no , pos , forward , true , it);
+ next_data(track_no, pos, forward, true, it);
// Then scan for 1st gap
- tape_track_t& track = m_tracks[ track_no ];
+ tape_track_t &track = m_tracks[track_no];
bool done = false;
track_iterator_t prev_it;
unsigned n_gaps = 1;
@@ -338,7 +349,7 @@ bool hti_format_t::next_gap(unsigned track_no , tape_pos_t& pos , bool forward ,
adv_res_t adv_res;
do {
prev_it = it;
- adv_res = adv_it(track_no , forward , it);
+ adv_res = adv_it(track_no, forward, it);
} while (adv_res == ADV_CONT_DATA);
pos = word_end_pos(prev_it);
}
@@ -358,33 +369,37 @@ bool hti_format_t::next_gap(unsigned track_no , tape_pos_t& pos , bool forward ,
adv_res_t adv_res;
do {
prev_it = it;
- adv_res = adv_it(track_no , forward , it);
+ adv_res = adv_it(track_no, forward, it);
} while (adv_res == ADV_CONT_DATA);
pos = prev_it->first;
}
}
// Set "pos" where minimum gap size is met
- pos_offset(pos , forward , min_gap);
+ pos_offset(pos, forward, min_gap);
return n_gaps == 0;
}
-bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t& track , bool old_format)
+bool hti_format_t::load_track(util::random_read &io, tape_track_t &track, bool old_format)
{
- uint8_t tmp[ 4 ];
- uint32_t tmp32;
tape_pos_t delta_pos = 0;
tape_pos_t last_word_end = 0;
track.clear();
while (1) {
- // Read no. of words to follow
- io_generic_read(io, tmp, offset, 4);
- offset += 4;
+ std::error_condition err;
+ size_t actual;
+ uint8_t tmp[4];
+ uint32_t tmp32;
- tmp32 = pick_integer_le(tmp, 0, 4);
+ // Read no. of words to follow
+ std::tie(err, actual) = read(io, tmp, 4);
+ if (err || (4 != actual)) {
+ return false;
+ }
+ tmp32 = get_u32le(tmp);
// Track ends
if (tmp32 == (uint32_t)-1) {
@@ -394,10 +409,11 @@ bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t&
unsigned n_words = tmp32;
// Read tape position of block
- io_generic_read(io, tmp, offset, 4);
- offset += 4;
-
- tmp32 = pick_integer_le(tmp, 0, 4);
+ std::tie(err, actual) = read(io, tmp, 4);
+ if (err || (4 != actual)) {
+ return false;
+ }
+ tmp32 = get_u32le(tmp);
tape_pos_t pos = (tape_pos_t)tmp32 + delta_pos;
@@ -407,14 +423,16 @@ bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t&
for (unsigned i = 0; i < n_words; i++) {
uint16_t tmp16;
- io_generic_read(io, tmp, offset, 2);
- offset += 2;
- tmp16 = pick_integer_le(tmp, 0, 2);
+ std::tie(err, actual) = read(io, tmp, 2);
+ if (err || (2 != actual)) {
+ return false;
+ }
+ tmp16 = get_u16le(tmp);
if (!old_format) {
- track.insert(std::make_pair(pos , tmp16));
+ track.insert(std::make_pair(pos, tmp16));
pos += word_length(tmp16);
- } else if (m_bits_per_word == 16) {
+ } else if (m_img_format == HTI_DELTA_MOD_16_BITS) {
// Convert HP9845 & HP85 old format
// Basically, in old format each word had 17 bits (an implicit 1
// was added at the end). In new format we just keep the 16 bits
@@ -429,7 +447,7 @@ bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t&
track.insert(std::make_pair(pos, tmp16));
pos += word_length(tmp16);
last_word_end = pos;
- delta_pos -= ONE_BIT_LEN;
+ delta_pos -= DELTA_ONE_BIT_LEN;
} else {
// Convert HP9825 old format
// In moving from old to new format we make the 17th bit at the
@@ -458,37 +476,35 @@ bool hti_format_t::load_track(io_generic *io , uint64_t& offset , tape_track_t&
}
if (bits_in_accum) {
track.insert(std::make_pair(pos, word_accum));
- tape_pos_t shift = (tape_pos_t)(16 - bits_in_accum) * ZERO_BIT_LEN;
+ tape_pos_t shift = (tape_pos_t)(16 - bits_in_accum) * DELTA_ZERO_BIT_LEN;
delta_pos += shift;
last_word_end = pos + word_length(word_accum);
}
}
}
-void hti_format_t::dump_sequence(io_generic *io , uint64_t& offset , tape_track_t::const_iterator it_start , unsigned n_words)
+void hti_format_t::dump_sequence(util::random_read_write &io, tape_track_t::const_iterator it_start, unsigned n_words)
{
if (n_words) {
- uint8_t tmp[ 8 ];
- place_integer_le(tmp, 0, 4, n_words);
- place_integer_le(tmp, 4, 4, it_start->first);
- io_generic_write(io, tmp, offset, 8);
- offset += 8;
+ uint8_t tmp[8];
+ put_u32le(&tmp[0], n_words);
+ put_u32le(&tmp[4], it_start->first);
+ write(io, tmp, 8); // FIXME: check for errors
for (unsigned i = 0; i < n_words; i++) {
- place_integer_le(tmp, 0, 2, it_start->second);
- io_generic_write(io, tmp, offset, 2);
- offset += 2;
+ put_u16le(tmp, it_start->second);
+ write(io, tmp, 2); // FIXME: check for errors
++it_start;
}
}
}
-hti_format_t::tape_pos_t hti_format_t::word_end_pos(const track_iterator_t& it)
+hti_format_t::tape_pos_t hti_format_t::word_end_pos(const track_iterator_t &it) const
{
return it->first + word_length(it->second);
}
-void hti_format_t::adjust_it(tape_track_t& track , track_iterator_t& it , tape_pos_t pos)
+void hti_format_t::adjust_it(tape_track_t &track, track_iterator_t &it, tape_pos_t pos) const
{
if (it != track.begin()) {
--it;
@@ -498,7 +514,7 @@ void hti_format_t::adjust_it(tape_track_t& track , track_iterator_t& it , tape_p
}
}
-void hti_format_t::ensure_a_lt_b(tape_pos_t& a , tape_pos_t& b)
+void hti_format_t::ensure_a_lt_b(tape_pos_t &a, tape_pos_t &b)
{
if (a > b) {
// Ensure A always comes before B
diff --git a/src/lib/formats/hti_tape.h b/src/lib/formats/hti_tape.h
index 72eb85d55cc..d343133583f 100644
--- a/src/lib/formats/hti_tape.h
+++ b/src/lib/formats/hti_tape.h
@@ -4,8 +4,8 @@
"HTI" format
- Format of images of DC-100 tape cassettes as used in HP 9825,
- HP 9845 and HP 85 systems.
+ Format of images of DC-100 tape cassettes as used in HP 264x,
+ HP 9825, HP 9845 and HP 85 systems.
*********************************************************************/
#ifndef MAME_FORMATS_HTI_TAPE_H
@@ -13,15 +13,15 @@
#pragma once
-#include "ioprocs.h"
+#include "utilfwd.h"
+#include <cstdint>
#include <map>
+
class hti_format_t
{
public:
- hti_format_t();
-
// Tape position, 1 unit = 1 inch / (968 * 1024)
typedef int32_t tape_pos_t;
@@ -34,11 +34,15 @@ public:
// Tape length: 140 ft of usable tape + 72" of punched tape at either end
static constexpr tape_pos_t TAPE_LENGTH = (140 * 12 + 72 * 2) * ONE_INCH_POS;
+ // Length of bits in delta modulation
// Length of 0 bits at slow tape speed: 1/(35200 Hz)
- static constexpr tape_pos_t ZERO_BIT_LEN = 619;
-
+ static constexpr tape_pos_t DELTA_ZERO_BIT_LEN = 619;
// Length of 1 bits at slow tape speed: 1.75 times ZERO_BIT_LEN
- static constexpr tape_pos_t ONE_BIT_LEN = 1083;
+ static constexpr tape_pos_t DELTA_ONE_BIT_LEN = 1083;
+ // Length of bits in Manchester modulation
+ // By "bits" here we mean each of the halves of data bit cells
+ // Zeros & ones have same length
+ static constexpr tape_pos_t MANCHESTER_BIT_LEN = 621;
// Words stored on tape
typedef uint16_t tape_word_t;
@@ -49,68 +53,88 @@ public:
// Iterator to access words on tape
typedef tape_track_t::iterator track_iterator_t;
- // Set no. of bits per word (needed when loading old format)
- void set_bits_per_word(unsigned bits) { m_bits_per_word = bits; }
+ enum image_format_t {
+ // Delta modulation, 16 bits per word, 2 tracks per cartridge
+ // HP 9845 & HP 85
+ HTI_DELTA_MOD_16_BITS,
+ // Delta modulation, 17 bits per word, 2 tracks per cartridge
+ // HP 9825
+ HTI_DELTA_MOD_17_BITS,
+ // Manchester modulation, 1 track per cartridge
+ // HP 264x
+ HTI_MANCHESTER_MOD
+ };
+
+ enum adv_res_t
+ {
+ ADV_NO_MORE_DATA,
+ ADV_CONT_DATA,
+ ADV_DISCONT_DATA
+ };
+
+ hti_format_t();
+
+ // Set image format
+ void set_image_format(image_format_t fmt) { m_img_format = fmt; }
- bool load_tape(io_generic *io);
- void save_tape(io_generic *io);
+ // Return number of tracks
+ unsigned no_of_tracks() const { return m_img_format == HTI_MANCHESTER_MOD ? 1 : 2; }
+
+ bool load_tape(util::random_read &io);
+ void save_tape(util::random_read_write &io);
void clear_tape();
// Return physical length of a bit on tape
- static constexpr tape_pos_t bit_length(bool bit) { return bit ? ONE_BIT_LEN : ZERO_BIT_LEN; }
+ tape_pos_t bit_length(bool bit) const
+ {
+ return m_img_format == HTI_MANCHESTER_MOD ? MANCHESTER_BIT_LEN : (bit ? DELTA_ONE_BIT_LEN : DELTA_ZERO_BIT_LEN);
+ }
// Return physical length of a 16-bit word on tape
- static tape_pos_t word_length(tape_word_t w);
+ tape_pos_t word_length(tape_word_t w) const;
- static tape_pos_t farthest_end(const track_iterator_t& it , bool forward);
+ tape_pos_t farthest_end(const track_iterator_t &it, bool forward) const;
- static bool pos_offset(tape_pos_t& pos , bool forward , tape_pos_t offset);
+ static bool pos_offset(tape_pos_t &pos, bool forward, tape_pos_t offset);
// Position of next hole tape will reach in a given direction
- static tape_pos_t next_hole(tape_pos_t pos , bool forward);
+ static tape_pos_t next_hole(tape_pos_t pos, bool forward);
// Write a data word on tape
- void write_word(unsigned track_no , tape_pos_t start , tape_word_t word , tape_pos_t& length , bool forward = true);
+ void write_word(unsigned track_no, tape_pos_t start, tape_word_t word, tape_pos_t &length, bool forward = true);
// Write a gap on tape
- void write_gap(unsigned track_no , tape_pos_t a , tape_pos_t b);
+ void write_gap(unsigned track_no, tape_pos_t a, tape_pos_t b);
// Check that a section of tape has no data (it's just gap)
- bool just_gap(unsigned track_no , tape_pos_t a , tape_pos_t b);
+ bool just_gap(unsigned track_no, tape_pos_t a, tape_pos_t b);
// Return position of next data word in a given direction
- bool next_data(unsigned track_no , tape_pos_t pos , bool forward , bool inclusive , track_iterator_t& it);
-
- enum adv_res_t
- {
- ADV_NO_MORE_DATA,
- ADV_CONT_DATA,
- ADV_DISCONT_DATA
- };
+ bool next_data(unsigned track_no, tape_pos_t pos, bool forward, bool inclusive, track_iterator_t &it);
// Advance an iterator to next word of data
- adv_res_t adv_it(unsigned track_no , bool forward , track_iterator_t& it);
+ adv_res_t adv_it(unsigned track_no, bool forward, track_iterator_t &it);
// Sync with the preamble of a record
- bool sync_with_record(unsigned track_no , track_iterator_t& it , unsigned& bit_idx);
+ bool sync_with_record(unsigned track_no, track_iterator_t &it, unsigned &bit_idx);
// Get a data word from record, after syncing
- adv_res_t next_word(unsigned track_no , track_iterator_t& it , unsigned& bit_idx , tape_word_t& word);
+ adv_res_t next_word(unsigned track_no, track_iterator_t &it, unsigned &bit_idx, tape_word_t &word);
// Scan for beginning of next gap in a given direction
- bool next_gap(unsigned track_no , tape_pos_t& pos , bool forward , tape_pos_t min_gap);
+ bool next_gap(unsigned track_no, tape_pos_t &pos, bool forward, tape_pos_t min_gap);
private:
// Content of tape tracks
- tape_track_t m_tracks[ 2 ];
- // Bits per word in old format
- unsigned m_bits_per_word;
+ tape_track_t m_tracks[2];
+ // Image format
+ image_format_t m_img_format;
- bool load_track(io_generic *io , uint64_t& offset , tape_track_t& track , bool old_format);
- static void dump_sequence(io_generic *io , uint64_t& offset , tape_track_t::const_iterator it_start , unsigned n_words);
+ bool load_track(util::random_read &io, tape_track_t &track, bool old_format);
+ static void dump_sequence(util::random_read_write &io, tape_track_t::const_iterator it_start, unsigned n_words);
- static tape_pos_t word_end_pos(const track_iterator_t& it);
- static void adjust_it(tape_track_t& track , track_iterator_t& it , tape_pos_t pos);
- static void ensure_a_lt_b(tape_pos_t& a , tape_pos_t& b);
+ tape_pos_t word_end_pos(const track_iterator_t &it) const;
+ void adjust_it(tape_track_t &track, track_iterator_t &it, tape_pos_t pos) const;
+ static void ensure_a_lt_b(tape_pos_t &a, tape_pos_t &b);
};
#endif // MAME_FORMATS_HTI_TAPE_H
diff --git a/src/lib/formats/hxchfe_dsk.cpp b/src/lib/formats/hxchfe_dsk.cpp
index 3fa42793a01..da8d660bb33 100644
--- a/src/lib/formats/hxchfe_dsk.cpp
+++ b/src/lib/formats/hxchfe_dsk.cpp
@@ -55,7 +55,7 @@
Track data
----------
- (first possible occurance at 0x0400)
+ (first possible occurrence at 0x0400)
Each track is encoded in a sequence of cell levels which are represented
by bits in the data.
@@ -101,70 +101,67 @@
#include "hxchfe_dsk.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include "osdcore.h" // osd_printf_*
+
+#include <tuple>
+
+
#define HFE_FORMAT_HEADER "HXCPICFE"
#define HEADER_LENGTH 512
#define TRACK_TABLE_LENGTH 1024
-hfe_format::hfe_format() : floppy_image_format_t(),
- m_cylinders(0),
- m_heads(0),
- m_track_encoding(UNKNOWN_ENCODING),
- m_bit_rate(0),
- m_floppy_rpm(0),
- m_interface_mode(DISABLE_FLOPPYMODE),
- m_write_allowed(true),
- m_single_step(true),
- m_track0s0_has_altencoding(false),
- m_track0s0_encoding(UNKNOWN_ENCODING),
- m_track0s1_has_altencoding(false),
- m_track0s1_encoding(UNKNOWN_ENCODING),
- m_selected_mode(DISABLE_FLOPPYMODE),
- m_selected_encoding(UNKNOWN_ENCODING)
+hfe_format::hfe_format() : floppy_image_format_t()
{
}
-const char *hfe_format::name() const
+const char *hfe_format::name() const noexcept
{
return "hfe";
}
-const char *hfe_format::description() const
+const char *hfe_format::description() const noexcept
{
return "SDCard HxC Floppy Emulator HFE File format";
}
-const char *hfe_format::extensions() const
+const char *hfe_format::extensions() const noexcept
{
return "hfe";
}
-bool hfe_format::supports_save() const
+bool hfe_format::supports_save() const noexcept
{
- return true;
+ return false;
}
-int hfe_format::identify(io_generic *io, uint32_t form_factor)
+int hfe_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t header[8];
-
- io_generic_read(io, &header, 0, sizeof(header));
- if ( memcmp( header, HFE_FORMAT_HEADER, 8 ) ==0) {
- return 100;
+ auto const [err, actual] = read_at(io, 0, &header, sizeof(header));
+ if (err || (sizeof(header) != actual)) {
+ return 0;
+ }
+ if (!memcmp(header, HFE_FORMAT_HEADER, 8)) {
+ return FIFID_SIGN;
}
return 0;
}
-bool hfe_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool hfe_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint8_t header[HEADER_LENGTH];
- uint8_t track_table[TRACK_TABLE_LENGTH];
+ std::error_condition err;
+ size_t actual;
int drivecyl, driveheads;
- image->get_maximal_geometry(drivecyl, driveheads);
+ image.get_maximal_geometry(drivecyl, driveheads);
// read header
- io_generic_read(io, header, 0, HEADER_LENGTH);
+ uint8_t header[HEADER_LENGTH];
+ std::tie(err, actual) = read_at(io, 0, header, HEADER_LENGTH); // FIXME: check for errors and premature EOF
// get values
// Format revision must be 0
@@ -174,101 +171,102 @@ bool hfe_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return false;
}
- m_cylinders = header[9] & 0xff;
- m_heads = header[10] & 0xff;
+ header_info info;
+ info.m_cylinders = header[9] & 0xff;
+ info.m_heads = header[10] & 0xff;
- if (drivecyl < m_cylinders)
+ if (drivecyl < info.m_cylinders)
{
- if (m_cylinders - drivecyl > DUMP_THRESHOLD)
+ if (info.m_cylinders - drivecyl > DUMP_THRESHOLD)
{
- osd_printf_error("hxchfe: Floppy disk has too many tracks for this drive (floppy tracks=%d, drive tracks=%d).\n", m_cylinders, drivecyl);
+ osd_printf_error("hxchfe: Floppy disk has too many tracks for this drive (floppy tracks=%d, drive tracks=%d).\n", info.m_cylinders, drivecyl);
return false;
}
else
{
// Some dumps has a few excess tracks to be safe,
// lets be nice and just skip those tracks
- osd_printf_warning("hxchfe: Floppy disk has a slight excess of tracks for this drive that will be discarded (floppy tracks=%d, drive tracks=%d).\n", m_cylinders, drivecyl);
- m_cylinders = drivecyl;
+ osd_printf_warning("hxchfe: Floppy disk has a slight excess of tracks for this drive that will be discarded (floppy tracks=%d, drive tracks=%d).\n", info.m_cylinders, drivecyl);
+ info.m_cylinders = drivecyl;
}
}
- if (m_cylinders <= drivecyl/2)
+ if (info.m_cylinders <= drivecyl/2)
{
- osd_printf_error("hxchfe: Double stepping not yet supported (floppy tracks=%d, drive tracks=%d).\n", m_cylinders, drivecyl);
+ osd_printf_error("hxchfe: Double stepping not yet supported (floppy tracks=%d, drive tracks=%d).\n", info.m_cylinders, drivecyl);
return false;
}
- m_track_encoding = (encoding_t)(header[11] & 0xff);
+ info.m_track_encoding = encoding_t(header[11] & 0xff);
- if (m_track_encoding > EMU_FM_ENCODING)
+ if (info.m_track_encoding > EMU_FM_ENCODING)
{
- osd_printf_error("hxchfe: Unknown track encoding %d.\n", m_track_encoding);
+ osd_printf_error("hxchfe: Unknown track encoding %d.\n", info.m_track_encoding);
return false;
}
- m_bit_rate = (header[12] & 0xff) | ((header[13] & 0xff)<<8);
+ info.m_bit_rate = get_u16le(&header[12]);
- if (m_bit_rate > 500)
+ if (info.m_bit_rate > 500)
{
- osd_printf_error("hxchfe: Unsupported bit rate %d.\n", m_bit_rate);
+ osd_printf_error("hxchfe: Unsupported bit rate %d.\n", info.m_bit_rate);
return false;
}
- int samplelength = 500000 / m_bit_rate;
+ int samplelength = 500000 / info.m_bit_rate;
// Not used in the HxC emulator
- m_floppy_rpm = (header[14] & 0xff) | ((header[15] & 0xff)<<8);
+ info.m_floppy_rpm = get_u16le(&header[14]);
- m_interface_mode = (floppymode_t)(header[16] & 0xff);
- if (m_interface_mode > S950_HD_FLOPPYMODE)
+ info.m_interface_mode = (floppymode_t)(header[16] & 0xff);
+ if (info.m_interface_mode > S950_HD_FLOPPYMODE)
{
- osd_printf_error("hxchfe: Unknown interface mode %d.\n", m_interface_mode);
+ osd_printf_error("hxchfe: Unknown interface mode %d.\n", info.m_interface_mode);
return false;
}
- m_write_allowed = (header[20] != 0);
- m_single_step = (header[21] != 0);
- m_track0s0_has_altencoding = (header[22] == 0x00);
- m_track0s0_encoding = (encoding_t)(header[23] & 0xff);
- m_track0s1_has_altencoding = (header[24] == 0x00);
- m_track0s1_encoding = (encoding_t)(header[25] & 0xff);
+ info.m_write_allowed = (header[20] != 0);
+ info.m_single_step = (header[21] != 0);
+ info.m_track0s0_has_altencoding = (header[22] == 0x00);
+ info.m_track0s0_encoding = (encoding_t)(header[23] & 0xff);
+ info.m_track0s1_has_altencoding = (header[24] == 0x00);
+ info.m_track0s1_encoding = (encoding_t)(header[25] & 0xff);
// read track lookup table (multiple of 512)
- int table_offset = (header[18] & 0xff) | ((header[19] & 0xff)<<8);
+ int table_offset = get_u16le(&header[18]);
- io_generic_read(io, track_table, table_offset<<9, TRACK_TABLE_LENGTH);
+ uint8_t track_table[TRACK_TABLE_LENGTH];
+ std::tie(err, actual) = read_at(io, table_offset<<9, track_table, TRACK_TABLE_LENGTH); // FIXME: check for errors and premature EOF
- for (int i=0; i < m_cylinders; i++)
+ for (int i=0; i < info.m_cylinders; i++)
{
- m_cyl_offset[i] = (track_table[4*i] & 0xff) | ((track_table[4*i+1] & 0xff)<<8);
- m_cyl_length[i] = (track_table[4*i+2] & 0xff) | ((track_table[4*i+3] & 0xff)<<8);
+ info.m_cyl_offset[i] = get_u16le(&track_table[4*i]);
+ info.m_cyl_length[i] = get_u16le(&track_table[4*i+2]);
}
// Load the tracks
std::vector<uint8_t> cylinder_buffer;
- for(int cyl=0; cyl < m_cylinders; cyl++)
+ for(int cyl=0; cyl < info.m_cylinders; cyl++)
{
// actual data read
- // The HFE format defines an interleave of the two sides per cylinder
- // at every 256 bytes
- cylinder_buffer.resize(m_cyl_length[cyl]);
- io_generic_read(io, &cylinder_buffer[0], m_cyl_offset[cyl]<<9, m_cyl_length[cyl]);
-
- generate_track_from_hfe_bitstream(cyl, 0, samplelength, &cylinder_buffer[0], m_cyl_length[cyl], image);
- if (m_heads == 2)
- generate_track_from_hfe_bitstream(cyl, 1, samplelength, &cylinder_buffer[0], m_cyl_length[cyl], image);
+ // The HFE format defines an interleave of the two sides per cylinder at every 256 bytes
+ cylinder_buffer.resize(info.m_cyl_length[cyl]);
+ std::tie(err, actual) = read_at(io, info.m_cyl_offset[cyl]<<9, &cylinder_buffer[0], info.m_cyl_length[cyl]); // FIXME: check for errors and premature EOF
+
+ generate_track_from_hfe_bitstream(cyl, 0, samplelength, &cylinder_buffer[0], info.m_cyl_length[cyl], image);
+ if (info.m_heads == 2)
+ generate_track_from_hfe_bitstream(cyl, 1, samplelength, &cylinder_buffer[0], info.m_cyl_length[cyl], image);
}
bool success = true;
// Find variant
- if (m_track_encoding == ISOIBM_FM_ENCODING || m_track_encoding == EMU_FM_ENCODING)
+ if (info.m_track_encoding == ISOIBM_FM_ENCODING || info.m_track_encoding == EMU_FM_ENCODING)
// FM is for single density
- image->set_variant((m_heads==1)? floppy_image::SSSD : floppy_image::DSSD);
+ image.set_variant((info.m_heads==1)? floppy_image::SSSD : floppy_image::DSSD);
else
{
// MFM encoding is for everything else
- if (m_track_encoding == ISOIBM_MFM_ENCODING || m_track_encoding == AMIGA_MFM_ENCODING)
+ if (info.m_track_encoding == ISOIBM_MFM_ENCODING || info.m_track_encoding == AMIGA_MFM_ENCODING)
{
// Each cylinder contains the samples of both sides, 8 samples per
// byte; the bitRate determines how many samples constitute a cell
@@ -279,18 +277,18 @@ bool hfe_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
// DSED: 2.8 MiB = 2*80*36*512 bytes; 400000 cells/track, 500 ns, 1 Mbit/s
// Use cylinder 1 (cyl 0 may have special encodings)
- int cellcount = (m_cyl_length[1] * 8 / 2) * 250 / m_bit_rate;
+ int cellcount = (info.m_cyl_length[1] * 8 / 2) * 250 / info.m_bit_rate;
if (cellcount > 300000)
- image->set_variant(floppy_image::DSED);
+ image.set_variant(floppy_image::DSED);
else
{
if (cellcount > 150000)
- image->set_variant(floppy_image::DSHD);
+ image.set_variant(floppy_image::DSHD);
else
{
if (cellcount > 90000)
// We cannot distinguish DSDD from DSQD without knowing the size of the floppy disk
- image->set_variant((m_heads==1)? floppy_image::SSDD : floppy_image::DSDD);
+ image.set_variant((info.m_heads==1)? floppy_image::SSDD : floppy_image::DSDD);
}
}
}
@@ -300,7 +298,7 @@ bool hfe_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return success;
}
-void hfe_format::generate_track_from_hfe_bitstream(int cyl, int head, int samplelength, const uint8_t *trackbuf, int track_end, floppy_image *image)
+void hfe_format::generate_track_from_hfe_bitstream(int cyl, int head, int samplelength, const uint8_t *trackbuf, int track_end, floppy_image &image)
{
// HFE has a minor issue: The track images do not sum up to 200 ms.
// Tracks are samples at 250 kbit/s for both FM and MFM, which yields
@@ -322,17 +320,11 @@ void hfe_format::generate_track_from_hfe_bitstream(int cyl, int head, int sample
// HFE does not define subtracks; set to 0
// MG_1 / MG_0 are (logical) levels that indicate transition / no change
- // MG_A / MG_B are physical flux directions
- //
- // Cell: | AAAABBBB | = MG_1 = | BBBBAAAA |
- // | AAAAAAAA | = MG_0 = | BBBBBBBB |
+ // MG_F is the position of a flux transition
- std::vector<uint32_t> &dest = image->get_buffer(cyl, head, 0);
+ std::vector<uint32_t> &dest = image.get_buffer(cyl, head, 0);
dest.clear();
- // Start with MG_A
- uint32_t cbit = floppy_image::MG_A;
-
int offset = 0x100;
if (head==0)
@@ -344,10 +336,6 @@ void hfe_format::generate_track_from_hfe_bitstream(int cyl, int head, int sample
uint8_t current = 0;
int time = 0;
- dest.push_back(cbit | time);
-
- cbit = floppy_image::MG_B;
-
// Oversampled FM images (250 kbit/s) start with a 0, where a 1 is
// expected for 125 kbit/s.
// In order to make an oversampled image look like a normally sampled one,
@@ -390,13 +378,8 @@ void hfe_format::generate_track_from_hfe_bitstream(int cyl, int head, int sample
{
time += samplelength;
if ((current & 1)!=0)
- {
// Append another transition to the vector
- dest.push_back(cbit | time);
-
- // Toggle the cell level
- cbit = (cbit == floppy_image::MG_A)? floppy_image::MG_B : floppy_image::MG_A;
- }
+ dest.push_back(floppy_image::MG_F | time);
// HFE uses little-endian bit order
current >>= 1;
@@ -409,299 +392,9 @@ void hfe_format::generate_track_from_hfe_bitstream(int cyl, int head, int sample
if (offset >= track_end) offset = track_end - 1;
}
- image->set_write_splice_position(cyl, head, 0, 0);
-}
-
-bool hfe_format::save(io_generic *io, floppy_image *image)
-{
- std::vector<uint8_t> cylbuf;
-
- // Create a buffer that is big enough to handle HD formats. We don't
- // know the track length until we generate the HFE bitstream.
- cylbuf.resize(0x10000);
-
- uint8_t header[HEADER_LENGTH];
- uint8_t track_table[TRACK_TABLE_LENGTH];
-
- int track_end = 0x61c0;
- int samplelength = 2000;
-
- // Set up header
- const char* sig = "HXCPICFE";
- memcpy(header, sig, 8);
-
- header[8] = 0;
- // Can we change the number of tracks or heads?
- image->get_actual_geometry(m_cylinders, m_heads);
-
- header[9] = m_cylinders;
- header[10] = m_heads;
- // Floppy RPM is not used
- header[14] = 0;
- header[15] = 0;
-
- // Bit rate and encoding will be set later, they may have changed by
- // reformatting. The selected encoding is UNKNOWN_ENCODING unless
- // explicitly set
- m_track_encoding = m_selected_encoding;
-
- // Take the old mode, unless we have specified a mode
- header[16] = (m_selected_mode != DISABLE_FLOPPYMODE)? m_selected_mode : m_interface_mode;
- header[17] = 0;
-
- // The track lookup table is located at offset 0x200 (as 512 multiple)
- header[18] = 1;
- header[19] = 0;
-
- header[20] = m_write_allowed? 0xff : 0x00;
- header[21] = m_single_step? 0xff : 0x00;
-
- // TODO: Allow for divergent track 0 format
- header[22] = m_track0s0_has_altencoding? 0x00 : 0xff;
- header[23] = m_track0s0_encoding;
- header[24] = m_track0s1_has_altencoding? 0x00 : 0xff;
- header[25] = m_track0s1_encoding;
-
- // Fill the remaining bytes with 0xff
- for (int i=26; i < HEADER_LENGTH; i++) header[i] = 0xff;
-
- // Don't write yet; we still have to find out the bit rate.
-
- // We won't have more than 200000 cells on the track
- for (int cyl=0; cyl < m_cylinders; cyl++)
- {
- // After the call, the encoding will be set to FM or MFM
- generate_hfe_bitstream_from_track(cyl, 0, samplelength, m_track_encoding, &cylbuf[0], track_end, image);
- if (m_heads == 2)
- generate_hfe_bitstream_from_track(cyl, 1, samplelength, m_track_encoding, &cylbuf[0], track_end, image);
-
- if (cyl==0)
- {
- // Complete the header and write it
- header[11] = m_track_encoding;
- m_bit_rate = 500000/samplelength;
- header[12] = m_bit_rate & 0xff;
- header[13] = (m_bit_rate >> 8) & 0xff;
-
- // Now write the header
- io_generic_write(io, header, 0, HEADER_LENGTH);
-
- // Set up the track lookup table
- // We need the encoding value to be sure about the track length
- int len = (m_track_encoding==ISOIBM_FM_ENCODING)? 0x61b0 : 0x61c0;
- int pos = 0x400;
-
- for (int i=0; i < m_cylinders; i++)
- {
- m_cyl_offset[i] = (pos >> 9);
- m_cyl_length[i] = len;
- pos += (len + 0x1ff) & 0xfe00;
- track_table[i*4] = m_cyl_offset[i] & 0xff;
- track_table[i*4+1] = (m_cyl_offset[i]>>8) & 0xff;
- track_table[i*4+2] = len & 0xff;
- track_table[i*4+3] = (len>>8) & 0xff;
- }
- // Set the remainder to 0xff
- for (int i=m_cylinders*4; i < TRACK_TABLE_LENGTH; i++)
- track_table[i] = 0xff;
-
- io_generic_write(io, track_table, 0x200, TRACK_TABLE_LENGTH);
- }
- // Write the current cylinder
- io_generic_write(io, &cylbuf[0], m_cyl_offset[cyl]<<9, (m_cyl_length[cyl] + 0x1ff) & 0xfe00);
- }
- return true;
-}
-
-void hfe_format::generate_hfe_bitstream_from_track(int cyl, int head, int& samplelength, encoding_t& encoding, uint8_t *cylinder_buffer, int track_end, floppy_image *image)
-{
- // We are using an own implementation here because the result of the
- // parent class method would require some post-processing that we
- // can easily avoid.
-
- // See floppy_image_format_t::generate_bitstream_from_track
- // as the original code
-
- // No subtracks definded
- std::vector<uint32_t> &tbuf = image->get_buffer(cyl, head, 0);
- if (tbuf.size() <= 1)
- {
- // Unformatted track
- // TODO must handle that according to HFE
- int track_size = 200000000/samplelength;
- memset(cylinder_buffer, 0, (track_size+7)/8);
- return;
- }
-
- // Find out whether we have FM or MFM recording, and determine the bit rate.
- // This is needed for the format header.
- //
- // The encoding may have changed by reformatting; we cannot rely on the
- // header when loading.
- //
- // FM: encoding 1 -> flux length = 4 us (min) ambivalent
- // encoding 10 -> flux length = 8 us (max) ambivalent
- // MFM: encoding 10 -> flux length = 4 us (min, DD) ambivalent
- // encoding 100 -> flux length = 6 us (DD) significant
- // encoding 1000 -> flux length = 8 us (max, DD) ambivalent
- // encoding 10 -> flux length = 2 us (min, HD) significant
- // encoding 100 -> flux length = 3 us (max, HD) significant
-
- // If we have MFM, we should very soon detect a flux length of 6 us.
- // But if we have FM, how long should we search to be sure?
- // We assume that after 2000 us we should have reached the first IDAM,
- // which contains a sequence 1001, implying a flux length of 6 us.
- // If there was no such flux in that area, this can safely be assumed to be FM.
-
- // Do it only for the first track; the format only supports one encoding.
- if (encoding == UNKNOWN_ENCODING)
- {
- bool mfm_recording = false;
- int time0 = 0;
- int minflux = 4000;
- int fluxlen = 0;
- // Skip the beginning (may have a short cell)
- for (int i=2; (i < tbuf.size()-1) && (time0 < 2000000) && !mfm_recording; i++)
- {
- time0 = tbuf[i] & floppy_image::TIME_MASK;
- fluxlen = (tbuf[i+1] & floppy_image::TIME_MASK) - time0;
- if ((fluxlen < 3500) || (fluxlen > 5500 && fluxlen < 6500))
- mfm_recording = true;
- if (fluxlen < minflux) minflux = fluxlen;
- }
- encoding = mfm_recording? ISOIBM_MFM_ENCODING : ISOIBM_FM_ENCODING;
-
- // samplelength = 1000ns => 10^6 cells/sec => 500 kbit/s
- // samplelength = 2000ns => 250 kbit/s
- // We stay with double sampling at 250 kbit/s for FM
- if (minflux < 3500) samplelength = 1000;
- else samplelength = 2000;
- }
-
- // Start at the write splice
- uint32_t splice = image->get_write_splice_position(cyl, head, 0);
-
- int cur_pos = splice;
- int cur_entry = 0;
-
- // Fast-forward to the write splice position (always 0 in this format)
- while (cur_entry < int(tbuf.size())-1 && (tbuf[cur_entry+1] & floppy_image::TIME_MASK) < cur_pos)
- cur_entry++;
-
- int period = samplelength;
- int period_adjust_base = period * 0.05;
-
- int min_period = int(samplelength*0.75);
- int max_period = int(samplelength*1.25);
- int phase_adjust = 0;
- int freq_hist = 0;
- uint32_t next = 0;
-
- int offset = 0x100;
-
- // Prepare offset for the format storage
- if (head==0)
- {
- offset = 0;
- track_end -= 0x0100;
- }
-
- uint8_t bit = 0x01;
- uint8_t current = 0;
-
- while (next < 200000000) {
- int edge = tbuf[cur_entry] & floppy_image::TIME_MASK;
-
- // Start of track? Use next entry.
- if (edge==0)
- {
- cur_pos = 0;
- edge = tbuf[++cur_entry] & floppy_image::TIME_MASK;
- }
-
- // Wrapped over end?
- if (edge < cur_pos) edge += 200000000;
-
- // End of cell
- next = cur_pos + period + phase_adjust;
-
- // End of the window is at next; edge is the actual transition
- if (edge >= next)
- {
- // No transition in the window -> 0
- phase_adjust = 0;
- }
- else
- {
- // Transition in the window -> 1
- current |= bit;
- int delta = edge - (next - period/2);
-
- phase_adjust = 0.65*delta;
-
- if (delta < 0)
- {
- if (freq_hist < 0) freq_hist--;
- else freq_hist = -1;
- }
- else
- {
- if (delta > 0)
- {
- if(freq_hist > 0) freq_hist++;
- else freq_hist = 1;
- }
- else freq_hist = 0;
- }
-
- if (freq_hist)
- {
- int afh = freq_hist < 0 ? -freq_hist : freq_hist;
- if (afh > 1)
- {
- int aper = period_adjust_base*delta/period;
- if (!aper)
- aper = freq_hist < 0 ? -1 : 1;
- period += aper;
-
- if (period < min_period) period = min_period;
- else if (period > max_period) period = max_period;
- }
- }
- }
-
- cur_pos = next;
- if(cur_pos >= 200000000) {
- cur_pos -= 200000000;
- cur_entry = 0;
- }
-
- bit = (bit << 1) & 0xff;
- if (bit == 0)
- {
- bit = 0x01;
- cylinder_buffer[offset++] = current;
- if ((offset & 0xff)==0) offset += 0x100;
- current = 0;
- }
-
- // Fast-forward to next cell
- while (cur_entry < int(tbuf.size())-1 && (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos)
- cur_entry++;
-
- // Reaching the end of the track
- if (cur_entry == int(tbuf.size())-1 && (tbuf[cur_entry] & floppy_image::TIME_MASK) < cur_pos)
- {
- // Wrap to index 0 or 1 depending on whether there is a transition exactly at the index hole
- cur_entry = (tbuf[int(tbuf.size())-1] & floppy_image::MG_MASK) != (tbuf[0] & floppy_image::MG_MASK) ?
- 0 : 1;
- }
- }
- // Write the current byte when not done
- if (bit != 0x01)
- cylinder_buffer[offset] = current;
+ image.set_write_splice_position(cyl, head, 0, 0);
}
-const floppy_format_type FLOPPY_HFE_FORMAT = &floppy_image_format_creator<hfe_format>;
+const hfe_format FLOPPY_HFE_FORMAT;
diff --git a/src/lib/formats/hxchfe_dsk.h b/src/lib/formats/hxchfe_dsk.h
index 75dd4c3c371..c7a1eac08ff 100644
--- a/src/lib/formats/hxchfe_dsk.h
+++ b/src/lib/formats/hxchfe_dsk.h
@@ -47,44 +47,44 @@ public:
hfe_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
-
- void set_floppy_mode(floppymode_t mode) { m_selected_mode = mode; }
- void set_encoding(encoding_t enc) { m_selected_encoding = enc; }
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
private:
- void generate_track_from_hfe_bitstream(int track, int head, int samplelength, const uint8_t *trackbuf, int track_end, floppy_image *image);
- void generate_hfe_bitstream_from_track(int track, int head, int& samplelength, encoding_t& encoding, uint8_t *trackbuf, int track_end, floppy_image *image);
+ static void generate_track_from_hfe_bitstream(int track, int head, int samplelength, const uint8_t *trackbuf, int track_end, floppy_image &image);
// Header fields from the HFE format
- int m_cylinders; // Number of track in the file
- int m_heads; // Number of valid side
- encoding_t m_track_encoding; // Track Encoding mode
- int m_bit_rate; // Bitrate in Kbit/s (max: 500)
- int m_floppy_rpm; // Rotation per minute
- floppymode_t m_interface_mode; // Floppy interface mode.
-
- bool m_write_allowed;
- bool m_single_step;
- bool m_track0s0_has_altencoding;
- encoding_t m_track0s0_encoding; // alternate track_encoding for track 0 Side 0
- bool m_track0s1_has_altencoding;
- encoding_t m_track0s1_encoding; // alternate track_encoding for track 0 Side 1
-
- int m_cyl_offset[256];
- int m_cyl_length[256];
-
- floppymode_t m_selected_mode;
- encoding_t m_selected_encoding;
+ struct header_info {
+ int m_cylinders = 0; // Number of track in the file
+ int m_heads = 0; // Number of valid side
+ encoding_t m_track_encoding = UNKNOWN_ENCODING; // Track Encoding mode
+ int m_bit_rate = 0; // Bitrate in Kbit/s (max: 500)
+ int m_floppy_rpm = 0; // Rotation per minute
+ floppymode_t m_interface_mode = DISABLE_FLOPPYMODE; // Floppy interface mode.
+
+ bool m_write_allowed = true;
+ bool m_single_step = true;
+ bool m_track0s0_has_altencoding = false;
+ encoding_t m_track0s0_encoding = UNKNOWN_ENCODING; // alternate track_encoding for track 0 Side 0
+ bool m_track0s1_has_altencoding = false;
+ encoding_t m_track0s1_encoding = UNKNOWN_ENCODING; // alternate track_encoding for track 0 Side 1
+
+ int m_cyl_offset[256];
+ int m_cyl_length[256];
+
+ floppymode_t m_selected_mode = DISABLE_FLOPPYMODE;
+ encoding_t m_selected_encoding = UNKNOWN_ENCODING;
+ };
+
+ static void set_floppy_mode(header_info &info, floppymode_t mode) { info.m_selected_mode = mode; }
+ static void set_encoding(header_info &info, encoding_t enc) { info.m_selected_encoding = enc; }
};
-extern const floppy_format_type FLOPPY_HFE_FORMAT;
+extern const hfe_format FLOPPY_HFE_FORMAT;
#endif // MAME_FORMATS_HXCHFE_DSK_H
diff --git a/src/lib/formats/hxcmfm_dsk.cpp b/src/lib/formats/hxcmfm_dsk.cpp
index dcc4718c114..d9fda047dea 100644
--- a/src/lib/formats/hxcmfm_dsk.cpp
+++ b/src/lib/formats/hxcmfm_dsk.cpp
@@ -1,10 +1,14 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
-#include <assert.h>
-
#include "hxcmfm_dsk.h"
+#include "ioprocs.h"
+
+#include <cstring>
+#include <tuple>
+
+
#define MFM_FORMAT_HEADER "HXCMFM"
#pragma pack(1)
@@ -37,47 +41,51 @@ mfm_format::mfm_format() : floppy_image_format_t()
{
}
-const char *mfm_format::name() const
+const char *mfm_format::name() const noexcept
{
return "mfm";
}
-const char *mfm_format::description() const
+const char *mfm_format::description() const noexcept
{
return "HxCFloppyEmulator floppy disk image";
}
-const char *mfm_format::extensions() const
+const char *mfm_format::extensions() const noexcept
{
return "mfm";
}
-bool mfm_format::supports_save() const
+bool mfm_format::supports_save() const noexcept
{
return true;
}
-int mfm_format::identify(io_generic *io, uint32_t form_factor)
+int mfm_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t header[7];
-
- io_generic_read(io, &header, 0, sizeof(header));
- if ( memcmp( header, MFM_FORMAT_HEADER, 6 ) ==0) {
- return 100;
+ auto const [err, actual] = read_at(io, 0, &header, sizeof(header)); // FIXME: does this really need to read 7 bytes? only 6 are checked. also check for premature EOF
+ if (err) {
+ return 0;
+ }
+ if (!memcmp(header, MFM_FORMAT_HEADER, 6)) {
+ return FIFID_SIGN;
}
return 0;
}
-bool mfm_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool mfm_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
+ std::error_condition err;
+ size_t actual;
MFMIMG header;
MFMTRACKIMG trackdesc;
// read header
- io_generic_read(io, &header, 0, sizeof(header));
+ std::tie(err, actual) = read_at(io, 0, &header, sizeof(header)); // FIXME: check for errors and premature EOF
int drivecyl, driveheads;
- image->get_maximal_geometry(drivecyl, driveheads);
+ image.get_maximal_geometry(drivecyl, driveheads);
bool skip_odd = (drivecyl < 50 && header.number_of_track >= 80);
int counter = 0;
@@ -86,12 +94,12 @@ bool mfm_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
for(int side=0; side < header.number_of_side; side++) {
if (!skip_odd || track%2 == 0) {
// read location of
- io_generic_read(io, &trackdesc,(header.mfmtracklistoffset)+( counter *sizeof(trackdesc)),sizeof(trackdesc));
+ std::tie(err, actual) = read_at(io, header.mfmtracklistoffset + (counter * sizeof(trackdesc)), &trackdesc, sizeof(trackdesc)); // FIXME: check for errors and premature EOF
trackbuf.resize(trackdesc.mfmtracksize);
// actual data read
- io_generic_read(io, &trackbuf[0], trackdesc.mfmtrackoffset, trackdesc.mfmtracksize);
+ std::tie(err, actual) = read_at(io, trackdesc.mfmtrackoffset, &trackbuf[0], trackdesc.mfmtracksize); // FIXME: check for errors and premature EOF
if (skip_odd) {
generate_track_from_bitstream(track/2, side, &trackbuf[0], trackdesc.mfmtracksize*8, image);
@@ -105,16 +113,16 @@ bool mfm_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
}
}
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
return true;
}
-bool mfm_format::save(io_generic *io, floppy_image *image)
+bool mfm_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
// TODO: HD support
MFMIMG header;
int track_count, head_count;
- image->get_actual_geometry(track_count, head_count);
+ image.get_actual_geometry(track_count, head_count);
memcpy(&header.headername, MFM_FORMAT_HEADER, 7);
header.number_of_track = track_count;
@@ -124,34 +132,34 @@ bool mfm_format::save(io_generic *io, floppy_image *image)
header.floppyiftype = 4;
header.mfmtracklistoffset = sizeof(MFMIMG);
- io_generic_write(io, &header, 0, sizeof(MFMIMG));
+ write_at(io, 0, &header, sizeof(MFMIMG)); // FIXME: check for errors
int tpos = sizeof(MFMIMG);
int dpos = tpos + track_count*head_count*sizeof(MFMTRACKIMG);
- uint8_t trackbuf[150000/8];
-
for(int track=0; track < track_count; track++) {
for(int side=0; side < head_count; side++) {
- int track_size;
- generate_bitstream_from_track(track, side, 2000, trackbuf, track_size, image);
- track_size = (track_size+7)/8;
+ auto trackbuf = generate_bitstream_from_track(track, side, 2000, image);
+ std::vector<uint8_t> packed((trackbuf.size() + 7) >> 3, 0);
+ for(uint32_t i = 0; i != trackbuf.size(); i++)
+ if(trackbuf[i])
+ packed[i >> 3] |= 0x80 >> (i & 7);
MFMTRACKIMG trackdesc;
trackdesc.track_number = track;
trackdesc.side_number = side;
- trackdesc.mfmtracksize = track_size;
+ trackdesc.mfmtracksize = packed.size();
trackdesc.mfmtrackoffset = dpos;
- io_generic_write(io, &trackdesc, tpos, sizeof(MFMTRACKIMG));
- io_generic_write(io, trackbuf, dpos, track_size);
+ write_at(io, tpos, &trackdesc, sizeof(MFMTRACKIMG)); // FIXME: check for errors
+ write_at(io, dpos, packed.data(), packed.size()); // FIXME: check for errors
tpos += sizeof(MFMTRACKIMG);
- dpos += track_size;
+ dpos += packed.size();
}
}
return true;
}
-const floppy_format_type FLOPPY_MFM_FORMAT = &floppy_image_format_creator<mfm_format>;
+const mfm_format FLOPPY_MFM_FORMAT;
diff --git a/src/lib/formats/hxcmfm_dsk.h b/src/lib/formats/hxcmfm_dsk.h
index 6616794f1ed..b4134b8ae9a 100644
--- a/src/lib/formats/hxcmfm_dsk.h
+++ b/src/lib/formats/hxcmfm_dsk.h
@@ -19,16 +19,16 @@ class mfm_format : public floppy_image_format_t
public:
mfm_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
-
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_MFM_FORMAT;
+extern const mfm_format FLOPPY_MFM_FORMAT;
#endif // MAME_FORMATS_HXCMFM_DSK_H
diff --git a/src/lib/formats/ibmxdf_dsk.cpp b/src/lib/formats/ibmxdf_dsk.cpp
new file mode 100644
index 00000000000..c9704361f65
--- /dev/null
+++ b/src/lib/formats/ibmxdf_dsk.cpp
@@ -0,0 +1,268 @@
+// license:BSD-3-Clause
+// copyright-holders:Sergey Svishchev
+/*********************************************************************
+
+ formats/ibmxdf_dsk.cpp
+
+ IBM Extended Density Format
+
+ References:
+ - http://www.os2museum.com/wp/the-xdf-diskette-format/
+ - https://fdutils.linux.lu/Fdutils.html#SEC47 and xdfcopy.c
+
+ Mapping XDF disk image to physical sectors on a 3.5" disk:
+
+ First track uses standard 512-byte sectors:
+ - lbn 0 (boot sector) = 0.129 (head.sector)
+ - lbn 1-11 (FAT) = 0.130-0.139, then 1.129
+ - lbn 12-19 (aux FS) = 0.1-0.8
+ - lbn 20-22 = padding, not written to disk
+ - lbn 23-36 (root directory) = 1.130-1.143
+ - lbn 37-41 = padding, not written to disk
+ - lbn 42-45 (start of data area) = 1.144-1.147
+
+ All other tracks use mixed size sectors, sequenced like so:
+ 0.131, 0.132, 1.134, 0.130, 1.130, 0.134, 1.132, 1.131
+
+ To do:
+ - 5.25 HD and 3.5 ED formats
+ - replace magic numbers
+
+*********************************************************************/
+
+#include "ibmxdf_dsk.h"
+#include "imageutl.h"
+
+#include "ioprocs.h"
+
+
+ibmxdf_format::ibmxdf_format() : wd177x_format(formats)
+{
+}
+
+const char *ibmxdf_format::name() const noexcept
+{
+ return "ibmxdf";
+}
+
+const char *ibmxdf_format::description() const noexcept
+{
+ return "IBM XDF disk image";
+}
+
+const char *ibmxdf_format::extensions() const noexcept
+{
+ return "xdf,img";
+}
+
+int ibmxdf_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ int type = find_size(io, form_factor, variants);
+
+ if (type != -1)
+ return FIFID_SIZE;
+ return 0;
+}
+
+int ibmxdf_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ if (size != 1884160)
+ return -1;
+
+ return 0;
+}
+
+int ibmxdf_format::get_image_offset(const format &f, int head, int track) const
+{
+ return (2 * track) * compute_track_size(formats[0]);
+}
+
+const wd177x_format::format &ibmxdf_format::get_track_format(const format &f, int head, int track) const
+{
+ int n = -1;
+
+ for (int i = 0; formats[i].form_factor; i++) {
+ if (&formats[i] == &f) {
+ n = i;
+ break;
+ }
+ }
+
+ if (n < 0) {
+ LOG_FORMATS("Error format not found\n");
+ return f;
+ }
+
+ if (head >= f.head_count) {
+ LOG_FORMATS("Error invalid head %d\n", head);
+ return f;
+ }
+
+ if (track >= f.track_count) {
+ LOG_FORMATS("Error invalid track %d\n", track);
+ return f;
+ }
+
+ if (track > 0) {
+ if (head == 1) {
+ const format &fh1 = formats_head1[n];
+ if (!fh1.form_factor) {
+ LOG_FORMATS("Error expected a head 1 format\n");
+ return f;
+ }
+ return fh1;
+ }
+ return f;
+ }
+
+ // Track 0
+
+ if (head == 1) {
+ const format &fh1t0 = formats_head1_track0[n];
+ if (fh1t0.form_factor) {
+ return fh1t0;
+ }
+ const format &fh1 = formats_head1[n];
+ if (fh1.form_factor) {
+ return fh1;
+ }
+ LOG_FORMATS("Error expected a head 1 format\n");
+ return f;
+ }
+
+ // Head 0
+
+ const format &ft0 = formats_track0[n];
+ if (ft0.form_factor) {
+ return ft0;
+ }
+
+ return f;
+}
+
+
+// Unverified gap sizes
+const ibmxdf_format::format ibmxdf_format::formats[] = {
+ {
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 4, 80, 2, 0, { 1024, 512, 2048, 8192 }, -1, { 131, 130, 132, 134 }, 50, 22, 74
+ },
+ {}
+};
+
+const ibmxdf_format::format ibmxdf_format::formats_head1[] = {
+ {
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 4, 80, 2, 0, { 2048, 512, 1024, 8192 }, -1, { 132, 130, 131, 134 }, 50, 22, 74
+ },
+ {}
+};
+
+const ibmxdf_format::format ibmxdf_format::formats_track0[] = {
+ {
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 19, 80, 2, 512, {}, -1, { 1, 138, 129, 139, 130, 2, 131, 3, 132, 4, 133, 5, 134, 6, 135, 7, 136, 8, 137 }, 50, 22, 74
+ },
+ {}
+};
+
+const ibmxdf_format::format ibmxdf_format::formats_head1_track0[] = {
+ {
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 19, 80, 2, 512, {}, -1, { 144, 135, 145, 136, 146, 137, 147, 138, 129, 139, 130, 140, 131, 141, 132, 142, 133, 143, 134 }, 50, 22, 74
+ },
+ {}
+};
+
+bool ibmxdf_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ int type = find_size(io, form_factor, variants);
+ if(type == -1)
+ return false;
+
+ const format &f = formats[type];
+
+ for(int track = 0; track < f.track_count; track++) {
+ for(int head = 0; head < f.head_count; head++) {
+ uint8_t sectdata[23 * 2 * 512]; // XXX magic
+ desc_s sectors[40];
+ floppy_image_format_t::desc_e *desc;
+ int current_size;
+ int end_gap_index;
+ const format &tf = get_track_format(f, head, track);
+
+ desc = get_desc_mfm(tf, current_size, end_gap_index);
+
+ int total_size = 200000000/tf.cell_size;
+ int remaining_size = total_size - current_size;
+ if(remaining_size < 0) {
+ osd_printf_error("ibmxdf_format: Incorrect track layout, max_size=%d, current_size=%d\n", total_size, current_size);
+ return false;
+ }
+
+ // Fixup the end gap
+ desc[end_gap_index].p2 = remaining_size / 16;
+ desc[end_gap_index + 1].p2 = remaining_size & 15;
+ desc[end_gap_index + 1].p1 >>= 16-(remaining_size & 15);
+
+ desc[16].p1 = get_track_dam_mfm(tf, head, track);
+
+ build_sector_description(tf, sectdata, sectors, track, head);
+ int const track_size = compute_track_size(f) * 2; // read both sides at once
+ /*auto const [err, actual] =*/ read_at(io, get_image_offset(f, head, track), sectdata, track_size); // FIXME: check for errors and premature EOF
+ generate_track(desc, track, head, sectors, tf.sector_count, total_size, image);
+ }
+ }
+
+ image.set_variant(f.variant);
+
+ return true;
+}
+
+static const int offsets[2][4] = { { 0, 11264, 1024, 12288 }, { 20480, 11776, 22528, 3072 } }; // XXX magic
+
+void ibmxdf_format::build_sector_description(const format &f, uint8_t *sectdata, desc_s *sectors, int track, int head) const
+{
+ switch (track)
+ {
+ case 0:
+ if (head == 0) {
+ for(int i=0; i<f.sector_count; i++) {
+ if (f.per_sector_id[i] < 129) // aux fs
+ sectors[i].data = sectdata + f.sector_base_size * (f.per_sector_id[i] + 11);
+ else
+ sectors[i].data = sectdata + f.sector_base_size * (f.per_sector_id[i] - 129);
+ sectors[i].size = f.sector_base_size;
+ sectors[i].sector_id = f.per_sector_id[i];
+ }
+ }
+ else {
+ for(int i=0; i<f.sector_count; i++) {
+ if (f.per_sector_id[i] == 129)
+ sectors[i].data = sectdata + f.sector_base_size * 11;
+ else if (f.per_sector_id[i] < 144)
+ sectors[i].data = sectdata + f.sector_base_size * (f.per_sector_id[i] - 130 + 23);
+ else // skip 5 sectors marked bad in the FAT
+ sectors[i].data = sectdata + f.sector_base_size * (f.per_sector_id[i] - 130 + 28);
+ sectors[i].size = f.sector_base_size;
+ sectors[i].sector_id = f.per_sector_id[i];
+ }
+ }
+ break;
+
+ default:
+ for(int i=0; i<f.sector_count; i++) {
+ sectors[i].data = sectdata + offsets[head][i];
+ sectors[i].size = f.per_sector_size[i];
+ sectors[i].sector_id = f.per_sector_id[i];
+ }
+ break;
+ }
+}
+
+const ibmxdf_format FLOPPY_IBMXDF_FORMAT;
+
diff --git a/src/lib/formats/ibmxdf_dsk.h b/src/lib/formats/ibmxdf_dsk.h
new file mode 100644
index 00000000000..c1c8558b687
--- /dev/null
+++ b/src/lib/formats/ibmxdf_dsk.h
@@ -0,0 +1,45 @@
+// license:BSD-3-Clause
+// copyright-holders:Sergey Svishchev
+/*********************************************************************
+
+ formats/xdf_dsk.h
+
+ IBM Extended Density Format
+
+*********************************************************************/
+#ifndef MAME_FORMATS_IBMXDF_DSK_H
+#define MAME_FORMATS_IBMXDF_DSK_H
+
+#pragma once
+
+#include "wd177x_dsk.h"
+
+class ibmxdf_format : public wd177x_format
+{
+public:
+ ibmxdf_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override { return false; }
+
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+ virtual const wd177x_format::format &get_track_format(const format &f, int head, int track) const override;
+
+private:
+ static const format formats[];
+ static const format formats_head1[];
+ static const format formats_track0[];
+ static const format formats_head1_track0[];
+
+ virtual void build_sector_description(const format &d, uint8_t *sectdata, desc_s *sectors, int track, int head) const override;
+};
+
+extern const ibmxdf_format FLOPPY_IBMXDF_FORMAT;
+
+#endif // MAME_FORMATS_IBMXDF_DSK_H
diff --git a/src/lib/formats/idpart_dsk.cpp b/src/lib/formats/idpart_dsk.cpp
new file mode 100644
index 00000000000..9d4c641fa41
--- /dev/null
+++ b/src/lib/formats/idpart_dsk.cpp
@@ -0,0 +1,53 @@
+// license:BSD-3-Clause
+// copyright-holders:Miodrag Milanovic
+/*********************************************************************
+
+ formats/idpart_dsk.cpp
+
+ Iskra Delta Partner format
+
+*********************************************************************/
+
+#include "formats/idpart_dsk.h"
+
+idpart_format::idpart_format() : upd765_format(formats)
+{
+}
+
+const char *idpart_format::name() const noexcept
+{
+ return "idpart";
+}
+
+const char *idpart_format::description() const noexcept
+{
+ return "Iskra Delta Partner disk image";
+}
+
+const char *idpart_format::extensions() const noexcept
+{
+ return "img";
+}
+
+// Unverified gap sizes.
+const idpart_format::format idpart_format::formats[] = {
+ {
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000,
+ 18, 73, 2,
+ 256, {},
+ 1, {},
+ 80, 50, 22
+ },
+ {
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000,
+ 18, 77, 2,
+ 256, {},
+ 1, {},
+ 80, 50, 22
+ },
+ {}
+};
+
+const idpart_format FLOPPY_IDPART_FORMAT;
diff --git a/src/lib/formats/idpart_dsk.h b/src/lib/formats/idpart_dsk.h
new file mode 100644
index 00000000000..6f5977a5cfa
--- /dev/null
+++ b/src/lib/formats/idpart_dsk.h
@@ -0,0 +1,32 @@
+// license:BSD-3-Clause
+// copyright-holders:Miodrag Milanovic
+/*********************************************************************
+
+ formats/idpart_dsk.h
+
+ Iskra Delta Partner format
+
+*********************************************************************/
+#ifndef MAME_FORMATS_IDPART_DSK_H
+#define MAME_FORMATS_IDPART_DSK_H
+
+#pragma once
+
+#include "upd765_dsk.h"
+
+class idpart_format : public upd765_format
+{
+public:
+ idpart_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const format formats[];
+};
+
+extern const idpart_format FLOPPY_IDPART_FORMAT;
+
+#endif // MAME_FORMATS_IDPART_DSK_H
diff --git a/src/lib/formats/imageutl.cpp b/src/lib/formats/imageutl.cpp
index 6ab427115e0..6bf2bbd3323 100644
--- a/src/lib/formats/imageutl.cpp
+++ b/src/lib/formats/imageutl.cpp
@@ -8,7 +8,7 @@
***************************************************************************/
-#include <ctype.h>
+#include <cctype>
#include "imageutl.h"
int compute_log2(int val)
diff --git a/src/lib/formats/imageutl.h b/src/lib/formats/imageutl.h
index f04624a46db..53b7170fc65 100644
--- a/src/lib/formats/imageutl.h
+++ b/src/lib/formats/imageutl.h
@@ -14,6 +14,10 @@
#include "osdcore.h"
+#ifndef LOG_FORMATS
+#define LOG_FORMATS(...) do { if (0) osd_printf_info(__VA_ARGS__); } while (false)
+#endif
+
/* -----------------------------------------------------------------------
* CRC stuff
* ----------------------------------------------------------------------- */
@@ -21,146 +25,6 @@ unsigned short ccitt_crc16(unsigned short crc, const unsigned char *buffer, size
unsigned short ccitt_crc16_one( unsigned short crc, const unsigned char data );
/* -----------------------------------------------------------------------
- * Alignment-friendly integer placement
- * ----------------------------------------------------------------------- */
-
-inline void place_integer_be(void *ptr, size_t offset, size_t size, uint64_t value)
-{
- uint8_t *byte_ptr = ((uint8_t *) ptr) + offset;
- uint16_t val16;
- uint32_t val32;
-
- switch (size)
- {
- case 2:
- val16 = big_endianize_int16((uint16_t) value);
- memcpy(byte_ptr, &val16, sizeof(val16));
- break;
-
- case 4:
- val32 = big_endianize_int32((uint32_t) value);
- memcpy(byte_ptr, &val32, sizeof(val32));
- break;
-
- default:
- if (size >= 1) byte_ptr[0] = (uint8_t) (value >> ((size - 1) * 8));
- if (size >= 2) byte_ptr[1] = (uint8_t) (value >> ((size - 2) * 8));
- if (size >= 3) byte_ptr[2] = (uint8_t) (value >> ((size - 3) * 8));
- if (size >= 4) byte_ptr[3] = (uint8_t) (value >> ((size - 4) * 8));
- if (size >= 5) byte_ptr[4] = (uint8_t) (value >> ((size - 5) * 8));
- if (size >= 6) byte_ptr[5] = (uint8_t) (value >> ((size - 6) * 8));
- if (size >= 7) byte_ptr[6] = (uint8_t) (value >> ((size - 7) * 8));
- if (size >= 8) byte_ptr[7] = (uint8_t) (value >> ((size - 8) * 8));
- break;
- }
-}
-
-inline uint64_t pick_integer_be(const void *ptr, size_t offset, size_t size)
-{
- uint64_t result = 0;
- const uint8_t *byte_ptr = ((const uint8_t *) ptr) + offset;
- uint16_t val16;
- uint32_t val32;
-
- switch (size)
- {
- case 1:
- result = *byte_ptr;
- break;
-
- case 2:
- memcpy(&val16, byte_ptr, sizeof(val16));
- result = big_endianize_int16(val16);
- break;
-
- case 4:
- memcpy(&val32, byte_ptr, sizeof(val32));
- result = big_endianize_int32(val32);
- break;
-
- default:
- if (size >= 1) result |= ((uint64_t) byte_ptr[0]) << ((size - 1) * 8);
- if (size >= 2) result |= ((uint64_t) byte_ptr[1]) << ((size - 2) * 8);
- if (size >= 3) result |= ((uint64_t) byte_ptr[2]) << ((size - 3) * 8);
- if (size >= 4) result |= ((uint64_t) byte_ptr[3]) << ((size - 4) * 8);
- if (size >= 5) result |= ((uint64_t) byte_ptr[4]) << ((size - 5) * 8);
- if (size >= 6) result |= ((uint64_t) byte_ptr[5]) << ((size - 6) * 8);
- if (size >= 7) result |= ((uint64_t) byte_ptr[6]) << ((size - 7) * 8);
- if (size >= 8) result |= ((uint64_t) byte_ptr[7]) << ((size - 8) * 8);
- break;
- }
- return result;
-}
-
-inline void place_integer_le(void *ptr, size_t offset, size_t size, uint64_t value)
-{
- uint8_t *byte_ptr = ((uint8_t *) ptr) + offset;
- uint16_t val16;
- uint32_t val32;
-
- switch (size)
- {
- case 2:
- val16 = little_endianize_int16((uint16_t) value);
- memcpy(byte_ptr, &val16, sizeof(val16));
- break;
-
- case 4:
- val32 = little_endianize_int32((uint32_t) value);
- memcpy(byte_ptr, &val32, sizeof(val32));
- break;
-
- default:
- if (size >= 1) byte_ptr[0] = (uint8_t) (value >> (0 * 8));
- if (size >= 2) byte_ptr[1] = (uint8_t) (value >> (1 * 8));
- if (size >= 3) byte_ptr[2] = (uint8_t) (value >> (2 * 8));
- if (size >= 4) byte_ptr[3] = (uint8_t) (value >> (3 * 8));
- if (size >= 5) byte_ptr[4] = (uint8_t) (value >> (4 * 8));
- if (size >= 6) byte_ptr[5] = (uint8_t) (value >> (5 * 8));
- if (size >= 7) byte_ptr[6] = (uint8_t) (value >> (6 * 8));
- if (size >= 8) byte_ptr[7] = (uint8_t) (value >> (7 * 8));
- break;
- }
-}
-
-static inline uint64_t pick_integer_le(const void *ptr, size_t offset, size_t size)
-{
- uint64_t result = 0;
- const uint8_t *byte_ptr = ((const uint8_t *) ptr) + offset;
- uint16_t val16;
- uint32_t val32;
-
- switch (size)
- {
- case 1:
- result = *byte_ptr;
- break;
-
- case 2:
- memcpy(&val16, byte_ptr, sizeof(val16));
- result = little_endianize_int16(val16);
- break;
-
- case 4:
- memcpy(&val32, byte_ptr, sizeof(val32));
- result = little_endianize_int32(val32);
- break;
-
- default:
- if (size >= 1) result |= ((uint64_t) byte_ptr[0]) << (0 * 8);
- if (size >= 2) result |= ((uint64_t) byte_ptr[1]) << (1 * 8);
- if (size >= 3) result |= ((uint64_t) byte_ptr[2]) << (2 * 8);
- if (size >= 4) result |= ((uint64_t) byte_ptr[3]) << (3 * 8);
- if (size >= 5) result |= ((uint64_t) byte_ptr[4]) << (4 * 8);
- if (size >= 6) result |= ((uint64_t) byte_ptr[5]) << (5 * 8);
- if (size >= 7) result |= ((uint64_t) byte_ptr[6]) << (6 * 8);
- if (size >= 8) result |= ((uint64_t) byte_ptr[7]) << (7 * 8);
- break;
- }
- return result;
-}
-
-/* -----------------------------------------------------------------------
* Miscellaneous
* ----------------------------------------------------------------------- */
diff --git a/src/lib/formats/imd_dsk.cpp b/src/lib/formats/imd_dsk.cpp
index 51337a7f890..907440dc922 100644
--- a/src/lib/formats/imd_dsk.cpp
+++ b/src/lib/formats/imd_dsk.cpp
@@ -2,20 +2,28 @@
// copyright-holders:Miodrag Milanovic
/*********************************************************************
- formats/imd_dsk.c
+ formats/imd_dsk.cpp
IMD disk images
*********************************************************************/
-#include <string.h>
-#include <assert.h>
-#include "flopimg.h"
+#include "imd_dsk.h"
+#include "flopimg_legacy.h"
+
+#include "ioprocs.h"
+
+#include "osdcore.h" // osd_printf_*
+
+#include <cstring>
+
+
struct imddsk_tag
{
int heads;
int tracks;
+ int track_sectors[84*2]; /* number of sectors for each track */
int sector_size;
uint64_t track_offsets[84*2]; /* offset within data for each track */
};
@@ -53,6 +61,11 @@ static int imd_get_tracks_per_disk(floppy_image_legacy *floppy)
return get_tag(floppy)->tracks;
}
+static int imd_get_sectors_per_track(floppy_image_legacy *floppy, int head, int track)
+{
+ return get_tag(floppy)->track_sectors[(track<<1) + head];
+}
+
static uint64_t imd_get_track_offset(floppy_image_legacy *floppy, int head, int track)
{
return get_tag(floppy)->track_offsets[(track<<1) + head];
@@ -155,18 +168,18 @@ static floperr_t imd_expand_file(floppy_image_legacy *floppy , uint64_t offset ,
return FLOPPY_ERROR_SUCCESS;
}
- auto buffer = global_alloc_array(uint8_t , size_after_off);
+ auto buffer = std::make_unique<uint8_t []>(size_after_off);
// Read the part of file after offset
- floppy_image_read(floppy , buffer , offset , size_after_off);
+ floppy_image_read(floppy, buffer.get(), offset, size_after_off);
// Add zeroes
- floppy_image_write_filler(floppy , 0 , offset , amount);
+ floppy_image_write_filler(floppy, 0, offset, amount);
// Write back the part of file after offset
- floppy_image_write(floppy, buffer, offset + amount, size_after_off);
+ floppy_image_write(floppy, buffer.get(), offset + amount, size_after_off);
- global_free_array(buffer);
+ buffer.reset();
// Update track offsets
struct imddsk_tag *tag = get_tag(floppy);
@@ -205,7 +218,7 @@ static floperr_t imd_write_indexed_sector(floppy_image_legacy *floppy, int head,
if (err) {
return err;
}
- // Fall through!
+ [[fallthrough]];
case 1:
case 3:
@@ -327,9 +340,11 @@ FLOPPY_CONSTRUCT( imd_dsk_construct )
tag->heads = 1;
do {
floppy_image_read(floppy, header, pos, 5);
- if ((header[2] & 1)==1) tag->heads = 2;
- tag->track_offsets[(header[1]<<1) + (header[2] & 1)] = pos;
sector_num = header[3];
+ int track = (header[1]<<1) + (header[2] & 1);
+ if ((header[2] & 1)==1) tag->heads = 2;
+ tag->track_offsets[track] = pos;
+ tag->track_sectors[track] = sector_num;
pos += 5 + sector_num; // skip header and sector numbering map
if(header[2] & 0x80) pos += sector_num; // skip cylinder numbering map
if(header[2] & 0x40) pos += sector_num; // skip head numbering map
@@ -358,6 +373,7 @@ FLOPPY_CONSTRUCT( imd_dsk_construct )
callbacks->get_heads_per_disk = imd_get_heads_per_disk;
callbacks->get_tracks_per_disk = imd_get_tracks_per_disk;
callbacks->get_indexed_sector_info = imd_get_indexed_sector_info;
+ callbacks->get_sectors_per_track = imd_get_sectors_per_track;
return FLOPPY_ERROR_SUCCESS;
}
@@ -367,30 +383,27 @@ FLOPPY_CONSTRUCT( imd_dsk_construct )
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/imd_dsk.c
+ formats/imd_dsk.cpp
IMD disk images
*********************************************************************/
-#include "emu.h" // emu_fatalerror
-#include "imd_dsk.h"
-
imd_format::imd_format()
{
}
-const char *imd_format::name() const
+const char *imd_format::name() const noexcept
{
return "imd";
}
-const char *imd_format::description() const
+const char *imd_format::description() const noexcept
{
return "IMD disk image";
}
-const char *imd_format::extensions() const
+const char *imd_format::extensions() const noexcept
{
return "imd";
}
@@ -409,100 +422,210 @@ void imd_format::fixnum(char *start, char *end) const
};
}
-int imd_format::identify(io_generic *io, uint32_t form_factor)
+int imd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
char h[4];
+ auto const [err, actual] = read_at(io, 0, h, 4);
+ if(err || (4 != actual))
+ return 0;
- io_generic_read(io, h, 0, 4);
if(!memcmp(h, "IMD ", 4))
- return 100;
+ return FIFID_SIGN;
return 0;
}
-bool imd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool imd_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint64_t size = io_generic_size(io);
- std::vector<uint8_t> img(size);
- io_generic_read(io, &img[0], 0, size);
+ std::vector<uint8_t> comment;
+ std::vector<std::vector<uint8_t> > snum;
+ std::vector<std::vector<uint8_t> > tnum;
+ std::vector<std::vector<uint8_t> > hnum;
+
+ std::vector<uint8_t> mode;
+ std::vector<uint8_t> track;
+ std::vector<uint8_t> head;
+ std::vector<uint8_t> sector_count;
+ std::vector<uint8_t> ssize;
+
+ int trackmult;
+ uint64_t size;
+ if(io.length(size))
+ return false;
+ auto const [err, img, actual] = read_at(io, 0, size);
+ if(err || (actual != size))
+ return false;
- uint64_t pos;
- for(pos=0; pos < size && img[pos] != 0x1a; pos++) {};
+ uint64_t pos, savepos;
+ for(pos=0; pos < size && img[pos] != 0x1a; pos++) { }
pos++;
+ comment.resize(pos);
+ memcpy(&comment[0], &img[0], pos);
+
if(pos >= size)
return false;
int tracks, heads;
- image->get_maximal_geometry(tracks, heads);
-
- while(pos < size) {
- uint8_t mode = img[pos++];
+ image.get_maximal_geometry(tracks, heads);
+
+ mode.clear();
+ track.clear();
+ head.clear();
+ sector_count.clear();
+ ssize.clear();
+ trackmult = 1;
+
+ // we have to walk the whole file to find out the number of tracks
+ savepos = pos;
+ uint8_t maxtrack = 0;
+ while(pos < size)
+ {
+ pos++; // skip mode
uint8_t track = img[pos++];
uint8_t head = img[pos++];
uint8_t sector_count = img[pos++];
- uint8_t ssize = img[pos++];
+ uint8_t sector_size = img[pos++];
+ int actual_size = sector_size < 7 ? 128 << sector_size : 8192;
+
+ if (track > maxtrack)
+ {
+ maxtrack = track;
+ }
+
+ pos += sector_count;
+ if (head & 0x80)
+ {
+ pos += sector_count;
+ }
+ if (head & 0x40)
+ {
+ pos += sector_count;
+ }
+
+ for (int i = 0; i < sector_count; i++)
+ {
+ uint8_t stype = img[pos++];
+ if (stype == 0 || stype > 8)
+ {
+ }
+ else
+ {
+ if (stype == 2 || stype == 4 || stype == 6 || stype == 8)
+ {
+ pos++;
+ }
+ else
+ {
+ pos += actual_size;
+ }
+ }
+ }
+ }
- if(track >= tracks)
+ if(form_factor == floppy_image::FF_525)
+ {
+ // On 5.25, check if the drive is QD or HD but we're a 40 track
+ // image. If so, put the image on even tracks.
+ if ((has_variant(variants, floppy_image::DSQD)) ||
+ (has_variant(variants, floppy_image::DSHD)))
{
- osd_printf_error("imd_format: Track %d exceeds maximum of %d\n", track, tracks);
+ if (maxtrack <= 39)
+ trackmult = 2;
+ }
+ else
+ {
+ if (maxtrack > 42)
+ return false;
+ }
+ }
+
+ pos = savepos;
+ while(pos < size) {
+ mode.push_back(img[pos++]);
+ track.push_back(img[pos++]);
+ head.push_back(img[pos++]);
+ sector_count.push_back(img[pos++]);
+ ssize.push_back(img[pos++]);
+
+ if(track.back() >= tracks)
+ {
+ osd_printf_error("imd_format: Track %d exceeds maximum of %d\n", track.back(), tracks);
return false;
}
- if((head & 0x3f) >= heads)
+ if((head.back() & 0x3f) >= heads)
{
- osd_printf_error("imd_format: Head %d exceeds maximum of %d\n", head & 0x3f, heads);
+ osd_printf_error("imd_format: Head %d exceeds maximum of %d\n", head.back() & 0x3f, heads);
return false;
}
- if(ssize == 0xff)
+ if(ssize.back() == 0xff)
{
- osd_printf_error("imd_format: Unsupported variable sector size on track %d head %d", track, head & 0x3f);
+ osd_printf_error("imd_format: Unsupported variable sector size on track %d head %d", track.back(), head.back() & 0x3f);
return false;
}
- uint32_t actual_size = ssize < 7 ? 128 << ssize : 8192;
+ uint32_t actual_size = ssize.back() < 7 ? 128 << ssize.back() : 8192;
static const int rates[3] = { 500000, 300000, 250000 };
- bool fm = mode < 3;
- int rate = rates[mode % 3];
+ bool fm = mode.back() < 3;
+ int rate = rates[mode.back() % 3];
int rpm = form_factor == floppy_image::FF_8 || (form_factor == floppy_image::FF_525 && rate >= 300000) ? 360 : 300;
int cell_count = (fm ? 1 : 2)*rate*60/rpm;
- const uint8_t *snum = &img[pos];
- pos += sector_count;
- const uint8_t *tnum = head & 0x80 ? &img[pos] : nullptr;
- if(tnum)
- pos += sector_count;
- const uint8_t *hnum = head & 0x40 ? &img[pos] : nullptr;
- if(hnum)
- pos += sector_count;
+ //const uint8_t *snum = &img[pos];
+ snum.push_back(std::vector<uint8_t>(sector_count.back()));
+ memcpy(&snum.back()[0], &img[pos], sector_count.back());
+ pos += sector_count.back();
+
+ //const uint8_t *tnum = head & 0x80 ? &img[pos] : nullptr;
+ if (head.back() & 0x80)
+ {
+ tnum.push_back(std::vector<uint8_t>(sector_count.back()));
+ memcpy(&tnum.back()[0], &img[pos], sector_count.back());
+ pos += sector_count.back();
+ }
+ else
+ {
+ tnum.push_back(std::vector<uint8_t>(0));
+ }
+
+ //const uint8_t *hnum = head & 0x40 ? &img[pos] : nullptr;
+ if (head.back() & 0x40)
+ {
+ hnum.push_back(std::vector<uint8_t>(sector_count.back()));
+ memcpy(&hnum.back()[0], &img[pos], sector_count.back());
+ pos += sector_count.back();
+ }
+ else
+ {
+ hnum.push_back(std::vector<uint8_t>(0));
+ }
- head &= 0x3f;
+ uint8_t chead = head.back() & 0x3f;
int gap_3 = calc_default_pc_gap3_size(form_factor, actual_size);
desc_pc_sector sects[256];
- for(int i=0; i<sector_count; i++) {
- uint8_t stype = img[pos++];
- sects[i].track = tnum ? tnum[i] : track;
- sects[i].head = hnum ? hnum[i] : head;
- sects[i].sector = snum[i];
- sects[i].size = ssize;
+ for(int i=0; i<sector_count.back(); i++) {
+ uint8_t stype = img[pos++];
+ sects[i].track = tnum.back().size() ? tnum.back()[i] : track.back();
+ sects[i].head = hnum.back().size() ? hnum.back()[i] : head.back();
+ sects[i].sector = snum.back()[i];
+ sects[i].size = ssize.back();
sects[i].actual_size = actual_size;
if(stype == 0 || stype > 8) {
sects[i].data = nullptr;
-
} else {
sects[i].deleted = stype == 3 || stype == 4 || stype == 7 || stype == 8;
sects[i].bad_crc = stype == 5 || stype == 6 || stype == 7 || stype == 8;
if(stype == 2 || stype == 4 || stype == 6 || stype == 8) {
- sects[i].data = global_alloc_array(uint8_t, actual_size);
+ sects[i].data = new uint8_t [actual_size];
memset(sects[i].data, img[pos++], actual_size);
-
} else {
sects[i].data = &img[pos];
pos += actual_size;
@@ -510,26 +633,25 @@ bool imd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
}
}
- if(sector_count) {
+ if(sector_count.back()) {
if(fm) {
- build_pc_track_fm(track, head, image, cell_count, sector_count, sects, gap_3);
+ build_pc_track_fm(track.back()*trackmult, chead, image, cell_count, sector_count.back(), sects, gap_3);
} else {
- build_pc_track_mfm(track, head, image, cell_count, sector_count, sects, gap_3);
+ build_pc_track_mfm(track.back()*trackmult, chead, image, cell_count, sector_count.back(), sects, gap_3);
}
}
- for(int i=0; i<sector_count; i++)
+ for(int i=0; i< sector_count.back(); i++)
if(sects[i].data && (sects[i].data < &img[0] || sects[i].data >= (&img[0] + size)))
- global_free_array(sects[i].data);
+ delete [] sects[i].data;
}
return true;
}
-
-bool imd_format::supports_save() const
+bool imd_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_IMD_FORMAT = &floppy_image_format_creator<imd_format>;
+const imd_format FLOPPY_IMD_FORMAT;
diff --git a/src/lib/formats/imd_dsk.h b/src/lib/formats/imd_dsk.h
index 39e43329969..ac0b613e16f 100644
--- a/src/lib/formats/imd_dsk.h
+++ b/src/lib/formats/imd_dsk.h
@@ -12,24 +12,23 @@
#include "flopimg.h"
-
class imd_format : public floppy_image_format_t
{
public:
imd_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
private:
void fixnum(char *start, char *end) const;
};
-extern const floppy_format_type FLOPPY_IMD_FORMAT;
+extern const imd_format FLOPPY_IMD_FORMAT;
#endif // MAME_FORMATS_IMD_DSK_H
diff --git a/src/lib/formats/img_dsk.cpp b/src/lib/formats/img_dsk.cpp
new file mode 100644
index 00000000000..66e097c281e
--- /dev/null
+++ b/src/lib/formats/img_dsk.cpp
@@ -0,0 +1,438 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ img_dsk.cpp
+
+ "IMG" disk format
+
+ This format is just a raw image of every sector on SSDD & SSSD 8"
+ floppy disks as used in Intel MDS-II systems.
+ Files with this format have no header/trailer and are exactly
+ 512512 bytes in size (52 sectors, 1 head, 77 tracks,
+ 128 bytes per sector) for SSDD format or 256256 bytes (26 sectors,
+ 1 head, 77 tracks, 128 bytes per sector) for SSSD format
+
+*********************************************************************/
+
+#include "img_dsk.h"
+
+#include "coretmpl.h" // BIT
+#include "ioprocs.h"
+
+#include "osdcore.h" // osd_printf_*
+
+
+// Debugging
+#define VERBOSE 0
+#define LOG(...) do { if (VERBOSE) osd_printf_info(__VA_ARGS__); } while (false)
+
+constexpr unsigned CELL_SIZE = 1200; // Bit cell size of MMFM format (1 µs)
+constexpr unsigned CELL_SIZE_FM = 2400; // Bit cell size of FM format (2 µs)
+constexpr uint8_t INDEX_AM = 0x0c; // Index address mark
+constexpr uint8_t ID_AM = 0x0e; // ID address mark
+constexpr uint8_t DATA_AM = 0x0b; // Data address mark
+constexpr uint8_t AM_CLOCK = 0x70; // Clock pattern of AM
+constexpr unsigned PREIDX_GAP = 45; // Size of pre-index gap
+constexpr unsigned SYNC_00_LEN = 18; // 00's in sync (gaps 1, 2, 3)
+constexpr unsigned SYNC_FF_LEN = 10; // FF's in sync (gaps 1, 2, 3)
+constexpr int ID_DATA_OFFSET = 35 * 16; // Nominal distance (in cells) between ID & DATA AM
+// Size of image file (SSDD)
+constexpr unsigned IMG_IMAGE_SIZE = img_format::TRACKS * img_format::HEADS * img_format::SECTORS * img_format::SECTOR_SIZE;
+// Size of image file (SSSD)
+constexpr unsigned IMG_IMAGE_SIZE_FM = img_format::TRACKS * img_format::HEADS * img_format::SECTORS_FM * img_format::SECTOR_SIZE;
+constexpr uint16_t CRC_POLY = 0x1021; // CRC-CCITT
+
+img_format::img_format()
+{
+}
+
+int img_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if (io.length(size)) {
+ return 0;
+ }
+
+ if (((form_factor == floppy_image::FF_8) || (form_factor == floppy_image::FF_UNKNOWN)) &&
+ (size == IMG_IMAGE_SIZE || size == IMG_IMAGE_SIZE_FM)) {
+ return FIFID_SIZE;
+ } else {
+ return 0;
+ }
+}
+
+bool img_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ uint64_t size;
+ if (io.length(size) || (size != IMG_IMAGE_SIZE && size != IMG_IMAGE_SIZE_FM)) {
+ return false;
+ }
+ bool is_dd = size == IMG_IMAGE_SIZE;
+ image.set_variant(is_dd ? floppy_image::SSDD : floppy_image::SSSD);
+
+ // Suck in the whole image
+ auto const [err, image_data, actual] = read_at(io, 0, size);
+ if (err || (actual != size)) {
+ return false;
+ }
+
+ for (unsigned cyl = 0; cyl < TRACKS; cyl++) {
+ if (is_dd) {
+ // SSDD (MMFM)
+ std::vector<uint32_t> track_data;
+
+ write_gap(track_data, 0 , PREIDX_GAP);
+ uint16_t crc = 0;
+ write_mmfm_byte(track_data , INDEX_AM , crc, AM_CLOCK);
+
+ // Compute interleave factor and skew for current track
+ unsigned il_factor;
+ unsigned skew;
+ if (cyl == 0) {
+ il_factor = 1;
+ skew = 51;
+ } else if (cyl == 1) {
+ il_factor = 4;
+ skew = 48;
+ } else {
+ il_factor = 5;
+ skew = (47 + 45 * (cyl - 2)) % 52;
+ }
+ std::vector<uint8_t> sector_list = interleaved_sectors(SECTORS, il_factor);
+
+ for (unsigned sector = 0; sector < SECTORS; sector++) {
+ unsigned real_sector = sector_list[ (sector + skew) % SECTORS ];
+ unsigned offset_in_image = (real_sector - 1 + cyl * SECTORS) * SECTOR_SIZE;
+ write_sector(track_data , cyl , real_sector , &image_data[ offset_in_image ]);
+ }
+ fill_with_gap4(track_data);
+ generate_track_from_levels(cyl , 0 , track_data , 0 , image);
+ } else {
+ // SSSD (FM)
+ // Compute interleave factor and skew for current track
+ unsigned il_factor;
+ unsigned skew;
+ if (cyl == 0) {
+ il_factor = 1;
+ skew = 25;
+ } else if (cyl == 1) {
+ il_factor = 12;
+ skew = 14;
+ } else {
+ il_factor = 6;
+ skew = (20 + 21 * (cyl - 2)) % 26;
+ }
+ std::vector<uint8_t> sector_list = interleaved_sectors(SECTORS_FM, il_factor);
+ desc_pc_sector sects[ SECTORS_FM ];
+ for (unsigned sector = 0; sector < SECTORS_FM; sector++) {
+ unsigned real_sector = sector_list[ (sector + skew) % SECTORS_FM ];
+ unsigned offset_in_image = (real_sector - 1 + cyl * SECTORS_FM) * SECTOR_SIZE;
+ sects[ sector ].actual_size = SECTOR_SIZE;
+ sects[ sector ].track = cyl;
+ sects[ sector ].head = 0;
+ sects[ sector ].size = 0;
+ sects[ sector ].sector = real_sector;
+ sects[ sector ].bad_crc = false;
+ sects[ sector ].deleted = false;
+ sects[ sector ].data = &image_data[offset_in_image];
+ }
+ build_pc_track_fm(cyl, 0, image, 83333, SECTORS_FM, sects, 33, 46, 32, 11);
+ }
+ }
+ return true;
+}
+
+bool img_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ bool is_dd = image.get_variant() == floppy_image::SSDD;
+
+ for (int cyl = 0; cyl < TRACKS; cyl++) {
+ if (is_dd) {
+ auto bitstream = generate_bitstream_from_track(cyl , 0 , CELL_SIZE , image , 0);
+ int pos = 0;
+ unsigned track_no , sector_no;
+ uint8_t sector_data[ SECTOR_SIZE ];
+ while (get_next_sector(bitstream , pos , track_no , sector_no , sector_data)) {
+ if (track_no == cyl && sector_no >= 1 && sector_no <= SECTORS) {
+ unsigned offset_in_image = (cyl * SECTORS + sector_no - 1) * SECTOR_SIZE;
+ /*auto const [err, actual] =*/ write_at(io, offset_in_image, sector_data, SECTOR_SIZE); // FIXME: check for errors
+ }
+ }
+ } else {
+ auto bitstream = generate_bitstream_from_track(cyl , 0 , CELL_SIZE_FM , image , 0);
+ auto sects = extract_sectors_from_bitstream_fm_pc(bitstream);
+ for (unsigned s = 1; s <= SECTORS_FM; s++) {
+ unsigned offset_in_image = (cyl * SECTORS_FM + s - 1) * SECTOR_SIZE;
+ /*auto const [err, actual] =*/ write_at(io, offset_in_image, sects[ s ].data(), SECTOR_SIZE); // FIXME: check for errors and premature EOF
+ }
+ }
+ }
+ return true;
+}
+
+const char *img_format::name() const noexcept
+{
+ return "mds2";
+}
+
+const char *img_format::description() const noexcept
+{
+ return "MDS-II floppy disk image";
+}
+
+const char *img_format::extensions() const noexcept
+{
+ return "img";
+}
+
+bool img_format::supports_save() const noexcept
+{
+ return true;
+}
+
+std::vector<uint8_t> img_format::interleaved_sectors(unsigned sects, unsigned il_factor)
+{
+ std::vector<uint8_t> out(sects);
+ unsigned idx = 0;
+ for (unsigned s = 0; s < sects; s++) {
+ while (out[ idx ] != 0) {
+ if (++idx >= sects) {
+ idx = 0;
+ }
+ }
+ out[ idx ] = s + 1;
+ idx += il_factor;
+ if (idx >= sects) {
+ idx -= sects;
+ }
+ }
+ return out;
+}
+
+void img_format::write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit , uint16_t &crc)
+{
+ bool had_transition = buffer.size() < 2 ? false : bit_r(buffer, buffer.size() - 1) || bit_r(buffer , buffer.size() - 2);
+ clock_bit = !data_bit && (clock_bit || !had_transition);
+ bit_w(buffer , clock_bit , CELL_SIZE);
+ bit_w(buffer , data_bit , CELL_SIZE);
+
+ if (util::BIT(crc , 15) ^ data_bit) {
+ crc = (crc << 1) ^ CRC_POLY;
+ } else {
+ crc <<= 1;
+ }
+}
+
+void img_format::write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint16_t &crc , uint8_t clock)
+{
+ for (int i = 7; i >= 0; i--) {
+ write_mmfm_bit(buffer , util::BIT(data , i) , util::BIT(clock , i) , crc);
+ }
+}
+
+void img_format::write_sync(std::vector<uint32_t> &buffer)
+{
+ write_gap(buffer , SYNC_00_LEN , SYNC_FF_LEN);
+}
+
+void img_format::write_crc(std::vector<uint32_t> &buffer , uint16_t crc)
+{
+ uint16_t xcrc = crc;
+ // Note that CRC is stored with MSB (x^15) first
+ for (unsigned i = 0; i < 16; i++) {
+ write_mmfm_bit(buffer , util::BIT(crc , 15 - i) , 0 , xcrc);
+ }
+}
+
+void img_format::write_gap(std::vector<uint32_t> &buffer , unsigned size_00 , unsigned size_ff)
+{
+ uint16_t crc = 0;
+ for (unsigned i = 0; i < size_00; ++i) {
+ write_mmfm_byte(buffer, 0 , crc);
+ }
+ for (unsigned i = 0; i < size_ff; ++i) {
+ write_mmfm_byte(buffer, 0xff , crc);
+ }
+}
+
+void img_format::write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_no , const uint8_t *sect_data)
+{
+ // **** On-disk format of a sector ****
+ //
+ // | Offset | Size | Value | Content |
+ // |--------+------+-------+----------------------|
+ // | 0 | 18 | 00 | Gap 1/3 |
+ // | 18 | 10 | ff | Gap 1/3 |
+ // | 28 | 1 | 0e | ID AM (clock = 70) |
+ // | 29 | 1 | xx | Track no. |
+ // | 30 | 1 | 00 | N/U |
+ // | 31 | 1 | xx | Sector no. |
+ // | 32 | 1 | 00 | N/U |
+ // | 33 | 2 | xx | ID CRC |
+ // | 35 | 18 | 00 | Gap 2 |
+ // | 53 | 10 | ff | Gap 2 |
+ // | 63 | 1 | 0b | Data AM (clock = 70) |
+ // | 64 | 128 | xx | Sector data |
+ // | 192 | 2 | xx | Data CRC |
+ // | 194 | | | |
+
+ // Gap1
+ write_sync(buffer);
+ // ID AM
+ uint16_t crc = 0;
+ write_mmfm_byte(buffer , ID_AM , crc , AM_CLOCK);
+ // Track #
+ write_mmfm_byte(buffer , track_no , crc);
+ write_mmfm_byte(buffer , 0 , crc);
+ // Sector #
+ write_mmfm_byte(buffer , sect_no , crc);
+ write_mmfm_byte(buffer , 0 , crc);
+ // ID CRC
+ write_crc(buffer , crc);
+ // Gap 2
+ write_sync(buffer);
+ // Data AM
+ crc = 0;
+ write_mmfm_byte(buffer , DATA_AM , crc, AM_CLOCK);
+ for (unsigned i = 0; i < SECTOR_SIZE; i++) {
+ // Data
+ write_mmfm_byte(buffer , sect_data[ i ] , crc);
+ }
+ // Data CRC
+ write_crc(buffer , crc);
+}
+
+void img_format::fill_with_gap4(std::vector<uint32_t> &buffer)
+{
+ // Cell count in a track (1 µs cells in a 1/6 s track)
+ unsigned cell_count = (500000 * 120) / 360;
+ unsigned cells_in_buffer = buffer.size();
+ // Size of gap 4
+ unsigned gap_4 = (cell_count - cells_in_buffer) / 16;
+ // Gap 4
+ write_gap(buffer , gap_4 , 0);
+ // Last cell to round everything up to 2E+8
+ if (buffer.size() * CELL_SIZE < 200000000) {
+ bit_w(buffer , false , 200000000 - buffer.size() * CELL_SIZE);
+ }
+}
+
+std::vector<uint8_t> img_format::get_next_id_n_block(const std::vector<bool> &bitstream , int& pos , int& start_pos)
+{
+ std::vector<uint8_t> res;
+ uint8_t data_sr;
+ uint8_t clock_sr;
+
+ // Look for either sync + ID AM or sync + DATA AM
+ do {
+ unsigned cnt_trans = 0;
+ while (pos < bitstream.size() && cnt_trans < 34) {
+ bool bit = bitstream[pos];
+ pos++;
+ if (cnt_trans < 32) {
+ if (!(util::BIT(cnt_trans , 0) ^ bit)) {
+ cnt_trans++;
+ } else {
+ cnt_trans = 0;
+ }
+ } else if (cnt_trans == 32) {
+ if (!bit) {
+ start_pos = pos;
+ cnt_trans++;
+ } else {
+ cnt_trans = 0;
+ }
+ } else if (!bit) {
+ cnt_trans++;
+ } else {
+ cnt_trans = 32;
+ }
+ }
+
+ if (pos == bitstream.size()) {
+ // End of track reached
+ return res;
+ }
+
+ // Get AM
+ data_sr = clock_sr = 0;
+ for (unsigned i = 0; i < 7; ++i) {
+ bool bit = bitstream[pos];
+ pos++;
+ clock_sr = (clock_sr << 1) | bit;
+ bit = bitstream[pos];
+ pos++;
+ data_sr = (data_sr << 1) | bit;
+ }
+ if (pos >= bitstream.size()) {
+ return res;
+ }
+ } while (clock_sr != AM_CLOCK);
+
+ // ID blocks: Track no. + 0 + sector no. + 0 + CRC
+ // Data blocks: Sector data + CRC
+ res.push_back(data_sr);
+ unsigned to_dump;
+ if (data_sr == ID_AM) {
+ to_dump = 4;
+ } else if (data_sr == DATA_AM) {
+ to_dump = SECTOR_SIZE;
+ } else {
+ to_dump = 0;
+ }
+
+ pos++;
+ for (unsigned i = 0; i < to_dump && pos < bitstream.size(); i++) {
+ data_sr = 0;
+ unsigned j;
+ for (j = 0; j < 8 && pos < bitstream.size(); j++) {
+ bool bit = bitstream[pos];
+ pos += 2;
+ data_sr = (data_sr << 1) | bit;
+ }
+ if (j == 8) {
+ res.push_back(data_sr);
+ }
+ }
+ return res;
+}
+
+bool img_format::get_next_sector(const std::vector<bool> &bitstream , int& pos , unsigned& track , unsigned& sector , uint8_t *sector_data)
+{
+ std::vector<uint8_t> block;
+ while (true) {
+ // Scan for ID block first
+ int id_pos = 0;
+ while (true) {
+ if (block.size() == 0) {
+ block = get_next_id_n_block(bitstream , pos , id_pos);
+ if (block.size() == 0) {
+ return false;
+ }
+ }
+ if (block[ 0 ] == ID_AM && block.size() >= 5) {
+ track = block[ 1 ];
+ sector = block[ 3 ];
+ break;
+ } else {
+ block.clear();
+ }
+ }
+ // Then for DATA block
+ int data_pos = 0;
+ block = get_next_id_n_block(bitstream , pos , data_pos);
+ if (block.size() == 0) {
+ return false;
+ }
+ if (block[ 0 ] == DATA_AM && block.size() >= (SECTOR_SIZE + 1) && abs((data_pos - id_pos) - ID_DATA_OFFSET) <= 64) {
+ break;
+ }
+ }
+
+ memcpy(sector_data , block.data() + 1 , SECTOR_SIZE);
+
+ return true;
+}
+
+const img_format FLOPPY_IMG_FORMAT;
diff --git a/src/lib/formats/img_dsk.h b/src/lib/formats/img_dsk.h
new file mode 100644
index 00000000000..567a18b273d
--- /dev/null
+++ b/src/lib/formats/img_dsk.h
@@ -0,0 +1,54 @@
+// license:BSD-3-Clause
+// copyright-holders: F. Ulivi
+/*********************************************************************
+
+ img_dsk.h
+
+ "IMG" disk format for SSDD & SSSD Intel MDS-II 8" disks
+
+*********************************************************************/
+#ifndef MAME_FORMATS_IMG_DSK_H
+#define MAME_FORMATS_IMG_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+
+#include <vector>
+
+class img_format : public floppy_image_format_t
+{
+public:
+ // Geometry constants
+ static constexpr unsigned TRACKS = 77;
+ static constexpr unsigned HEADS = 1;
+ static constexpr unsigned SECTORS = 52;
+ static constexpr unsigned SECTORS_FM = 26;
+ static constexpr unsigned SECTOR_SIZE = 128;
+
+ img_format();
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
+
+private:
+ static std::vector<uint8_t> interleaved_sectors(unsigned sects, unsigned il_factor);
+ static void write_mmfm_bit(std::vector<uint32_t> &buffer , bool data_bit , bool clock_bit , uint16_t &crc);
+ static void write_mmfm_byte(std::vector<uint32_t> &buffer , uint8_t data , uint16_t &crc , uint8_t clock = 0);
+ static void write_sync(std::vector<uint32_t> &buffer);
+ static void write_crc(std::vector<uint32_t> &buffer , uint16_t crc);
+ static void write_gap(std::vector<uint32_t> &buffer , unsigned size_00 , unsigned size_ff);
+ static void write_sector(std::vector<uint32_t> &buffer , uint8_t track_no , uint8_t sect_no , const uint8_t *sect_data);
+ static void fill_with_gap4(std::vector<uint32_t> &buffer);
+ static std::vector<uint8_t> get_next_id_n_block(const std::vector<bool> &bitstream , int& pos , int& start_pos);
+ static bool get_next_sector(const std::vector<bool> &bitstream , int& pos , unsigned& track , unsigned& sector , uint8_t *sector_data);
+};
+
+extern const img_format FLOPPY_IMG_FORMAT;
+
+#endif // MAME_FORMATS_IMG_DSK_H
diff --git a/src/lib/formats/ioprocs.cpp b/src/lib/formats/ioprocs.cpp
deleted file mode 100644
index 69943c13dc0..00000000000
--- a/src/lib/formats/ioprocs.cpp
+++ /dev/null
@@ -1,215 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Nathan Woods
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include "osdcore.h"
-#include "ioprocs.h"
-#include "corefile.h"
-
-
-/*********************************************************************
- ioprocs implementation on stdio
-*********************************************************************/
-
-static void stdio_closeproc(void *file)
-{
- fclose((FILE*)file);
-}
-
-static int stdio_seekproc(void *file, int64_t offset, int whence)
-{
- return fseek((FILE*)file, (long) offset, whence);
-}
-
-static size_t stdio_readproc(void *file, void *buffer, size_t length)
-{
- return fread(buffer, 1, length, (FILE*)file);
-}
-
-static size_t stdio_writeproc(void *file, const void *buffer, size_t length)
-{
- return fwrite(buffer, 1, length, (FILE*)file);
-}
-
-static uint64_t stdio_filesizeproc(void *file)
-{
- long l, sz;
- l = ftell((FILE*)file);
- if (fseek((FILE*)file, 0, SEEK_END))
- return (size_t) -1;
- sz = ftell((FILE*)file);
- if (fseek((FILE*)file, l, SEEK_SET))
- return (size_t) -1;
- return (size_t) sz;
-}
-
-const struct io_procs stdio_ioprocs =
-{
- stdio_closeproc,
- stdio_seekproc,
- stdio_readproc,
- stdio_writeproc,
- stdio_filesizeproc
-};
-
-const struct io_procs stdio_ioprocs_noclose =
-{
- nullptr,
- stdio_seekproc,
- stdio_readproc,
- stdio_writeproc,
- stdio_filesizeproc
-};
-
-/*********************************************************************
- ioprocs implementation on corefile
-*********************************************************************/
-
-static void corefile_closeproc(void *file)
-{
- delete (util::core_file*)file;
-}
-
-static int corefile_seekproc(void *file, int64_t offset, int whence)
-{
- return ((util::core_file*)file)->seek(offset, whence);
-}
-
-static size_t corefile_readproc(void *file, void *buffer, size_t length)
-{
- return ((util::core_file*)file)->read(buffer, length);
-}
-
-static size_t corefile_writeproc(void *file, const void *buffer, size_t length)
-{
- return ((util::core_file*)file)->write(buffer, length);
-}
-
-static uint64_t corefile_filesizeproc(void *file)
-{
- const auto l = ((util::core_file*)file)->tell();
- if (((util::core_file*)file)->seek(0, SEEK_END))
- return (size_t) -1;
- const auto sz = ((util::core_file*)file)->tell();
- if (((util::core_file*)file)->seek(l, SEEK_SET))
- return uint64_t(-1);
- return uint64_t(sz);
-}
-
-const struct io_procs corefile_ioprocs =
-{
- corefile_closeproc,
- corefile_seekproc,
- corefile_readproc,
- corefile_writeproc,
- corefile_filesizeproc
-};
-
-const struct io_procs corefile_ioprocs_noclose =
-{
- nullptr,
- corefile_seekproc,
- corefile_readproc,
- corefile_writeproc,
- corefile_filesizeproc
-};
-
-
-
-/*********************************************************************
- calls for accessing generic IO
-*********************************************************************/
-
-static void io_generic_seek(struct io_generic *genio, uint64_t offset)
-{
- genio->procs->seekproc(genio->file, offset, SEEK_SET);
-}
-
-
-
-void io_generic_close(struct io_generic *genio)
-{
- if (genio->procs->closeproc)
- genio->procs->closeproc(genio->file);
-}
-
-
-
-void io_generic_read(struct io_generic *genio, void *buffer, uint64_t offset, size_t length)
-{
- uint64_t size;
- size_t bytes_read;
-
- size = io_generic_size(genio);
- if (size <= offset)
- {
- bytes_read = 0;
- }
- else
- {
- io_generic_seek(genio, offset);
- bytes_read = genio->procs->readproc(genio->file, buffer, length);
- }
- memset(((uint8_t *) buffer) + bytes_read, genio->filler, length - bytes_read);
-}
-
-
-
-void io_generic_write(struct io_generic *genio, const void *buffer, uint64_t offset, size_t length)
-{
- uint64_t filler_size = 0;
- char filler_buffer[1024];
- size_t bytes_to_write;
- uint64_t size;
-
- size = io_generic_size(genio);
-
- if (size < offset)
- {
- filler_size = offset - size;
- offset = size;
- }
-
- io_generic_seek(genio, offset);
-
- if (filler_size)
- {
- memset(filler_buffer, genio->filler, sizeof(filler_buffer));
- do
- {
- bytes_to_write = (filler_size > sizeof(filler_buffer)) ? sizeof(filler_buffer) : (size_t) filler_size;
- genio->procs->writeproc(genio->file, filler_buffer, bytes_to_write);
- filler_size -= bytes_to_write;
- }
- while(filler_size > 0);
- }
-
- if (length > 0)
- genio->procs->writeproc(genio->file, buffer, length);
-}
-
-
-
-void io_generic_write_filler(struct io_generic *genio, uint8_t filler, uint64_t offset, size_t length)
-{
- uint8_t buffer[512];
- size_t this_length;
-
- memset(buffer, filler, std::min(length, sizeof(buffer)));
-
- while(length > 0)
- {
- this_length = std::min(length, sizeof(buffer));
- io_generic_write(genio, buffer, offset, this_length);
- offset += this_length;
- length -= this_length;
- }
-}
-
-
-
-uint64_t io_generic_size(struct io_generic *genio)
-{
- return genio->procs->filesizeproc(genio->file);
-}
diff --git a/src/lib/formats/ioprocs.h b/src/lib/formats/ioprocs.h
deleted file mode 100644
index c569e52dd2b..00000000000
--- a/src/lib/formats/ioprocs.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Nathan Woods
-/*********************************************************************
-
- ioprocs.h
-
- File IO abstraction layer
-
-*********************************************************************/
-#ifndef MAME_FORMATS_IOPROCS_H
-#define MAME_FORMATS_IOPROCS_H
-
-#pragma once
-
-#include <stdint.h>
-#include <stdlib.h>
-
-
-
-/***************************************************************************
-
- Type definitions
-
-***************************************************************************/
-
-struct io_procs
-{
- void (*closeproc)(void *file);
- int (*seekproc)(void *file, int64_t offset, int whence);
- size_t (*readproc)(void *file, void *buffer, size_t length);
- size_t (*writeproc)(void *file, const void *buffer, size_t length);
- uint64_t (*filesizeproc)(void *file);
-};
-
-
-
-struct io_generic
-{
- const struct io_procs *procs;
- void *file;
- uint8_t filler;
-};
-
-
-/***************************************************************************
-
- Globals
-
-***************************************************************************/
-
-extern const struct io_procs stdio_ioprocs;
-extern const struct io_procs stdio_ioprocs_noclose;
-extern const struct io_procs corefile_ioprocs;
-extern const struct io_procs corefile_ioprocs_noclose;
-
-
-
-/***************************************************************************
-
- Prototypes
-
-***************************************************************************/
-
-void io_generic_close(struct io_generic *genio);
-void io_generic_read(struct io_generic *genio, void *buffer, uint64_t offset, size_t length);
-void io_generic_write(struct io_generic *genio, const void *buffer, uint64_t offset, size_t length);
-void io_generic_write_filler(struct io_generic *genio, uint8_t filler, uint64_t offset, size_t length);
-uint64_t io_generic_size(struct io_generic *genio);
-
-#endif // MAME_FORMATS_IOPROCS_H
diff --git a/src/lib/formats/ipf_dsk.cpp b/src/lib/formats/ipf_dsk.cpp
index da50e850ea8..b47eca030f2 100644
--- a/src/lib/formats/ipf_dsk.cpp
+++ b/src/lib/formats/ipf_dsk.cpp
@@ -1,63 +1,129 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
-#include <assert.h>
#include "ipf_dsk.h"
-const floppy_format_type FLOPPY_IPF_FORMAT = &floppy_image_format_creator<ipf_format>;
+#include "ioprocs.h"
+#include "multibyte.h"
-ipf_format::ipf_format(): tinfos(nullptr), tcount(0), type(0), release(0), revision(0), encoder_type(0),
-encoder_revision(0), origin(0), min_cylinder(0), max_cylinder(0), min_head(0), max_head(0), credit_day(0), credit_time(0)
-{
-}
+#include <cstring>
+
+
+struct ipf_format::ipf_decode {
+ struct track_info {
+ uint32_t cylinder = 0, head = 0, type = 0;
+ uint32_t sigtype = 0, process = 0, reserved[3] = { 0, 0, 0 };
+ uint32_t size_bytes = 0, size_cells = 0;
+ uint32_t index_bytes = 0, index_cells = 0;
+ uint32_t datasize_cells = 0, gapsize_cells = 0;
+ uint32_t block_count = 0, weak_bits = 0;
+
+ uint32_t data_size_bits = 0;
+
+ bool info_set = false;
+
+ const uint8_t *data = nullptr;
+ uint32_t data_size = 0;
+ };
+
+ std::vector<track_info> tinfos;
+ uint32_t tcount = 0;
+
+ uint32_t type = 0, release = 0, revision = 0;
+ uint32_t encoder_type = 0, encoder_revision = 0, origin = 0;
+ uint32_t min_cylinder = 0, max_cylinder = 0, min_head = 0, max_head = 0;
+ uint32_t credit_day = 0, credit_time = 0;
+ uint32_t platform[4] = {}, extra[5] = {};
+
+ uint32_t crc32r(const uint8_t *data, uint32_t size);
+
+ bool parse_info(const uint8_t *info);
+ bool parse_imge(const uint8_t *imge);
+ bool parse_data(const uint8_t *data, uint32_t &pos, uint32_t max_extra_size);
+
+ bool scan_one_tag(uint8_t *data, size_t size, uint32_t &pos, uint8_t *&tag, uint32_t &tsize);
+ bool scan_all_tags(uint8_t *data, size_t size);
+ static uint32_t rb(const uint8_t *&p, int count);
+
+ track_info *get_index(uint32_t idx);
+
+ void track_write_raw(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t cells, bool &context);
+ void track_write_mfm(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t start_offset, uint32_t patlen, uint32_t cells, bool &context);
+ void track_write_weak(std::vector<uint32_t>::iterator &tpos, uint32_t cells);
+ bool generate_block_data(const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, std::vector<uint32_t>::iterator tlimit, bool &context);
-const char *ipf_format::name() const
+ bool gap_description_to_reserved_size(const uint8_t *&data, const uint8_t *dlimit, uint32_t &res_size);
+ bool generate_gap_from_description(const uint8_t *&data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, uint32_t size, bool pre, bool &context);
+ bool generate_block_gap_0(uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, std::vector<uint32_t>::iterator &tpos, bool &context);
+ bool generate_block_gap_1(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context);
+ bool generate_block_gap_2(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context);
+ bool generate_block_gap_3(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context);
+ bool generate_block_gap(uint32_t gap_type, uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, bool &context);
+
+ bool generate_block(const track_info &t, uint32_t idx, uint32_t ipos, std::vector<uint32_t> &track, uint32_t &pos, uint32_t &dpos, uint32_t &gpos, uint32_t &spos, bool &context);
+ uint32_t block_compute_real_size(const track_info &t);
+
+ void timing_set(std::vector<uint32_t> &track, uint32_t start, uint32_t end, uint32_t time);
+ bool generate_timings(const track_info &t, std::vector<uint32_t> &track, const std::vector<uint32_t> &data_pos, const std::vector<uint32_t> &gap_pos);
+
+ void rotate(std::vector<uint32_t> &track, uint32_t offset, uint32_t size);
+ void mark_track_splice(std::vector<uint32_t> &track, uint32_t offset, uint32_t size);
+ bool generate_track(track_info &t, floppy_image &image);
+ bool generate_tracks(floppy_image &image);
+
+ bool parse(uint8_t *data, size_t size, floppy_image &image);
+};
+
+
+const ipf_format FLOPPY_IPF_FORMAT;
+
+const char *ipf_format::name() const noexcept
{
return "ipf";
}
-const char *ipf_format::description() const
+const char *ipf_format::description() const noexcept
{
return "SPS floppy disk image";
}
-const char *ipf_format::extensions() const
+const char *ipf_format::extensions() const noexcept
{
return "ipf";
}
-bool ipf_format::supports_save() const
+bool ipf_format::supports_save() const noexcept
{
return false;
}
-int ipf_format::identify(io_generic *io, uint32_t form_factor)
+int ipf_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
static const uint8_t refh[12] = { 0x43, 0x41, 0x50, 0x53, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0xd5, 0x73, 0xba };
uint8_t h[12];
- io_generic_read(io, h, 0, 12);
+ auto const [err, actual] = read_at(io, 0, h, 12);
+ if(err || (12 != actual))
+ return 0;
if(!memcmp(h, refh, 12))
- return 100;
+ return FIFID_SIGN;
return 0;
}
-bool ipf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool ipf_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint64_t size = io_generic_size(io);
- std::vector<uint8_t> data(size);
- io_generic_read(io, &data[0], 0, size);
- bool res = parse(data, image);
- return res;
-}
-
-uint32_t ipf_format::r32(const uint8_t *p)
-{
- return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ uint64_t size;
+ if(io.length(size))
+ return false;
+ auto const [err, data, actual] = read_at(io, 0, size);
+ if(err || (actual != size))
+ return false;
+ ipf_decode dec;
+ return dec.parse(data.get(), size, image);
}
-uint32_t ipf_format::rb(const uint8_t *&p, int count)
+uint32_t ipf_format::ipf_decode::rb(const uint8_t *&p, int count)
{
uint32_t v = 0;
for(int i=0; i<count; i++)
@@ -65,7 +131,7 @@ uint32_t ipf_format::rb(const uint8_t *&p, int count)
return v;
}
-uint32_t ipf_format::crc32r(const uint8_t *data, uint32_t size)
+uint32_t ipf_format::ipf_decode::crc32r(const uint8_t *data, uint32_t size)
{
// Reversed crc32
uint32_t crc = 0xffffffff;
@@ -80,117 +146,113 @@ uint32_t ipf_format::crc32r(const uint8_t *data, uint32_t size)
return ~crc;
}
-bool ipf_format::parse(std::vector<uint8_t> &data, floppy_image *image)
+bool ipf_format::ipf_decode::parse(uint8_t *data, size_t size, floppy_image &image)
{
- image->set_variant(floppy_image::DSDD); // Not handling anything else yet
+ image.set_variant(floppy_image::DSDD); // Not handling anything else yet
tcount = 84*2+1; // Usual max
- tinfos = global_alloc_array_clear<track_info>(tcount);
- bool res = scan_all_tags(data);
+ tinfos.resize(tcount);
+ bool res = scan_all_tags(data, size);
if(res)
res = generate_tracks(image);
- global_free_array(tinfos);
- tinfos = nullptr;
+ tinfos.clear();
return res;
}
-bool ipf_format::parse_info(const uint8_t *info)
+bool ipf_format::ipf_decode::parse_info(const uint8_t *info)
{
- type = r32(info+12);
+ type = get_u32be(info+12);
if(type != 1)
return false;
- encoder_type = r32(info+16); // 1 for CAPS, 2 for SPS
- encoder_revision = r32(info+20); // 1 always
- release = r32(info+24);
- revision = r32(info+28);
- origin = r32(info+32); // Original source reference
- min_cylinder = r32(info+36);
- max_cylinder = r32(info+40);
- min_head = r32(info+44);
- max_head = r32(info+48);
- credit_day = r32(info+52); // year*1e4 + month*1e2 + day
- credit_time = r32(info+56); // hour*1e7 + min*1e5 + sec*1e3 + msec
+ encoder_type = get_u32be(info+16); // 1 for CAPS, 2 for SPS
+ encoder_revision = get_u32be(info+20); // 1 always
+ release = get_u32be(info+24);
+ revision = get_u32be(info+28);
+ origin = get_u32be(info+32); // Original source reference
+ min_cylinder = get_u32be(info+36);
+ max_cylinder = get_u32be(info+40);
+ min_head = get_u32be(info+44);
+ max_head = get_u32be(info+48);
+ credit_day = get_u32be(info+52); // year*1e4 + month*1e2 + day
+ credit_time = get_u32be(info+56); // hour*1e7 + min*1e5 + sec*1e3 + msec
for(int i=0; i<4; i++)
- platform[i] = r32(info+60+4*i);
+ platform[i] = get_u32be(info+60+4*i);
for(int i=0; i<5; i++)
- extra[i] = r32(info+76+4*i);
+ extra[i] = get_u32be(info+76+4*i);
return true;
}
-ipf_format::track_info *ipf_format::get_index(uint32_t idx)
+ipf_format::ipf_decode::track_info *ipf_format::ipf_decode::get_index(uint32_t idx)
{
if(idx > 1000)
return nullptr;
if(idx >= tcount) {
- auto ti1 = global_alloc_array_clear<track_info>(idx+1);
- memcpy(ti1, tinfos, tcount*sizeof(tinfos));
- global_free_array(tinfos);
+ tinfos.resize(idx+1);
tcount = idx+1;
- tinfos = ti1;
}
- return tinfos+idx;
+ return &tinfos[idx];
}
-bool ipf_format::parse_imge(const uint8_t *imge)
+bool ipf_format::ipf_decode::parse_imge(const uint8_t *imge)
{
- track_info *t = get_index(r32(imge+64));
+ track_info *t = get_index(get_u32be(imge+64));
if(!t)
return false;
t->info_set = true;
- t->cylinder = r32(imge+12);
+ t->cylinder = get_u32be(imge+12);
if(t->cylinder < min_cylinder || t->cylinder > max_cylinder)
return false;
- t->head = r32(imge+16);
+ t->head = get_u32be(imge+16);
if(t->head < min_head || t->head > max_head)
return false;
- t->type = r32(imge+20);
- t->sigtype = r32(imge+24); // 1 for 2us cells, no other value valid
- t->size_bytes = r32(imge+28);
- t->index_bytes = r32(imge+32);
- t->index_cells = r32(imge+36);
- t->datasize_cells = r32(imge+40);
- t->gapsize_cells = r32(imge+44);
- t->size_cells = r32(imge+48);
- t->block_count = r32(imge+52);
- t->process = r32(imge+56); // encoder process, always 0
- t->weak_bits = r32(imge+60);
- t->reserved[0] = r32(imge+68);
- t->reserved[1] = r32(imge+72);
- t->reserved[2] = r32(imge+76);
+ t->type = get_u32be(imge+20);
+ t->sigtype = get_u32be(imge+24); // 1 for 2us cells, no other value valid
+ t->size_bytes = get_u32be(imge+28);
+ t->index_bytes = get_u32be(imge+32);
+ t->index_cells = get_u32be(imge+36);
+ t->datasize_cells = get_u32be(imge+40);
+ t->gapsize_cells = get_u32be(imge+44);
+ t->size_cells = get_u32be(imge+48);
+ t->block_count = get_u32be(imge+52);
+ t->process = get_u32be(imge+56); // encoder process, always 0
+ t->weak_bits = get_u32be(imge+60);
+ t->reserved[0] = get_u32be(imge+68);
+ t->reserved[1] = get_u32be(imge+72);
+ t->reserved[2] = get_u32be(imge+76);
return true;
}
-bool ipf_format::parse_data(const uint8_t *data, uint32_t &pos, uint32_t max_extra_size)
+bool ipf_format::ipf_decode::parse_data(const uint8_t *data, uint32_t &pos, uint32_t max_extra_size)
{
- track_info *t = get_index(r32(data+24));
+ track_info *t = get_index(get_u32be(data+24));
if(!t)
return false;
- t->data_size_bits = r32(data+16);
+ t->data_size_bits = get_u32be(data+16);
t->data = data+28;
- t->data_size = r32(data+12);
+ t->data_size = get_u32be(data+12);
if(t->data_size > max_extra_size)
return false;
- if(crc32r(t->data, t->data_size) != r32(data+20))
+ if(crc32r(t->data, t->data_size) != get_u32be(data+20))
return false;
pos += t->data_size;
return true;
}
-bool ipf_format::scan_one_tag(std::vector<uint8_t> &data, uint32_t &pos, uint8_t *&tag, uint32_t &tsize)
+bool ipf_format::ipf_decode::scan_one_tag(uint8_t *data, size_t size, uint32_t &pos, uint8_t *&tag, uint32_t &tsize)
{
- if(data.size()-pos < 12)
+ if(size-pos < 12)
return false;
tag = &data[pos];
- tsize = r32(tag+4);
- if(data.size()-pos < tsize)
+ tsize = get_u32be(tag+4);
+ if(size-pos < tsize)
return false;
- uint32_t crc = r32(tag+8);
+ uint32_t crc = get_u32be(tag+8);
tag[8] = tag[9] = tag[10] = tag[11] = 0;
if(crc32r(tag, tsize) != crc)
return false;
@@ -198,18 +260,17 @@ bool ipf_format::scan_one_tag(std::vector<uint8_t> &data, uint32_t &pos, uint8_t
return true;
}
-bool ipf_format::scan_all_tags(std::vector<uint8_t> &data)
+bool ipf_format::ipf_decode::scan_all_tags(uint8_t *data, size_t size)
{
uint32_t pos = 0;
- uint32_t size = data.size();
while(pos != size) {
uint8_t *tag;
uint32_t tsize;
- if(!scan_one_tag(data, pos, tag, tsize))
+ if(!scan_one_tag(data, size, pos, tag, tsize))
return false;
- switch(r32(tag)) {
+ switch(get_u32be(tag)) {
case 0x43415053: // CAPS
if(tsize != 12)
return false;
@@ -243,21 +304,21 @@ bool ipf_format::scan_all_tags(std::vector<uint8_t> &data)
return true;
}
-bool ipf_format::generate_tracks(floppy_image *image)
+bool ipf_format::ipf_decode::generate_tracks(floppy_image &image)
{
for(uint32_t i = 0; i != tcount; i++) {
- track_info *t = tinfos + i;
- if(t->info_set && t->data) {
+ track_info &t = tinfos[i];
+ if(t.info_set && t.data) {
if(!generate_track(t, image))
return false;
- } else if(t->info_set || t->data)
+ } else if(t.info_set || t.data)
return false;
}
return true;
}
-void ipf_format::rotate(std::vector<uint32_t> &track, uint32_t offset, uint32_t size)
+void ipf_format::ipf_decode::rotate(std::vector<uint32_t> &track, uint32_t offset, uint32_t size)
{
uint32_t done = 0;
for(uint32_t bpos=0; done < size; bpos++) {
@@ -278,7 +339,7 @@ void ipf_format::rotate(std::vector<uint32_t> &track, uint32_t offset, uint32_t
}
}
-void ipf_format::mark_track_splice(std::vector<uint32_t> &track, uint32_t offset, uint32_t size)
+void ipf_format::ipf_decode::mark_track_splice(std::vector<uint32_t> &track, uint32_t offset, uint32_t size)
{
for(int i=0; i<3; i++) {
uint32_t pos = (offset + i) % size;
@@ -291,79 +352,79 @@ void ipf_format::mark_track_splice(std::vector<uint32_t> &track, uint32_t offset
}
}
-void ipf_format::timing_set(std::vector<uint32_t> &track, uint32_t start, uint32_t end, uint32_t time)
+void ipf_format::ipf_decode::timing_set(std::vector<uint32_t> &track, uint32_t start, uint32_t end, uint32_t time)
{
for(uint32_t i=start; i != end; i++)
track[i] = (track[i] & floppy_image::MG_MASK) | time;
}
-bool ipf_format::generate_timings(track_info *t, std::vector<uint32_t> &track, const std::vector<uint32_t> &data_pos, const std::vector<uint32_t> &gap_pos)
+bool ipf_format::ipf_decode::generate_timings(const track_info &t, std::vector<uint32_t> &track, const std::vector<uint32_t> &data_pos, const std::vector<uint32_t> &gap_pos)
{
- timing_set(track, 0, t->size_cells, 2000);
+ timing_set(track, 0, t.size_cells, 2000);
- switch(t->type) {
+ switch(t.type) {
case 2: break;
case 3:
- if(t->block_count >= 4)
+ if(t.block_count >= 4)
timing_set(track, gap_pos[3], data_pos[4], 1890);
- if(t->block_count >= 5) {
+ if(t.block_count >= 5) {
timing_set(track, data_pos[4], gap_pos[4], 1890);
timing_set(track, gap_pos[4], data_pos[5], 1990);
}
- if(t->block_count >= 6) {
+ if(t.block_count >= 6) {
timing_set(track, data_pos[5], gap_pos[5], 1990);
timing_set(track, gap_pos[5], data_pos[6], 2090);
}
- if(t->block_count >= 7)
+ if(t.block_count >= 7)
timing_set(track, data_pos[6], gap_pos[6], 2090);
break;
case 4:
- timing_set(track, gap_pos[t->block_count-1], data_pos[0], 1890);
+ timing_set(track, gap_pos[t.block_count-1], data_pos[0], 1890);
timing_set(track, data_pos[0], gap_pos[0], 1890);
timing_set(track, gap_pos[0], data_pos[1], 1990);
- if(t->block_count >= 2) {
+ if(t.block_count >= 2) {
timing_set(track, data_pos[1], gap_pos[1], 1990);
timing_set(track, gap_pos[1], data_pos[2], 2090);
}
- if(t->block_count >= 3)
+ if(t.block_count >= 3)
timing_set(track, data_pos[2], gap_pos[2], 2090);
break;
case 5:
- if(t->block_count >= 6)
+ if(t.block_count >= 6)
timing_set(track, data_pos[5], gap_pos[5], 2100);
break;
case 6:
- if(t->block_count >= 2)
+ if(t.block_count >= 2)
timing_set(track, data_pos[1], gap_pos[1], 2200);
- if(t->block_count >= 3)
+ if(t.block_count >= 3)
timing_set(track, data_pos[2], gap_pos[2], 1800);
break;
case 7:
- if(t->block_count >= 2)
+ if(t.block_count >= 2)
timing_set(track, data_pos[1], gap_pos[1], 2100);
break;
case 8:
- if(t->block_count >= 2)
+ if(t.block_count >= 2)
timing_set(track, data_pos[1], gap_pos[1], 2200);
- if(t->block_count >= 3)
+ if(t.block_count >= 3)
timing_set(track, data_pos[2], gap_pos[2], 2100);
- if(t->block_count >= 5)
+ if(t.block_count >= 5)
timing_set(track, data_pos[4], gap_pos[4], 1900);
- if(t->block_count >= 6)
+ if(t.block_count >= 6)
timing_set(track, data_pos[5], gap_pos[5], 1800);
- if(t->block_count >= 7)
+ if(t.block_count >= 7)
timing_set(track, data_pos[6], gap_pos[6], 1700);
break;
case 9: {
- uint32_t mask = r32(t->data + 32*t->block_count + 12);
- for(uint32_t i=1; i<t->block_count; i++)
+ uint32_t mask = get_u32be(t.data + 32*t.block_count + 12);
+ for(uint32_t i=1; i<t.block_count; i++)
timing_set(track, data_pos[i], gap_pos[i], mask & (1 << (i-1)) ? 1900 : 2100);
break;
}
@@ -375,55 +436,55 @@ bool ipf_format::generate_timings(track_info *t, std::vector<uint32_t> &track, c
return true;
}
-bool ipf_format::generate_track(track_info *t, floppy_image *image)
+bool ipf_format::ipf_decode::generate_track(track_info &t, floppy_image &image)
{
- if(!t->size_cells)
+ if(!t.size_cells)
return true;
- if(t->data_size < 32*t->block_count)
+ if(t.data_size < 32*t.block_count)
return false;
// Annoyingly enough, too small gaps are ignored, changing the
// total track size. Artifact stemming from the byte-only support
// of old times?
- t->size_cells = block_compute_real_size(t);
+ t.size_cells = block_compute_real_size(t);
- if(t->index_cells >= t->size_cells)
+ if(t.index_cells >= t.size_cells)
return false;
- std::vector<uint32_t> track(t->size_cells);
- std::vector<uint32_t> data_pos(t->block_count+1);
- std::vector<uint32_t> gap_pos(t->block_count);
- std::vector<uint32_t> splice_pos(t->block_count);
+ std::vector<uint32_t> track(t.size_cells);
+ std::vector<uint32_t> data_pos(t.block_count+1);
+ std::vector<uint32_t> gap_pos(t.block_count);
+ std::vector<uint32_t> splice_pos(t.block_count);
bool context = false;
uint32_t pos = 0;
- for(uint32_t i = 0; i != t->block_count; i++) {
- if(!generate_block(t, i, i == t->block_count-1 ? t->size_cells - t->index_cells : 0xffffffff, track, pos, data_pos[i], gap_pos[i], splice_pos[i], context)) {
+ for(uint32_t i = 0; i != t.block_count; i++) {
+ if(!generate_block(t, i, i == t.block_count-1 ? t.size_cells - t.index_cells : 0xffffffff, track, pos, data_pos[i], gap_pos[i], splice_pos[i], context)) {
return false;
}
}
- if(pos != t->size_cells) {
+ if(pos != t.size_cells) {
return false;
}
- data_pos[t->block_count] = pos;
+ data_pos[t.block_count] = pos;
- mark_track_splice(track, splice_pos[t->block_count-1], t->size_cells);
+ mark_track_splice(track, splice_pos[t.block_count-1], t.size_cells);
if(!generate_timings(t, track, data_pos, gap_pos)) {
return false;
}
- if(t->index_cells)
- rotate(track, t->size_cells - t->index_cells, t->size_cells);
+ if(t.index_cells)
+ rotate(track, t.size_cells - t.index_cells, t.size_cells);
- generate_track_from_levels(t->cylinder, t->head, track, splice_pos[t->block_count-1] + t->index_cells, image);
+ generate_track_from_levels(t.cylinder, t.head, track, splice_pos[t.block_count-1] + t.index_cells, image);
return true;
}
-void ipf_format::track_write_raw(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t cells, bool &context)
+void ipf_format::ipf_decode::track_write_raw(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t cells, bool &context)
{
for(uint32_t i=0; i != cells; i++)
*tpos++ = data[i>>3] & (0x80 >> (i & 7)) ? MG_1 : MG_0;
@@ -431,7 +492,7 @@ void ipf_format::track_write_raw(std::vector<uint32_t>::iterator &tpos, const ui
context = tpos[-1] == MG_1;
}
-void ipf_format::track_write_mfm(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t start_offset, uint32_t patlen, uint32_t cells, bool &context)
+void ipf_format::ipf_decode::track_write_mfm(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t start_offset, uint32_t patlen, uint32_t cells, bool &context)
{
patlen *= 2;
for(uint32_t i=0; i != cells; i++) {
@@ -445,13 +506,13 @@ void ipf_format::track_write_mfm(std::vector<uint32_t>::iterator &tpos, const ui
}
}
-void ipf_format::track_write_weak(std::vector<uint32_t>::iterator &tpos, uint32_t cells)
+void ipf_format::ipf_decode::track_write_weak(std::vector<uint32_t>::iterator &tpos, uint32_t cells)
{
for(uint32_t i=0; i != cells; i++)
*tpos++ = floppy_image::MG_N;
}
-bool ipf_format::generate_block_data(const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, std::vector<uint32_t>::iterator tlimit, bool &context)
+bool ipf_format::ipf_decode::generate_block_data(const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, std::vector<uint32_t>::iterator tlimit, bool &context)
{
for(;;) {
if(data >= dlimit)
@@ -493,7 +554,7 @@ bool ipf_format::generate_block_data(const uint8_t *data, const uint8_t *dlimit,
}
}
-bool ipf_format::generate_block_gap_0(uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, std::vector<uint32_t>::iterator &tpos, bool &context)
+bool ipf_format::ipf_decode::generate_block_gap_0(uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, std::vector<uint32_t>::iterator &tpos, bool &context)
{
spos = ipos >= 16 && ipos+16 <= gap_cells ? ipos : gap_cells >> 1;
track_write_mfm(tpos, &pattern, 0, 8, spos, context);
@@ -506,7 +567,7 @@ bool ipf_format::generate_block_gap_0(uint32_t gap_cells, uint8_t pattern, uint3
return true;
}
-bool ipf_format::gap_description_to_reserved_size(const uint8_t *&data, const uint8_t *dlimit, uint32_t &res_size)
+bool ipf_format::ipf_decode::gap_description_to_reserved_size(const uint8_t *&data, const uint8_t *dlimit, uint32_t &res_size)
{
res_size = 0;
for(;;) {
@@ -531,7 +592,7 @@ bool ipf_format::gap_description_to_reserved_size(const uint8_t *&data, const ui
}
}
-bool ipf_format::generate_gap_from_description(const uint8_t *&data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, uint32_t size, bool pre, bool &context)
+bool ipf_format::ipf_decode::generate_gap_from_description(const uint8_t *&data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, uint32_t size, bool pre, bool &context)
{
const uint8_t *data1 = data;
uint32_t res_size;
@@ -601,7 +662,7 @@ bool ipf_format::generate_gap_from_description(const uint8_t *&data, const uint8
}
-bool ipf_format::generate_block_gap_1(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context)
+bool ipf_format::ipf_decode::generate_block_gap_1(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context)
{
if(ipos >= 16 && ipos < gap_cells-16)
spos = ipos;
@@ -610,7 +671,7 @@ bool ipf_format::generate_block_gap_1(uint32_t gap_cells, uint32_t &spos, uint32
return generate_gap_from_description(data, dlimit, tpos, gap_cells, true, context);
}
-bool ipf_format::generate_block_gap_2(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context)
+bool ipf_format::ipf_decode::generate_block_gap_2(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context)
{
if(ipos >= 16 && ipos < gap_cells-16)
spos = ipos;
@@ -619,7 +680,7 @@ bool ipf_format::generate_block_gap_2(uint32_t gap_cells, uint32_t &spos, uint32
return generate_gap_from_description(data, dlimit, tpos, gap_cells, false, context);
}
-bool ipf_format::generate_block_gap_3(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context)
+bool ipf_format::ipf_decode::generate_block_gap_3(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context)
{
if(ipos >= 16 && ipos < gap_cells-16)
spos = ipos;
@@ -646,7 +707,7 @@ bool ipf_format::generate_block_gap_3(uint32_t gap_cells, uint32_t &spos, uint32
return generate_gap_from_description(data, dlimit, tpos+spos+delta, gap_cells - spos - delta, false, context);
}
-bool ipf_format::generate_block_gap(uint32_t gap_type, uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, bool &context)
+bool ipf_format::ipf_decode::generate_block_gap(uint32_t gap_type, uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, bool &context)
{
switch(gap_type) {
case 0:
@@ -662,13 +723,13 @@ bool ipf_format::generate_block_gap(uint32_t gap_type, uint32_t gap_cells, uint8
}
}
-bool ipf_format::generate_block(track_info *t, uint32_t idx, uint32_t ipos, std::vector<uint32_t> &track, uint32_t &pos, uint32_t &dpos, uint32_t &gpos, uint32_t &spos, bool &context)
+bool ipf_format::ipf_decode::generate_block(const track_info &t, uint32_t idx, uint32_t ipos, std::vector<uint32_t> &track, uint32_t &pos, uint32_t &dpos, uint32_t &gpos, uint32_t &spos, bool &context)
{
- const uint8_t *data = t->data;
- const uint8_t *data_end = t->data + t->data_size;
+ const uint8_t *data = t.data;
+ const uint8_t *data_end = t.data + t.data_size;
const uint8_t *thead = data + 32*idx;
- uint32_t data_cells = r32(thead);
- uint32_t gap_cells = r32(thead+4);
+ uint32_t data_cells = get_u32be(thead);
+ uint32_t gap_cells = get_u32be(thead+4);
if(gap_cells < 8)
gap_cells = 0;
@@ -683,24 +744,24 @@ bool ipf_format::generate_block(track_info *t, uint32_t idx, uint32_t ipos, std:
dpos = pos;
gpos = dpos + data_cells;
pos = gpos + gap_cells;
- if(pos > t->size_cells)
+ if(pos > t.size_cells)
return false;
- if(!generate_block_data(data + r32(thead+28), data_end, track.begin()+dpos, track.begin()+gpos, context))
+ if(!generate_block_data(data + get_u32be(thead+28), data_end, track.begin()+dpos, track.begin()+gpos, context))
return false;
- if(!generate_block_gap(r32(thead+20), gap_cells, r32(thead+24), spos, ipos > gpos ? ipos-gpos : 0, data + r32(thead+8), data_end, track.begin()+gpos, context))
+ if(!generate_block_gap(get_u32be(thead+20), gap_cells, get_u32be(thead+24), spos, ipos > gpos ? ipos-gpos : 0, data + get_u32be(thead+8), data_end, track.begin()+gpos, context))
return false;
spos += gpos;
return true;
}
-uint32_t ipf_format::block_compute_real_size(track_info *t)
+uint32_t ipf_format::ipf_decode::block_compute_real_size(const track_info &t)
{
uint32_t size = 0;
- const uint8_t *thead = t->data;
- for(unsigned int i=0; i != t->block_count; i++) {
- uint32_t data_cells = r32(thead);
- uint32_t gap_cells = r32(thead+4);
+ const uint8_t *thead = t.data;
+ for(unsigned int i=0; i != t.block_count; i++) {
+ uint32_t data_cells = get_u32be(thead);
+ uint32_t gap_cells = get_u32be(thead+4);
if(gap_cells < 8)
gap_cells = 0;
diff --git a/src/lib/formats/ipf_dsk.h b/src/lib/formats/ipf_dsk.h
index 7db2415afd4..a1c36f02f8d 100644
--- a/src/lib/formats/ipf_dsk.h
+++ b/src/lib/formats/ipf_dsk.h
@@ -12,82 +12,18 @@
class ipf_format : public floppy_image_format_t
{
public:
- ipf_format();
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
-
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
private:
- struct track_info {
- uint32_t cylinder, head, type;
- uint32_t sigtype, process, reserved[3];
- uint32_t size_bytes, size_cells;
- uint32_t index_bytes, index_cells;
- uint32_t datasize_cells, gapsize_cells;
- uint32_t block_count, weak_bits;
-
- uint32_t data_size_bits;
-
- bool info_set;
-
- const uint8_t *data;
- uint32_t data_size;
- };
-
- track_info *tinfos;
- uint32_t tcount;
-
- uint32_t type, release, revision;
- uint32_t encoder_type, encoder_revision, origin;
- uint32_t min_cylinder, max_cylinder, min_head, max_head;
- uint32_t credit_day, credit_time;
- uint32_t platform[4], extra[5];
-
- uint32_t crc32r(const uint8_t *data, uint32_t size);
-
- bool parse_info(const uint8_t *info);
- bool parse_imge(const uint8_t *imge);
- bool parse_data(const uint8_t *data, uint32_t &pos, uint32_t max_extra_size);
-
- bool scan_one_tag(std::vector<uint8_t> &data, uint32_t &pos, uint8_t *&tag, uint32_t &tsize);
- bool scan_all_tags(std::vector<uint8_t> &data);
- static uint32_t r32(const uint8_t *p);
- static uint32_t rb(const uint8_t *&p, int count);
-
- track_info *get_index(uint32_t idx);
-
- void track_write_raw(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t cells, bool &context);
- void track_write_mfm(std::vector<uint32_t>::iterator &tpos, const uint8_t *data, uint32_t start_offset, uint32_t patlen, uint32_t cells, bool &context);
- void track_write_weak(std::vector<uint32_t>::iterator &tpos, uint32_t cells);
- bool generate_block_data(const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, std::vector<uint32_t>::iterator tlimit, bool &context);
-
- bool gap_description_to_reserved_size(const uint8_t *&data, const uint8_t *dlimit, uint32_t &res_size);
- bool generate_gap_from_description(const uint8_t *&data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, uint32_t size, bool pre, bool &context);
- bool generate_block_gap_0(uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, std::vector<uint32_t>::iterator &tpos, bool &context);
- bool generate_block_gap_1(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context);
- bool generate_block_gap_2(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context);
- bool generate_block_gap_3(uint32_t gap_cells, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator &tpos, bool &context);
- bool generate_block_gap(uint32_t gap_type, uint32_t gap_cells, uint8_t pattern, uint32_t &spos, uint32_t ipos, const uint8_t *data, const uint8_t *dlimit, std::vector<uint32_t>::iterator tpos, bool &context);
-
- bool generate_block(track_info *t, uint32_t idx, uint32_t ipos, std::vector<uint32_t> &track, uint32_t &pos, uint32_t &dpos, uint32_t &gpos, uint32_t &spos, bool &context);
- uint32_t block_compute_real_size(track_info *t);
-
- void timing_set(std::vector<uint32_t> &track, uint32_t start, uint32_t end, uint32_t time);
- bool generate_timings(track_info *t, std::vector<uint32_t> &track, const std::vector<uint32_t> &data_pos, const std::vector<uint32_t> &gap_pos);
-
- void rotate(std::vector<uint32_t> &track, uint32_t offset, uint32_t size);
- void mark_track_splice(std::vector<uint32_t> &track, uint32_t offset, uint32_t size);
- bool generate_track(track_info *t, floppy_image *image);
- bool generate_tracks(floppy_image *image);
-
- bool parse(std::vector<uint8_t> &data, floppy_image *image);
+ struct ipf_decode;
};
-extern const floppy_format_type FLOPPY_IPF_FORMAT;
+extern const ipf_format FLOPPY_IPF_FORMAT;
#endif // MAME_FORMATS_IPF_DSK_H
diff --git a/src/lib/formats/iq151_dsk.cpp b/src/lib/formats/iq151_dsk.cpp
index dcffad85751..19f8af53f2f 100644
--- a/src/lib/formats/iq151_dsk.cpp
+++ b/src/lib/formats/iq151_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/iq151_dsk.c
+ formats/iq151_dsk.cpp
iq151 format
*********************************************************************/
-#include <assert.h>
-
#include "formats/iq151_dsk.h"
iq151_format::iq151_format() : upd765_format(formats)
{
}
-const char *iq151_format::name() const
+const char *iq151_format::name() const noexcept
{
return "iq151";
}
-const char *iq151_format::description() const
+const char *iq151_format::description() const noexcept
{
return "IQ151 disk image";
}
-const char *iq151_format::extensions() const
+const char *iq151_format::extensions() const noexcept
{
return "iqd";
}
@@ -44,4 +42,4 @@ const iq151_format::format iq151_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_IQ151_FORMAT = &floppy_image_format_creator<iq151_format>;
+const iq151_format FLOPPY_IQ151_FORMAT;
diff --git a/src/lib/formats/iq151_dsk.h b/src/lib/formats/iq151_dsk.h
index 8ad1253d113..7153fd9d011 100644
--- a/src/lib/formats/iq151_dsk.h
+++ b/src/lib/formats/iq151_dsk.h
@@ -19,14 +19,14 @@ class iq151_format : public upd765_format
public:
iq151_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_IQ151_FORMAT;
+extern const iq151_format FLOPPY_IQ151_FORMAT;
#endif // MAME_FORMATS_IQ151_DSK_H
diff --git a/src/lib/formats/itt3030_dsk.cpp b/src/lib/formats/itt3030_dsk.cpp
index 305ab644233..840b88474b5 100644
--- a/src/lib/formats/itt3030_dsk.cpp
+++ b/src/lib/formats/itt3030_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/itt3030_dsk.c
+ formats/itt3030_dsk.cpp
ITT3030 560K disk image format
@@ -10,25 +10,23 @@
*********************************************************************/
-#include <assert.h>
-
#include "formats/itt3030_dsk.h"
itt3030_format::itt3030_format() : wd177x_format(formats)
{
}
-const char *itt3030_format::name() const
+const char *itt3030_format::name() const noexcept
{
return "itt3030";
}
-const char *itt3030_format::description() const
+const char *itt3030_format::description() const noexcept
{
return "ITT3030 disk image";
}
-const char *itt3030_format::extensions() const
+const char *itt3030_format::extensions() const noexcept
{
return "dsk";
}
@@ -50,9 +48,10 @@ const itt3030_format::format itt3030_format::formats[] = {
256, {},
1, {},
32, 22, 31
- }
+ },
+ {}
};
-const floppy_format_type FLOPPY_ITT3030_FORMAT = &floppy_image_format_creator<itt3030_format>;
+const itt3030_format FLOPPY_ITT3030_FORMAT;
diff --git a/src/lib/formats/itt3030_dsk.h b/src/lib/formats/itt3030_dsk.h
index 7239f2619b7..2cb5140e0b9 100644
--- a/src/lib/formats/itt3030_dsk.h
+++ b/src/lib/formats/itt3030_dsk.h
@@ -19,15 +19,15 @@ class itt3030_format : public wd177x_format
public:
itt3030_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_ITT3030_FORMAT;
+extern const itt3030_format FLOPPY_ITT3030_FORMAT;
#endif // MAME_FORMATS_ITT3030_DSK_H
diff --git a/src/lib/formats/jfd_dsk.cpp b/src/lib/formats/jfd_dsk.cpp
index 7f2dc52e23b..46e933cedf1 100644
--- a/src/lib/formats/jfd_dsk.cpp
+++ b/src/lib/formats/jfd_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Nigel Barnes
/*********************************************************************
- formats/jfd_dsk.c
+ formats/jfd_dsk.cpp
JASPP Floppy Disk image format
@@ -158,9 +158,16 @@
*********************************************************************/
-#include <zlib.h>
#include "formats/jfd_dsk.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include "osdcore.h" // osd_printf_*
+
+#include <zlib.h>
+
+
static const uint8_t JFD_HEADER[4] = { 'J', 'F', 'D', 'I' };
static const uint8_t GZ_HEADER[2] = { 0x1f, 0x8b };
@@ -168,26 +175,31 @@ jfd_format::jfd_format()
{
}
-const char *jfd_format::name() const
+const char *jfd_format::name() const noexcept
{
return "jfd";
}
-const char *jfd_format::description() const
+const char *jfd_format::description() const noexcept
{
return "JASPP Floppy Disk image";
}
-const char *jfd_format::extensions() const
+const char *jfd_format::extensions() const noexcept
{
return "jfd";
}
-int jfd_format::identify(io_generic *io, uint32_t form_factor)
+int jfd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size) || !size)
+ return 0;
+
std::vector<uint8_t> img(size);
- io_generic_read(io, &img[0], 0, size);
+ auto const [ioerr, actual] = read_at(io, 0, &img[0], size);
+ if (ioerr || (actual != size))
+ return 0;
int err;
std::vector<uint8_t> gz_ptr(4);
@@ -211,26 +223,31 @@ int jfd_format::identify(io_generic *io, uint32_t form_factor)
err = inflateEnd(&d_stream);
if (err != Z_OK) return 0;
- img = gz_ptr;
+ img = std::move(gz_ptr);
}
if (!memcmp(&img[0], JFD_HEADER, sizeof(JFD_HEADER))) {
- return 100;
+ return FIFID_SIGN;
}
return 0;
}
-bool jfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool jfd_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return false;
+
std::vector<uint8_t> img(size);
- io_generic_read(io, &img[0], 0, size);
+ auto const [ioerr, actual] = read_at(io, 0, &img[0], size);
+ if (ioerr || (actual != size))
+ return false;
int err;
std::vector<uint8_t> gz_ptr;
z_stream d_stream;
- int inflate_size = (img[size - 1] << 24) | (img[size - 2] << 16) | (img[size - 3] << 8) | img[size - 4];
+ int inflate_size = get_u32le(&img[size - 4]);
if (!memcmp(&img[0], GZ_HEADER, sizeof(GZ_HEADER))) {
gz_ptr.resize(inflate_size);
@@ -259,7 +276,7 @@ bool jfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return false;
}
size = inflate_size;
- img = gz_ptr;
+ img = std::move(gz_ptr);
}
osd_printf_verbose("jfd_dsk: loading %s\n", &img[48]);
@@ -352,14 +369,14 @@ bool jfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
build_wd_track_mfm(track / 2, track % 2, image, den[0] * 50000, spt, sects, 90, 32, 22);
}
}
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
return true;
}
-bool jfd_format::supports_save() const
+bool jfd_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_JFD_FORMAT = &floppy_image_format_creator<jfd_format>;
+const jfd_format FLOPPY_JFD_FORMAT;
diff --git a/src/lib/formats/jfd_dsk.h b/src/lib/formats/jfd_dsk.h
index 50db9f9caa5..c4e01614619 100644
--- a/src/lib/formats/jfd_dsk.h
+++ b/src/lib/formats/jfd_dsk.h
@@ -19,15 +19,15 @@ class jfd_format : public floppy_image_format_t
public:
jfd_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_JFD_FORMAT;
+extern const jfd_format FLOPPY_JFD_FORMAT;
#endif // MAME_FORMATS_JFD_DSK_H
diff --git a/src/lib/formats/juku_dsk.cpp b/src/lib/formats/juku_dsk.cpp
index 548651a3427..46d448a2c61 100644
--- a/src/lib/formats/juku_dsk.cpp
+++ b/src/lib/formats/juku_dsk.cpp
@@ -1,8 +1,8 @@
// license: BSD-3-Clause
-// copyright-holders: Dirk Best
+// copyright-holders: Dirk Best, Märt Põder
/***************************************************************************
- Juku E5101
+ Juku E5101/E5104
Disk image format
@@ -14,32 +14,32 @@ juku_format::juku_format() : wd177x_format(formats)
{
}
-const char *juku_format::name() const
+const char *juku_format::name() const noexcept
{
return "juku";
}
-const char *juku_format::description() const
+const char *juku_format::description() const noexcept
{
return "Juku disk image";
}
-const char *juku_format::extensions() const
+const char *juku_format::extensions() const noexcept
{
return "juk";
}
const juku_format::format juku_format::formats[] =
{
- { // 800k 5 1/4 inch double density single sided - gaps unverified (CP/M)
- floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ { // 386k 5.25" double density single sided - gaps unverified (CP/M)
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
2000, 10, 80, 1, 512, {}, 1, {}, 32, 22, 35
},
- { // 800k 5 1/4 inch double density double sided - gaps unverified (CP/M)
+ { // 786k 5.25" double density "outout" double sided - gaps unverified (CP/M)
floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
2000, 10, 80, 2, 512, {}, 1, {}, 32, 22, 35
},
{}
};
-const floppy_format_type FLOPPY_JUKU_FORMAT = &floppy_image_format_creator<juku_format>;
+const juku_format FLOPPY_JUKU_FORMAT;
diff --git a/src/lib/formats/juku_dsk.h b/src/lib/formats/juku_dsk.h
index 73c33e952f4..c78363ece14 100644
--- a/src/lib/formats/juku_dsk.h
+++ b/src/lib/formats/juku_dsk.h
@@ -1,8 +1,8 @@
// license: BSD-3-Clause
-// copyright-holders: Dirk Best
+// copyright-holders: Dirk Best, Märt Põder
/***************************************************************************
- Juku E5101
+ Juku E5101/E5104
Disk image format
@@ -19,14 +19,14 @@ class juku_format : public wd177x_format
public:
juku_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_JUKU_FORMAT;
+extern const juku_format FLOPPY_JUKU_FORMAT;
#endif // MAME_FORMATS_JUKU_DSK_H
diff --git a/src/lib/formats/jvc_dsk.cpp b/src/lib/formats/jvc_dsk.cpp
index 5035e989f22..fcc5d97a5b5 100644
--- a/src/lib/formats/jvc_dsk.cpp
+++ b/src/lib/formats/jvc_dsk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -8,7 +8,7 @@
Used by Jeff Vavasour's CoCo Emulators
- Documentation taken from Tim Linder's web site:
+ Documentation taken from Tim Lindner's web site:
http://tlindner.macmess.org/?page_id=86
A. Header length
@@ -104,43 +104,51 @@
***************************************************************************/
-#include "emu.h"
-#include "imageutl.h"
#include "jvc_dsk.h"
+#include "ioprocs.h"
+
+#include "osdcore.h" // osd_printf_*
+
+
jvc_format::jvc_format()
{
}
-const char *jvc_format::name() const
+const char *jvc_format::name() const noexcept
{
return "jvc";
}
-const char *jvc_format::description() const
+const char *jvc_format::description() const noexcept
{
return "JVC disk image";
}
-const char *jvc_format::extensions() const
+const char *jvc_format::extensions() const noexcept
{
return "jvc,dsk";
}
-bool jvc_format::parse_header(io_generic *io, int &header_size, int &tracks, int &heads, int &sectors, int &sector_size, int &base_sector_id)
+bool jvc_format::parse_header(util::random_read &io, int &header_size, int &tracks, int &heads, int &sectors, int &sector_size, int &base_sector_id)
{
// The JVC format has a header whose size is the size of the image modulo 256. Currently, we only
// handle up to five header bytes
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size) || !size)
+ return false;
header_size = size % 256;
uint8_t header[5];
- // if we know that this is a header of a bad size, we can fail
- // immediately; otherwise read the header
- if (header_size >= sizeof(header))
+ // if we know that this is a header of a bad size, we can fail immediately; otherwise read the header
+ if (header_size >= sizeof(header)) // TODO: wouldn't this make more sense with > than >=? The first case in the following switch statement is unreachable as-is.
return false;
if (header_size > 0)
- io_generic_read(io, header, 0, header_size);
+ {
+ auto const [err, actual] = read_at(io, 0, header, header_size);
+ if (err || (actual != header_size))
+ return false;
+ }
// default values
heads = 1;
@@ -150,41 +158,69 @@ bool jvc_format::parse_header(io_generic *io, int &header_size, int &tracks, int
switch (header_size)
{
- case 5: emu_fatalerror("jvc_format: sector attribute flag unsupported\n");
- break;
+ case 5:
+ osd_printf_info("jvc_format: sector attribute flag unsupported\n");
+ return false;
case 4: base_sector_id = header[3];
- // no break
+ [[fallthrough]];
case 3: sector_size = 128 << header[2];
- // no break
+ [[fallthrough]];
case 2: heads = header[1];
- // no break
+ [[fallthrough]];
case 1: sectors = header[0];
- // no break
+ [[fallthrough]];
case 0: tracks = (size - header_size) / sector_size / sectors / heads;
break;
}
- osd_printf_verbose("Floppy disk image geometry: %d tracks, %d head(s), %d sectors with %d bytes.\n", tracks, heads, sectors, sector_size);
+ if (tracks > 82)
+ {
+ osd_printf_info("jvc_format: track count of %d unsupported\n", tracks);
+ return false;
+ }
+
+ osd_printf_verbose("jvc_format: Floppy disk image geometry: %d tracks, %d head(s), %d sectors with %d bytes.\n", tracks, heads, sectors, sector_size);
return tracks * heads * sectors * sector_size == (size - header_size);
}
-int jvc_format::identify(io_generic *io, uint32_t form_factor)
+int jvc_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
int header_size, tracks, heads, sectors, sector_size, sector_base_id;
- return parse_header(io, header_size, tracks, heads, sectors, sector_size, sector_base_id) ? 50 : 0;
+ if (parse_header(io, header_size, tracks, heads, sectors, sector_size, sector_base_id))
+ return FIFID_SIZE;
+ else
+ return 0;
}
-bool jvc_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool jvc_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
int header_size, track_count, head_count, sector_count, sector_size, sector_base_id;
+ int max_tracks, max_heads;
if (!parse_header(io, header_size, track_count, head_count, sector_count, sector_size, sector_base_id))
return false;
// safety check
if (sector_count * sector_size > 10000)
- emu_fatalerror("jvc_format: incorrect track layout\n");
+ {
+ osd_printf_error("jvc_format: incorrect track layout\n");
+ return false;
+ }
+
+ image.get_maximal_geometry(max_tracks, max_heads);
+
+ if (track_count > max_tracks)
+ {
+ osd_printf_error("jvc_format: Floppy disk has too many tracks for this drive (floppy tracks=%d, drive tracks=%d).\n", track_count, max_tracks);
+ return false;
+ }
+
+ if (head_count > max_heads)
+ {
+ osd_printf_error("jvc_format: Floppy disk has too many sides for this drive (floppy sides=%d, drive sides=%d).\n", head_count, max_heads);
+ return false;
+ }
int file_offset = header_size;
@@ -208,7 +244,7 @@ bool jvc_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
sectors[interleave[i]].bad_crc = false;
sectors[interleave[i]].data = &sector_data[sector_offset];
- io_generic_read(io, sectors[interleave[i]].data, file_offset, sector_size);
+ /*auto const [err, actual] =*/ read_at(io, file_offset, sectors[interleave[i]].data, sector_size); // FIXME: check for errors and premature EOF
sector_offset += sector_size;
file_offset += sector_size;
@@ -221,15 +257,12 @@ bool jvc_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-bool jvc_format::save(io_generic *io, floppy_image *image)
+bool jvc_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
- uint8_t bitstream[500000/8];
- uint8_t sector_data[50000];
- desc_xs sectors[256];
uint64_t file_offset = 0;
int track_count, head_count;
- image->get_actual_geometry(track_count, head_count);
+ image.get_actual_geometry(track_count, head_count);
// we'll write a header if the disk is two-sided
if (head_count == 2)
@@ -237,7 +270,7 @@ bool jvc_format::save(io_generic *io, floppy_image *image)
uint8_t header[2];
header[0] = 18;
header[1] = 2;
- io_generic_write(io, header, file_offset, sizeof(header));
+ /*auto const [err, actual] =*/ write_at(io, file_offset, header, sizeof(header)); // FIXME: check for errors
file_offset += sizeof(header);
}
@@ -246,17 +279,19 @@ bool jvc_format::save(io_generic *io, floppy_image *image)
{
for (int head = 0; head < head_count; head++)
{
- int track_size;
- generate_bitstream_from_track(track, head, 2000, bitstream, track_size, image);
- extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sector_data, sizeof(sector_data));
+ auto bitstream = generate_bitstream_from_track(track, head, 2000, image);
+ auto sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
for (int i = 0; i < 18; i++)
{
- if (sectors[1 + i].size != 256)
- emu_fatalerror("jvc_format: invalid sector size: %d\n", sectors[1 + i].size);
-
- io_generic_write(io, sectors[1 + i].data, file_offset, sectors[1 + i].size);
- file_offset += sectors[1 + i].size;
+ if (sectors[1 + i].size() != 256)
+ {
+ osd_printf_error("jvc_format: invalid sector size: %d\n", sectors[1 + i].size());
+ return false;
+ }
+
+ /*auto const [err, actual] =*/ write_at(io, file_offset, sectors[1 + i].data(), 256); // FIXME: check for errors
+ file_offset += 256;
}
}
}
@@ -264,9 +299,9 @@ bool jvc_format::save(io_generic *io, floppy_image *image)
return true;
}
-bool jvc_format::supports_save() const
+bool jvc_format::supports_save() const noexcept
{
return true;
}
-const floppy_format_type FLOPPY_JVC_FORMAT = &floppy_image_format_creator<jvc_format>;
+const jvc_format FLOPPY_JVC_FORMAT;
diff --git a/src/lib/formats/jvc_dsk.h b/src/lib/formats/jvc_dsk.h
index cbf1f341cde..ff350dcad40 100644
--- a/src/lib/formats/jvc_dsk.h
+++ b/src/lib/formats/jvc_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -31,19 +31,19 @@ public:
int header_size;
};
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
private:
- bool parse_header(io_generic *io, int &header_size, int &tracks, int &heads, int &sectors, int &sector_size, int &base_sector_id);
+ static bool parse_header(util::random_read &io, int &header_size, int &tracks, int &heads, int &sectors, int &sector_size, int &base_sector_id);
};
-extern const floppy_format_type FLOPPY_JVC_FORMAT;
+extern const jvc_format FLOPPY_JVC_FORMAT;
#endif // MAME_FORMATS_JVC_DSK_H
diff --git a/src/lib/formats/kaypro_dsk.cpp b/src/lib/formats/kaypro_dsk.cpp
index e9924ce11c9..3c417a0cf76 100644
--- a/src/lib/formats/kaypro_dsk.cpp
+++ b/src/lib/formats/kaypro_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Olivier Galibert, Robbbert
/*********************************************************************
- formats/kaypro_dsk.c
+ formats/kaypro_dsk.cpp
Kaypro disk image format
@@ -16,27 +16,25 @@
*********************************************************************/
-#include <assert.h>
-
#include "formats/kaypro_dsk.h"
kayproii_format::kayproii_format() : upd765_format(formats)
{
}
-const char *kayproii_format::name() const
+const char *kayproii_format::name() const noexcept
{
- return "kaypro";
+ return "kaypro2";
}
-const char *kayproii_format::description() const
+const char *kayproii_format::description() const noexcept
{
return "Kaypro disk image";
}
-const char *kayproii_format::extensions() const
+const char *kayproii_format::extensions() const noexcept
{
- return "kay";
+ return "kay,dsk";
}
const kayproii_format::format kayproii_format::formats[] = {
@@ -51,19 +49,19 @@ kaypro2x_format::kaypro2x_format() : upd765_format(formats)
{
}
-const char *kaypro2x_format::name() const
+const char *kaypro2x_format::name() const noexcept
{
- return "kaypro";
+ return "kaypro2x";
}
-const char *kaypro2x_format::description() const
+const char *kaypro2x_format::description() const noexcept
{
return "Kaypro disk image";
}
-const char *kaypro2x_format::extensions() const
+const char *kaypro2x_format::extensions() const noexcept
{
- return "kay";
+ return "kay,dsk";
}
const kaypro2x_format::format kaypro2x_format::formats[] = {
@@ -74,5 +72,5 @@ const kaypro2x_format::format kaypro2x_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_KAYPROII_FORMAT = &floppy_image_format_creator<kayproii_format>;
-const floppy_format_type FLOPPY_KAYPRO2X_FORMAT = &floppy_image_format_creator<kaypro2x_format>;
+const kayproii_format FLOPPY_KAYPROII_FORMAT;
+const kaypro2x_format FLOPPY_KAYPRO2X_FORMAT;
diff --git a/src/lib/formats/kaypro_dsk.h b/src/lib/formats/kaypro_dsk.h
index 462873781b4..3f3d4c86919 100644
--- a/src/lib/formats/kaypro_dsk.h
+++ b/src/lib/formats/kaypro_dsk.h
@@ -18,9 +18,9 @@ class kayproii_format : public upd765_format {
public:
kayproii_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
@@ -30,14 +30,14 @@ class kaypro2x_format : public upd765_format {
public:
kaypro2x_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_KAYPROII_FORMAT;
-extern const floppy_format_type FLOPPY_KAYPRO2X_FORMAT;
+extern const kayproii_format FLOPPY_KAYPROII_FORMAT;
+extern const kaypro2x_format FLOPPY_KAYPRO2X_FORMAT;
#endif // MAME_FORMATS_KAYPRO_DSK_H
diff --git a/src/lib/formats/kc85_dsk.cpp b/src/lib/formats/kc85_dsk.cpp
index 84111ff1171..5383a67afa0 100644
--- a/src/lib/formats/kc85_dsk.cpp
+++ b/src/lib/formats/kc85_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/kc85_dsk.c
+ formats/kc85_dsk.cpp
kc85 format
*********************************************************************/
-#include <assert.h>
-
#include "formats/kc85_dsk.h"
kc85_format::kc85_format() : upd765_format(formats)
{
}
-const char *kc85_format::name() const
+const char *kc85_format::name() const noexcept
{
return "kc85";
}
-const char *kc85_format::description() const
+const char *kc85_format::description() const noexcept
{
return "KC85 disk image";
}
-const char *kc85_format::extensions() const
+const char *kc85_format::extensions() const noexcept
{
return "img";
}
@@ -60,4 +58,4 @@ const kc85_format::format kc85_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_KC85_FORMAT = &floppy_image_format_creator<kc85_format>;
+const kc85_format FLOPPY_KC85_FORMAT;
diff --git a/src/lib/formats/kc85_dsk.h b/src/lib/formats/kc85_dsk.h
index b28ab1e6d98..eae13b93f63 100644
--- a/src/lib/formats/kc85_dsk.h
+++ b/src/lib/formats/kc85_dsk.h
@@ -18,14 +18,14 @@ class kc85_format : public upd765_format {
public:
kc85_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_KC85_FORMAT;
+extern const kc85_format FLOPPY_KC85_FORMAT;
#endif // MAME_FORMATS_KC85_DSK_H
diff --git a/src/lib/formats/kc_cas.cpp b/src/lib/formats/kc_cas.cpp
index 713def9ac83..34c52786b9c 100644
--- a/src/lib/formats/kc_cas.cpp
+++ b/src/lib/formats/kc_cas.cpp
@@ -13,10 +13,10 @@
********************************************************************/
-#include <assert.h>
-
#include "kc_cas.h"
+#include <cstring>
+
#define SMPLO -32768
#define SMPHI 32767
#define SILENCE 0
@@ -38,7 +38,7 @@ enum
};
// image size
-static int kc_image_size;
+static int kc_image_size; // FIXME: global variable prevents multiple instances
/*******************************************************************
Generate one high-low cycle of sample data
@@ -232,7 +232,7 @@ static int kc_handle_sss(int16_t *buffer, const uint8_t *casdata)
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int kc_kcc_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int kc_kcc_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return kc_handle_kcc(buffer, bytes);
}
@@ -243,13 +243,13 @@ static int kc_kcc_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
********************************************************************/
static int kc_kcc_to_wav_size(const uint8_t *casdata, int caslen)
{
- kc_image_size = caslen ;
+ kc_image_size = caslen;
return kc_handle_kcc( nullptr, casdata );
}
-static const struct CassetteLegacyWaveFiller kc_kcc_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller kc_kcc_legacy_fill_wave =
{
kc_kcc_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -260,19 +260,19 @@ static const struct CassetteLegacyWaveFiller kc_kcc_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error kc_kcc_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error kc_kcc_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &kc_kcc_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &kc_kcc_legacy_fill_wave);
}
static cassette_image::error kc_kcc_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &kc_kcc_legacy_fill_wave);
+ return cassette->legacy_construct(&kc_kcc_legacy_fill_wave);
}
-static const struct CassetteFormat kc_kcc_format =
+static const cassette_image::Format kc_kcc_format =
{
"kcc,kcb",
kc_kcc_identify,
@@ -284,7 +284,7 @@ static const struct CassetteFormat kc_kcc_format =
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int kc_tap_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int kc_tap_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return kc_handle_tap(buffer, bytes);
}
@@ -295,13 +295,13 @@ static int kc_tap_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
********************************************************************/
static int kc_tap_to_wav_size(const uint8_t *casdata, int caslen)
{
- kc_image_size = caslen ;
+ kc_image_size = caslen;
return kc_handle_tap( nullptr, casdata );
}
-static const struct CassetteLegacyWaveFiller kc_tap_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller kc_tap_legacy_fill_wave =
{
kc_tap_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -312,19 +312,19 @@ static const struct CassetteLegacyWaveFiller kc_tap_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error kc_tap_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error kc_tap_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &kc_tap_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &kc_tap_legacy_fill_wave);
}
static cassette_image::error kc_tap_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &kc_tap_legacy_fill_wave);
+ return cassette->legacy_construct(&kc_tap_legacy_fill_wave);
}
-static const struct CassetteFormat kc_tap_format =
+static const cassette_image::Format kc_tap_format =
{
"tap,853,854,855,tp2,kcm",
kc_tap_identify,
@@ -336,7 +336,7 @@ static const struct CassetteFormat kc_tap_format =
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int kc_sss_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int kc_sss_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return kc_handle_sss(buffer, bytes);
}
@@ -347,13 +347,13 @@ static int kc_sss_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
********************************************************************/
static int kc_sss_to_wav_size(const uint8_t *casdata, int caslen)
{
- kc_image_size = caslen ;
+ kc_image_size = caslen;
return kc_handle_sss( nullptr, casdata );
}
-static const struct CassetteLegacyWaveFiller kc_sss_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller kc_sss_legacy_fill_wave =
{
kc_sss_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -364,18 +364,18 @@ static const struct CassetteLegacyWaveFiller kc_sss_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error kc_sss_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error kc_sss_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &kc_sss_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &kc_sss_legacy_fill_wave);
}
static cassette_image::error kc_sss_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &kc_sss_legacy_fill_wave);
+ return cassette->legacy_construct(&kc_sss_legacy_fill_wave);
}
-static const struct CassetteFormat kc_sss_format =
+static const cassette_image::Format kc_sss_format =
{
"sss",
kc_sss_identify,
diff --git a/src/lib/formats/kim1_cas.cpp b/src/lib/formats/kim1_cas.cpp
index 1bfccc7f8a0..de2ee022632 100644
--- a/src/lib/formats/kim1_cas.cpp
+++ b/src/lib/formats/kim1_cas.cpp
@@ -1,14 +1,16 @@
// license:BSD-3-Clause
// copyright-holders:Wilbert Pol
-#include <assert.h>
-
#include "kim1_cas.h"
+#include "multibyte.h"
+
+#include <cstring>
+
#define SMPLO -32768
#define SMPHI 32767
-static int cas_size;
+static int cas_size; // FIXME: global variable prevents multiple instances
static inline int kim1_output_signal( int16_t *buffer, int sample_pos, int high )
@@ -87,8 +89,8 @@ static int kim1_handle_kim(int16_t *buffer, const uint8_t *casdata)
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 );
+ address = get_u16le( &casdata[4] );
+ size = get_u16le( &casdata[6] );
file_id = casdata[8];
data_pos = 9;
@@ -143,7 +145,7 @@ static int kim1_handle_kim(int16_t *buffer, const uint8_t *casdata)
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int kim1_kim_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int kim1_kim_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return kim1_handle_kim( buffer, bytes );
}
@@ -159,7 +161,7 @@ static int kim1_kim_to_wav_size(const uint8_t *casdata, int caslen)
return kim1_handle_kim( nullptr, casdata );
}
-static const struct CassetteLegacyWaveFiller kim1_kim_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller kim1_kim_legacy_fill_wave =
{
kim1_kim_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -171,19 +173,19 @@ static const struct CassetteLegacyWaveFiller kim1_kim_legacy_fill_wave =
};
-static cassette_image::error kim1_kim_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error kim1_kim_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &kim1_kim_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &kim1_kim_legacy_fill_wave);
}
static cassette_image::error kim1_kim_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &kim1_kim_legacy_fill_wave);
+ return cassette->legacy_construct(&kim1_kim_legacy_fill_wave);
}
-static const struct CassetteFormat kim1_kim_format =
+static const cassette_image::Format kim1_kim_format =
{
"kim,kim1",
kim1_kim_identify,
diff --git a/src/lib/formats/lviv_lvt.cpp b/src/lib/formats/lviv_lvt.cpp
index 09e91eac9fd..e37508ab529 100644
--- a/src/lib/formats/lviv_lvt.cpp
+++ b/src/lib/formats/lviv_lvt.cpp
@@ -1,7 +1,6 @@
// license:BSD-3-Clause
// copyright-holders:Krzysztof Strzecha
/* .LVT tape images */
-#include <assert.h>
#include "lviv_lvt.h"
@@ -19,9 +18,7 @@
static int16_t *lviv_emit_level(int16_t *p, int count, int level)
{
- int i;
-
- for (i=0; i<count; i++)
+ for (int i=0; i<count; i++)
{
*(p++) = level;
}
@@ -47,11 +44,9 @@ static int16_t* lviv_output_bit(int16_t *p, uint8_t b)
static int16_t* lviv_output_byte(int16_t *p, uint8_t byte)
{
- int i;
-
p = lviv_output_bit (p, 0);
- for (i=0; i<8; i++)
+ for (int i=0; i<8; i++)
p = lviv_output_bit(p,(byte>>i) & 0x01);
p = lviv_output_bit (p, 1);
@@ -76,25 +71,24 @@ static int lviv_cassette_calculate_size_in_samples(const uint8_t *bytes, int len
/*************************************************************************************/
-static int lviv_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int lviv_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
- int i;
int16_t * p = buffer;
int data_size;
- for (i=0; i<LVIV_LVT_HEADER_PILOT_LENGTH; i++)
+ for (int i=0; i<LVIV_LVT_HEADER_PILOT_LENGTH; i++)
p = lviv_output_bit (p, 1);
- for (i=0; i<10; i++)
+ for (int i=0; i<10; i++)
p = lviv_output_byte (p, bytes[0x09]);
- for (i=0; i<6; i++)
+ for (int 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++)
+ for (int i=0; i<LVIV_LVT_BLOCK_PILOT_LENGTH; i++)
p = lviv_output_bit (p, 1);
data_size = length - ( LVIV_LVT_HEADER_PILOT_SAMPLES +
@@ -103,7 +97,7 @@ static int lviv_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
LVIV_LVT_BLOCK_PILOT_SAMPLES );
data_size/=660;
- for (i=0; i<data_size; i++)
+ for (int i=0; i<data_size; i++)
p = lviv_output_byte (p, bytes[0x10+i]);
return p - buffer;
@@ -111,7 +105,7 @@ static int lviv_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
-static const struct CassetteLegacyWaveFiller lviv_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller lviv_legacy_fill_wave =
{
lviv_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -124,21 +118,21 @@ static const struct CassetteLegacyWaveFiller lviv_legacy_fill_wave =
-static cassette_image::error lviv_lvt_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error lviv_lvt_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &lviv_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &lviv_legacy_fill_wave);
}
static cassette_image::error lviv_lvt_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &lviv_legacy_fill_wave);
+ return cassette->legacy_construct(&lviv_legacy_fill_wave);
}
-static const struct CassetteFormat lviv_lvt_image_format =
+static const cassette_image::Format lviv_lvt_image_format =
{
"lvt,lvr,lv0,lv1,lv2,lv3",
lviv_lvt_identify,
diff --git a/src/lib/formats/lw30_dsk.cpp b/src/lib/formats/lw30_dsk.cpp
new file mode 100644
index 00000000000..dd1536755d5
--- /dev/null
+++ b/src/lib/formats/lw30_dsk.cpp
@@ -0,0 +1,213 @@
+// license:BSD-3-Clause
+// copyright-holders:Bartman/Abyss
+/***************************************************************************
+
+ Brother LW-30 Disk image format
+ see https://github.com/BartmanAbyss/brother-diskconv for disk conversion tool
+
+***************************************************************************/
+#include "lw30_dsk.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <array>
+#include <cassert>
+#include <cstring>
+
+
+namespace {
+ constexpr uint16_t sync_table[]{
+ 0xdaef, 0xb7ad, 0xfbbe, 0xeadf, 0xbffa, 0xaeb6, 0xf5d7, 0xdbee, 0xbaab, 0xfdbd,
+ 0xebde, 0xd5f7, 0xafb5, 0xf6d6, 0xdded, 0xbbaa, 0xedbb, 0xd6dd, 0xb5f6, 0xf7af,
+ 0xded5, 0xbdeb, 0xabfd, 0xeeba, 0xd7db, 0xb6f5, 0xfaae, 0xdfbf, 0xbeea, 0xadfb,
+ 0xefb7, 0xdada, 0xb7ef, 0xfbad, 0xeabe, 0xbfdf, 0xaefa, 0xf5b6, 0xdbd7, 0xbaee,
+ 0xfdab, 0xebbd, 0xd5de, 0xaff7, 0xf6b5, 0xddd6, 0xbbed, 0xaadd, 0xedf6, 0xd6af,
+ 0xb5d5, 0xf7eb, 0xdefd, 0xbdba, 0xabdb, 0xeef5, 0xd7ae, 0xb6bf, 0xfaea, 0xdffb,
+ 0xbeb7, 0xadda, 0xefef, 0xdaad, 0xb7be, 0xfbdf, 0xeafa, 0xbfb6, 0xaed7, 0xf5ee,
+ 0xdbab, 0xbabd, 0xfdde, 0xebf7, 0xd5b5, 0xafd6, 0xf6ed, 0xddaa, 0xd6bb, 0xb5dd
+ };
+
+ constexpr uint8_t gcr_table[]{
+ 0xaa, 0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7,
+ 0xba, 0xbb, 0xbd, 0xbe, 0xbf, 0xd5, 0xd6, 0xd7,
+ 0xda, 0xdb, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed,
+ 0xee, 0xef, 0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd,
+ 0xfe, 0xff // FE, FF are reserved
+ };
+
+ // format
+ constexpr uint8_t sector_prefix[]{
+ 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xab
+ };
+
+ // write
+ constexpr uint8_t sector_header[]{
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xed
+ };
+
+ // write
+ constexpr uint8_t sector_footer[]{
+ 0xf5, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd
+ };
+
+ constexpr uint8_t sector_interleave1[]{ // 1-based
+ 1, 6, 11, 4, 9, 2, 7, 12, 5, 10, 3, 8
+ };
+
+ constexpr uint8_t sector_interleave2[]{ // 1-based
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
+ };
+
+ constexpr uint8_t sector_interleave3[]{ // 1-based
+ 1, 4, 7, 10, 6, 9, 12, 3, 11, 2, 5, 8
+ };
+
+ void gcr_encode_5_to_8(const uint8_t *input, uint8_t *output)
+ {
+ // input:
+ // 76543210
+ // --------
+ // 00000111 0
+ // 11222223 1
+ // 33334444 2
+ // 45555566 3
+ // 66677777 4
+
+ output[0] = gcr_table[(input[0] >> 3) & 0x1f];
+ output[1] = gcr_table[((input[0] << 2) & 0x1f) | ((input[1] >> 6) & 0b00000011)];
+ output[2] = gcr_table[(input[1] >> 1) & 0x1f];
+ output[3] = gcr_table[((input[1] << 4) & 0x1f) | ((input[2] >> 4) & 0b00001111)];
+ output[4] = gcr_table[((input[2] << 1) & 0x1f) | ((input[3] >> 7) & 0b00000001)];
+ output[5] = gcr_table[(input[3] >> 2) & 0x1f];
+ output[6] = gcr_table[((input[3] << 3) & 0x1f) | ((input[4] >> 5) & 0b00000111)];
+ output[7] = gcr_table[input[4] & 0x1f];
+ }
+
+ std::array<uint8_t, 3> checksum_256_bytes(const uint8_t *input)
+ {
+ size_t i = 0;
+ uint8_t a = 0;
+ uint8_t c = input[i++];
+ uint8_t d = input[i++];
+ uint8_t e = input[i++];
+ for(size_t b = 0; b < 253; b++) {
+ a = d;
+ if(c & 0b10000000)
+ a ^= 1;
+ d = c;
+ c = a;
+ a = (d << 1) ^ e;
+ e = d;
+ d = a;
+ e ^= input[i++];
+ }
+
+ return { c, d, e };
+ }
+
+ std::array<uint8_t, 416> gcr_encode_and_checksum(const uint8_t *input /* 256 bytes */) {
+ std::array<uint8_t, 416> output;
+ for(int i = 0; i < 51; i++)
+ gcr_encode_5_to_8(&input[i * 5], &output[i * 8]);
+
+ auto checksum = checksum_256_bytes(input);
+ std::array<uint8_t, 5> end_and_checksum{ input[255], checksum[0], checksum[1], checksum[2], 0x58 };
+ gcr_encode_5_to_8(&end_and_checksum[0], &output[408]);
+
+ return output;
+ }
+}
+
+static constexpr int TRACKS_PER_DISK = 78;
+static constexpr int SECTORS_PER_TRACK = 12;
+static constexpr int SECTOR_SIZE = 256;
+
+static constexpr int RPM = 300;
+static constexpr int CELLS_PER_REV = 250'000 / (RPM / 60);
+
+// format track: 0xaa (2), 0xaa (48), 12*sector
+// format sector: sector_prefix (8), track_sync (2), sector_sync (2), predata (19), payload=0xaa (414), postdata (13), 0xaa (42), should come out to ~4,000 bits
+// write sector: (after sector_sync, 0xdd) sector_header (2+14), payload (416), sector_footer (11)
+
+// from write_format, write_sector_data_header_data_footer
+static constexpr int raw_sector_size = 8/*sector_prefix*/ + 2/*track_sync*/ + 2/*sector_sync*/ + 1/*0xdd*/ + 16/*sector_header*/ + 416/*payload*/ + 11/*sector_footer*/ + 42/*0xaa*/;
+static constexpr int raw_track_size = 2/*0xaa*/ + 48/*0xaa*/ + SECTORS_PER_TRACK * raw_sector_size;
+
+int lw30_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size = 0;
+ if(io.length(size))
+ return 0;
+
+ if(size == TRACKS_PER_DISK * SECTORS_PER_TRACK * SECTOR_SIZE)
+ return FIFID_SIZE; // identified by size
+
+ return 0;
+}
+
+bool lw30_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ uint8_t trackdata[SECTORS_PER_TRACK * SECTOR_SIZE], rawdata[CELLS_PER_REV / 8];
+ memset(rawdata, 0xaa, sizeof(rawdata));
+ for(int track = 0; track < TRACKS_PER_DISK; track++) {
+ auto const [err, actual] = read_at(io, track * SECTORS_PER_TRACK * SECTOR_SIZE, trackdata, SECTORS_PER_TRACK * SECTOR_SIZE);
+ if(err || (actual != SECTORS_PER_TRACK * SECTOR_SIZE))
+ return false;
+ size_t i = 0;
+ for(int x = 0; x < 2 + 48; x++)
+ rawdata[i++] = 0xaa;
+ auto interleave_offset = (track % 4) * 4;
+ for(size_t s = interleave_offset; s < interleave_offset + SECTORS_PER_TRACK; s++) {
+ auto sector = sector_interleave1[s % SECTORS_PER_TRACK] - 1;
+ // according to check_track_and_sector
+ for(const auto& d : sector_prefix) // 8 bytes
+ rawdata[i++] = d;
+ put_u16le(&rawdata[i], sync_table[track]);
+ i += 2;
+ put_u16le(&rawdata[i], sync_table[sector]);
+ i += 2;
+ rawdata[i++] = 0xdd;
+ for(const auto& d : sector_header) // 16 bytes
+ rawdata[i++] = d;
+ auto payload = gcr_encode_and_checksum(trackdata + sector * SECTOR_SIZE); // 256 -> 416 bytes
+ for(const auto &d : payload)
+ rawdata[i++] = d;
+ for(const auto &d : sector_footer) // 11 bytes
+ rawdata[i++] = d;
+ for(int x = 0; x < 42; x++)
+ rawdata[i++] = 0xaa;
+ }
+ assert(i == raw_track_size);
+ assert(i <= CELLS_PER_REV / 8);
+ generate_track_from_bitstream(track, 0, rawdata, CELLS_PER_REV, image);
+ }
+
+ image.set_variant(floppy_image::SSDD);
+
+ return true;
+}
+
+const char *lw30_format::name() const noexcept
+{
+ return "lw30";
+}
+
+const char *lw30_format::description() const noexcept
+{
+ return "Brother LW-30 floppy disk image";
+}
+
+const char *lw30_format::extensions() const noexcept
+{
+ return "img";
+}
+
+bool lw30_format::supports_save() const noexcept
+{
+ // TODO
+ return false;
+}
+
+const lw30_format FLOPPY_LW30_FORMAT;
diff --git a/src/lib/formats/lw30_dsk.h b/src/lib/formats/lw30_dsk.h
new file mode 100644
index 00000000000..cfff33d5888
--- /dev/null
+++ b/src/lib/formats/lw30_dsk.h
@@ -0,0 +1,29 @@
+// license:BSD-3-Clause
+// copyright-holders:Bartman/Abyss
+/***************************************************************************
+
+ Brother LW-30 Disk image format
+ see https://github.com/BartmanAbyss/brother-diskconv for disk conversion tool
+
+***************************************************************************/
+#ifndef MAME_FORMATS_LW30_DSK_H
+#define MAME_FORMATS_LW30_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+
+class lw30_format : public floppy_image_format_t
+{
+public:
+ virtual int identify(util::random_read& io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t>& variants, floppy_image &image) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
+};
+
+extern const lw30_format FLOPPY_LW30_FORMAT;
+
+#endif // MAME_FORMATS_LW30_DSK_H
diff --git a/src/lib/formats/m20_dsk.cpp b/src/lib/formats/m20_dsk.cpp
index cdd0e230db7..02905ecfa0d 100644
--- a/src/lib/formats/m20_dsk.cpp
+++ b/src/lib/formats/m20_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/m20_dsk.c
+ formats/m20_dsk.cpp
Olivetti M20 floppy-disk images
@@ -16,45 +16,53 @@
#include "m20_dsk.h"
+#include "ioprocs.h"
+
+
m20_format::m20_format()
{
}
-const char *m20_format::name() const
+const char *m20_format::name() const noexcept
{
return "m20";
}
-const char *m20_format::description() const
+const char *m20_format::description() const noexcept
{
return "M20 disk image";
}
-const char *m20_format::extensions() const
+const char *m20_format::extensions() const noexcept
{
return "img";
}
-bool m20_format::supports_save() const
+bool m20_format::supports_save() const noexcept
{
return true;
}
-int m20_format::identify(io_generic *io, uint32_t form_factor)
+int m20_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- if(io_generic_size(io) == 286720)
- return 50;
+ uint64_t size;
+ if (io.length(size))
+ return 0;
+
+ if (size == 286720)
+ return FIFID_SIZE;
+
return 0;
}
-bool m20_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool m20_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
for (int track = 0; track < 35; track++)
for (int head = 0; head < 2; head ++) {
bool mfm = track || head;
desc_pc_sector sects[16];
uint8_t sectdata[16*256];
- io_generic_read(io, sectdata, 16*256*(track*2+head), 16*256);
+ /*auto const [err, actual] =*/ read_at(io, 16*256*(track*2+head), sectdata, 16*256); // FIXME: check for errors and premature EOF
for (int i = 0; i < 16; i++) {
int j = i/2 + (i & 1 ? 0 : 8);
sects[i].track = track;
@@ -76,41 +84,36 @@ bool m20_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-bool m20_format::save(io_generic *io, floppy_image *image)
+bool m20_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
- uint8_t bitstream[500000/8];
- uint8_t sector_data[50000];
- desc_xs sectors[256];
- int track_size;
uint64_t file_offset = 0;
int track_count, head_count;
- track_count = 35; head_count = 2; //FIXME: use image->get_actual_geometry(track_count, head_count) instead
+ track_count = 35; head_count = 2; //FIXME: use image.get_actual_geometry(track_count, head_count) instead
- // initial fm track
- generate_bitstream_from_track(0, 0, 4000, bitstream, track_size, image);
- extract_sectors_from_bitstream_fm_pc(bitstream, track_size, sectors, sector_data, sizeof(sector_data));
+ // initial FM track
+ auto bitstream = generate_bitstream_from_track(0, 0, 4000, image);
+ auto sectors = extract_sectors_from_bitstream_fm_pc(bitstream);
- for (int i = 0; i < 16; i++)
- {
- io_generic_write(io, sectors[i + 1].data, file_offset, 128);
+ for (int i = 0; i < 16; i++) {
+ /*auto const [err, actual] =*/ write_at(io, file_offset, sectors[i + 1].data(), 128); // FIXME: check for errors
file_offset += 256; //128;
}
- // rest are mfm tracks
- for (int track = 0; track < track_count; track++)
- {
- for (int head = 0; head < head_count; head++)
- {
+ // rest are MFM tracks
+ for (int track = 0; track < track_count; track++) {
+ for (int head = 0; head < head_count; head++) {
// skip track 0, head 0
- if (track == 0) { if (head_count == 1) break; else head++; }
+ if (track == 0) {
+ if (head_count == 1) break;
+ else head++;
+ }
- generate_bitstream_from_track(track, head, 2000, bitstream, track_size, image);
- extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sector_data, sizeof(sector_data));
+ bitstream = generate_bitstream_from_track(track, head, 2000, image);
+ sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
- for (int i = 0; i < 16; i++)
- {
- io_generic_write(io, sectors[i + 1].data, file_offset, 256);
+ for (int i = 0; i < 16; i++) {
+ /*auto const [err, actual] =*/ write_at(io, file_offset, sectors[i + 1].data(), 256); // FIXME: check for errors
file_offset += 256;
}
}
@@ -119,4 +122,4 @@ bool m20_format::save(io_generic *io, floppy_image *image)
return true;
}
-const floppy_format_type FLOPPY_M20_FORMAT = &floppy_image_format_creator<m20_format>;
+const m20_format FLOPPY_M20_FORMAT;
diff --git a/src/lib/formats/m20_dsk.h b/src/lib/formats/m20_dsk.h
index fe06d75df22..ef37c0304c9 100644
--- a/src/lib/formats/m20_dsk.h
+++ b/src/lib/formats/m20_dsk.h
@@ -2,7 +2,7 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/m20_dsk.c
+ formats/m20_dsk.h
Olivetti M20 floppy-disk images
@@ -18,16 +18,16 @@ class m20_format : public floppy_image_format_t {
public:
m20_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_M20_FORMAT;
+extern const m20_format FLOPPY_M20_FORMAT;
#endif // MAME_FORMATS_M20_DSK_H
diff --git a/src/lib/formats/m5_dsk.cpp b/src/lib/formats/m5_dsk.cpp
index 9652eedbb72..6184bf9fbfb 100644
--- a/src/lib/formats/m5_dsk.cpp
+++ b/src/lib/formats/m5_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/m5_dsk.c
+ formats/m5_dsk.cpp
sord m5 format
*********************************************************************/
-#include <assert.h>
-
#include "formats/m5_dsk.h"
m5_format::m5_format() : upd765_format(formats)
{
}
-const char *m5_format::name() const
+const char *m5_format::name() const noexcept
{
return "m5";
}
-const char *m5_format::description() const
+const char *m5_format::description() const noexcept
{
return "Sord M5 disk image";
}
-const char *m5_format::extensions() const
+const char *m5_format::extensions() const noexcept
{
return "dsk";
}
@@ -44,4 +42,4 @@ const m5_format::format m5_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_M5_FORMAT = &floppy_image_format_creator<m5_format>;
+const m5_format FLOPPY_M5_FORMAT;
diff --git a/src/lib/formats/m5_dsk.h b/src/lib/formats/m5_dsk.h
index f26d96dffc7..1f1430fe082 100644
--- a/src/lib/formats/m5_dsk.h
+++ b/src/lib/formats/m5_dsk.h
@@ -18,14 +18,14 @@ class m5_format : public upd765_format {
public:
m5_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_M5_FORMAT;
+extern const m5_format FLOPPY_M5_FORMAT;
#endif // MAME_FORMATS_M5_DSK_H
diff --git a/src/lib/formats/mbee_cas.cpp b/src/lib/formats/mbee_cas.cpp
index 24024f66f5d..f8c42602b2b 100644
--- a/src/lib/formats/mbee_cas.cpp
+++ b/src/lib/formats/mbee_cas.cpp
@@ -56,8 +56,6 @@ TAP - has an ID header of TAP_DGOS_BEE or MBEE, null terminated.
********************************************************************/
-#include <assert.h>
-
#include "mbee_cas.h"
#define WAVEENTRY_LOW -32768
@@ -211,7 +209,7 @@ static int mbee_handle_tap(int16_t *buffer, const uint8_t *bytes)
Generate samples for the tape image
********************************************************************/
-static int mbee_tap_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int mbee_tap_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return mbee_handle_tap(buffer, bytes);
}
@@ -227,7 +225,7 @@ static int mbee_tap_calculate_size_in_samples(const uint8_t *bytes, int length)
return mbee_handle_tap(nullptr, bytes);
}
-static const struct CassetteLegacyWaveFiller mbee_tap_config =
+static const cassette_image::LegacyWaveFiller mbee_tap_config =
{
mbee_tap_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -238,17 +236,17 @@ static const struct CassetteLegacyWaveFiller mbee_tap_config =
0 /* trailer_samples */
};
-static cassette_image::error mbee_tap_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error mbee_tap_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &mbee_tap_config);
+ return cassette->legacy_identify(opts, &mbee_tap_config);
}
static cassette_image::error mbee_tap_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &mbee_tap_config);
+ return cassette->legacy_construct(&mbee_tap_config);
}
-static const struct CassetteFormat mbee_tap_image_format =
+static const cassette_image::Format mbee_tap_image_format =
{
"tap",
mbee_tap_identify,
diff --git a/src/lib/formats/mdos_dsk.cpp b/src/lib/formats/mdos_dsk.cpp
new file mode 100644
index 00000000000..a14df309db4
--- /dev/null
+++ b/src/lib/formats/mdos_dsk.cpp
@@ -0,0 +1,300 @@
+// license:BSD-3-Clause
+// copyright-holders: 68bit
+/*
+ * mdos_dsk.cpp - Motorola MDOS compatible disk images
+ *
+ * The format is largely IBM 3740 compatible, with 77 tracks, and 26 sectors
+ * per track, and a sector size of 128 bytes. Single sided disks were initially
+ * supported and later software supported double sided disk with 52 sectors per
+ * cylinder. The sectors are numbered from one, and the sectors indexes on the
+ * second side continue from the first side rather than starting again at
+ * one.
+ *
+ * The disk drives run at 360rpm, and the bit clock frequency at 500kHz. This
+ * gives 6 revs per second, and 500000 / 6 = 83333 cells per track. MAME uses
+ * disk rotation position units of 1/200000000 of a turn and the cells size in
+ * this unit is 200000000 / (500000 / 6) = 2400.
+ *
+ * The M68SFDC2 user guide details the format gaps. "The Post-Index Gap is
+ * defined as the 32 bytes between Index Address Mark and the ID Address Mark
+ * for sector 1 (excluding the address mark bytes). This gap is always 32 bytes
+ * in length and is not affect by any up-dating process. The ID Gap consists of
+ * 17 bytes between the ID Field and the Data Field. This gap may vary in size
+ * slightly after the Data Field has been updated. The Data Gap consists of 33
+ * bytes between the Data Field and the next ID File. This gap may also vary
+ * slightly after the Data Field has been updated. The Pre-Index Gap is a space
+ * of 320 bytes between the last data field on the track and the Index Address
+ * Mark. This gap may also vary in length."
+ *
+ * These gaps do appear to include the leading zero bytes before the sync
+ * marks, in contrast to the gaps defined in the formats below so corrections
+ * have been applied. The trailing gap has been checked and it close to the 320
+ * bytes expected.
+ *
+ * The MDOS 3.05 FORMAT command with (the current emulator timing) varies from
+ * the above slightly. An index marker has been added, 106 bytes after the
+ * start of the track - a 100 byte gap and then 6 bytes of zeros and an index
+ * marker 0xf77a. This index marker does not appear to be used. The post-index
+ * marker gap is 30 bytes between the index marker and the address marker - a
+ * gap of 24 bytes and then 6 bytes of zeros and then the address marker
+ * 0xf57e. The ID Gap consist of roughly 18 bytes between the ID Field and the
+ * Data file - 12 bytes gap and 6 bytes of zero, and then the data marker
+ * 0xf565. The Data Gap consists of 33 bytes - a gap of 27 bytes and 6 bytes of
+ * zeros and then the address marker 0xf57e. The Pre-Index gap is a space of
+ * 209 bytes, but this now extends to the start of the track to the index
+ * marker.
+ *
+ * TODO The MDOS 3 FORMAT command writes a zero in the address mark 'head'
+ * field irrespective of the disk head and the software ignores this field when
+ * read.
+ */
+
+#include "mdos_dsk.h"
+#include "imageutl.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <tuple>
+
+
+mdos_format::mdos_format() : wd177x_format(formats)
+{
+}
+
+const char *mdos_format::name() const noexcept
+{
+ return "mdos";
+}
+
+const char *mdos_format::description() const noexcept
+{
+ return "Motorola MDOS compatible disk image";
+}
+
+const char *mdos_format::extensions() const noexcept
+{
+ return "dsk";
+}
+
+int mdos_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ int const type = find_size(io, form_factor, variants);
+
+ if (type != -1)
+ return FIFID_SIZE | FIFID_STRUCT;
+
+ return 0;
+}
+
+bool mdos_format::check_ascii(const uint8_t *str, size_t len, const char* name)
+{
+ LOG_FORMATS(" %s: \"", name);
+ for (int i = 0; i < len; i++) {
+ uint8_t ch = str[i];
+ if (ch == 0) {
+ LOG_FORMATS("\\0");
+ } else if (ch < 0x20 || ch > 0x7f) {
+ return 0;
+ } else {
+ LOG_FORMATS("%c", ch);
+ }
+ }
+ LOG_FORMATS("\"\n");
+ return 1;
+}
+
+int mdos_format::parse_date_field(const uint8_t *str)
+{
+ uint8_t high = str[0];
+ uint8_t low = str[1];
+
+ if (high < 0x30 || high > 0x39 || low < 0x30 || low > 0x39)
+ return -1;
+
+ return (high - 0x30) * 10 + (low - 0x30);
+}
+
+int mdos_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ std::error_condition err;
+ size_t actual;
+
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ // Look at the disk ID sector.
+ disk_id_sector info;
+ std::tie(err, actual) = read_at(io, 0, &info, sizeof(disk_id_sector));
+ if (err || (sizeof(disk_id_sector) != actual))
+ return -1;
+
+ LOG_FORMATS("MDOS floppy dsk: size %d bytes, %d total sectors, %d remaining bytes, expected form factor %x\n", (uint32_t)size, (uint32_t)size / 128, (uint32_t)size % 128, form_factor);
+
+ // The 'unused' area is not necessarily zero filled and is ignored
+ // in the identification of a MDOS format image.
+
+ // Expect an ASCII id, version, revision, and 'user name' strings.
+ if (!check_ascii(info.id, sizeof(info.id), "id"))
+ return -1;
+
+ if (!check_ascii(info.version, sizeof(info.version), "version"))
+ return -1;
+
+ if (!check_ascii(info.revision, sizeof(info.revision), "revision"))
+ return -1;
+
+ if (!check_ascii(info.date, sizeof(info.date), "date"))
+ return -1;
+
+ if (!check_ascii(info.username, sizeof(info.username), "username"))
+ return -1;
+
+ // The date should be the numeric day, month and year.
+ int const month = parse_date_field(info.date);
+ int const day = parse_date_field(info.date + 2);
+ int const year = parse_date_field(info.date + 4);
+
+ LOG_FORMATS(" day %d, month %d, year %d\n", day, month, year);
+ if (day < 1 || day > 32 || month < 1 || month > 12 || year < 0)
+ return -1;
+
+ // The RIB address seems to be a sequence of 16 bit cluster numbers,
+ // ending in a zero word. The maximum size appears to be 20 bytes, or
+ // 10 words. The area beyond the zero word is not always zero filled,
+ // and is ignored here. Check that it is consistent with this.
+ for (int i = 0; i < 10; i++) {
+ uint16_t cluster = get_u16be(&info.rib_addr[i * 2]);
+
+ if (cluster == 0)
+ break;
+
+ // The lowest value cluster here appears to be 92, just after
+ // the director sectors.
+ if (cluster < 92) {
+ LOG_FORMATS(" id sector rib address %d unexpectedly low\n", cluster);
+ return -1;
+ }
+
+ if (cluster * 4 * 128 + 4 * 128 > size) {
+ LOG_FORMATS(" id sector rib address %d beyond disk extent\n", cluster);
+ return -1;
+ }
+ }
+
+ // Check that the cluster allocation table and the cluster available
+ // table are consistent with the disk size, that no clusters beyond
+ // the extent of the disk are free or available.
+
+ uint8_t cluster_allocation[128], cluster_available[128];
+ std::tie(err, actual) = read_at(io, 1 * 128, &cluster_allocation, sizeof(cluster_allocation));
+ if (err || (sizeof(cluster_allocation) != actual))
+ return -1;
+ std::tie(err, actual) = read_at(io, 2 * 128, &cluster_available, sizeof(cluster_available));
+ if (err || (sizeof(cluster_available) != actual))
+ return -1;
+
+ for (int cluster = 0; cluster < sizeof(cluster_allocation) * 8; cluster++) {
+ if (cluster * 4 * 128 + 4 * 128 > size) {
+ // This cluster does not fit within the disk size, so
+ // should be marked as not free and not available.
+ uint8_t mask = 1 << (7 - (cluster & 7));
+ uint8_t offset = cluster >> 3;
+ if ((cluster_allocation[offset] & mask) == 0) {
+ LOG_FORMATS(" unexpected free cluster %d beyond disk extent\n", cluster);
+ return -1;
+ }
+ if ((cluster_available[offset] & mask) == 0) {
+ LOG_FORMATS(" unexpected available cluster %d beyond disk extent\n", cluster);
+ return -1;
+ }
+ }
+ }
+
+ for (int i = 0; formats[i].form_factor; i++) {
+ const format &f = formats[i];
+ LOG_FORMATS(" checking format %d with form factor %02x, %d sectors, %d heads\n",
+ i, f.form_factor, f.sector_count, f.head_count);
+ if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
+ continue;
+
+ unsigned int format_size = 0;
+ for (int track=0; track < f.track_count; track++) {
+ for (int head=0; head < f.head_count; head++) {
+ const format &tf = get_track_format(f, head, track);
+ format_size += compute_track_size(tf);
+ }
+ }
+
+ LOG_FORMATS(" image size %u\n", format_size);
+ if (format_size != size)
+ continue;
+
+ LOG_FORMATS("MDOS matching format index %d\n", i);
+ return i;
+ }
+ return -1;
+}
+
+const wd177x_format::format &mdos_format::get_track_format(const format &f, int head, int track) const
+{
+ int n = -1;
+
+ for (int i = 0; formats[i].form_factor; i++) {
+ if (&formats[i] == &f) {
+ n = i;
+ break;
+ }
+ }
+
+ if (n < 0) {
+ LOG_FORMATS("Error format not found\n");
+ return f;
+ }
+
+ if (head >= f.head_count) {
+ LOG_FORMATS("Error invalid head %d\n", head);
+ return f;
+ }
+
+ if (track >= f.track_count) {
+ LOG_FORMATS("Error invalid track %d\n", track);
+ return f;
+ }
+
+ if (head == 1) {
+ const format &fh1 = formats_head1[n];
+ if (!fh1.form_factor) {
+ LOG_FORMATS("Error expected a head 1 format\n");
+ return f;
+ }
+ return fh1;
+ }
+
+ return f;
+}
+
+const mdos_format::format mdos_format::formats[] = {
+ { // 55 250.25K 8 inch single sided single density
+ floppy_image::FF_8, floppy_image::SSSD, floppy_image::FM,
+ 2400, 26, 77, 1, 128, {}, 1, {}, 32-6, 17-6, 33-6
+ },
+ { // 57 500.5K 8 inch double sided single density
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2400, 26, 77, 2, 128, {}, 1, {}, 32-6, 17-6, 33-6
+ },
+ {}
+};
+
+const mdos_format::format mdos_format::formats_head1[] = {
+ { // 55 250.25K 8 inch single sided single density
+ },
+ { // 57 500.5K 8 inch double sided single density
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2400, 26, 77, 2, 128, {}, 27, {}, 32-6, 17-6, 33-6
+ },
+ {}
+};
+
+const mdos_format FLOPPY_MDOS_FORMAT;
diff --git a/src/lib/formats/mdos_dsk.h b/src/lib/formats/mdos_dsk.h
new file mode 100644
index 00000000000..379ef318f09
--- /dev/null
+++ b/src/lib/formats/mdos_dsk.h
@@ -0,0 +1,45 @@
+// license:BSD-3-Clause
+// copyright-holders:68bit
+/*
+ * mdos_dsk.h - Motorola MDOS compatible disk images
+ */
+#ifndef MAME_FORMATS_MDOS_DSK_H
+#define MAME_FORMATS_MDOS_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+#include "wd177x_dsk.h"
+
+class mdos_format : public wd177x_format
+{
+public:
+ mdos_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual const wd177x_format::format &get_track_format(const format &f, int head, int track) const override;
+
+private:
+ struct disk_id_sector
+ {
+ uint8_t id[8];
+ uint8_t version[2];
+ uint8_t revision[2];
+ uint8_t date[6];
+ uint8_t username[20];
+ uint8_t rib_addr[20];
+ uint8_t unused[70];
+ };
+ static const format formats[];
+ static const format formats_head1[];
+ static bool check_ascii(const uint8_t *, size_t len, const char* name);
+ static int parse_date_field(const uint8_t *str);
+};
+
+extern const mdos_format FLOPPY_MDOS_FORMAT;
+
+#endif // MAME_FORMATS_MDOS_DSK_H
diff --git a/src/lib/formats/mfi_dsk.cpp b/src/lib/formats/mfi_dsk.cpp
index b6a8a18460a..eb1e632ec03 100644
--- a/src/lib/formats/mfi_dsk.cpp
+++ b/src/lib/formats/mfi_dsk.cpp
@@ -1,10 +1,16 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
-#include <assert.h>
-
#include "mfi_dsk.h"
+
+#include "ioprocs.h"
+
#include <zlib.h>
+#include <cstring>
+#include <functional>
+#include <tuple>
+
+
/*
Mess floppy image structure:
@@ -27,37 +33,37 @@
simple "compress" function.
Track data consists of a series of 32-bits lsb-first values
- representing magnetic cells. Bits 0-27 indicate the sizes, and bits
- 28-31 the types. Type can be:
- - 0, MG_A -> Magnetic orientation A
- - 1, MG_B -> Magnetic orientation B
- - 2, MG_N -> Non-magnetized zone (neutral)
- - 3, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
-
- Remember that the fdcs detect transitions, not absolute levels, so
- the actual physical significance of the orientation A and B is
- arbitrary.
-
- Tracks data is aligned so that the index pulse is at the start,
- whether the disk is hard-sectored or not.
-
- The size is the angular size in units of 1/200,000,000th of a turn.
- Such a size, not coincidentally at all, is also the flyover time in
- nanoseconds for a perfectly stable 300rpm drive. That makes the
- standard cell size of a MFM 3.5" DD floppy at 2000 exactly for
- instance (2us). Smallest expected cell size is 500 (ED density
- drives).
-
- The sum of all sizes must of course be 200,000,000.
-
- An unformatted track is equivalent to one big MG_N cell covering a
- whole turn, but is encoded as zero-size.
+ representing magnetic cells. Bits 0-27 indicate the position,
+ delta-packed (e.g. difference with the previous position, starts at
+ 0), and bits 28-31 the types. Type can be:
+
+ - 0, MG_F -> Flux orientation change
+ - 1, MG_N -> Non-magnetized zone (neutral)
+ - 2, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
+ - 3, MG_E -> End of zone
+
+ Tracks data is aligned so that the index pulse is at the start for soft-
+ sectored disks. For hard-sectored disks, the sector hole for the first
+ sector is at the start and the index hole is half a sector from the end
+ of the track.
+
+ The position is the angular position in units of 1/200,000,000th of
+ a turn. A size in such units, not coincidentally at all, is also
+ the flyover time in nanoseconds for a perfectly stable 300rpm drive.
+ That makes the standard cell size of a MFM 3.5" DD floppy at 2000
+ exactly for instance (2us). Smallest expected cell size is 500 (ED
+ density drives).
+
+ An unformatted track is equivalent to a pair (MG_N, 0), (MG_E,
+ 199999999) but is encoded as zero-size.
The "track splice" information indicates where to start writing
if you try to rewrite a physical disk with the data. Some
preservation formats encode that information, it is guessed for
others. The write track function of fdcs should set it. The
- representation is the angular position relative to the index.
+ representation is the angular position relative to the index, for
+ soft-sectored disks, and the first sector hole for hard-sectored
+ disks.
The media type is divided in two parts. The first half
indicate the physical form factor, i.e. all medias with that
@@ -68,107 +74,156 @@
TODO: big-endian support
*/
-const char mfi_format::sign[16] = "MESSFLOPPYIMAGE"; // Includes the final \0
+const char mfi_format::sign_old[16] = "MESSFLOPPYIMAGE"; // Includes the final \0
+const char mfi_format::sign[16] = "MAMEFLOPPYIMAGE"; // Includes the final \0
mfi_format::mfi_format() : floppy_image_format_t()
{
}
-const char *mfi_format::name() const
+const char *mfi_format::name() const noexcept
{
return "mfi";
}
-const char *mfi_format::description() const
+const char *mfi_format::description() const noexcept
{
- return "MESS floppy image";
+ return "MAME floppy image";
}
-const char *mfi_format::extensions() const
+const char *mfi_format::extensions() const noexcept
{
return "mfi";
}
-bool mfi_format::supports_save() const
+bool mfi_format::supports_save() const noexcept
{
return true;
}
-int mfi_format::identify(io_generic *io, uint32_t form_factor)
+int mfi_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
header h;
+ auto const [err, actual] = read_at(io, 0, &h, sizeof(header));
+ if (err || (sizeof(header) != actual))
+ return 0;
- io_generic_read(io, &h, 0, sizeof(header));
- if(memcmp( h.sign, sign, 16 ) == 0 &&
+ if((!memcmp(h.sign, sign, 16) || !memcmp(h.sign, sign_old, 16)) &&
(h.cyl_count & CYLINDER_MASK) <= 84 &&
(h.cyl_count >> RESOLUTION_SHIFT) < 3 &&
h.head_count <= 2 &&
(!form_factor || !h.form_factor || h.form_factor == form_factor))
- return 100;
+ return FIFID_SIGN|FIFID_STRUCT;
+
return 0;
}
-bool mfi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool mfi_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
+ std::error_condition err;
+ size_t actual;
+
header h;
- entry entries[84*2*4];
- io_generic_read(io, &h, 0, sizeof(header));
- int resolution = h.cyl_count >> RESOLUTION_SHIFT;
+ std::tie(err, actual) = read_at(io, 0, &h, sizeof(header));
+ if(err || (sizeof(header) != actual))
+ return false;
+ int const resolution = h.cyl_count >> RESOLUTION_SHIFT;
h.cyl_count &= CYLINDER_MASK;
- io_generic_read(io, &entries, sizeof(header), (h.cyl_count << resolution)*h.head_count*sizeof(entry));
- image->set_variant(h.variant);
+ image.set_form_variant(h.form_factor, h.variant);
+
+ if(!h.cyl_count)
+ return true;
+
+ entry entries[84*2*4];
+ std::tie(err, actual) = read_at(io, sizeof(header), &entries, (h.cyl_count << resolution)*h.head_count*sizeof(entry));
+ if(err || (((h.cyl_count << resolution)*h.head_count*sizeof(entry)) != actual))
+ return false;
+
+ std::function<void (const std::vector<uint32_t> &src, std::vector<uint32_t> &track)> converter;
+
+ if(!memcmp(h.sign, sign, 16)) {
+ converter = [] (const std::vector<uint32_t> &src, std::vector<uint32_t> &track) {
+ uint32_t ctime = 0;
+ for(uint32_t mg : src) {
+ ctime += mg & TIME_MASK;
+ track.push_back((mg & MG_MASK) | ctime);
+ }
+ };
+
+ } else {
+ converter = [] (const std::vector<uint32_t> &src, std::vector<uint32_t> &track) {
+ unsigned int cell_count = src.size();
+ uint32_t mg = src[0] & MG_MASK;
+ uint32_t wmg = src[cell_count - 1] & MG_MASK;
+ if(mg != wmg && (mg == OLD_MG_A || mg == OLD_MG_B) && (wmg == OLD_MG_A || wmg == OLD_MG_B))
+ // Flux change at 0, add it
+ track.push_back(MG_F | 0);
+
+ uint32_t ctime = 0;
+ for(unsigned int i=0; i != cell_count; i++) {
+ uint32_t nmg = src[i] & MG_MASK;
+ if(nmg == OLD_MG_N || nmg == OLD_MG_D) {
+ track.push_back((nmg == OLD_MG_N ? MG_N : MG_D) | ctime);
+ ctime += src[i] & TIME_MASK;
+ track.push_back(MG_E | (ctime-1));
+ nmg = 0xffffffff;
+ } else {
+ if(mg != 0xffffffff && mg != nmg)
+ track.push_back(MG_F | ctime);
+ ctime += src[i] & TIME_MASK;
+ }
+ mg = nmg;
+ }
+ };
+ }
std::vector<uint8_t> compressed;
+ std::vector<uint32_t> uncompressed;
entry *ent = entries;
for(unsigned int cyl=0; cyl <= (h.cyl_count - 1) << 2; cyl += 4 >> resolution)
for(unsigned int head=0; head != h.head_count; head++) {
- image->set_write_splice_position(cyl >> 2, head, ent->write_splice, cyl & 3);
+ image.set_write_splice_position(cyl >> 2, head, ent->write_splice, cyl & 3);
if(ent->uncompressed_size == 0) {
// Unformatted track
- image->get_buffer(cyl >> 2, head, cyl & 3).clear();
+ image.get_buffer(cyl >> 2, head, cyl & 3).clear();
ent++;
continue;
}
+ unsigned int cell_count = ent->uncompressed_size/4;
compressed.resize(ent->compressed_size);
+ uncompressed.resize(cell_count);
- io_generic_read(io, &compressed[0], ent->offset, ent->compressed_size);
-
- unsigned int cell_count = ent->uncompressed_size/4;
- std::vector<uint32_t> &trackbuf = image->get_buffer(cyl >> 2, head, cyl & 3);;
- trackbuf.resize(cell_count);
+ std::tie(err, actual) = read_at(io, ent->offset, &compressed[0], ent->compressed_size); // FIXME: check for errors and premature EOF
uLongf size = ent->uncompressed_size;
- if(uncompress((Bytef *)&trackbuf[0], &size, &compressed[0], ent->compressed_size) != Z_OK)
+ if(uncompress((Bytef *)uncompressed.data(), &size, &compressed[0], ent->compressed_size) != Z_OK) {
+ fprintf(stderr, "fail1\n");
return false;
-
- uint32_t cur_time = 0;
- for(unsigned int i=0; i != cell_count; i++) {
- uint32_t next_cur_time = cur_time + (trackbuf[i] & TIME_MASK);
- trackbuf[i] = (trackbuf[i] & MG_MASK) | cur_time;
- cur_time = next_cur_time;
}
- if(cur_time != 200000000)
- return false;
+ std::vector<uint32_t> &trackbuf = image.get_buffer(cyl >> 2, head, cyl & 3);
+ trackbuf.clear();
+
+ converter(uncompressed, trackbuf);
ent++;
}
return true;
}
-bool mfi_format::save(io_generic *io, floppy_image *image)
+bool mfi_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
int tracks, heads;
- image->get_actual_geometry(tracks, heads);
- int resolution = image->get_resolution();
+ image.get_actual_geometry(tracks, heads);
+ int resolution = image.get_resolution();
int max_track_size = 0;
for(int track=0; track <= (tracks-1) << 2; track += 4 >> resolution)
for(int head=0; head<heads; head++) {
- int tsize = image->get_buffer(track >> 2, head, track & 3).size();
+ int tsize = image.get_buffer(track >> 2, head, track & 3).size();
if(tsize > max_track_size)
max_track_size = tsize;
}
@@ -178,56 +233,49 @@ bool mfi_format::save(io_generic *io, floppy_image *image)
memcpy(h.sign, sign, 16);
h.cyl_count = tracks | (resolution << RESOLUTION_SHIFT);
h.head_count = heads;
- h.form_factor = image->get_form_factor();
- h.variant = image->get_variant();
+ h.form_factor = image.get_form_factor();
+ h.variant = image.get_variant();
- io_generic_write(io, &h, 0, sizeof(header));
+ write_at(io, 0, &h, sizeof(header)); // FIXME: check for errors
memset(entries, 0, sizeof(entries));
int pos = sizeof(header) + (tracks << resolution)*heads*sizeof(entry);
int epos = 0;
- auto precomp = global_alloc_array(uint32_t, max_track_size);
- auto postcomp = global_alloc_array(uint8_t, max_track_size*4 + 1000);
+ auto precomp = std::make_unique<uint32_t []>(max_track_size);
+ auto postcomp = std::make_unique<uint8_t []>(max_track_size*4 + 1000);
for(int track=0; track <= (tracks-1) << 2; track += 4 >> resolution)
for(int head=0; head<heads; head++) {
- std::vector<uint32_t> &buffer = image->get_buffer(track >> 2, head, track & 3);
+ const std::vector<uint32_t> &buffer = image.get_buffer(track >> 2, head, track & 3);
int tsize = buffer.size();
if(!tsize) {
epos++;
continue;
}
- memcpy(precomp, &buffer[0], tsize*4);
- for(int j=0; j<tsize-1; j++)
- precomp[j] = (precomp[j] & floppy_image::MG_MASK) |
- ((precomp[j+1] & floppy_image::TIME_MASK) -
- (precomp[j] & floppy_image::TIME_MASK));
- precomp[tsize-1] = (precomp[tsize-1] & floppy_image::MG_MASK) |
- (200000000 - (precomp[tsize-1] & floppy_image::TIME_MASK));
+ uint32_t ctime = 0;
+ for(int i=0; i != tsize; i++) {
+ precomp[i] = (buffer[i] & MG_MASK) | ((buffer[i] & TIME_MASK) - ctime);
+ ctime = buffer[i] & TIME_MASK;
+ }
uLongf csize = max_track_size*4 + 1000;
- if(compress(postcomp, &csize, (const Bytef *)precomp, tsize*4) != Z_OK) {
- global_free_array(precomp);
- global_free_array(postcomp);
+ if(compress(postcomp.get(), &csize, (const Bytef *)precomp.get(), tsize*4) != Z_OK)
return false;
- }
entries[epos].offset = pos;
entries[epos].uncompressed_size = tsize*4;
entries[epos].compressed_size = csize;
- entries[epos].write_splice = image->get_write_splice_position(track >> 2, head, track & 3);
+ entries[epos].write_splice = image.get_write_splice_position(track >> 2, head, track & 3);
epos++;
- io_generic_write(io, postcomp, pos, csize);
+ write_at(io, pos, postcomp.get(), csize); // FIXME: check for errors
pos += csize;
}
- io_generic_write(io, entries, sizeof(header), (tracks << resolution)*heads*sizeof(entry));
- global_free_array(precomp);
- global_free_array(postcomp);
+ write_at(io, sizeof(header), entries, (tracks << resolution)*heads*sizeof(entry)); // FIXME: check for errors
return true;
}
-const floppy_format_type FLOPPY_MFI_FORMAT = &floppy_image_format_creator<mfi_format>;
+const mfi_format FLOPPY_MFI_FORMAT;
diff --git a/src/lib/formats/mfi_dsk.h b/src/lib/formats/mfi_dsk.h
index 7cb84e999f0..7241bcfab32 100644
--- a/src/lib/formats/mfi_dsk.h
+++ b/src/lib/formats/mfi_dsk.h
@@ -12,14 +12,14 @@ class mfi_format : public floppy_image_format_t
public:
mfi_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
private:
enum {
@@ -27,28 +27,34 @@ private:
MG_MASK = 0xf0000000,
MG_SHIFT = 28,
- MG_A = (0 << MG_SHIFT),
- MG_B = (1 << MG_SHIFT),
- MG_N = (2 << MG_SHIFT),
- MG_D = (3 << MG_SHIFT),
+ OLD_MG_A = (0 << MG_SHIFT),
+ OLD_MG_B = (1 << MG_SHIFT),
+ OLD_MG_N = (2 << MG_SHIFT),
+ OLD_MG_D = (3 << MG_SHIFT),
+
+ MG_F = (0 << MG_SHIFT), //!< - 0, MG_F -> Flux orientation change
+ MG_N = (1 << MG_SHIFT), //!< - 1, MG_N -> Non-magnetized zone (neutral)
+ MG_D = (2 << MG_SHIFT), //!< - 2, MG_D -> Damaged zone, reads as neutral but cannot be changed by writing
+ MG_E = (3 << MG_SHIFT), //!< - 3, MG_E -> End of zone
RESOLUTION_SHIFT = 30,
CYLINDER_MASK = 0x3fffffff
};
+ static const char sign_old[16];
static const char sign[16];
struct header {
- char sign[16];
- unsigned int cyl_count, head_count;
- unsigned int form_factor, variant;
+ uint8_t sign[16];
+ uint32_t cyl_count, head_count;
+ uint32_t form_factor, variant;
};
struct entry {
- unsigned int offset, compressed_size, uncompressed_size, write_splice;
+ uint32_t offset, compressed_size, uncompressed_size, write_splice;
};
};
-extern const floppy_format_type FLOPPY_MFI_FORMAT;
+extern const mfi_format FLOPPY_MFI_FORMAT;
#endif // MAME_FORMATS_MFI_DSK_H
diff --git a/src/lib/formats/mfm_hd.cpp b/src/lib/formats/mfm_hd.cpp
index 032be8622cb..738f0999863 100644
--- a/src/lib/formats/mfm_hd.cpp
+++ b/src/lib/formats/mfm_hd.cpp
@@ -116,7 +116,6 @@
**************************************************************************/
-#include "emu.h"
#include "mfm_hd.h"
#include "imageutl.h"
@@ -295,9 +294,9 @@ int mfmhd_generic_format::chs_to_lba(int cylinder, int head, int sector)
else return -1;
}
-chd_error mfmhd_generic_format::load(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head)
+std::error_condition mfmhd_generic_format::load(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head)
{
- chd_error state = CHDERR_NONE;
+ std::error_condition state;
uint8_t sector_content[16384];
int sectorcount = m_param.sectors_per_track;
@@ -383,8 +382,9 @@ chd_error mfmhd_generic_format::load(chd_file* chdfile, uint16_t* trackimage, in
int lbaposition = chs_to_lba(cylinder, head, sec_number);
if (lbaposition>=0)
{
- chd_error state = chdfile->read_units(lbaposition, sector_content);
- if (state != CHDERR_NONE) break;
+ state = chdfile->read_units(lbaposition, sector_content);
+ if (state)
+ break;
}
else
{
@@ -414,7 +414,7 @@ chd_error mfmhd_generic_format::load(chd_file* chdfile, uint16_t* trackimage, in
if (TRACE_LAYOUT) osd_printf_verbose("\n");
// Gap 4
- if (state == CHDERR_NONE)
+ if (!state)
{
// Fill the rest with 0x4e
mfm_encode(trackimage, position, 0x4e, tracksize-position);
@@ -434,7 +434,7 @@ enum
CHECK_CRC
};
-chd_error mfmhd_generic_format::save(chd_file* chdfile, uint16_t* trackimage, int tracksize, int current_cylinder, int current_head)
+std::error_condition mfmhd_generic_format::save(chd_file* chdfile, uint16_t* trackimage, int tracksize, int current_cylinder, int current_head)
{
if (TRACE_RWTRACK) osd_printf_verbose("%s: write back (c=%d,h=%d) to CHD\n", tag(), current_cylinder, current_head);
@@ -491,7 +491,7 @@ chd_error mfmhd_generic_format::save(chd_file* chdfile, uint16_t* trackimage, in
bool countgap3 = false;
bool countsync = false;
- chd_error chdstate = CHDERR_NONE;
+ std::error_condition chdstate;
if (TRACE_IMAGE)
{
@@ -644,7 +644,7 @@ chd_error mfmhd_generic_format::save(chd_file* chdfile, uint16_t* trackimage, in
if (TRACE_DETAIL) osd_printf_verbose("%s: Writing sector chs=(%d,%d,%d) to CHD\n", tag(), current_cylinder, current_head, sector);
chdstate = chdfile->write_units(chs_to_lba(current_cylinder, current_head, sector), buffer);
- if (chdstate != CHDERR_NONE)
+ if (chdstate)
{
osd_printf_verbose("%s: Write error while writing sector chs=(%d,%d,%d)\n", tag(), cylinder, head, sector);
}
diff --git a/src/lib/formats/mfm_hd.h b/src/lib/formats/mfm_hd.h
index 7d28adc518d..fd2b260ba24 100644
--- a/src/lib/formats/mfm_hd.h
+++ b/src/lib/formats/mfm_hd.h
@@ -55,30 +55,30 @@ public:
// could be changed, but we do not support this (yet). These are defined
// in the CHD and must match those of the device. They are stored by the GDDD tag.
// The encoding is not stored in the CHD but is also supposed to be immutable.
- int cylinders;
- int heads;
- int sectors_per_track;
- int sector_size;
+ int cylinders = 0;
+ int heads = 0;
+ int sectors_per_track = 0;
+ int sector_size = 0;
mfmhd_enc_t encoding;
// Parameters like interleave, precompensation, write current can be changed
// on every write operation. They are stored by the GDDI tag (first record).
- int interleave;
- int cylskew;
- int headskew;
- int write_precomp_cylinder; // if -1, no wpcom on the disks
- int reduced_wcurr_cylinder; // if -1, no rwc on the disks
+ int interleave = 0;
+ int cylskew = 0;
+ int headskew = 0;
+ int write_precomp_cylinder = 0; // if -1, no wpcom on the disks
+ int reduced_wcurr_cylinder = 0; // if -1, no rwc on the disks
// Parameters for the track layout that are supposed to be the same for
// all tracks and that do not change (until the next reformat).
// Also, they do not have any influence on the CHD file.
// They are stored by the GDDI tag (second record).
- int gap1;
- int gap2;
- int gap3;
- int sync;
- int headerlen;
- int ecctype; // -1 is CRC
+ int gap1 = 0;
+ int gap2 = 0;
+ int gap3 = 0;
+ int sync = 0;
+ int headerlen = 0;
+ int ecctype = 0; // -1 is CRC
bool sane_rec() const
{
@@ -147,14 +147,14 @@ class mfmhd_image_format_t
{
public:
mfmhd_image_format_t(): m_lastbit(false), m_current_crc(0)
- { m_devtag = std::string("mfmhd_image_format_t"); };
- virtual ~mfmhd_image_format_t() {};
+ { m_devtag = std::string("mfmhd_image_format_t"); }
+ virtual ~mfmhd_image_format_t() {}
// Load the image.
- virtual chd_error load(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head) = 0;
+ virtual std::error_condition load(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head) = 0;
// Save the image.
- virtual chd_error save(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head) = 0;
+ virtual std::error_condition save(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head) = 0;
// Return the original parameters of the image
mfmhd_layout_params* get_initial_params() { return &m_param_old; }
@@ -195,9 +195,9 @@ protected:
class mfmhd_generic_format : public mfmhd_image_format_t
{
public:
- mfmhd_generic_format() { m_devtag = std::string("mfmhd_generic_format"); };
- chd_error load(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head) override;
- chd_error save(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head) override;
+ mfmhd_generic_format() { m_devtag = std::string("mfmhd_generic_format"); }
+ std::error_condition load(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head) override;
+ std::error_condition save(chd_file* chdfile, uint16_t* trackimage, int tracksize, int cylinder, int head) override;
// Yes, we want to save all parameters
virtual bool save_param(mfmhd_param_t type) override { return true; }
diff --git a/src/lib/formats/mm_dsk.cpp b/src/lib/formats/mm_dsk.cpp
index 0634c0e3ede..42de60d3d72 100644
--- a/src/lib/formats/mm_dsk.cpp
+++ b/src/lib/formats/mm_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/mm_dsk.c
+ formats/mm_dsk.cpp
mm format
*********************************************************************/
-#include <assert.h>
-
#include "formats/mm_dsk.h"
mm1_format::mm1_format() : upd765_format(formats)
{
}
-const char *mm1_format::name() const
+const char *mm1_format::name() const noexcept
{
return "mm1";
}
-const char *mm1_format::description() const
+const char *mm1_format::description() const noexcept
{
return "Nokia MikroMikko 1 disk image";
}
-const char *mm1_format::extensions() const
+const char *mm1_format::extensions() const noexcept
{
return "dsk";
}
@@ -35,17 +33,17 @@ mm2_format::mm2_format() : upd765_format(formats)
{
}
-const char *mm2_format::name() const
+const char *mm2_format::name() const noexcept
{
return "mm2";
}
-const char *mm2_format::description() const
+const char *mm2_format::description() const noexcept
{
return "Nokia MikroMikko 2 disk image";
}
-const char *mm2_format::extensions() const
+const char *mm2_format::extensions() const noexcept
{
return "dsk";
}
@@ -82,8 +80,9 @@ const mm2_format::format mm2_format::formats[] = {
512, {},
1, {},
80, 50, 22, 80
- }
+ },
+ {}
};
-const floppy_format_type FLOPPY_MM1_FORMAT = &floppy_image_format_creator<mm1_format>;
-const floppy_format_type FLOPPY_MM2_FORMAT = &floppy_image_format_creator<mm2_format>;
+const mm1_format FLOPPY_MM1_FORMAT;
+const mm2_format FLOPPY_MM2_FORMAT;
diff --git a/src/lib/formats/mm_dsk.h b/src/lib/formats/mm_dsk.h
index aec52093076..2d063df0667 100644
--- a/src/lib/formats/mm_dsk.h
+++ b/src/lib/formats/mm_dsk.h
@@ -18,9 +18,9 @@ class mm1_format : public upd765_format {
public:
mm1_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
@@ -30,15 +30,15 @@ class mm2_format : public upd765_format {
public:
mm2_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_MM1_FORMAT;
-extern const floppy_format_type FLOPPY_MM2_FORMAT;
+extern const mm1_format FLOPPY_MM1_FORMAT;
+extern const mm2_format FLOPPY_MM2_FORMAT;
#endif // MAME_FORMATS_MM_DSK_H
diff --git a/src/lib/formats/ms0515_dsk.cpp b/src/lib/formats/ms0515_dsk.cpp
index d16e8dbbf73..9b8b8944b61 100644
--- a/src/lib/formats/ms0515_dsk.cpp
+++ b/src/lib/formats/ms0515_dsk.cpp
@@ -10,23 +10,21 @@
#include "formats/ms0515_dsk.h"
-#include <assert.h>
-
ms0515_format::ms0515_format() : wd177x_format(formats)
{
}
-const char *ms0515_format::name() const
+const char *ms0515_format::name() const noexcept
{
return "ms0515";
}
-const char *ms0515_format::description() const
+const char *ms0515_format::description() const noexcept
{
return "MS 0515 disk image";
}
-const char *ms0515_format::extensions() const
+const char *ms0515_format::extensions() const noexcept
{
return "img";
}
@@ -52,4 +50,4 @@ const ms0515_format::format ms0515_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_MS0515_FORMAT = &floppy_image_format_creator<ms0515_format>;
+const ms0515_format FLOPPY_MS0515_FORMAT;
diff --git a/src/lib/formats/ms0515_dsk.h b/src/lib/formats/ms0515_dsk.h
index 1b6dc2d1e77..c49e2342463 100644
--- a/src/lib/formats/ms0515_dsk.h
+++ b/src/lib/formats/ms0515_dsk.h
@@ -17,14 +17,14 @@ class ms0515_format : public wd177x_format
public:
ms0515_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_MS0515_FORMAT;
+extern const ms0515_format FLOPPY_MS0515_FORMAT;
#endif // MAME_FORMATS_MS0515_DSK_H
diff --git a/src/lib/formats/msx_dsk.cpp b/src/lib/formats/msx_dsk.cpp
index ab0b5bab529..8ec0739b266 100644
--- a/src/lib/formats/msx_dsk.cpp
+++ b/src/lib/formats/msx_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/msx_dsk.c
+ formats/msx_dsk.cpp
MSX disk images
@@ -15,23 +15,31 @@ msx_format::msx_format() : upd765_format(formats)
{
}
-const char *msx_format::name() const
+const char *msx_format::name() const noexcept
{
return "msx";
}
-const char *msx_format::description() const
+const char *msx_format::description() const noexcept
{
return "MSX disk image";
}
-const char *msx_format::extensions() const
+const char *msx_format::extensions() const noexcept
{
return "dsk";
}
// Unverified gap sizes
const msx_format::format msx_format::formats[] = {
+ { /* 180K 5 1/4 inch double density single sided */
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 9, 40, 1, 512, {}, 1, {}, 80, 50, 22, 80
+ },
+ { /* 360K 5 1/4 inch double density */
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 9, 40, 2, 512, {}, 1, {}, 80, 50, 22, 80
+ },
{ /* 3 1/2 inch single sided double density */
// 80x4e 12x00 3xf6 fc
// 26x4e 12x00 3xf5 fe 2x00 01 02 f7 24x4e 12x00 3xf5 fb 512xe5 f7
@@ -58,4 +66,4 @@ const msx_format::format msx_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_MSX_FORMAT = &floppy_image_format_creator<msx_format>;
+const msx_format FLOPPY_MSX_FORMAT;
diff --git a/src/lib/formats/msx_dsk.h b/src/lib/formats/msx_dsk.h
index 8b4b941fcf7..9e6c13e6f30 100644
--- a/src/lib/formats/msx_dsk.h
+++ b/src/lib/formats/msx_dsk.h
@@ -12,23 +12,21 @@
#pragma once
-//#include "wd177x_dsk.h"
#include "upd765_dsk.h"
-//class msx_format : public wd177x_format {
class msx_format: public upd765_format
{
public:
msx_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_MSX_FORMAT;
+extern const msx_format FLOPPY_MSX_FORMAT;
#endif // MAME_FORMATS_MSX_DSK_H
diff --git a/src/lib/formats/mtx_dsk.cpp b/src/lib/formats/mtx_dsk.cpp
index 95420f40aab..7b37a09ce06 100644
--- a/src/lib/formats/mtx_dsk.cpp
+++ b/src/lib/formats/mtx_dsk.cpp
@@ -14,17 +14,17 @@ mtx_format::mtx_format() : wd177x_format(formats)
{
}
-const char *mtx_format::name() const
+const char *mtx_format::name() const noexcept
{
return "mtx";
}
-const char *mtx_format::description() const
+const char *mtx_format::description() const noexcept
{
return "Memotech MTX disk image";
}
-const char *mtx_format::extensions() const
+const char *mtx_format::extensions() const noexcept
{
return "mfloppy";
}
@@ -50,4 +50,4 @@ const mtx_format::format mtx_format::formats[] =
{}
};
-const floppy_format_type FLOPPY_MTX_FORMAT = &floppy_image_format_creator<mtx_format>;
+const mtx_format FLOPPY_MTX_FORMAT;
diff --git a/src/lib/formats/mtx_dsk.h b/src/lib/formats/mtx_dsk.h
index 2c1509af93d..b4f7e6e9c72 100644
--- a/src/lib/formats/mtx_dsk.h
+++ b/src/lib/formats/mtx_dsk.h
@@ -19,14 +19,14 @@ class mtx_format : public wd177x_format
public:
mtx_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_MTX_FORMAT;
+extern const mtx_format FLOPPY_MTX_FORMAT;
#endif // MAME_FORMATS_MTX_DSK_H
diff --git a/src/lib/formats/mz_cas.cpp b/src/lib/formats/mz_cas.cpp
index cfcf9f36ed8..ddc35b62663 100644
--- a/src/lib/formats/mz_cas.cpp
+++ b/src/lib/formats/mz_cas.cpp
@@ -1,9 +1,9 @@
// license:BSD-3-Clause
// copyright-holders:Nathan Woods
-#include <string.h>
-#include <assert.h>
#include "mz_cas.h"
+#include <cstring>
+
#ifndef VERBOSE
#define VERBOSE 0
#endif
@@ -69,7 +69,7 @@ static int fill_wave_b(int16_t *buffer, int offs, int byte)
return count;
}
-static int fill_wave(int16_t *buffer, int length, uint8_t *code)
+static int fill_wave(int16_t *buffer, int length, const uint8_t *code, int)
{
static int16_t *beg;
static uint16_t csum = 0;
@@ -301,7 +301,7 @@ static int fill_wave(int16_t *buffer, int length, uint8_t *code)
-static const struct CassetteLegacyWaveFiller mz700_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller mz700_legacy_fill_wave =
{
fill_wave, /* fill_wave */
1, /* chunk_size */
@@ -314,21 +314,21 @@ static const struct CassetteLegacyWaveFiller mz700_legacy_fill_wave =
-static cassette_image::error mz700_cas_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error mz700_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &mz700_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &mz700_legacy_fill_wave);
}
static cassette_image::error mz700_cas_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &mz700_legacy_fill_wave);
+ return cassette->legacy_construct(&mz700_legacy_fill_wave);
}
-static const struct CassetteFormat mz700_cas_format =
+static const cassette_image::Format mz700_cas_format =
{
"m12,mzf,mzt",
mz700_cas_identify,
diff --git a/src/lib/formats/nabupc_dsk.cpp b/src/lib/formats/nabupc_dsk.cpp
new file mode 100644
index 00000000000..00205c1e560
--- /dev/null
+++ b/src/lib/formats/nabupc_dsk.cpp
@@ -0,0 +1,228 @@
+// license:BSD-3-Clause
+// copyright-holders:Brian Johnson
+/*********************************************************************
+
+ formats/nabupc_dsk.cpp
+
+ NABU PC floppy-disk images
+
+ Disk Layouts:
+ 200k - 1 head , 40 tracks, 5 1024 byte sectors per track
+ 400k - 2 heads, 40 tracks, 5 1024 byte sectors per track
+ 800k - 2 heads, 80 tracks, 5 1024 byte sectors per track
+*********************************************************************/
+
+#include "nabupc_dsk.h"
+
+#include "imageutl.h"
+#include "ioprocs.h"
+#include "strformat.h"
+
+
+const nabupc_format::format nabupc_format::formats[] =
+{
+ { // 200k 40 track single sided double density (nabu)
+ floppy_image::FF_525, floppy_image::SSDD,
+ 40, 1, 32, 22, 80,
+ {0x00, 0x28, 0x00, 0x03, 0x07, 0x00, 0xc2, 0x00, 0x5f, 0x00, 0xe0, 0x00, 0x00, 0x18, 0x01, 0x00, 0x03, 0x07}
+ },
+ { // 400k 40 track double sided double density (nabu)
+ floppy_image::FF_525, floppy_image::DSDD,
+ 40, 2, 32, 22, 80,
+ {0x01, 0x28, 0x00, 0x04, 0x0f, 0x01, 0xc4, 0x00, 0xbf, 0x00, 0xe0, 0x00, 0x00, 0x30, 0x01, 0x00, 0x03, 0x07}
+ },
+ { // 800k 80 track double sided double density (nabu)
+ floppy_image::FF_35, floppy_image::DSDD,
+ 80, 2, 32, 22, 80,
+ {0x02, 0x28, 0x00, 0x04, 0x0f, 0x00, 0x8c, 0x01, 0x7f, 0x01, 0xfc, 0x00, 0x00, 0x61, 0x01, 0x00, 0x03, 0x07}
+ },
+ {}
+};
+
+nabupc_format::nabupc_format()
+{
+}
+
+int nabupc_format::find_format(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if (io.length(size)) {
+ return -1;
+ }
+
+ for (int i = 0; formats[i].form_factor; i++) {
+ const format &f = formats[i];
+
+ if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor) {
+ continue;
+ }
+ if (!variants.empty() && !has_variant(variants, f.variant)) {
+ continue;
+ }
+ if (size == (uint64_t)sector_size * sector_count * f.track_count * f.head_count) {
+ LOG_FORMATS("nabupc: Found nabupc disk: %d\n", i);
+ return i;
+ }
+ LOG_FORMATS("nabupc: no match\n");
+ }
+ return -1;
+}
+
+int nabupc_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+
+ int const format = find_format(io, form_factor, variants);
+
+ if (format == -1) {
+ return 0;
+ }
+
+ return FIFID_SIZE;
+}
+
+void nabupc_format::build_nabu_track_mfm(int track, int head, floppy_image &image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2, const uint8_t *dpb)
+{
+ std::vector<uint32_t> track_data;
+
+ if (dpb[1] != 0 && track == 0 && head == 0) {
+ for (int i = 0; i < 2; i++) raw_w(track_data, 16, 0x4489);
+ raw_w(track_data, 16, 0x1054); // 4e
+ for (int i = 0; i < 11; i++) mfm_w(track_data, 8, 0x4e);
+ mfm_w(track_data, 8, 0x19);
+ mfm_w(track_data, 8, 0x0F);
+ mfm_w(track_data, 8, 0x2D);
+ mfm_w(track_data, 8, 0x1A);
+ for (int i = 0; i < 18; i++) mfm_w(track_data, 8, dpb[i]);
+ }
+
+ for (int i = 0; i < gap_1; i++) mfm_w(track_data, 8, 0x4e);
+
+ int total_size = 0;
+ for (int i = 0; i < sector_count; i++) total_size += sects[i].actual_size;
+
+ int etpos = int(track_data.size()) + (sector_count * (12 + 3 + 5 + 2 + gap_2 + 12 + 3 + 1+ 2) + total_size) * 16;
+
+ if (etpos > cell_count) {
+ throw std::invalid_argument(util::string_format("Incorrect layout on track %d head %d, expected_size=%d, current_size=%d", track, head, cell_count, etpos));
+ }
+
+ if (etpos + gap_3 * 16 * (sector_count - 1) > cell_count) {
+ gap_3 = (cell_count - etpos) / 16 / (sector_count - 1);
+ }
+
+ // Build the track
+ for (int i = 0; i < sector_count; i++) {
+ uint16_t crc;
+ // sync and IDAM and gap 2
+ for (int j = 0; j < 12; j++) mfm_w(track_data, 8, 0x00);
+ unsigned int cpos = track_data.size();
+ for (int j = 0; j < 3; j++) raw_w(track_data, 16, 0x4489);
+ mfm_w(track_data, 8, 0xfe);
+ mfm_w(track_data, 8, sects[i].track);
+ mfm_w(track_data, 8, sects[i].head);
+ mfm_w(track_data, 8, sects[i].sector);
+ mfm_w(track_data, 8, sects[i].size);
+ crc = calc_crc_ccitt(track_data, cpos, track_data.size());
+ mfm_w(track_data, 16, crc);
+ for (int j = 0; j < gap_2; j++) mfm_w(track_data, 8, 0x4e);
+
+ if (!sects[i].data) {
+ for (int j = 0; j < 12 + 4 + sects[i].actual_size + 2 + (i != sector_count - 1 ? gap_3 : 0); j++) mfm_w(track_data, 8, 0x4e);
+ } else {
+ // sync, DAM, data and gap 3
+ for (int j = 0; j < 12; j++) mfm_w(track_data, 8, 0x00);
+ cpos = track_data.size();
+ for (int j = 0; j < 3; j++) raw_w(track_data, 16, 0x4489);
+ mfm_w(track_data, 8, sects[i].deleted ? 0xf8 : 0xfb);
+ for (int j = 0; j < sects[i].actual_size; j++) mfm_w(track_data, 8, sects[i].data[j]);
+ crc = calc_crc_ccitt(track_data, cpos, track_data.size());
+ if (sects[i].bad_crc) {
+ crc = 0xffff^crc;
+ }
+ mfm_w(track_data, 16, crc);
+ if (i != sector_count - 1) {
+ for (int j = 0; j < gap_3; j++) mfm_w(track_data, 8, 0x4e);
+ }
+ }
+ }
+
+ // Gap 4b
+
+ while (int(track_data.size()) < cell_count - 15) mfm_w(track_data, 8, 0x4e);
+ raw_w(track_data, cell_count - int(track_data.size()), 0x9254 >> (16 + int(track_data.size()) - cell_count));
+
+ generate_track_from_levels(track, head, track_data, 0, image);
+}
+
+bool nabupc_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ int const type = find_format(io, form_factor, variants);
+
+ if (type == -1) {
+ return false;
+ }
+
+ const format &f = formats[type];
+
+ for (int track = 0; track < f.track_count; track++) {
+ for (int head = 0; head < f.head_count; head++) {
+ desc_pc_sector sects[sector_count];
+ uint8_t sectdata[sector_count * sector_size];
+ /*auto const [err, actual] =*/ read_at(io, (track * f.head_count + head) * sector_count * sector_size, sectdata, sector_count * sector_size); // FIXME: check for errors and premature EOF
+ for (int i = 0; i < sector_count; i++) {
+ sects[i].track = track;
+ sects[i].head = head;
+ sects[i].sector = i + 1;
+ sects[i].size = 3;
+ sects[i].actual_size = sector_size;
+ sects[i].data = sectdata + sector_size * i;
+ sects[i].deleted = false;
+ sects[i].bad_crc = false;
+ }
+ build_nabu_track_mfm(track, head, image, 100000, sector_count, sects, f.gap_3, f.gap_1, f.gap_2, f.dpb);
+ }
+ }
+ return true;
+}
+
+bool nabupc_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+
+ int heads, tracks;
+ image.get_actual_geometry(tracks, heads);
+ for (int track = 0; track < tracks; track++) {
+ for (int head = 0; head < heads; head++) {
+ uint64_t file_offset = (track * heads + head) * sector_count * sector_size;
+ auto bitstream = generate_bitstream_from_track(track, head, 2000, image);
+ auto sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
+ for (int i = 0; i < sector_count; i++) {
+ /*auto const [err, actual] =*/ write_at(io, file_offset, sectors[i + 1].data(), sector_size); // FIXME: check for errors
+ file_offset += sector_size;
+ }
+ }
+ }
+
+ return true;
+}
+
+const char *nabupc_format::name() const noexcept
+{
+ return "nabupc";
+}
+
+const char *nabupc_format::description() const noexcept
+{
+ return "NABU PC CP/M Disk Image";
+}
+
+const char *nabupc_format::extensions() const noexcept
+{
+ return "img,dsk";
+}
+
+bool nabupc_format::supports_save() const noexcept
+{
+ return true;
+}
+
+const nabupc_format FLOPPY_NABUPC_FORMAT;
diff --git a/src/lib/formats/nabupc_dsk.h b/src/lib/formats/nabupc_dsk.h
new file mode 100644
index 00000000000..bedfe2a2e7b
--- /dev/null
+++ b/src/lib/formats/nabupc_dsk.h
@@ -0,0 +1,53 @@
+// license:BSD-3-Clause
+// copyright-holders:Brian Johnson
+/*********************************************************************
+
+ formats/nabupc_dsk.cpp
+
+ NABU PC floppy-disk images
+
+*********************************************************************/
+#ifndef MAME_FORMATS_NABUPC_DSK_H
+#define MAME_FORMATS_NABUPC_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+
+class nabupc_format : public floppy_image_format_t {
+public:
+ struct format {
+ uint32_t form_factor; // See floppy_image for possible values
+ uint32_t variant; // See floppy_image for possible values
+ int track_count;
+ int head_count;
+ int gap_1;
+ int gap_2;
+ int gap_3;
+ uint8_t dpb[18];
+ };
+
+ nabupc_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+
+ virtual bool supports_save() const noexcept override;
+
+protected:
+ static void build_nabu_track_mfm(int track, int head, floppy_image &image, int cell_count, int sector_count, const desc_pc_sector *sects, int gap_3, int gap_1, int gap_2, const uint8_t *dpb);
+ int find_format(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const;
+private:
+ static constexpr int sector_count = 5;
+ static constexpr int sector_size = 1024;
+ static const format formats[];
+};
+
+extern const nabupc_format FLOPPY_NABUPC_FORMAT;
+
+#endif // MAME_FORMATS_NABUPC_DSK_H
diff --git a/src/lib/formats/nanos_dsk.cpp b/src/lib/formats/nanos_dsk.cpp
index 1536d0677c8..299b6558fcd 100644
--- a/src/lib/formats/nanos_dsk.cpp
+++ b/src/lib/formats/nanos_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/nanos_dsk.c
+ formats/nanos_dsk.cpp
nanos format
*********************************************************************/
-#include <assert.h>
-
#include "formats/nanos_dsk.h"
nanos_format::nanos_format() : upd765_format(formats)
{
}
-const char *nanos_format::name() const
+const char *nanos_format::name() const noexcept
{
return "nanos";
}
-const char *nanos_format::description() const
+const char *nanos_format::description() const noexcept
{
return "NANOS disk image";
}
-const char *nanos_format::extensions() const
+const char *nanos_format::extensions() const noexcept
{
return "img";
}
@@ -45,4 +43,4 @@ const nanos_format::format nanos_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_NANOS_FORMAT = &floppy_image_format_creator<nanos_format>;
+const nanos_format FLOPPY_NANOS_FORMAT;
diff --git a/src/lib/formats/nanos_dsk.h b/src/lib/formats/nanos_dsk.h
index 444768fbb49..8974ae9fc86 100644
--- a/src/lib/formats/nanos_dsk.h
+++ b/src/lib/formats/nanos_dsk.h
@@ -18,14 +18,14 @@ class nanos_format : public upd765_format {
public:
nanos_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_NANOS_FORMAT;
+extern const nanos_format FLOPPY_NANOS_FORMAT;
#endif // MAME_FORMATS_NANOS_DSK_H
diff --git a/src/lib/formats/nascom_dsk.cpp b/src/lib/formats/nascom_dsk.cpp
index 258de89c326..43a625029ee 100644
--- a/src/lib/formats/nascom_dsk.cpp
+++ b/src/lib/formats/nascom_dsk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -14,17 +14,17 @@ nascom_format::nascom_format() : wd177x_format(formats)
{
}
-const char *nascom_format::name() const
+const char *nascom_format::name() const noexcept
{
return "nascom";
}
-const char *nascom_format::description() const
+const char *nascom_format::description() const noexcept
{
return "Nascom disk image";
}
-const char *nascom_format::extensions() const
+const char *nascom_format::extensions() const noexcept
{
return "dsk";
}
@@ -50,4 +50,4 @@ const nascom_format::format nascom_format::formats[] =
{}
};
-const floppy_format_type FLOPPY_NASCOM_FORMAT = &floppy_image_format_creator<nascom_format>;
+const nascom_format FLOPPY_NASCOM_FORMAT;
diff --git a/src/lib/formats/nascom_dsk.h b/src/lib/formats/nascom_dsk.h
index 297cdb536eb..20b29b7c465 100644
--- a/src/lib/formats/nascom_dsk.h
+++ b/src/lib/formats/nascom_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -19,14 +19,14 @@ class nascom_format : public wd177x_format
public:
nascom_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_NASCOM_FORMAT;
+extern const nascom_format FLOPPY_NASCOM_FORMAT;
#endif // MAME_FORMATS_NASCOM_DSK_H
diff --git a/src/lib/formats/naslite_dsk.cpp b/src/lib/formats/naslite_dsk.cpp
index cea109c2dc2..3bd0ad45fd7 100644
--- a/src/lib/formats/naslite_dsk.cpp
+++ b/src/lib/formats/naslite_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/naslite_dsk.c
+ formats/naslite_dsk.cpp
NASLite 1.72MB with funky interleaving format
*********************************************************************/
-#include <assert.h>
-
#include "formats/naslite_dsk.h"
naslite_format::naslite_format() : upd765_format(formats)
{
}
-const char *naslite_format::name() const
+const char *naslite_format::name() const noexcept
{
return "NASLite";
}
-const char *naslite_format::description() const
+const char *naslite_format::description() const noexcept
{
return "NASLite disk image";
}
-const char *naslite_format::extensions() const
+const char *naslite_format::extensions() const noexcept
{
return "img";
}
@@ -52,4 +50,4 @@ void naslite_format::build_sector_description(const format &f, uint8_t *sectdata
}
}
-const floppy_format_type FLOPPY_NASLITE_FORMAT = &floppy_image_format_creator<naslite_format>;
+const naslite_format FLOPPY_NASLITE_FORMAT;
diff --git a/src/lib/formats/naslite_dsk.h b/src/lib/formats/naslite_dsk.h
index 1cad11e1137..07c3c3929b2 100644
--- a/src/lib/formats/naslite_dsk.h
+++ b/src/lib/formats/naslite_dsk.h
@@ -19,9 +19,9 @@ class naslite_format : public upd765_format
public:
naslite_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
protected:
static const format formats[];
@@ -29,6 +29,6 @@ protected:
virtual void build_sector_description(const format &d, uint8_t *sectdata, desc_s *sectors, int track, int head) const override;
};
-extern const floppy_format_type FLOPPY_NASLITE_FORMAT;
+extern const naslite_format FLOPPY_NASLITE_FORMAT;
#endif // MAME_FORMATS_NASLITE_DSK_H
diff --git a/src/lib/formats/nes_dsk.cpp b/src/lib/formats/nes_dsk.cpp
index 60dbba5dafa..237fd2c4fd7 100644
--- a/src/lib/formats/nes_dsk.cpp
+++ b/src/lib/formats/nes_dsk.cpp
@@ -8,13 +8,11 @@
*********************************************************************/
-
-#include <string.h>
-#include <assert.h>
-
#include "formats/nes_dsk.h"
#include "formats/basicdsk.h"
+#include <cstring>
+
/*****************************************************************************
diff --git a/src/lib/formats/nes_dsk.h b/src/lib/formats/nes_dsk.h
index d222beb03f9..a6b589380cb 100644
--- a/src/lib/formats/nes_dsk.h
+++ b/src/lib/formats/nes_dsk.h
@@ -12,7 +12,7 @@
#pragma once
-#include "flopimg.h"
+#include "flopimg_legacy.h"
LEGACY_FLOPPY_OPTIONS_EXTERN(nes_only);
diff --git a/src/lib/formats/nfd_dsk.cpp b/src/lib/formats/nfd_dsk.cpp
index 4d9d8fab980..a51b461e144 100644
--- a/src/lib/formats/nfd_dsk.cpp
+++ b/src/lib/formats/nfd_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Fabio Priuli
/*********************************************************************
- formats/nfd_dsk.h
+ formats/nfd_dsk.cpp
PC98 NFD disk images (info from: http://www.geocities.jp/t98next/dev.html )
@@ -77,46 +77,55 @@
*********************************************************************/
- #include <assert.h>
-
#include "nfd_dsk.h"
+#include "ioprocs.h"
+
+#include "osdcomm.h" // little_endianize_int*
+
+#include <cstring>
+
+
nfd_format::nfd_format()
{
}
-const char *nfd_format::name() const
+const char *nfd_format::name() const noexcept
{
return "nfd";
}
-const char *nfd_format::description() const
+const char *nfd_format::description() const noexcept
{
return "NFD disk image";
}
-const char *nfd_format::extensions() const
+const char *nfd_format::extensions() const noexcept
{
return "nfd";
}
-int nfd_format::identify(io_generic *io, uint32_t form_factor)
+int nfd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t h[16];
- io_generic_read(io, h, 0, 16);
+ auto const [err, actual] = read_at(io, 0, h, 16); // TODO: does it really need 16 bytes? it only looks at 14.
+ if (err || (16 != actual))
+ return 0;
- if (strncmp((const char *)h, "T98FDDIMAGE.R0", 14) == 0 || strncmp((const char *)h, "T98FDDIMAGE.R1", 14) == 0)
- return 100;
+ if (!memcmp(h, "T98FDDIMAGE.R0", 14) || !memcmp(h, "T98FDDIMAGE.R1", 14))
+ return FIFID_SIGN;
return 0;
}
-bool nfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool nfd_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return false;
uint8_t h[0x120], hsec[0x10];
- io_generic_read(io, h, 0, 0x120);
- int format_version = !strncmp((const char *)h, "T98FDDIMAGE.R0", 14) ? 0 : 1;
+ read_at(io, 0, h, 0x120); // FIXME: check for errors and premature EOF
+ int format_version = !memcmp(h, "T98FDDIMAGE.R0", 14) ? 0 : 1;
// sector map (the 164th entry is only used by rev.1 format, loops with track < 163 are correct for rev.0)
uint8_t disk_type = 0;
@@ -140,7 +149,7 @@ bool nfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
int curr_track_size = 0;
// read sector map absolute location
- io_generic_read(io, hsec, pos, 4);
+ read_at(io, pos, hsec, 4); // FIXME: check for errors and premature EOF
pos += 4;
uint32_t secmap_addr = little_endianize_int32(*(uint32_t *)(hsec));
@@ -149,14 +158,14 @@ bool nfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
// read actual sector map for the sectors of this track
// for rev.1 format the first 0x10 are a track summary:
// first WORD is # of sectors, second WORD is # of special data sectors
- io_generic_read(io, hsec, secmap_addr, 0x10);
+ read_at(io, secmap_addr, hsec, 0x10); // FIXME: check for errors and premature EOF
secmap_addr += 0x10;
num_secs[track] = little_endianize_int16(*(uint16_t *)(hsec));
num_specials[track] = little_endianize_int16(*(uint16_t *)(hsec + 0x2));
for (int sect = 0; sect < num_secs[track]; sect++)
{
- io_generic_read(io, hsec, secmap_addr, 0x10);
+ read_at(io, secmap_addr, hsec, 0x10); // FIXME: check for errors and premature EOF
if (track == 0 && sect == 0)
disk_type = hsec[0xb]; // can this change across the disk? I don't think so...
@@ -175,7 +184,7 @@ bool nfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
for (int sect = 0; sect < num_specials[track]; sect++)
{
- io_generic_read(io, hsec, secmap_addr, 0x10);
+ read_at(io, secmap_addr, hsec, 0x10); // FIXME: check for errors and premature EOF
secmap_addr += 0x10;
curr_track_size += (hsec[9] + 1) * little_endianize_int32(*(uint32_t *)(hsec + 0x0a));
}
@@ -198,7 +207,7 @@ bool nfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
// read sector map for this sector
// for rev.0 format each sector uses 0x10 bytes
- io_generic_read(io, hsec, pos, 0x10);
+ read_at(io, pos, hsec, 0x10); // FIXME: check for errors and premature EOF
if (track == 0 && sect == 0)
disk_type = hsec[0xa]; // can this change across the disk? I don't think so...
@@ -228,13 +237,13 @@ bool nfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
switch (disk_type)
{
case 0x10: // 640K disk, 2DD
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
break;
//case 0x30: // 1.44M disk, ?? (no images found)
// break;
case 0x90: // 1.2M disk, 2HD
default:
- image->set_variant(floppy_image::DSHD);
+ image.set_variant(floppy_image::DSHD);
break;
}
@@ -245,7 +254,7 @@ bool nfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
for (int track = 0; track < 163 && pos < size; track++)
{
- io_generic_read(io, sect_data, pos, track_sizes[track]);
+ read_at(io, pos, sect_data, track_sizes[track]); // FIXME: check for errors and premature EOF
for (int i = 0; i < num_secs[track]; i++)
{
@@ -268,15 +277,15 @@ bool nfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
if (mfm[track * 26])
build_pc_track_mfm(track / 2, track % 2, image, cell_count, num_secs[track], sects, calc_default_pc_gap3_size(form_factor, (128 << sec_sizes[track * 26])));
else
- build_pc_track_fm(track / 2, track % 2, image, cell_count, num_secs[track], sects, calc_default_pc_gap3_size(form_factor, (128 << sec_sizes[track * 26])));
+ build_pc_track_fm(track / 2, track % 2, image, cell_count / 2, num_secs[track], sects, calc_default_pc_gap3_size(form_factor, (128 << sec_sizes[track * 26])));
}
return true;
}
-bool nfd_format::supports_save() const
+bool nfd_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_NFD_FORMAT = &floppy_image_format_creator<nfd_format>;
+const nfd_format FLOPPY_NFD_FORMAT;
diff --git a/src/lib/formats/nfd_dsk.h b/src/lib/formats/nfd_dsk.h
index 56cf6cdb506..d0decfb1e17 100644
--- a/src/lib/formats/nfd_dsk.h
+++ b/src/lib/formats/nfd_dsk.h
@@ -20,15 +20,15 @@ class nfd_format : public floppy_image_format_t
public:
nfd_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_NFD_FORMAT;
+extern const nfd_format FLOPPY_NFD_FORMAT;
#endif // MAME_FORMATS_NFD_DSK_H
diff --git a/src/lib/formats/opd_dsk.cpp b/src/lib/formats/opd_dsk.cpp
index b45b58762ed..e3bed7a2017 100644
--- a/src/lib/formats/opd_dsk.cpp
+++ b/src/lib/formats/opd_dsk.cpp
@@ -15,31 +15,32 @@ opd_format::opd_format() : wd177x_format(formats)
{
}
-const char *opd_format::name() const
+const char *opd_format::name() const noexcept
{
return "opd";
}
-const char *opd_format::description() const
+const char *opd_format::description() const noexcept
{
return "Opus Discovery disk image";
}
-const char *opd_format::extensions() const
+const char *opd_format::extensions() const noexcept
{
return "opd,opu";
}
-int opd_format::identify(io_generic *io, uint32_t form_factor)
+int opd_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int const type = find_size(io, form_factor, variants);
if (type != -1)
- return 90;
+ return FIFID_SIZE;
+
return 0;
}
-int opd_format::get_image_offset(const format &f, int head, int track)
+int opd_format::get_image_offset(const format &f, int head, int track) const
{
return (f.track_count * head + track) * compute_track_size(f);
}
@@ -58,4 +59,4 @@ const opd_format::format opd_format::formats[] =
};
-const floppy_format_type FLOPPY_OPD_FORMAT = &floppy_image_format_creator<opd_format>;
+const opd_format FLOPPY_OPD_FORMAT;
diff --git a/src/lib/formats/opd_dsk.h b/src/lib/formats/opd_dsk.h
index 45e002092d6..a5fce693ede 100644
--- a/src/lib/formats/opd_dsk.h
+++ b/src/lib/formats/opd_dsk.h
@@ -21,17 +21,17 @@ class opd_format : public wd177x_format
public:
opd_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual int get_image_offset(const format &f, int head, int track) override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_OPD_FORMAT;
+extern const opd_format FLOPPY_OPD_FORMAT;
#endif // MAME_FORMATS_OPD_DSK_H
diff --git a/src/lib/formats/orao_cas.cpp b/src/lib/formats/orao_cas.cpp
index 2d0f96b2a14..8cc77e3bf6f 100644
--- a/src/lib/formats/orao_cas.cpp
+++ b/src/lib/formats/orao_cas.cpp
@@ -5,22 +5,23 @@
Tape support for Orao TAP format
*/
-#include <assert.h>
#include "orao_cas.h"
#define ORAO_WAV_FREQUENCY 44100
-#define WAVE_HIGH -32768
-#define WAVE_LOW 0
+#define WAVE_HIGH -24576
+#define WAVE_LOW 24576
-#define ORAO_WAVE_ONE 24
-#define ORAO_WAVE_ZERO 11
+#define ORAO_WAVE_ONE 17
+#define ORAO_WAVE_ZERO 9
#define ORAO_HEADER_SIZE 360
-static int16_t wave_data;
-static int len;
+static int16_t wave_data; // FIXME: global variables prevent multiple instances
+static int len;
+static int startpos;
+static bool newformat;
static void orao_output_wave( int16_t **buffer, int length ) {
if ( buffer == nullptr ) {
@@ -41,7 +42,13 @@ static int orao_cas_to_wav_size( const uint8_t *casdata, int caslen ) {
return -1;
}
size = 0;
- for (i=ORAO_HEADER_SIZE;i<caslen-ORAO_HEADER_SIZE;i++) {
+ startpos = 0;
+ newformat = true;
+ if (casdata[0]==0x68 && casdata[1]==0x01 && casdata[2]==0x00) {
+ startpos = ORAO_HEADER_SIZE;
+ newformat = false;
+ }
+ for (i=startpos;i<caslen-startpos;i++) {
for (j=0;j<8;j++) {
b = (casdata[i] >> j) & 1;
if (b==0) {
@@ -55,14 +62,15 @@ static int orao_cas_to_wav_size( const uint8_t *casdata, int caslen ) {
return size;
}
-static int orao_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
- int i,j,size;
+static int orao_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) {
+ int i,j,size,k;
uint8_t b;
size = 0;
if (bytes == nullptr) return -1;
- for (i=ORAO_HEADER_SIZE;i<len-ORAO_HEADER_SIZE;i++) {
+ for (i=startpos;i<len-startpos;i++) {
for (j=0;j<8;j++) {
- b = (bytes[i] >> j) & 1;
+ k = newformat ? (7-j) : j;
+ b = (bytes[i] >> k) & 1;
if (b==0) {
wave_data = WAVE_LOW;
orao_output_wave(&buffer,ORAO_WAVE_ZERO);
@@ -85,7 +93,7 @@ static int orao_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
-static const struct CassetteLegacyWaveFiller orao_legacy_fill_wave = {
+static const cassette_image::LegacyWaveFiller orao_legacy_fill_wave = {
orao_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
0, /* chunk_samples */
@@ -97,19 +105,19 @@ static const struct CassetteLegacyWaveFiller orao_legacy_fill_wave = {
-static cassette_image::error orao_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) {
- return cassette_legacy_identify( cassette, opts, &orao_legacy_fill_wave );
+static cassette_image::error orao_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) {
+ return cassette->legacy_identify( opts, &orao_legacy_fill_wave );
}
static cassette_image::error orao_cassette_load( cassette_image *cassette ) {
- return cassette_legacy_construct( cassette, &orao_legacy_fill_wave );
+ return cassette->legacy_construct( &orao_legacy_fill_wave );
}
-static const struct CassetteFormat orao_cassette_format = {
+static const cassette_image::Format orao_cassette_format = {
"tap",
orao_cassette_identify,
orao_cassette_load,
diff --git a/src/lib/formats/oric_dsk.cpp b/src/lib/formats/oric_dsk.cpp
index 45cbcab0fee..15bb5ce1455 100644
--- a/src/lib/formats/oric_dsk.cpp
+++ b/src/lib/formats/oric_dsk.cpp
@@ -2,72 +2,87 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/oric_dsk.c
+ formats/oric_dsk.cpp
Oric disk images
*********************************************************************/
-#include "emu.h" // logerror
-#include "formats/oric_dsk.h"
+#include "oric_dsk.h"
-oric_dsk_format::oric_dsk_format()
-{
-}
+#include "ioprocs.h"
+#include "multibyte.h"
+#include "osdcore.h"
+
+#include <cstring>
-const char *oric_dsk_format::name() const
+
+const char *oric_dsk_format::name() const noexcept
{
return "oric_dsk";
}
-const char *oric_dsk_format::description() const
+const char *oric_dsk_format::description() const noexcept
{
return "Oric disk image";
}
-const char *oric_dsk_format::extensions() const
+const char *oric_dsk_format::extensions() const noexcept
{
return "dsk";
}
-bool oric_dsk_format::supports_save() const
+bool oric_dsk_format::supports_save() const noexcept
{
return true;
}
-int oric_dsk_format::identify(io_generic *io, uint32_t form_factor)
+int oric_dsk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t h[256];
- io_generic_read(io, h, 0, 256);
+ /*auto const [err, actual] =*/ read_at(io, 0, h, 256); // FIXME: check for errors and premature EOF
if(memcmp(h, "MFM_DISK", 8))
return 0;
- int sides = (h[11] << 24) | (h[10] << 16) | (h[ 9] << 8) | h[ 8];
- int tracks = (h[15] << 24) | (h[14] << 16) | (h[13] << 8) | h[12];
- int geom = (h[19] << 24) | (h[18] << 16) | (h[17] << 8) | h[16];
+ int sides = get_u32le(&h[ 8]);
+ int tracks = get_u32le(&h[12]);
+ int geom = get_u32le(&h[16]);
- int size = io_generic_size(io);
+ uint64_t size;
+ if(io.length(size))
+ return 0;
if(sides < 0 || sides > 2 || geom != 1 || size != 256+6400*sides*tracks)
return 0;
- return 100;
+ return FIFID_SIGN|FIFID_SIZE|FIFID_STRUCT;
}
-bool oric_dsk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool oric_dsk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t h[256];
uint8_t t[6250+3];
t[6250] = t[6251] = t[6252] = 0;
- io_generic_read(io, h, 0, 256);
-
- int sides = (h[11] << 24) | (h[10] << 16) | (h[ 9] << 8) | h[ 8];
- int tracks = (h[15] << 24) | (h[14] << 16) | (h[13] << 8) | h[12];
+ read_at(io, 0, h, 256); // FIXME: check for errors and premature EOF
+
+ int sides = get_u32le(&h[ 8]);
+ int tracks = get_u32le(&h[12]);
+
+ int max_tracks, max_sides;
+ image.get_maximal_geometry(max_tracks, max_sides);
+ if (tracks > max_tracks) {
+ osd_printf_error("oric_dsk: Floppy disk has too many tracks for this drive (floppy tracks=%d, drive tracks=%d).\n", tracks, max_tracks);
+ return false;
+ }
+ if (sides > max_sides) {
+ osd_printf_warning("oric_dsk: Floppy disk has excess of heads for this drive that will be discarded (floppy heads=%d, drive heads=%d).\n", sides, max_sides);
+ sides = max_sides;
+ }
for(int side=0; side<sides; side++)
for(int track=0; track<tracks; track++) {
- io_generic_read(io, t, 256+6400*(tracks*side + track), 6250);
+ read_at(io, 256+6400*(tracks*side + track), t, 6250); // FIXME: check for errors and premature EOF
std::vector<uint32_t> stream;
int sector_size = 128;
for(int i=0; i<6250; i++) {
@@ -103,9 +118,105 @@ bool oric_dsk_format::load(io_generic *io, uint32_t form_factor, floppy_image *i
return true;
}
-bool oric_dsk_format::save(io_generic *io, floppy_image *image)
+const oric_dsk_format FLOPPY_ORIC_DSK_FORMAT;
+
+
+const char *oric_jasmin_format::name() const noexcept
+{
+ return "oric_jasmin";
+}
+
+const char *oric_jasmin_format::description() const noexcept
+{
+ return "Oric Jasmin pure sector image";
+}
+
+const char *oric_jasmin_format::extensions() const noexcept
+{
+ return "dsk";
+}
+
+bool oric_jasmin_format::supports_save() const noexcept
{
return true;
}
-const floppy_format_type FLOPPY_ORIC_DSK_FORMAT = &floppy_image_format_creator<oric_dsk_format>;
+int oric_jasmin_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if(io.length(size))
+ return 0;
+
+ bool const can_ds = variants.empty() || has_variant(variants, floppy_image::DSDD);
+ if(size == 41*17*256 || (can_ds && size == 41*17*256*2))
+ return FIFID_SIZE;
+
+ return 0;
+}
+
+bool oric_jasmin_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ uint64_t size;
+ if(io.length(size))
+ return false;
+
+ bool const can_ds = variants.empty() || has_variant(variants, floppy_image::DSDD);
+
+ if(size != 41*17*256 && (!can_ds || size != 41*17*256*2))
+ return false;
+
+ int const heads = size == 41*17*256 ? 1 : 2;
+
+ auto const [err, data, actual] = read_at(io, 0, size);
+ if(err || (actual != size))
+ return false;
+
+ for(int head = 0; head != heads; head++)
+ for(int track = 0; track != 41; track++) {
+ desc_pc_sector sdesc[17];
+ for(int s = 0; s != 17; s++) {
+ int sector = (s + 6*(track+1)) % 17;
+ sdesc[s].track = track;
+ sdesc[s].head = head;
+ sdesc[s].sector = sector + 1;
+ sdesc[s].size = 1;
+ sdesc[s].actual_size = 256;
+ sdesc[s].data = &data[256 * (sector + track*17 + head*17*41)];
+ sdesc[s].deleted = false;
+ sdesc[s].bad_crc = false;
+ }
+
+ build_wd_track_mfm(track, head, image, 100000, 17, sdesc, 38, 40);
+ }
+
+ image.set_form_variant(floppy_image::FF_3, heads == 2 ? floppy_image::DSDD : floppy_image::SSDD);
+
+ return true;
+}
+
+bool oric_jasmin_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ int tracks, heads;
+ image.get_actual_geometry(tracks, heads);
+
+ bool can_ds = variants.empty() || has_variant(variants, floppy_image::DSDD);
+ if(heads == 2 && !can_ds)
+ return false;
+ if(heads == 0)
+ heads = 1;
+
+ uint8_t zero[256];
+ memset(zero, 0, 256);
+
+ for(int head = 0; head != heads; head++)
+ for(int track = 0; track != 41; track++) {
+ auto sectors = extract_sectors_from_bitstream_mfm_pc(generate_bitstream_from_track(track, head, 2000, image));
+ for(unsigned int sector = 0; sector != 17; sector ++) {
+ uint8_t const *const data = (sector+1 < sectors.size() && !sectors[sector+1].empty()) ? sectors[sector+1].data() : zero;
+ /*auto const [err, actual] =*/ write_at(io, 256 * (sector + track*17 + head*17*41), data, 256); // FIXME: check for errors
+ }
+ }
+ return true;
+}
+
+const oric_jasmin_format FLOPPY_ORIC_JASMIN_FORMAT;
diff --git a/src/lib/formats/oric_dsk.h b/src/lib/formats/oric_dsk.h
index 5104273864b..4b040de86f4 100644
--- a/src/lib/formats/oric_dsk.h
+++ b/src/lib/formats/oric_dsk.h
@@ -17,17 +17,33 @@
class oric_dsk_format : public floppy_image_format_t
{
public:
- oric_dsk_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
-
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ oric_dsk_format() = default;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_ORIC_DSK_FORMAT;
+extern const oric_dsk_format FLOPPY_ORIC_DSK_FORMAT;
+
+class oric_jasmin_format : public floppy_image_format_t
+{
+public:
+ oric_jasmin_format() = default;
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
+};
+
+extern const oric_jasmin_format FLOPPY_ORIC_JASMIN_FORMAT;
#endif // MAME_FORMATS_ORIC_DSK_H
diff --git a/src/lib/formats/oric_tap.cpp b/src/lib/formats/oric_tap.cpp
index c9f84dc7c2a..a8b11a5ee7a 100644
--- a/src/lib/formats/oric_tap.cpp
+++ b/src/lib/formats/oric_tap.cpp
@@ -1,8 +1,10 @@
// license:BSD-3-Clause
// copyright-holders:Kevin Thacker
-#include <assert.h>
-
#include "oric_tap.h"
+#include "imageutl.h"
+
+#include "multibyte.h"
+
#define ORIC_WAV_DEBUG 0
#define LOG(x) do { if (ORIC_WAV_DEBUG) printf x; } while (0)
@@ -314,8 +316,8 @@ static int oric_cassette_calculate_size_in_samples(const uint8_t *bytes, int len
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));
+ end = get_u16be(&header[4]);
+ start = get_u16be(&header[6]);
LOG(("start (from header): %02x\n",start));
LOG(("end (from header): %02x\n",end));
oric.data_length = end - start + 1;
@@ -343,145 +345,141 @@ static int oric_cassette_calculate_size_in_samples(const uint8_t *bytes, int len
}
/* length is length of sample buffer to fill! */
-static int oric_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int oric_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
unsigned char header[9];
- uint8_t *data_ptr;
- int16_t *p;
- int i;
- uint8_t data;
-
- p = buffer;
-
+ int16_t *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++)
+ if (bytes == CODE_HEADER)
+ {
+ for (int i = 0; i < ORIC_WAVESAMPLES_HEADER; i++)
*(p++) = WAVEENTRY_NULL;
}
- else if (bytes == CODE_TRAILER) {
- for (i = 0; i < ORIC_WAVESAMPLES_TRAILER; i++)
+ else if (bytes == CODE_TRAILER)
+ {
+ for (int 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++;
+ /* 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;
- switch (oric.cassette_state)
+ oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE;
+ const uint8_t *data_ptr = bytes;
+
+ while ((data_ptr < (bytes + oric.tap_size)) && (p < (buffer+length)) )
{
- case ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE:
- {
- if (data==ORIC_SYNC_BYTE)
- {
- LOG_FORMATS("found sync byte!\n");
- /* found first sync byte */
- oric.cassette_state = ORIC_CASSETTE_GOT_SYNC_BYTE;
- }
- }
- break;
+ const uint8_t data = data_ptr[0];
+ data_ptr++;
- case ORIC_CASSETTE_GOT_SYNC_BYTE:
+ switch (oric.cassette_state)
{
- if (data!=ORIC_SYNC_BYTE)
+ case ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE:
{
- /* 0.25 second pause */
- p = oric_fill_pause(p, oric_seconds_to_samples(0.25));
-
- LOG_FORMATS("found end of sync bytes!\n");
- /* found end of sync bytes */
- for (i=0; i<ORIC_LEADER_LENGTH; i++)
+ if (data == ORIC_SYNC_BYTE)
{
- p = oric_output_byte(p,0x016);
+ LOG_FORMATS("found sync byte!\n");
+ /* found first sync byte */
+ oric.cassette_state = ORIC_CASSETTE_GOT_SYNC_BYTE;
}
+ }
+ break;
- if (data==0x024)
+ case ORIC_CASSETTE_GOT_SYNC_BYTE:
+ {
+ if (data != ORIC_SYNC_BYTE)
{
- //LOG_FORMATS("reading header!\n");
- p = oric_output_byte(p,data);
- oric.cassette_state = ORIC_CASSETTE_READ_HEADER;
- oric.data_count = 0;
- oric.data_length = 9;
+ /* 0.25 second pause */
+ p = oric_fill_pause(p, oric_seconds_to_samples(0.25));
+
+ LOG_FORMATS("found end of sync bytes!\n");
+ /* found end of sync bytes */
+ for (int i = 0; i < ORIC_LEADER_LENGTH; i++)
+ {
+ p = oric_output_byte(p,0x016);
+ }
+
+ if (data == 0x024)
+ {
+ //LOG_FORMATS("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;
+ 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)
+ case ORIC_CASSETTE_READ_HEADER:
{
- //LOG_FORMATS("finished reading header!\n");
- oric.cassette_state = ORIC_CASSETTE_READ_FILENAME;
- }
- }
- break;
+ header[oric.data_count] = data;
+ p = oric_output_byte(p, data);
+ oric.data_count++;
- case ORIC_CASSETTE_READ_FILENAME:
- {
- p = oric_output_byte(p, data);
+ if (oric.data_count==oric.data_length)
+ {
+ //LOG_FORMATS("finished reading header!\n");
+ oric.cassette_state = ORIC_CASSETTE_READ_FILENAME;
+ }
+ }
+ break;
- /* got end of filename? */
- if (data==0)
+ case ORIC_CASSETTE_READ_FILENAME:
{
- uint16_t end, start;
- LOG_FORMATS("got end of filename\n");
+ p = oric_output_byte(p, data);
- /* oric includes a small delay, but I don't see
- it being 1 bits */
- for (i=0; i<100; i++)
+ /* got end of filename? */
+ if (data == 0)
{
- p = oric_output_bit(p,1);
- }
+ uint16_t end, start;
+ LOG_FORMATS("got end of filename\n");
- oric.cassette_state = ORIC_CASSETTE_WRITE_DATA;
- oric.data_count = 0;
+ /* oric includes a small delay, but I don't see
+ it being 1 bits */
+ for (int i = 0; i < 100; i++)
+ {
+ p = oric_output_bit(p,1);
+ }
- 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;
+ oric.cassette_state = ORIC_CASSETTE_WRITE_DATA;
+ oric.data_count = 0;
- case ORIC_CASSETTE_WRITE_DATA:
- {
- p = oric_output_byte(p, data);
- oric.data_count++;
+ end = get_u16be(&header[4]);
+ start = get_u16be(&header[6]);
+ LOG(("start (from header): %02x\n",start));
+ LOG(("end (from header): %02x\n",end));
+ oric.data_length = end - start + 1;
+ }
+ }
+ break;
- if (oric.data_count==oric.data_length)
+ case ORIC_CASSETTE_WRITE_DATA:
{
- LOG_FORMATS("finished writing data!\n");
- oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE;
+ p = oric_output_byte(p, data);
+ oric.data_count++;
+
+ if (oric.data_count==oric.data_length)
+ {
+ LOG_FORMATS("finished writing data!\n");
+ oric.cassette_state = ORIC_CASSETTE_SEARCHING_FOR_SYNC_BYTE;
+ }
}
- }
- break;
+ break;
+ }
}
}
-}
return p - buffer;
}
-static const struct CassetteLegacyWaveFiller oric_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller oric_legacy_fill_wave =
{
oric_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -494,21 +492,21 @@ static const struct CassetteLegacyWaveFiller oric_legacy_fill_wave =
-static cassette_image::error oric_tap_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error oric_tap_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &oric_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &oric_legacy_fill_wave);
}
static cassette_image::error oric_tap_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &oric_legacy_fill_wave);
+ return cassette->legacy_construct(&oric_legacy_fill_wave);
}
-static const struct CassetteFormat oric_tap_format =
+static const cassette_image::Format oric_tap_format =
{
"tap",
oric_tap_identify,
diff --git a/src/lib/formats/os9_dsk.cpp b/src/lib/formats/os9_dsk.cpp
index dd874f7d236..ca79ced2145 100644
--- a/src/lib/formats/os9_dsk.cpp
+++ b/src/lib/formats/os9_dsk.cpp
@@ -1,8 +1,8 @@
// license:BSD-3-Clause
-// copyright-holders:tim lindner
+// copyright-holders:tim lindner, 68bit
/*********************************************************************
- formats/os9_dsk.c
+ formats/os9_dsk.cpp
OS-9 disk images
@@ -12,99 +12,587 @@
Available: http://www.colorcomputerarchive.com/coco/Documents/Manuals/
Operating Systems/OS-9 Level 2 Manual (Tandy).pdf
+ Some OS9 floppy drivers use a base sector ID of zero, for example SWTPC
+ and Gimix OS9, and some drivers use a base sector ID of one, for example
+ COCO OS9. The disk images do not encode the sector IDs so it is a
+ challenge to detect this base ID by looking at the disk content.
+
+ The OS9 disk header, at LSN0, does not necessarily encode the base sector
+ ID either. However it may optionally include a block of the device
+ descriptor information from when it was formatted and although this
+ optional content can vary across OS9 variants it appears to be regular
+ enough for COCO compatible formats to reply on it to identify a COCO
+ compatible disk and determine the base sector ID for these. All non-COCO
+ formats are assumed to have a base sector ID of zero and not exceptions to
+ this have been noted.
+
+ Commonly the 'coco' bit, bit 5, in the optional 'type' byte is set in COCO
+ format disks. Alternatively some OS9 formats set bit 7 of the optional
+ 'density' byte to indicate a COCO format disk, for example some Gimix
+ drivers with COCO support. Some of the other optional information also can
+ be checked for consistency to better identify the format.
+
+ Some formats encode the first track, on the first head only, in single
+ density even when the remainder of the disk is encoded in double density,
+ and this can typically be identified from the disk geometry and the image
+ size and the total number of logical sectors written in the header.
+ E.g. At least some versions of OS9 for the SWTPC and the Gimix encode this
+ first track in single density whereas COCO format disks are typically all
+ double density. The number of sectors on track zero is also typically
+ encoded in the optional header information and can be checked for
+ consistency.
+
*********************************************************************/
-#include "emu.h"
-#include "formats/os9_dsk.h"
+#include "os9_dsk.h"
+#include "imageutl.h"
-#include "formats/imageutl.h"
+#include "coretmpl.h" // BIT
+#include "ioprocs.h"
+#include "multibyte.h"
os9_format::os9_format() : wd177x_format(formats)
{
}
-const char *os9_format::name() const
+const char *os9_format::name() const noexcept
{
return "os9";
}
-const char *os9_format::description() const
+const char *os9_format::description() const noexcept
{
return "OS-9 floppy disk image";
}
-const char *os9_format::extensions() const
+const char *os9_format::extensions() const noexcept
{
- return "dsk,os9";
+ return "os9,dsk";
}
-int os9_format::identify(io_generic *io, uint32_t form_factor)
+int os9_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int const type = find_size(io, form_factor, variants);
if (type != -1)
- return 75;
+ return FIFID_SIZE | FIFID_HINT;
+
return 0;
}
-int os9_format::find_size(io_generic *io, uint32_t form_factor)
+int os9_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ uint8_t os9_header[0x60];
+ /*auto const [err, actual] =*/ read_at(io, 0, os9_header, sizeof(os9_header)); // FIXME: check for errors and premature EOF
- uint8_t os9_header[0x20];
- io_generic_read(io, os9_header, 0, 0x20);
+ int os9_total_sectors = get_u24be(&os9_header[0x00]);
+ int os9_heads = util::BIT(os9_header[0x10], 0) ? 2 : 1;
+ int os9_sectors = get_u16be(&os9_header[0x11]);
- int os9_total_sectors = pick_integer_be(os9_header, 0x00, 3);
- int os9_heads = BIT(os9_header[0x10], 0) ? 2 : 1;
- int os9_sectors = pick_integer_be(os9_header, 0x11, 2);
+ if (os9_total_sectors <= 0 || os9_heads <= 0 || os9_sectors <= 0)
+ return -1;
- if (os9_total_sectors > 0 && os9_heads > 0 && os9_sectors > 0) {
- int os9_tracks = os9_total_sectors / os9_sectors / os9_heads;
+ // The optional header information, which may contain a copy of the
+ // device descriptor used to format the disk. COCO format disks are
+ // expected to include this optional information.
+ int opt_dtype = os9_header[0x3f + 0];
+ int opt_type = os9_header[0x3f + 3];
+ int opt_density = os9_header[0x3f + 4];
+ int opt_cylinders = get_u16be(&os9_header[0x3f + 5]);
+ int opt_sides = os9_header[0x3f + 7];
+ int opt_sectors_per_track = get_u16be(&os9_header[0x3f + 9]);
+ int opt_track0_sectors = get_u16be(&os9_header[0x3f + 11]);
+ int opt_interleave = os9_header[0x3f + 13];
- // now let's see if we have valid info
- if ((os9_tracks * os9_heads * os9_sectors * 256) == size) {
- for (int i=0; formats[i].form_factor; i++) {
- const format &f = formats[i];
+ int opt_mfm = util::BIT(opt_density, 0);
- if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
- continue;
+ // The NitrOS9 rb1773 driver uses bit 1 of opt_type to distinguish
+ // between a sector base ID of zero or one, so recognise that here.
+ int opt_sector_base_id = util::BIT(opt_type, 1) ? 0 : 1;
+ int opt_sector_size = util::BIT(opt_type, 2) ? 512 : 256;
+ int opt_coco = util::BIT(opt_type, 5);
+
+ // Some OS9 versions appear to use bit 7 of the opt_density rather
+ // than bit 5 of opt_type to signify a COCO format disk. E.g. Gimix
+ // OS9 is documented to use this bit and had a floppy driver that
+ // could read both non-COCO and COCO format disks.
+ if (util::BIT(opt_density, 7))
+ opt_coco = 1;
+
+ // COCO format disks are expected for have an opt_dtype of 1.
+ if (opt_dtype != 1)
+ opt_coco = 0;
+
+ for (int i=0; formats[i].form_factor; i++) {
+ const format &f = formats[i];
+ if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
+ continue;
+
+ if ((os9_heads != f.head_count) || (os9_sectors != f.sector_count)) {
+ continue;
+ }
- if ((os9_tracks == f.track_count) && (os9_heads == f.head_count) && (os9_sectors == f.sector_count))
- return i;
+ unsigned int format_size = 0;
+ for (int track=0; track < f.track_count; track++) {
+ for (int head=0; head < f.head_count; head++) {
+ const format &tf = get_track_format(f, head, track);
+ format_size += compute_track_size(tf);
}
}
- }
+ if (format_size != size)
+ continue;
+
+ if (format_size < os9_total_sectors * 256)
+ continue;
+
+ // Accept the format if the total number of sectors used is
+ // consistent with at least 10 sectors being used on track
+ // zero, as happens when the driver or descriptor is coded for
+ // a single density track zero with 10 sectors and so uses
+ // only 10 of these track zero sectors.
+
+ if (f.sector_count < 10 || format_size > (os9_total_sectors + f.sector_count - 10) * 256)
+ continue;
+
+ const format &tf = get_track_format(f, 0, 0);
+
+ if (opt_coco) {
+ // Looks like a COCO format disk.
+
+ // Check the sector base ID.
+ if (f.sector_base_id != opt_sector_base_id &&
+ (f.sector_base_id != -1 || f.per_sector_id[0] != opt_sector_base_id))
+ continue;
+ if (tf.sector_base_id != opt_sector_base_id &&
+ (tf.sector_base_id != -1 || tf.per_sector_id[0] != opt_sector_base_id))
+ continue;
+
+ // Check some other optional fields for consistency.
+
+ // If the device descriptor is not MFM capable then
+ // the disk it not expected to be MFM encoded.
+ if (opt_mfm == 0 && f.encoding == floppy_image::MFM)
+ continue;
+
+ // Should not be more cylinders than suppored in the
+ // device descriptor.
+ if (f.track_count > opt_cylinders)
+ continue;
+
+ // Should not be more heads than suppored in the
+ // device descriptor.
+ if (f.head_count > opt_sides)
+ continue;
+
+ // Should not be more sectors per track than supported
+ // in the device descriptor.
+ if (f.sector_count > opt_sectors_per_track ||
+ tf.sector_count > opt_track0_sectors) {
+ continue;
+ }
+
+ // The sector size should be consistent.
+ if (opt_sector_size != f.sector_base_size)
+ continue;
+
+ // The sector interleave should not be greater than
+ // the number of sectors.
+ if (opt_interleave > f.sector_count)
+ continue;
+ }
+ else
+ {
+ // Not a recognizable COCO OS9 format. The options
+ // can not be relied on here, they are driver
+ // dependent, and the sector base ID is assumed to be
+ // zero. Check that format sector base ID is zero.
+ if (f.sector_base_id != 0 && (f.sector_base_id != -1 || f.per_sector_id[0] != 0))
+ continue;
+ if (tf.sector_base_id != 0 && (tf.sector_base_id != -1 || tf.per_sector_id[0] != 0))
+ continue;
+ }
+
+ LOG_FORMATS("os9_dsk: matching format index %d: tracks %d, sectors %d, sides: %d\n", i, f.track_count, f.sector_count, f.head_count);
+ return i;
+ }
return -1;
}
+const wd177x_format::format &os9_format::get_track_format(const format &f, int head, int track) const
+{
+ int n = -1;
+
+ for (int i = 0; formats[i].form_factor; i++) {
+ if (&formats[i] == &f) {
+ n = i;
+ break;
+ }
+ }
+
+ if (n < 0) {
+ LOG_FORMATS("os9_dsk: Error format not found\n");
+ return f;
+ }
+
+ if (head >= f.head_count) {
+ LOG_FORMATS("os9_dsk: Error invalid head %d\n", head);
+ return f;
+ }
+
+ if (track >= f.track_count) {
+ LOG_FORMATS("os9_dsk: Error invalid track %d\n", track);
+ return f;
+ }
+
+ if (track == 0 && head == 0) {
+ const format &t0 = formats_track0[n];
+ if (t0.form_factor)
+ return t0;
+ }
+
+ return f;
+}
+
const os9_format::format os9_format::formats[] = {
- { // 5"25 160K double density
+ // COCO formats, with a sector base ID of one.
+
+ { // 0 157.5K 5"25 double density
floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
2000, 18, 35, 1, 256, {}, -1, {1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 16, 5, 11, 17, 6, 12, 18}, 18, 28, 20
},
- { // 5"25 160K double density
+ { // 1 315K 5"25 double density
floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
2000, 18, 35, 2, 256, {}, -1, {1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 16, 5, 11, 17, 6, 12, 18}, 18, 28, 20
},
- { // 5"25 160K double density
+ { // 2 180K 5"25 double density
floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
2000, 18, 40, 1, 256, {}, -1, {1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 16, 5, 11, 17, 6, 12, 18}, 18, 28, 20
},
- { // 5"25 160K double density
+ { // 3 360K 5"25 double density
floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
2000, 18, 40, 2, 256, {}, -1, {1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 16, 5, 11, 17, 6, 12, 18}, 18, 28, 20
},
- { // 5"25 160K double density
+ { // 4 360K 5"25 double density
floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
2000, 18, 80, 1, 256, {}, -1, {1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 16, 5, 11, 17, 6, 12, 18}, 18, 28, 20
},
- { // 5"25 160K double density
+ { // 5 720K 5"25 double density
floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
2000, 18, 80, 2, 256, {}, -1, {1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 16, 5, 11, 17, 6, 12, 18}, 18, 28, 20
},
+ { // 6 360K 3"5 double density
+ floppy_image::FF_35, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 80, 1, 256, {}, -1, {1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 16, 5, 11, 17, 6, 12, 18}, 18, 28, 20
+ },
+ { // 7 720K 3"5 double density
+ floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {1, 7, 13, 2, 8, 14, 3, 9, 15, 4, 10, 16, 5, 11, 17, 6, 12, 18}, 18, 28, 20
+ },
+
+ // Non-COCO formats, with a sector base ID of zero.
+
+ { // 8 87.5K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 1, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 9 100K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 1, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 10 200K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 1, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 11 175K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 12 200K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 13 400K 5 1/4 inch single density
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 14 138.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 35, 1, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 15 140K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 35, 1, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 16 278.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 35, 2, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 17 280K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 35, 2, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 18 158.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 40, 1, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 19 160K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 40, 1, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 20 318.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 40, 2, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 21 320K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 40, 2, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 22 318.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 80, 1, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 23 320K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 80, 1, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 24 638.5 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 80, 2, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 25 640K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 80, 2, 256, {}, -1, {0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13}, 18, 28, 20
+ },
+ { // 26 155.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 35, 1, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 27 157.5K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 35, 1, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 28 311K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 29 315K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 35, 2, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 30 178K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 40, 1, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 31 180K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 18, 40, 1, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 32 356K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 33 360K 5 1/4 inch double density
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 18, 40, 2, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 34 358K 5 1/4 inch quad density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 18, 80, 1, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 35 360K 5 1/4 inch quad density
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 18, 80, 1, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 36 716K 5 1/4 inch quad density (single density track 0)
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 37 720K 5 1/4 inch quad density
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 18, 80, 2, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 80, 22, 24
+ },
+ { // 38 288.75K 8 inch single density
+ floppy_image::FF_8, floppy_image::SSSD, floppy_image::FM,
+ 2000, 15, 77, 1, 256, {}, -1, {0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13}, 40, 12, 12
+ },
+ { // 39 577.5K 8 inch single density
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13}, 40, 12, 12
+ },
+ { // 40 308K 8 inch single density
+ floppy_image::FF_8, floppy_image::SSSD, floppy_image::FM,
+ 2000, 16, 77, 1, 256, {}, -1, {0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15}, 35, 12, 12
+ },
+ { // 41 616K 8 inch single density
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 16, 77, 2, 256, {}, -1, {0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15}, 35, 12, 12
+ },
+ { // 42 497.75K 8 inch double density (single density track 0)
+ floppy_image::FF_8, floppy_image::SSDD, floppy_image::MFM,
+ 1000, 26, 77, 1, 256, {}, -1, {0, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8, 1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7}, 80, 22, 24
+ },
+ { // 43 500.5K 8 inch double density
+ floppy_image::FF_8, floppy_image::SSDD, floppy_image::MFM,
+ 1000, 26, 77, 1, 256, {}, -1, {0, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8, 1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7}, 80, 22, 24
+ },
+ { // 44 995.5K 8 inch double density (single density track 0)
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {0, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8, 1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7}, 80, 22, 24
+ },
+ { // 45 1001K 8 inch double density
+ floppy_image::FF_8, floppy_image::DSDD, floppy_image::MFM,
+ 1000, 26, 77, 2, 256, {}, -1, {0, 19, 12, 5, 24, 17, 10, 3, 22, 15, 8, 1, 20, 13, 6, 25, 18, 11, 4, 23, 16, 9, 2, 21, 14, 7}, 80, 22, 24
+ },
+ { // 46 1440K 3 1/2 inch high density (single density track 0)
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {0, 25, 14, 3, 28, 17, 6, 31, 20, 9, 34, 23, 12, 1, 26, 15, 4, 29, 18, 7, 32, 21, 10, 35, 24, 13, 2, 27, 16, 5, 30, 19, 8, 33, 22, 11}, 80, 22, 24
+ },
+ { // 47 1440K 3 1/2 inch high density.
+ floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
+ 1000, 36, 80, 2, 256, {}, -1, {0, 25, 14, 3, 28, 17, 6, 31, 20, 9, 34, 23, 12, 1, 26, 15, 4, 29, 18, 7, 32, 21, 10, 35, 24, 13, 2, 27, 16, 5, 30, 19, 8, 33, 22, 11}, 80, 22, 24
+ },
+ {}
+};
+
+const os9_format::format os9_format::formats_track0[] = {
+ // COCO formats, with a sector base ID of one.
+
+ { // 0 157.5K 5"25 double density
+ },
+ { // 1 315K 5"25 double density
+ },
+ { // 2 180K 5"25 double density
+ },
+ { // 3 360K 5"25 double density
+ },
+ { // 4 360K 5"25 double density
+ },
+ { // 5 720K 5"25 double density
+ },
+ { // 6 360K 3"5 double density
+ },
+ { // 7 720K 3"5 double density
+ },
+
+ // Non-COCO formats, with a sector base ID of zero.
+
+ { // 8 87.5K 5 1/4 inch single density
+ },
+ { // 9 100K 5 1/4 inch single density
+ },
+ { // 10 200K 5 1/4 inch single density
+ },
+ { // 11 175K 5 1/4 inch single density
+ },
+ { // 12 200K 5 1/4 inch single density
+ },
+ { // 13 400K 5 1/4 inch single density
+ },
+ { // 14 138.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 1, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 15 140K 5 1/4 inch double density
+ },
+ { // 16 278.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 17 280K 5 1/4 inch double density
+ },
+ { // 18 158.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 1, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 19 160K 5 1/4 inch double density
+ },
+ { // 20 318.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 21 320K 5 1/4 inch double density
+ },
+ { // 22 318.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 1, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 23 320K 5 1/4 inch double density
+ },
+ { // 24 638.5 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 25 640K 5 1/4 inch double density
+ },
+ { // 26 155.5K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 1, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 27 157.5K 5 1/4 inch double density
+ },
+ { // 28 311K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 35, 2, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 29 315K 5 1/4 inch double density
+ },
+ { // 30 178K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 1, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 31 180K 5 1/4 inch double density
+ },
+ { // 32 356K 5 1/4 inch double density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 40, 2, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 33 360K 5 1/4 inch double density
+ },
+ { // 34 358K 5 1/4 inch quad density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 1, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 35 360K 5 1/4 inch quad density
+ },
+ { // 36 716K 5 1/4 inch quad density (single density track 0)
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {0, 3, 6, 9, 2, 5, 8, 1, 4, 7}, 40, 16, 11
+ },
+ { // 37 720K 5 1/4 inch quad density
+ },
+ { // 38 288.75K 8 inch single density
+ },
+ { // 39 577.5K 8 inch single density
+ },
+ { // 40 308K 8 inch single density
+ },
+ { // 41 616K 8 inch single density
+ },
+ { // 42 497.75K 8 inch double density (single density track 0)
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 1, 256, {}, -1, {0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13}, 40, 12, 12
+ },
+ { // 43 500.5K 8 inch double density
+ },
+ { // 44 995.5K 8 inch double density (single density track 0)
+ floppy_image::FF_8, floppy_image::DSSD, floppy_image::FM,
+ 2000, 15, 77, 2, 256, {}, -1, {0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13}, 40, 12, 12
+ },
+ { // 45 1001K 8 inch double density
+ },
+ { // 46 1440K 3 1/2 inch high density (single density track 0)
+ floppy_image::FF_35, floppy_image::DSSD, floppy_image::FM,
+ 2000, 18, 80, 2, 256, {}, -1, {0, 5, 10, 15, 2, 7, 12, 17, 4, 9, 14, 1, 6, 11, 16, 3, 8, 13}, 40, 12, 12
+ },
+ { // 47 1440K 3 1/2 inch high density.
+ },
{}
};
-const floppy_format_type FLOPPY_OS9_FORMAT = &floppy_image_format_creator<os9_format>;
+const os9_format FLOPPY_OS9_FORMAT;
diff --git a/src/lib/formats/os9_dsk.h b/src/lib/formats/os9_dsk.h
index 47f3521f85d..9cb22bde2e1 100644
--- a/src/lib/formats/os9_dsk.h
+++ b/src/lib/formats/os9_dsk.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:tim lindner
+// copyright-holders:tim lindner, 68bit
/*********************************************************************
formats/os9_dsk.h
@@ -18,16 +18,18 @@ class os9_format : public wd177x_format {
public:
os9_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual int find_size(io_generic *io, uint32_t form_factor) override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual const wd177x_format::format &get_track_format(const format &f, int head, int track) const override;
private:
static const format formats[];
+ static const format formats_track0[];
};
-extern const floppy_format_type FLOPPY_OS9_FORMAT;
+extern const os9_format FLOPPY_OS9_FORMAT;
#endif // MAME_FORMATS_OS9_DSK_H
diff --git a/src/lib/formats/p2000t_cas.cpp b/src/lib/formats/p2000t_cas.cpp
new file mode 100644
index 00000000000..2e84cf0fdd8
--- /dev/null
+++ b/src/lib/formats/p2000t_cas.cpp
@@ -0,0 +1,322 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+
+#include "p2000t_cas.h"
+
+#include "coretmpl.h" // BIT
+
+#include <ostream>
+
+
+// This code will reproduce the timing of a P2000 mini cassette tape.
+constexpr double P2000_CLOCK_PERIOD = 0.000084;
+constexpr double P2000_BOT_GAP = 1;
+constexpr double P2000_BOB_GAP = 0.515;
+constexpr double P2000_MARK_GAP = 0.085;
+constexpr double P2000_END_GAP = 0.155;
+constexpr double P2000_EOT_GAP = 1.8;
+constexpr int P2000_HIGH = 0x7FFFFFFF;
+constexpr int P2000_LOW = -1 * 0x7FFFFFFF;
+
+
+#define CHR(x) \
+ err = (x); \
+ if (err != cassette_image::error::SUCCESS) \
+ return err;
+
+/*
+Here's a description on how the P2000t stores data on tape.
+
+## Tape Format
+
+Each track (or side) of a cassette is divided into 40 block of information. A
+file may comprise between 1 and 40 blocks, depending on its length.
+
+## Blocks, Gaps, Marks and Records
+
+At the start of the tape is an area of clear (erased) tape, the BOT (beginning
+of tape) gap; to read pas this gap takes approximately 1 second. After this, the
+first block starts, followed directly by the second, third and so on. After the
+last block on the track comes the EOT (end of tape) gap; if all 40 blocks on the
+tracks are used, this area of erased tape has a length equivalent to 1.8 seconds
+of reading time.
+
+ | BOT | BLOCK 1 | BLOCK 2 | BLOCK ... | EOT |
+
+### BLOCK
+
+Each tape block is made up of five sections of tape:
+
+| START GAP | MARK | MARK GAP | DATA RECORD | END GAP |
+
+- Start gap: A section of erased tape separating the start of one block from the
+ end of the previous block. This takes approx. 515ms to read over.
+- Mark: Four bytes of recorded information (described below)
+- Mark Gap: A section of erased tape separating the mark from the data record;
+ length about 85ms.
+- Data Record: 1056 bytes of recorded data (described below)
+- End Gap: A section of erased tape of around 155 ms.
+
+### MARK
+
+The mark is made up of four bytes with the following bit patterns:
+
+ - preamble syncronization pattern (0xAA)
+ - 0000 0000 (0x00)
+ - 0000 0000 (0x00)
+ - postamble syncronization pattern (0xAA)
+
+The function of the synchronisation byte is to make sure the RDC clock is set
+properly.
+
+### DATA RECORD
+
+The data record contains the information which has been written onto the tape.
+It compromises five sections:
+
+| sync byte | header | data | check sum | post sync |
+
+- sync byte: Preamble synchronisation pattern 0xAA
+- header: 32 bytes which specify the contents and type of the data section. (See below)
+- data section: 1024 bytes of information.
+- checksum: 2 bytes that contain checksum
+- post sync: 0xAA
+
+### HEADER
+
+See the struct declaration below for details on what is contained.
+
+### DATA
+
+The data section consists of 1024 bytes written in serial form, least
+significant byte first.
+
+### Checksum
+
+The checksum is only calculated for the header and data section. The algorithm
+is not documented, an implementation can be found below.
+*/
+
+// Specifies the type of data stored in the tape block
+enum P2000_File_Type : uint8_t
+{
+ Basic = 'B',
+ Program = 'P',
+ Viewdata = 'V',
+ WordProcessing = 'W',
+ Other = 'O',
+};
+
+// Represents the internal code for data, differrent codes are used in different countries
+// only relevant when P2000_File_Type is Program.
+enum P2000_Data_Type : uint8_t
+{
+ German = 'D',
+ Swedish = 'S',
+ Dutch_English = 'U',
+};
+
+
+// This is the 32 byte header definition used by the P2000, it is mainly
+// here for documentation.
+struct P2000T_Header
+{
+ // Starting address in ram where data should go. This address is supplied by
+ // the application program when calling the monitor cassette routine to write
+ // the data on cassette in the first place.
+ uint16_t data_transfer_address;
+ // Total # of bytes which make up the file (can be spread over many blocks).
+ // The monitor uses this to determine how many blocks to read.
+ uint16_t file_length;
+ // # bytes in this record that are actually used. For example if this is 256
+ // only 256 bytes will be loaded in ram
+ uint16_t data_section_length;
+ // The eight character file name identifies the file to which the record
+ // belongs; it will be the same in all records making up the file. Each record
+ // except the first is considered an extension.
+ char file_name[8];
+ // Addition file extension.
+ char ext[3];
+ // This file type specifies the type of data stored.
+ P2000_File_Type file_type;
+ // Code and region information.
+ P2000_Data_Type data_code;
+ // Start address where program should start. (if type = Program)
+ uint16_t start_addr;
+ // Address in ram where the program should load (if type = Program)
+ uint16_t load_addr;
+ // Unused.
+ char reserved[8];
+ // Record number (i.e. which block)
+ uint8_t rec_nr;
+};
+
+std::ostream &operator<<(std::ostream &os, P2000T_Header const &hdr)
+{
+ return os << "File: " << std::string(hdr.file_name, 8) << '.'
+ << std::string(hdr.ext, 3) << " " << hdr.file_length;
+}
+
+static cassette_image::error p2000t_cas_identify(cassette_image *cass, cassette_image::Options *opts)
+{
+ opts->bits_per_sample = 32;
+ opts->channels = 1;
+ opts->sample_frequency = 44100;
+ return cassette_image::error::SUCCESS;
+}
+
+uint16_t rotr16a(uint16_t x, uint16_t n)
+{
+ return (x >> n) | (x << (16 - n));
+}
+
+void update_chksum(uint16_t *de, bool bit)
+{
+ // Reverse engineered from monitor.rom
+ // code is at: [0x07ac, 0x07c5]
+ uint8_t e = *de & 0xff;
+ uint8_t d = (*de >> 8) & 0xff;
+ e = e ^ (bit ? 1 : 0);
+ if (e & 0x01)
+ {
+ e = e ^ 2;
+ d = d ^ 0x40;
+ }
+ else
+ {
+ d = d ^ 0x00;
+ }
+ *de = rotr16a((d << 8) | e, 1);
+}
+
+/*
+ A transition on a clock boundary from low to high is a 1.
+ A transition on a clock boundary from high to low is a 0
+ An intermediate transition halfway between the clock boundary
+ can occur when there are consecutive 0s or 1s. See the example
+ below where the clock is marked by a |
+
+
+ 1 0 1 1 0 0
+ RDA: _|----|____|--__|----|__--|__--
+ RDC: _|-___|-___|-___|-___|-___|-___
+ ^ ^
+ |-- clock signal |-- intermediate transition.
+
+ This signal can be written by a simple algorithm where the first bit
+ is always false (transition to low, half clock). Now only one bit is needed
+ to determine what the next partial clock should look like.
+
+ This works because we are always guaranteed that a block starts with 0xAA,
+ and hence will ALWAYS find a signal like this on tape: _-- (low, high, high)
+ after a gap. This is guaranteed when the tape is moving forward as well as
+ backwards.
+*/
+cassette_image::error p2000t_put_bit(cassette_image *cass, double *time_index, bool bit)
+{
+ const int channel = 0;
+ cassette_image::error err = cassette_image::error::SUCCESS;
+ CHR(cass->put_sample(channel, *time_index, P2000_CLOCK_PERIOD, bit ? P2000_HIGH : P2000_LOW));
+ *time_index += P2000_CLOCK_PERIOD;
+
+ CHR(cass->put_sample(channel, *time_index, P2000_CLOCK_PERIOD, bit ? P2000_LOW : P2000_HIGH));
+ *time_index += P2000_CLOCK_PERIOD;
+ return err;
+}
+
+// Store byte of data, updating the checksum
+cassette_image::error p2000t_put_byte(cassette_image *cass, double *time_index, uint16_t *chksum, uint8_t byte)
+{
+ cassette_image::error err = cassette_image::error::SUCCESS;
+ for (int i = 0; i < 8 && err == cassette_image::error::SUCCESS; i++)
+ {
+ update_chksum(chksum, util::BIT(byte, i));
+ CHR(p2000t_put_bit(cass, time_index, util::BIT(byte, i)));
+ }
+ return err;
+}
+
+// Store a sequence of bytes, updating the checksum
+cassette_image::error p2000t_put_bytes(cassette_image *cass, double *time_index, uint16_t *chksum, const uint8_t *bytes, const uint16_t cByte)
+{
+ cassette_image::error err = cassette_image::error::SUCCESS;
+ for (int i = 0; i < cByte && err == cassette_image::error::SUCCESS; i++)
+ {
+ CHR(p2000t_put_byte(cass, time_index, chksum, bytes[i]));
+ }
+ return err;
+}
+
+// Insert time seconds of silence.
+cassette_image::error p2000t_silence(cassette_image *cassette,
+double *time_index,
+double time)
+{
+ auto err = cassette->put_sample(0, *time_index, time, 0);
+ *time_index += time;
+ return err;
+}
+
+static cassette_image::error p2000t_cas_load(cassette_image *cassette)
+{
+ cassette_image::error err = cassette_image::error::SUCCESS;
+ uint64_t image_size = cassette->image_size();
+ constexpr int CAS_BLOCK = 1280;
+
+ /*
+ The cas format is pretty simple. it consists of a sequence of blocks,
+ where a block consists of the following:
+
+ [0-256] P2000 memory address 0x6000 - 0x6100
+ .... Nonsense (keyboard status etc.)
+ 0x30 P200T_Header
+ 0x50
+ ... Nonsense..
+ [0-1024] Data block
+
+ This means that one block gets stored in 1280 bytes.
+ */
+ if (image_size % CAS_BLOCK != 0)
+ {
+ return cassette_image::error::INVALID_IMAGE;
+ }
+
+ uint8_t block[CAS_BLOCK];
+ constexpr uint8_t BLOCK_MARK[4] = { 0xAA, 0x00, 0x00, 0xAA };
+ auto blocks = image_size / CAS_BLOCK;
+ double time_idx = 0;
+
+ // Beginning of tape marker
+ CHR(p2000t_silence(cassette, &time_idx, P2000_BOT_GAP));
+ for (int i = 0; i < blocks; i++)
+ {
+ uint16_t crc = 0, unused = 0;
+ cassette->image_read(&block, CAS_BLOCK * i, CAS_BLOCK);
+
+ // Insert sync header.. 0xAA, 0x00, 0x00, 0xAA
+ CHR(p2000t_silence(cassette, &time_idx, P2000_BOB_GAP));
+ CHR(p2000t_put_bytes(cassette, &time_idx, &unused, BLOCK_MARK, std::size(BLOCK_MARK)));
+ CHR(p2000t_silence(cassette, &time_idx, P2000_MARK_GAP));
+
+ // Insert data block
+ CHR(p2000t_put_byte(cassette, &time_idx, &unused, 0xAA));
+ CHR(p2000t_put_bytes(cassette, &time_idx, &crc, block + 0x30, 32));
+ CHR(p2000t_put_bytes(cassette, &time_idx, &crc, block + 256, 1024));
+ CHR(p2000t_put_bytes(cassette, &time_idx, &unused, ( uint8_t * )&crc, 2));
+ CHR(p2000t_put_byte(cassette, &time_idx, &unused, 0xAA));
+
+ // Block finished.
+ CHR(p2000t_silence(cassette, &time_idx, P2000_END_GAP));
+ }
+
+ // End of tape marker
+ return p2000t_silence(cassette, &time_idx, P2000_EOT_GAP);
+}
+
+static const cassette_image::Format p2000t_cas = {
+ "cas", p2000t_cas_identify, p2000t_cas_load, nullptr /* no save */
+};
+
+CASSETTE_FORMATLIST_START(p2000t_cassette_formats)
+CASSETTE_FORMAT(p2000t_cas)
+CASSETTE_FORMATLIST_END
diff --git a/src/lib/formats/p2000t_cas.h b/src/lib/formats/p2000t_cas.h
new file mode 100644
index 00000000000..6d63e314a33
--- /dev/null
+++ b/src/lib/formats/p2000t_cas.h
@@ -0,0 +1,19 @@
+// license:BSD-3-Clause
+// copyright-holders:Erwin Jansen
+/*********************************************************************
+
+ p2000t_cas.h
+
+ Format code for P2000t .cas cassette files.
+
+*********************************************************************/
+#ifndef MAME_FORMATS_P2000T_CAS_H
+#define MAME_FORMATS_P2000T_CAS_H
+
+#pragma once
+
+#include "cassimg.h"
+
+CASSETTE_FORMATLIST_EXTERN(p2000t_cassette_formats);
+
+#endif // MAME_FORMATS_P2000T_CAS_H
diff --git a/src/lib/formats/p6001_cas.cpp b/src/lib/formats/p6001_cas.cpp
index 37e1ffcfa2c..cdb0b922680 100644
--- a/src/lib/formats/p6001_cas.cpp
+++ b/src/lib/formats/p6001_cas.cpp
@@ -4,22 +4,19 @@
* NEC PC-6001 cassette format handling
*/
-#include <assert.h>
-
#include "p6001_cas.h"
#define WAVE_HIGH 0x5a9e
#define WAVE_LOW -0x5a9e
-static int cas_size;
+static int cas_size; // FIXME: global variable prevents multiple instances
static int pc6001_fill_wave(int16_t* buffer, uint8_t data, int sample_pos)
{
- int x;
int sample_count = 0;
// one byte = 8 samples
- for(x=0;x<8;x++)
+ for(int x=0;x<8;x++)
{
if(buffer)
buffer[sample_pos+x] = ((data >> (7-x)) & 1) ? WAVE_HIGH : WAVE_LOW;
@@ -56,12 +53,12 @@ static int pc6001_cas_to_wav_size (const uint8_t *casdata, int caslen)
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int pc6001_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int pc6001_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return pc6001_handle_cas(buffer,bytes);
}
-static const struct CassetteLegacyWaveFiller pc6001_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller pc6001_legacy_fill_wave =
{
pc6001_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -72,20 +69,20 @@ static const struct CassetteLegacyWaveFiller pc6001_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error pc6001_cas_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error pc6001_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &pc6001_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &pc6001_legacy_fill_wave);
}
static cassette_image::error pc6001_cas_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &pc6001_legacy_fill_wave);
+ return cassette->legacy_construct(&pc6001_legacy_fill_wave);
}
-static const struct CassetteFormat pc6001_cassette_format = {
+static const cassette_image::Format pc6001_cassette_format = {
"cas",
pc6001_cas_identify,
pc6001_cas_load,
diff --git a/src/lib/formats/pasti_dsk.cpp b/src/lib/formats/pasti_dsk.cpp
index 3bf5fa7a243..ae82ee7939b 100644
--- a/src/lib/formats/pasti_dsk.cpp
+++ b/src/lib/formats/pasti_dsk.cpp
@@ -1,7 +1,12 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
-#include "emu.h" // logerror
#include "pasti_dsk.h"
+#include "imageutl.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <cstring>
// Pasti format supported using the documentation at
// http://www.sarnau.info/atari:pasti_file_format
@@ -17,34 +22,34 @@ pasti_format::pasti_format()
{
}
-const char *pasti_format::name() const
+const char *pasti_format::name() const noexcept
{
return "pasti";
}
-const char *pasti_format::description() const
+const char *pasti_format::description() const noexcept
{
return "Atari PASTI floppy disk image";
}
-const char *pasti_format::extensions() const
+const char *pasti_format::extensions() const noexcept
{
return "stx";
}
-bool pasti_format::supports_save() const
+bool pasti_format::supports_save() const noexcept
{
return false;
}
-int pasti_format::identify(io_generic *io, uint32_t form_factor)
+int pasti_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t h[16];
- io_generic_read(io, h, 0, 16);
+ /*auto const [err, actual] =*/ read_at(io, 0, h, 16); // FIXME: check for errors and premature EOF
if(!memcmp(h, "RSY\0\3\0", 6) &&
- (1 || (h[10] >= 80 && h[10] <= 82) || (h[10] >= 160 && h[10] <= 164)))
- return 100;
+ (1 || (h[10] >= 80 && h[10] <= 82) || (h[10] >= 160 && h[10] <= 164))) // TODO: why is this check disabled?
+ return FIFID_SIGN;
return 0;
}
@@ -59,10 +64,10 @@ static void hexdump(const uint8_t *d, int s)
}
}
-bool pasti_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool pasti_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t fh[16];
- io_generic_read(io, fh, 0, 16);
+ read_at(io, 0, fh, 16); // FIXME: check for errors and premature EOF
std::vector<uint8_t> raw_track;
@@ -77,18 +82,18 @@ bool pasti_format::load(io_generic *io, uint32_t form_factor, floppy_image *imag
for(int track=0; track < tracks; track++) {
for(int head=0; head < heads; head++) {
uint8_t th[16];
- io_generic_read(io, th, pos, 16);
- int entry_len = th[0] | (th[1] << 8) | (th[2] << 16) | (th[3] << 24);
- int fuzz_len = th[4] | (th[5] << 8) | (th[6] << 16) | (th[7] << 24);
- int sect = th[8] | (th[9] << 8);
- int flags = th[10] | (th[11] << 8);
- int track_len = th[12] | (th[13] << 8);
+ read_at(io, pos, th, 16); // FIXME: check for errors and premature EOF
+ int entry_len = get_u32le(&th[0]);
+ int fuzz_len = get_u32le(&th[4]);
+ int sect = get_u16le(&th[8]);
+ int flags = get_u16le(&th[10]);
+ int track_len = get_u16le(&th[12]);
int track_num = th[14];
int flags2 = th[15];
raw_track.resize(entry_len-16);
- io_generic_read(io, &raw_track[0], pos+16, entry_len-16);
+ read_at(io, pos+16, &raw_track[0], entry_len-16); // FIXME: check for errors and premature EOF
uint8_t *fuzz = fuzz_len ? &raw_track[16*sect] : nullptr;
uint8_t *bdata = fuzz ? fuzz+fuzz_len : &raw_track[16*sect];
@@ -96,13 +101,13 @@ bool pasti_format::load(io_generic *io, uint32_t form_factor, floppy_image *imag
int syncpos = -1;
if(flags & 0x0080) {
- syncpos = tdata[0] | (tdata[1] << 8);
+ syncpos = get_u16le(tdata);
tdata += 2;
}
int tsize = 0;
if(flags & 0x0040) {
- tsize = tdata[0] | (tdata[1] << 8);
+ tsize = get_u16le(tdata);
tdata += 2;
} else
tdata = nullptr;
@@ -127,10 +132,10 @@ bool pasti_format::load(io_generic *io, uint32_t form_factor, floppy_image *imag
for(int s=0; s<sect; s++) {
uint8_t *sh = &raw_track[16*s];
- int s_off = sh[0] | (sh[1] << 8) | (sh[2] << 16) | (sh[3] << 24);
- int s_pos = sh[4] | (sh[5] << 8);
- int s_time = sh[6] | (sh[7] << 8);
- int s_flags = sh[14] | (sh[15] << 8);
+ int s_off = get_u32le(&sh[0]);
+ int s_pos = get_u16le(&sh[4]);
+ int s_time = get_u16le(&sh[6]);
+ int s_flags = get_u16le(&sh[14]);
obs.sectors[s].data = bdata + s_off;
obs.sectors[s].fuzzy_mask = nullptr;
@@ -155,14 +160,14 @@ bool pasti_format::load(io_generic *io, uint32_t form_factor, floppy_image *imag
}
}
- image->set_variant(floppy_image::DSDD);
+ image.set_form_variant(floppy_image::FF_35, heads > 1 ? floppy_image::DSDD : floppy_image::SSDD);
return true;
}
-const floppy_format_type FLOPPY_PASTI_FORMAT = &floppy_image_format_creator<pasti_format>;
+const pasti_format FLOPPY_PASTI_FORMAT;
-void pasti_format::wd_generate_track_from_observations(int track, int head, floppy_image *image, wd_obs &obs)
+void pasti_format::wd_generate_track_from_observations(int track, int head, floppy_image &image, const wd_obs &obs)
{
if(!obs.track_data)
wd_generate_track_from_sectors_only(track, head, image, obs);
@@ -267,7 +272,7 @@ void pasti_format::wd_generate_sector_data(std::vector<uint32_t> &track, const w
mfm_w(track, 8, crc, cell_size);
}
-void pasti_format::wd_generate_track_from_sectors_and_track(int track, int head, floppy_image *image, wd_obs &obs)
+void pasti_format::wd_generate_track_from_sectors_and_track(int track, int head, floppy_image &image, const wd_obs &obs)
{
if(0)
printf("Track %d head %d sectors %d\n", track, head, obs.sector_count);
@@ -344,7 +349,7 @@ void pasti_format::wd_generate_track_from_sectors_and_track(int track, int head,
generate_track_from_levels(track, head, trackbuf, 0, image);
}
-void pasti_format::wd_generate_track_from_sectors_only(int track, int head, floppy_image *image, wd_obs &obs)
+void pasti_format::wd_generate_track_from_sectors_only(int track, int head, floppy_image &image, const wd_obs &obs)
{
if(0) {
printf("Track %d head %d sectors %d\n", track, head, obs.sector_count);
@@ -429,7 +434,7 @@ uint16_t pasti_format::byte_to_mfm(uint8_t data, bool context)
return (expand[(data >> 4) | (context ? 16 : 0)] << 8) | expand[data & 0x1f];
}
-void pasti_format::match_mfm_data(wd_obs &obs, int tpos, const uint8_t *data, int size, uint8_t context, int &bcount, int &tend, bool &synced)
+void pasti_format::match_mfm_data(const wd_obs &obs, int tpos, const uint8_t *data, int size, uint8_t context, int &bcount, int &tend, bool &synced)
{
uint16_t shift = byte_to_mfm(context, true);
int bc = 0;
@@ -474,7 +479,7 @@ void pasti_format::match_mfm_data(wd_obs &obs, int tpos, const uint8_t *data, in
}
}
-void pasti_format::match_raw_data(wd_obs &obs, int tpos, const uint8_t *data, int size, uint8_t context, int &bcount, int &tend)
+void pasti_format::match_raw_data(const wd_obs &obs, int tpos, const uint8_t *data, int size, uint8_t context, int &bcount, int &tend)
{
tend = tpos;
for(bcount=0; bcount != size; bcount++) {
@@ -503,7 +508,7 @@ uint16_t pasti_format::calc_crc(const uint8_t *data, int size, uint16_t crc1)
return crc;
}
-void pasti_format::map_sectors_in_track(wd_obs &obs, wd_sect_info *sect_infos)
+void pasti_format::map_sectors_in_track(const wd_obs &obs, wd_sect_info *sect_infos)
{
for(int i=0; i != obs.sector_count; i++) {
sect_infos[i].hstart = -1;
diff --git a/src/lib/formats/pasti_dsk.h b/src/lib/formats/pasti_dsk.h
index a98baffcb90..5ca54e610ae 100644
--- a/src/lib/formats/pasti_dsk.h
+++ b/src/lib/formats/pasti_dsk.h
@@ -12,13 +12,13 @@ class pasti_format : public floppy_image_format_t
public:
pasti_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
static const desc_e xdesc[];
@@ -37,7 +37,7 @@ protected:
int track_size, sector_count;
};
- void wd_generate_track_from_observations(int track, int head, floppy_image *image, wd_obs &obs);
+ static void wd_generate_track_from_observations(int track, int head, floppy_image &image, const wd_obs &obs);
private:
struct wd_sect_info {
@@ -46,21 +46,21 @@ private:
bool hsynced, dsynced;
};
- void map_sectors_in_track(wd_obs &obs, wd_sect_info *sect_infos);
- void match_mfm_data(wd_obs &obs, int tpos, const uint8_t *data, int size, uint8_t context, int &bcount, int &ccount, bool &synced);
- void match_raw_data(wd_obs &obs, int tpos, const uint8_t *data, int size, uint8_t context, int &bcount, int &ccount);
- uint16_t byte_to_mfm(uint8_t data, bool context);
- uint16_t calc_crc(const uint8_t *data, int size, uint16_t crc);
+ static void map_sectors_in_track(const wd_obs &obs, wd_sect_info *sect_infos);
+ static void match_mfm_data(const wd_obs &obs, int tpos, const uint8_t *data, int size, uint8_t context, int &bcount, int &ccount, bool &synced);
+ static void match_raw_data(const wd_obs &obs, int tpos, const uint8_t *data, int size, uint8_t context, int &bcount, int &ccount);
+ static uint16_t byte_to_mfm(uint8_t data, bool context);
+ static uint16_t calc_crc(const uint8_t *data, int size, uint16_t crc);
- void wd_generate_unsynced_gap(std::vector<uint32_t> &track, const wd_obs &obs, int tstart, int tend, uint32_t cell_size);
- void wd_generate_synced_gap(std::vector<uint32_t> &track, const wd_obs &obs, int tstart, int tend, uint32_t cell_size);
- void wd_generate_gap(std::vector<uint32_t> &track, const wd_obs &obs, int tstart, int tend, bool synced, uint32_t cell_size_start, uint32_t cell_size_end);
- void wd_generate_sector_header(std::vector<uint32_t> &track, const wd_obs &obs, int sector, int tstart, uint32_t cell_size);
- void wd_generate_sector_data(std::vector<uint32_t> &track, const wd_obs &obs, int sector, int tstart, uint32_t cell_size);
- void wd_generate_track_from_sectors_and_track(int track, int head, floppy_image *image, wd_obs &obs);
- void wd_generate_track_from_sectors_only(int track, int head, floppy_image *image, wd_obs &obs);
+ static void wd_generate_unsynced_gap(std::vector<uint32_t> &track, const wd_obs &obs, int tstart, int tend, uint32_t cell_size);
+ static void wd_generate_synced_gap(std::vector<uint32_t> &track, const wd_obs &obs, int tstart, int tend, uint32_t cell_size);
+ static void wd_generate_gap(std::vector<uint32_t> &track, const wd_obs &obs, int tstart, int tend, bool synced, uint32_t cell_size_start, uint32_t cell_size_end);
+ static void wd_generate_sector_header(std::vector<uint32_t> &track, const wd_obs &obs, int sector, int tstart, uint32_t cell_size);
+ static void wd_generate_sector_data(std::vector<uint32_t> &track, const wd_obs &obs, int sector, int tstart, uint32_t cell_size);
+ static void wd_generate_track_from_sectors_and_track(int track, int head, floppy_image &image, const wd_obs &obs);
+ static void wd_generate_track_from_sectors_only(int track, int head, floppy_image &image, const wd_obs &obs);
};
-extern const floppy_format_type FLOPPY_PASTI_FORMAT;
+extern const pasti_format FLOPPY_PASTI_FORMAT;
#endif // MAME_FORMATS_PASTI_DSK_H
diff --git a/src/lib/formats/pc98_dsk.cpp b/src/lib/formats/pc98_dsk.cpp
index 3d85a4380dd..36dac83e2af 100644
--- a/src/lib/formats/pc98_dsk.cpp
+++ b/src/lib/formats/pc98_dsk.cpp
@@ -2,17 +2,16 @@
// copyright-holders:Angelo Salese
/*********************************************************************
- formats/pc98_dsk.c
+ formats/pc98_dsk.cpp
PC-98 disk images
*********************************************************************/
-#include <string.h>
-#include <assert.h>
-
#include "formats/pc98_dsk.h"
+#include <cstring>
+
/* ----------------------------------------------------------------------- */
@@ -20,17 +19,17 @@ pc98_format::pc98_format() : upd765_format(formats)
{
}
-const char *pc98_format::name() const
+const char *pc98_format::name() const noexcept
{
return "pc98";
}
-const char *pc98_format::description() const
+const char *pc98_format::description() const noexcept
{
return "PC-98 floppy disk image";
}
-const char *pc98_format::extensions() const
+const char *pc98_format::extensions() const noexcept
{
return "dsk,ima,img,ufi,360,hdm";
}
@@ -84,8 +83,12 @@ const pc98_format::format pc98_format::formats[] = {
floppy_image::FF_525, floppy_image::DSHD, floppy_image::MFM,
1200, 8, 77, 2, 1024, {}, 1, {}, 80, 50, 22, 84
},
+ { /* 1MB 5 1/4 inch 256bps n88 basic disk type */
+ floppy_image::FF_525, floppy_image::DSHD, floppy_image::MFM,
+ 1200, 26, 77, 2, 256, {}, 1, {}, 80, 50, 21, 77
+ },
{}
};
-const floppy_format_type FLOPPY_PC98_FORMAT = &floppy_image_format_creator<pc98_format>;
+const pc98_format FLOPPY_PC98_FORMAT;
diff --git a/src/lib/formats/pc98_dsk.h b/src/lib/formats/pc98_dsk.h
index f6c0fa2dc95..5600dc05277 100644
--- a/src/lib/formats/pc98_dsk.h
+++ b/src/lib/formats/pc98_dsk.h
@@ -23,14 +23,14 @@ class pc98_format : public upd765_format
public:
pc98_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_PC98_FORMAT;
+extern const pc98_format FLOPPY_PC98_FORMAT;
#endif // MAME_FORMATS_PC98_DSK_H
diff --git a/src/lib/formats/pc98fdi_dsk.cpp b/src/lib/formats/pc98fdi_dsk.cpp
index 5b37b1ede37..10822ab316d 100644
--- a/src/lib/formats/pc98fdi_dsk.cpp
+++ b/src/lib/formats/pc98fdi_dsk.cpp
@@ -2,75 +2,90 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/pc98fdi_dsk.h
+ formats/pc98fdi_dsk.cpp
PC98FDI disk images
*********************************************************************/
-#include <assert.h>
#include "pc98fdi_dsk.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <tuple>
+
+
pc98fdi_format::pc98fdi_format()
{
}
-const char *pc98fdi_format::name() const
+const char *pc98fdi_format::name() const noexcept
{
return "pc98_fdi";
}
-const char *pc98fdi_format::description() const
+const char *pc98fdi_format::description() const noexcept
{
return "PC98 FDI disk image";
}
-const char *pc98fdi_format::extensions() const
+const char *pc98fdi_format::extensions() const noexcept
{
return "fdi";
}
-int pc98fdi_format::identify(io_generic *io, uint32_t form_factor)
+int pc98fdi_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
- uint8_t h[32];
+ uint64_t size;
+ if(io.length(size))
+ return 0;
- io_generic_read(io, h, 0, 32);
- uint32_t hsize = little_endianize_int32(*(uint32_t *) (h + 0x8));
- uint32_t psize = little_endianize_int32(*(uint32_t *) (h + 0xc));
- uint32_t ssize = little_endianize_int32(*(uint32_t *) (h + 0x10));
- uint32_t scnt = little_endianize_int32(*(uint32_t *) (h + 0x14));
- uint32_t sides = little_endianize_int32(*(uint32_t *) (h + 0x18));
- uint32_t ntrk = little_endianize_int32(*(uint32_t *) (h + 0x1c));
+ uint8_t h[32];
+ auto const [err, actual] = read_at(io, 0, h, 32);
+ if(err || (32 != actual))
+ return 0;
+
+ uint32_t const hsize = get_u32le(h + 0x8);
+ uint32_t const psize = get_u32le(h + 0xc);
+ uint32_t const ssize = get_u32le(h + 0x10);
+ uint32_t const scnt = get_u32le(h + 0x14);
+ uint32_t const sides = get_u32le(h + 0x18);
+ uint32_t const ntrk = get_u32le(h + 0x1c);
if(size == hsize + psize && psize == ssize*scnt*sides*ntrk)
- return 100;
+ return FIFID_STRUCT;
return 0;
}
-bool pc98fdi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool pc98fdi_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint8_t h[32];
+ std::error_condition err;
+ size_t actual;
- io_generic_read(io, h, 0, 32);
+ uint8_t h[32];
+ std::tie(err, actual) = read_at(io, 0, h, 32);
+ if(err || (32 != actual))
+ return false;
- uint32_t hsize = little_endianize_int32(*(uint32_t *)(h+0x8));
- uint32_t sector_size = little_endianize_int32(*(uint32_t *)(h+0x10));
- uint32_t sector_count = little_endianize_int32(*(uint32_t *)(h+0x14));
- uint32_t head_count = little_endianize_int32(*(uint32_t *)(h+0x18));
- uint32_t track_count = little_endianize_int32(*(uint32_t *)(h+0x1c));
+ uint32_t const hsize = get_u32le(h + 0x8);
+ uint32_t const sector_size = get_u32le(h + 0x10);
+ uint32_t const sector_count = get_u32le(h + 0x14);
+ uint32_t const head_count = get_u32le(h + 0x18);
+ uint32_t const track_count = get_u32le(h + 0x1c);
- int cell_count = form_factor == floppy_image::FF_35 ? 200000 : 166666;
+ int const cell_count = form_factor == floppy_image::FF_35 ? 200000 : 166666;
- int ssize;
- for(ssize=0; (128 << ssize) < sector_size; ssize++) {};
+ int ssize = 0;
+ while ((128 << ssize) < sector_size)
+ ssize++;
desc_pc_sector sects[256];
uint8_t sect_data[65536];
- for(int track=0; track < track_count; track++)
+ for(int track=0; track < track_count; track++) {
for(int head=0; head < head_count; head++) {
- io_generic_read(io, sect_data, hsize + sector_size*sector_count*(track*head_count + head), sector_size*sector_count);
+ std::tie(err, actual) = read_at(io, hsize + sector_size*sector_count*(track*head_count + head), sect_data, sector_size*sector_count); // FIXME: check for errors and premature EOF
for(int i=0; i<sector_count; i++) {
sects[i].track = track;
@@ -85,13 +100,14 @@ bool pc98fdi_format::load(io_generic *io, uint32_t form_factor, floppy_image *im
build_pc_track_mfm(track, head, image, cell_count, sector_count, sects, calc_default_pc_gap3_size(form_factor, sector_size));
}
+ }
return true;
}
-bool pc98fdi_format::supports_save() const
+bool pc98fdi_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_PC98FDI_FORMAT = &floppy_image_format_creator<pc98fdi_format>;
+const pc98fdi_format FLOPPY_PC98FDI_FORMAT;
diff --git a/src/lib/formats/pc98fdi_dsk.h b/src/lib/formats/pc98fdi_dsk.h
index 18c2d9edc7d..3b279edb394 100644
--- a/src/lib/formats/pc98fdi_dsk.h
+++ b/src/lib/formats/pc98fdi_dsk.h
@@ -20,15 +20,15 @@ class pc98fdi_format : public floppy_image_format_t
public:
pc98fdi_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_PC98FDI_FORMAT;
+extern const pc98fdi_format FLOPPY_PC98FDI_FORMAT;
#endif // MAME_FORMATS_PC98FDI_DSK_H
diff --git a/src/lib/formats/pc_dsk.cpp b/src/lib/formats/pc_dsk.cpp
index e4d7e02f47d..96d7662e5fb 100644
--- a/src/lib/formats/pc_dsk.cpp
+++ b/src/lib/formats/pc_dsk.cpp
@@ -2,180 +2,38 @@
// copyright-holders:Nathan Woods
/*********************************************************************
- formats/pc_dsk.c
+ formats/pc_dsk.cpp
PC disk images
*********************************************************************/
-#include <string.h>
-#include <assert.h>
-
#include "formats/pc_dsk.h"
-#include "formats/basicdsk.h"
-
-struct pc_disk_sizes
-{
- uint32_t 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 */
- {21*2*80*512, 21, 2}, /* 3 1/2 inch high density DMF */
- {36*2*80*512, 36, 2} /* 3 1/2 inch enhanced density */
-};
-
-
-
-static floperr_t pc_dsk_compute_geometry(floppy_image_legacy *floppy, struct basicdsk_geometry *geometry)
-{
- int i;
- uint64_t 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_t 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_t) 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 = params->lookup_int(PARAM_HEADS);
- geometry.tracks = params->lookup_int(PARAM_TRACKS);
- geometry.sectors = params->lookup_int(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);
-}
+#include "ioprocs.h"
+#include <cstring>
-/* ----------------------------------------------------------------------- */
-
-LEGACY_FLOPPY_OPTIONS_START( pc )
- LEGACY_FLOPPY_OPTION( pc_dsk, "dsk,ima,img,ufi,360", "PC floppy disk image", pc_dsk_identify, pc_dsk_construct, nullptr,
- HEADS([1]-2)
- TRACKS(40/[80])
- SECTORS(8/[9]/10/15/18/36))
-LEGACY_FLOPPY_OPTIONS_END
pc_format::pc_format() : upd765_format(formats)
{
}
-const char *pc_format::name() const
+const char *pc_format::name() const noexcept
{
return "pc";
}
-const char *pc_format::description() const
+const char *pc_format::description() const noexcept
{
return "PC floppy disk image";
}
-const char *pc_format::extensions() const
+const char *pc_format::extensions() const noexcept
{
return "dsk,ima,img,ufi,360";
}
-int pc_format::identify(io_generic *io, uint32_t form_factor)
-{
- uint64_t size = io_generic_size(io);
-
- /* some 360K images have a 512-byte header */
- if (size == 368640 + 0x200) {
- file_header_skip_bytes = 0x200;
- }
-
- /* some 1.44MB images have a 1024-byte footer */
- if (size == 1474560 + 0x400) {
- file_footer_skip_bytes = 0x400;
- }
-
- return upd765_format::identify(io, form_factor);
-}
-
const pc_format::format pc_format::formats[] = {
{ /* 160K 5 1/4 inch double density single sided */
floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
@@ -236,4 +94,4 @@ const pc_format::format pc_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_PC_FORMAT = &floppy_image_format_creator<pc_format>;
+const pc_format FLOPPY_PC_FORMAT;
diff --git a/src/lib/formats/pc_dsk.h b/src/lib/formats/pc_dsk.h
index b4080e1dcc7..94c23238e9b 100644
--- a/src/lib/formats/pc_dsk.h
+++ b/src/lib/formats/pc_dsk.h
@@ -15,26 +15,19 @@
#include "flopimg.h"
#include "upd765_dsk.h"
-/**************************************************************************/
-
-LEGACY_FLOPPY_OPTIONS_EXTERN(pc);
-
-
class pc_format : public upd765_format
{
public:
pc_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
-
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_PC_FORMAT;
+extern const pc_format FLOPPY_PC_FORMAT;
#endif // MAME_FORMATS_PC_DSK_H
diff --git a/src/lib/formats/phc25_cas.cpp b/src/lib/formats/phc25_cas.cpp
index 2cc9ea013e6..e6dab7e88e1 100644
--- a/src/lib/formats/phc25_cas.cpp
+++ b/src/lib/formats/phc25_cas.cpp
@@ -29,8 +29,6 @@ enough to make it work.
********************************************************************/
-#include <assert.h>
-
#include "phc25_cas.h"
#define WAVEENTRY_LOW -32768
@@ -40,7 +38,7 @@ enough to make it work.
#define PHC25_HEADER_BYTES 16
// image size
-static int phc25_image_size;
+static int phc25_image_size; // FIXME: global variable prevents multiple instances
static int phc25_put_samples(int16_t *buffer, int sample_pos, int count, int level)
{
@@ -131,7 +129,7 @@ static int phc25_handle_cassette(int16_t *buffer, const uint8_t *bytes)
Generate samples for the tape image
********************************************************************/
-static int phc25_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int phc25_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return phc25_handle_cassette(buffer, bytes);
}
@@ -147,7 +145,7 @@ static int phc25_cassette_calculate_size_in_samples(const uint8_t *bytes, int le
return phc25_handle_cassette(nullptr, bytes);
}
-static const struct CassetteLegacyWaveFiller phc25_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller phc25_legacy_fill_wave =
{
phc25_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -158,17 +156,17 @@ static const struct CassetteLegacyWaveFiller phc25_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error phc25_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error phc25_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &phc25_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &phc25_legacy_fill_wave);
}
static cassette_image::error phc25_cassette_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &phc25_legacy_fill_wave);
+ return cassette->legacy_construct(&phc25_legacy_fill_wave);
}
-static const struct CassetteFormat phc25_cassette_image_format =
+static const cassette_image::Format phc25_cassette_image_format =
{
"phc",
phc25_cassette_identify,
diff --git a/src/lib/formats/pk8020_dsk.cpp b/src/lib/formats/pk8020_dsk.cpp
index e21fdbdb98e..c5250949829 100644
--- a/src/lib/formats/pk8020_dsk.cpp
+++ b/src/lib/formats/pk8020_dsk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -17,17 +17,17 @@ pk8020_format::pk8020_format() : wd177x_format(formats)
{
}
-const char *pk8020_format::name() const
+const char *pk8020_format::name() const noexcept
{
return "pk8020";
}
-const char *pk8020_format::description() const
+const char *pk8020_format::description() const noexcept
{
return "PK-8020 disk image";
}
-const char *pk8020_format::extensions() const
+const char *pk8020_format::extensions() const noexcept
{
return "kdi";
}
@@ -41,4 +41,4 @@ const pk8020_format::format pk8020_format::formats[] =
{}
};
-const floppy_format_type FLOPPY_PK8020_FORMAT = &floppy_image_format_creator<pk8020_format>;
+const pk8020_format FLOPPY_PK8020_FORMAT;
diff --git a/src/lib/formats/pk8020_dsk.h b/src/lib/formats/pk8020_dsk.h
index f6a66a4f797..dcbd71ff308 100644
--- a/src/lib/formats/pk8020_dsk.h
+++ b/src/lib/formats/pk8020_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -19,14 +19,14 @@ class pk8020_format : public wd177x_format
public:
pk8020_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_PK8020_FORMAT;
+extern const pk8020_format FLOPPY_PK8020_FORMAT;
#endif // MAME_FORMATS_PK8020_DSK_H
diff --git a/src/lib/formats/pmd_cas.cpp b/src/lib/formats/pmd_cas.cpp
index 79e8820b4bb..224a3b0af5b 100644
--- a/src/lib/formats/pmd_cas.cpp
+++ b/src/lib/formats/pmd_cas.cpp
@@ -10,10 +10,10 @@
********************************************************************/
-#include <assert.h>
-
#include "pmd_cas.h"
+#include "multibyte.h"
+
#define WAVEENTRY_LOW -32768
#define WAVEENTRY_HIGH 32767
@@ -26,7 +26,7 @@
#define PMD85_BITS_PER_BYTE 11
// image size
-static int pmd85_image_size;
+static int pmd85_image_size; // FIXME: global variable prevents multiple instances
static int pmd85_emit_level(int16_t *buffer, int sample_pos, int count, int level)
{
@@ -91,7 +91,7 @@ static void pmd85_printf_image_info(const uint8_t *bytes, int sample_count)
#if 0
char track_name[9];
uint32_t sec = (uint32_t)(sample_count/PMD85_WAV_FREQUENCY);
- uint16_t addr = (bytes[0x33]<<8) | bytes[0x32];
+ uint16_t addr = get_u16le(&bytes[0x32]);
strncpy(track_name, (char*)&bytes[0x36], 8);
track_name[8] = '\0';
@@ -137,7 +137,7 @@ static int pmd85_handle_cassette(int16_t *buffer, const uint8_t *bytes)
int data_pos = 0;
while (data_pos < pmd85_image_size)
{
- uint16_t block_size = (bytes[data_pos + 1]<<8) | bytes[data_pos];
+ uint16_t block_size = get_u16le(&bytes[data_pos]);
int pause_len = PMD85_PAUSE_BITS;
data_pos += 2;
@@ -168,7 +168,7 @@ static int pmd85_handle_cassette(int16_t *buffer, const uint8_t *bytes)
Generate samples for the tape image
********************************************************************/
-static int pmd85_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int pmd85_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return pmd85_handle_cassette(buffer, bytes);
}
@@ -184,7 +184,7 @@ static int pmd85_cassette_calculate_size_in_samples(const uint8_t *bytes, int le
return pmd85_handle_cassette(nullptr, bytes);
}
-static const struct CassetteLegacyWaveFiller pmd85_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller pmd85_legacy_fill_wave =
{
pmd85_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -195,17 +195,17 @@ static const struct CassetteLegacyWaveFiller pmd85_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error pmd85_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error pmd85_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &pmd85_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &pmd85_legacy_fill_wave);
}
static cassette_image::error pmd85_cassette_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &pmd85_legacy_fill_wave);
+ return cassette->legacy_construct(&pmd85_legacy_fill_wave);
}
-static const struct CassetteFormat pmd85_cassette_image_format =
+static const cassette_image::Format pmd85_cassette_image_format =
{
"pmd,tap,ptp",
pmd85_cassette_identify,
diff --git a/src/lib/formats/poly_dsk.cpp b/src/lib/formats/poly_dsk.cpp
index cf746b78624..8b8f48780f8 100644
--- a/src/lib/formats/poly_dsk.cpp
+++ b/src/lib/formats/poly_dsk.cpp
@@ -10,54 +10,63 @@
#include "poly_dsk.h"
+#include "ioprocs.h"
+
+#include <cstring>
+
+
poly_cpm_format::poly_cpm_format()
{
}
-const char *poly_cpm_format::name() const
+const char *poly_cpm_format::name() const noexcept
{
return "cpm";
}
-const char *poly_cpm_format::description() const
+const char *poly_cpm_format::description() const noexcept
{
return "Poly CP/M disk image";
}
-const char *poly_cpm_format::extensions() const
+const char *poly_cpm_format::extensions() const noexcept
{
return "cpm";
}
-bool poly_cpm_format::supports_save() const
+bool poly_cpm_format::supports_save() const noexcept
{
return true;
}
-int poly_cpm_format::identify(io_generic *io, uint32_t form_factor)
+int poly_cpm_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint8_t boot[16];
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return 0;
// check for valid sizes
if (size == 630784 || size == 622592 || size == 256256)
{
// check for Poly CP/M boot sector
- io_generic_read(io, boot, 0, 16);
- if (memcmp(boot, "\x86\xc3\xb7\x00\x00\x8e\x10\xc0\xbf\x00\x01\xbf\xe0\x60\x00\x00", 16) == 0)
+ uint8_t boot[16];
+ auto const [err, actual] = read_at(io, 0, boot, 16);
+ if (!err && (16 == actual) && !memcmp(boot, "\x86\xc3\xb7\x00\x00\x8e\x10\xc0\xbf\x00\x01\xbf\xe0\x60\x00\x00", 16))
{
- return 100;
+ return FIFID_SIZE|FIFID_SIGN;
}
}
+
return 0;
}
-bool poly_cpm_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool poly_cpm_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- int total_tracks, spt, bps, head_num;
-
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size) || io.seek(0, SEEK_SET))
+ return false;
+ int total_tracks, spt, bps, head_num;
switch (size)
{
case 622592:
@@ -83,8 +92,7 @@ bool poly_cpm_format::load(io_generic *io, uint32_t form_factor, floppy_image *i
break;
}
- int cell_count = (form_factor == floppy_image::FF_525) ? 50000 : 100000;
- int offset = 0;
+ int const cell_count = (form_factor == floppy_image::FF_525) ? 50000 : 100000;
for (int track = 0; track < total_tracks; track++)
for (int head = 0; head < head_num; head++)
@@ -105,8 +113,7 @@ bool poly_cpm_format::load(io_generic *io, uint32_t form_factor, floppy_image *i
sects[i].deleted = false;
sects[i].bad_crc = false;
sects[i].data = &sect_data[sdatapos];
- io_generic_read(io, sects[i].data, offset, bps);
- offset += bps;
+ /*auto const [err, actual] =*/ read(io, sects[i].data, bps); // FIXME: check for errors and premature EOF
sdatapos += bps;
}
// gap sizes unverified
@@ -115,4 +122,4 @@ bool poly_cpm_format::load(io_generic *io, uint32_t form_factor, floppy_image *i
return true;
}
-const floppy_format_type FLOPPY_POLY_CPM_FORMAT = &floppy_image_format_creator<poly_cpm_format>;
+const poly_cpm_format FLOPPY_POLY_CPM_FORMAT;
diff --git a/src/lib/formats/poly_dsk.h b/src/lib/formats/poly_dsk.h
index 65b69c69270..736ad1cd2ab 100644
--- a/src/lib/formats/poly_dsk.h
+++ b/src/lib/formats/poly_dsk.h
@@ -17,14 +17,14 @@ class poly_cpm_format : public floppy_image_format_t
public:
poly_cpm_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_POLY_CPM_FORMAT;
+extern const poly_cpm_format FLOPPY_POLY_CPM_FORMAT;
#endif // MAME_FORMATS_POLY_DSK_H
diff --git a/src/lib/formats/ppg_dsk.cpp b/src/lib/formats/ppg_dsk.cpp
index dd08a841e00..aa0c9fcb487 100644
--- a/src/lib/formats/ppg_dsk.cpp
+++ b/src/lib/formats/ppg_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Sergey Svishchev
/*********************************************************************
- formats/ppg_dsk.c
+ formats/ppg_dsk.cpp
PPG Waveterm formats
*********************************************************************/
-#include <assert.h>
-
#include "formats/ppg_dsk.h"
ppg_format::ppg_format() : wd177x_format(formats)
{
}
-const char *ppg_format::name() const
+const char *ppg_format::name() const noexcept
{
return "ppg";
}
-const char *ppg_format::description() const
+const char *ppg_format::description() const noexcept
{
return "PPG Waveterm disk image";
}
-const char *ppg_format::extensions() const
+const char *ppg_format::extensions() const noexcept
{
return "wta";
}
@@ -64,4 +62,4 @@ void ppg_format::build_sector_description(const format &f, uint8_t *sectdata, de
}
}
-const floppy_format_type FLOPPY_PPG_FORMAT = &floppy_image_format_creator<ppg_format>;
+const ppg_format FLOPPY_PPG_FORMAT;
diff --git a/src/lib/formats/ppg_dsk.h b/src/lib/formats/ppg_dsk.h
index 2b610133e07..25e010df04d 100644
--- a/src/lib/formats/ppg_dsk.h
+++ b/src/lib/formats/ppg_dsk.h
@@ -19,9 +19,9 @@ class ppg_format : public wd177x_format
public:
ppg_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
@@ -29,6 +29,6 @@ private:
virtual void build_sector_description(const format &d, uint8_t *sectdata, desc_s *sectors, int track, int head) const override;
};
-extern const floppy_format_type FLOPPY_PPG_FORMAT;
+extern const ppg_format FLOPPY_PPG_FORMAT;
#endif // MAME_FORMATS_PPG_DSK_H
diff --git a/src/lib/formats/primoptp.cpp b/src/lib/formats/primoptp.cpp
index 5c6779b6613..81eaf65b65a 100644
--- a/src/lib/formats/primoptp.cpp
+++ b/src/lib/formats/primoptp.cpp
@@ -2,10 +2,8 @@
// copyright-holders:Krzysztof Strzecha
/* .PTP Microkey Primo tape images */
-
-#include <assert.h>
-
#include "primoptp.h"
+#include "imageutl.h"
#define PRIMO_WAVEENTRY_LOW -32768
@@ -30,9 +28,7 @@ static uint32_t primo_tape_image_length;
static int16_t *primo_emit_level(int16_t *p, int count, int level)
{
- int i;
-
- for (i=0; i<count; i++) *(p++) = level;
+ for (int i=0; i<count; i++) *(p++) = level;
return p;
}
@@ -142,12 +138,12 @@ static int primo_cassette_calculate_size_in_samples(const uint8_t *bytes, int le
return size_in_samples;
}
-static int primo_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int primo_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
int i = 0, j = 0, k;
int16_t *p = buffer;
- uint8_t *b = bytes;
+ const uint8_t *b = bytes;
uint32_t file_size = 0;
uint16_t block_size = 0;
@@ -215,7 +211,7 @@ static int primo_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
return p - buffer;
}
-static const struct CassetteLegacyWaveFiller primo_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller primo_legacy_fill_wave =
{
primo_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -226,17 +222,17 @@ static const struct CassetteLegacyWaveFiller primo_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error primo_ptp_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error primo_ptp_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &primo_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &primo_legacy_fill_wave);
}
static cassette_image::error primo_ptp_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &primo_legacy_fill_wave);
+ return cassette->legacy_construct(&primo_legacy_fill_wave);
}
-static const struct CassetteFormat primo_ptp_image_format =
+static const cassette_image::Format primo_ptp_image_format =
{
"ptp",
primo_ptp_identify,
diff --git a/src/lib/formats/pyldin_dsk.cpp b/src/lib/formats/pyldin_dsk.cpp
index de43ab1947b..56fb52ed620 100644
--- a/src/lib/formats/pyldin_dsk.cpp
+++ b/src/lib/formats/pyldin_dsk.cpp
@@ -2,30 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/pyldin_dsk.c
+ formats/pyldin_dsk.cpp
pyldin format
*********************************************************************/
-#include <assert.h>
#include "formats/pyldin_dsk.h"
pyldin_format::pyldin_format() : upd765_format(formats)
{
}
-const char *pyldin_format::name() const
+const char *pyldin_format::name() const noexcept
{
return "pyldin";
}
-const char *pyldin_format::description() const
+const char *pyldin_format::description() const noexcept
{
return "PYLDIN disk image";
}
-const char *pyldin_format::extensions() const
+const char *pyldin_format::extensions() const noexcept
{
return "img";
}
@@ -44,4 +43,4 @@ const pyldin_format::format pyldin_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_PYLDIN_FORMAT = &floppy_image_format_creator<pyldin_format>;
+const pyldin_format FLOPPY_PYLDIN_FORMAT;
diff --git a/src/lib/formats/pyldin_dsk.h b/src/lib/formats/pyldin_dsk.h
index dcb9302e29a..faf8a2041f1 100644
--- a/src/lib/formats/pyldin_dsk.h
+++ b/src/lib/formats/pyldin_dsk.h
@@ -19,14 +19,14 @@ class pyldin_format : public upd765_format
public:
pyldin_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_PYLDIN_FORMAT;
+extern const pyldin_format FLOPPY_PYLDIN_FORMAT;
#endif // MAME_FORMATS_PYLDIN_DSK_H
diff --git a/src/lib/formats/ql_dsk.cpp b/src/lib/formats/ql_dsk.cpp
index b3ceb233218..265c7c2ea11 100644
--- a/src/lib/formats/ql_dsk.cpp
+++ b/src/lib/formats/ql_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/ql_dsk.c
+ formats/ql_dsk.cpp
Sinclair QL disk image formats
*********************************************************************/
-#include <assert.h>
-
#include "formats/ql_dsk.h"
ql_format::ql_format() : wd177x_format(formats)
{
}
-const char *ql_format::name() const
+const char *ql_format::name() const noexcept
{
return "ql";
}
-const char *ql_format::description() const
+const char *ql_format::description() const noexcept
{
return "Sinclair QL disk image";
}
-const char *ql_format::extensions() const
+const char *ql_format::extensions() const noexcept
{
return "dsk,img";
}
@@ -52,4 +50,4 @@ const ql_format::format ql_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_QL_FORMAT = &floppy_image_format_creator<ql_format>;
+const ql_format FLOPPY_QL_FORMAT;
diff --git a/src/lib/formats/ql_dsk.h b/src/lib/formats/ql_dsk.h
index 6ed9f5ec64f..4a51062bc30 100644
--- a/src/lib/formats/ql_dsk.h
+++ b/src/lib/formats/ql_dsk.h
@@ -19,14 +19,14 @@ class ql_format : public wd177x_format
public:
ql_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_QL_FORMAT;
+extern const ql_format FLOPPY_QL_FORMAT;
#endif // MAME_FORMATS_QL_DSK_H
diff --git a/src/lib/formats/rc759_dsk.cpp b/src/lib/formats/rc759_dsk.cpp
new file mode 100644
index 00000000000..537803d1993
--- /dev/null
+++ b/src/lib/formats/rc759_dsk.cpp
@@ -0,0 +1,41 @@
+// license: BSD-3-Clause
+// copyright-holders: Dirk Best
+/***************************************************************************
+
+ Regnecentralen RC759 Piccoline
+
+ Disk image format
+
+***************************************************************************/
+
+#include "rc759_dsk.h"
+
+rc759_format::rc759_format() : wd177x_format(formats)
+{
+}
+
+const char *rc759_format::name() const noexcept
+{
+ return "rc759";
+}
+
+const char *rc759_format::description() const noexcept
+{
+ return "RC759 disk image";
+}
+
+const char *rc759_format::extensions() const noexcept
+{
+ return "img";
+}
+
+const rc759_format::format rc759_format::formats[] =
+{
+ {
+ floppy_image::FF_525, floppy_image::DSHD, floppy_image::MFM,
+ 1200, 8, 77, 2, 1024, {}, 1, {}, 50, 22, 54
+ },
+ {}
+};
+
+const rc759_format FLOPPY_RC759_FORMAT;
diff --git a/src/lib/formats/rc759_dsk.h b/src/lib/formats/rc759_dsk.h
new file mode 100644
index 00000000000..f49bb19f210
--- /dev/null
+++ b/src/lib/formats/rc759_dsk.h
@@ -0,0 +1,33 @@
+// license: BSD-3-Clause
+// copyright-holders: Dirk Best
+/***************************************************************************
+
+ Regnecentralen RC759 Piccoline
+
+ Disk image format
+
+***************************************************************************/
+
+#ifndef MAME_FORMATS_RC759_DSK_H
+#define MAME_FORMATS_RC759_DSK_H
+
+#pragma once
+
+#include "wd177x_dsk.h"
+
+class rc759_format : public wd177x_format
+{
+public:
+ rc759_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const format formats[];
+};
+
+extern const rc759_format FLOPPY_RC759_FORMAT;
+
+#endif // MAME_FORMATS_RC759_DSK_H
diff --git a/src/lib/formats/rk_cas.cpp b/src/lib/formats/rk_cas.cpp
index 18504fcb130..25bbb79eea3 100644
--- a/src/lib/formats/rk_cas.cpp
+++ b/src/lib/formats/rk_cas.cpp
@@ -5,7 +5,6 @@
Tape support for RK format
*/
-#include <assert.h>
#include "rk_cas.h"
@@ -20,13 +19,12 @@
#define RK_SIZE_22 22
#define RK_SIZE_60 60
-static int data_size;
+static int data_size; // FIXME: global variable prevents multiple instances
static int16_t *rk_emit_level(int16_t *p, int count, int level)
{
- int i;
- for (i=0; i<count; i++)
+ for (int i=0; i<count; i++)
{
*(p++) = level;
}
@@ -79,7 +77,7 @@ static int gam_cas_to_wav_size( const uint8_t *casdata, int caslen ) {
return (RK_HEADER_LEN * 8 * 2 + caslen * 8 * 2) * RK_SIZE_20;
}
-static int rk20_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
+static int rk20_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) {
int i;
int16_t * p = buffer;
@@ -95,7 +93,7 @@ static int rk20_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
return p - buffer;
}
-static int rk22_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
+static int rk22_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) {
int i;
int16_t * p = buffer;
@@ -111,7 +109,7 @@ static int rk22_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
return p - buffer;
}
-static int rk60_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
+static int rk60_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) {
int i;
int16_t * p = buffer;
@@ -127,7 +125,7 @@ static int rk60_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
return p - buffer;
}
-static int gam_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
+static int gam_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int ) {
int i;
int16_t * p = buffer;
@@ -141,7 +139,7 @@ static int gam_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes ) {
return p - buffer;
}
-static const struct CassetteLegacyWaveFiller rk20_legacy_fill_wave = {
+static const cassette_image::LegacyWaveFiller rk20_legacy_fill_wave = {
rk20_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
0, /* chunk_samples */
@@ -151,15 +149,15 @@ static const struct CassetteLegacyWaveFiller rk20_legacy_fill_wave = {
0 /* trailer_samples */
};
-static cassette_image::error rk20_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) {
- return cassette_legacy_identify( cassette, opts, &rk20_legacy_fill_wave );
+static cassette_image::error rk20_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) {
+ return cassette->legacy_identify( opts, &rk20_legacy_fill_wave );
}
static cassette_image::error rk20_cassette_load( cassette_image *cassette ) {
- return cassette_legacy_construct( cassette, &rk20_legacy_fill_wave );
+ return cassette->legacy_construct( &rk20_legacy_fill_wave );
}
-static const struct CassetteLegacyWaveFiller rk22_legacy_fill_wave = {
+static const cassette_image::LegacyWaveFiller rk22_legacy_fill_wave = {
rk22_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
0, /* chunk_samples */
@@ -169,15 +167,15 @@ static const struct CassetteLegacyWaveFiller rk22_legacy_fill_wave = {
0 /* trailer_samples */
};
-static cassette_image::error rk22_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) {
- return cassette_legacy_identify( cassette, opts, &rk22_legacy_fill_wave );
+static cassette_image::error rk22_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) {
+ return cassette->legacy_identify( opts, &rk22_legacy_fill_wave );
}
static cassette_image::error rk22_cassette_load( cassette_image *cassette ) {
- return cassette_legacy_construct( cassette, &rk22_legacy_fill_wave );
+ return cassette->legacy_construct( &rk22_legacy_fill_wave );
}
-static const struct CassetteLegacyWaveFiller gam_legacy_fill_wave = {
+static const cassette_image::LegacyWaveFiller gam_legacy_fill_wave = {
gam_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
0, /* chunk_samples */
@@ -187,15 +185,15 @@ static const struct CassetteLegacyWaveFiller gam_legacy_fill_wave = {
0 /* trailer_samples */
};
-static cassette_image::error gam_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) {
- return cassette_legacy_identify( cassette, opts, &gam_legacy_fill_wave );
+static cassette_image::error gam_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) {
+ return cassette->legacy_identify( opts, &gam_legacy_fill_wave );
}
static cassette_image::error gam_cassette_load( cassette_image *cassette ) {
- return cassette_legacy_construct( cassette, &gam_legacy_fill_wave );
+ return cassette->legacy_construct( &gam_legacy_fill_wave );
}
-static const struct CassetteLegacyWaveFiller rk60_legacy_fill_wave = {
+static const cassette_image::LegacyWaveFiller rk60_legacy_fill_wave = {
rk60_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
0, /* chunk_samples */
@@ -205,71 +203,71 @@ static const struct CassetteLegacyWaveFiller rk60_legacy_fill_wave = {
0 /* trailer_samples */
};
-static cassette_image::error rk60_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) {
- return cassette_legacy_identify( cassette, opts, &rk60_legacy_fill_wave );
+static cassette_image::error rk60_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) {
+ return cassette->legacy_identify( opts, &rk60_legacy_fill_wave );
}
static cassette_image::error rk60_cassette_load( cassette_image *cassette ) {
- return cassette_legacy_construct( cassette, &rk60_legacy_fill_wave );
+ return cassette->legacy_construct( &rk60_legacy_fill_wave );
}
-static const struct CassetteFormat rku_cassette_format = {
+static const cassette_image::Format rku_cassette_format = {
"rku",
rk20_cassette_identify,
rk20_cassette_load,
nullptr
};
-static const struct CassetteFormat rk8_cassette_format = {
+static const cassette_image::Format rk8_cassette_format = {
"rk8",
rk60_cassette_identify,
rk60_cassette_load,
nullptr
};
-static const struct CassetteFormat rks_cassette_format = {
+static const cassette_image::Format rks_cassette_format = {
"rks",
rk20_cassette_identify,
rk20_cassette_load,
nullptr
};
-static const struct CassetteFormat rko_cassette_format = {
+static const cassette_image::Format rko_cassette_format = {
"rko",
rk20_cassette_identify,
rk20_cassette_load,
nullptr
};
-static const struct CassetteFormat rkr_cassette_format = {
+static const cassette_image::Format rkr_cassette_format = {
"rk,rkr",
rk20_cassette_identify,
rk20_cassette_load,
nullptr
};
-static const struct CassetteFormat rka_cassette_format = {
+static const cassette_image::Format rka_cassette_format = {
"rka",
rk20_cassette_identify,
rk20_cassette_load,
nullptr
};
-static const struct CassetteFormat rkm_cassette_format = {
+static const cassette_image::Format rkm_cassette_format = {
"rkm",
rk22_cassette_identify,
rk22_cassette_load,
nullptr
};
-static const struct CassetteFormat rkp_cassette_format = {
+static const cassette_image::Format rkp_cassette_format = {
"rkp",
rk20_cassette_identify,
rk20_cassette_load,
nullptr
};
-static const struct CassetteFormat gam_cassette_format = {
+static const cassette_image::Format gam_cassette_format = {
"gam,g16,pki",
gam_cassette_identify,
gam_cassette_load,
diff --git a/src/lib/formats/roland_dsk.cpp b/src/lib/formats/roland_dsk.cpp
new file mode 100644
index 00000000000..4a7c7157c99
--- /dev/null
+++ b/src/lib/formats/roland_dsk.cpp
@@ -0,0 +1,38 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#include "roland_dsk.h"
+
+roland_sdisk_format::roland_sdisk_format() : wd177x_format(formats)
+{
+}
+
+const char *roland_sdisk_format::name() const noexcept
+{
+ return "roland_sdisk";
+}
+
+const char *roland_sdisk_format::description() const noexcept
+{
+ return "Roland S-Disk image";
+}
+
+const char *roland_sdisk_format::extensions() const noexcept
+{
+ return "out,w30";
+}
+
+const roland_sdisk_format::format roland_sdisk_format::formats[] = {
+ {
+ floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM,
+ 2000,
+ 9, 80, 2,
+ 512, {},
+ 1, {},
+ 80, 22, 24
+ },
+ // TODO: add HD format used by S-750/S-760/S-770
+ {}
+};
+
+const roland_sdisk_format FLOPPY_ROLAND_SDISK_FORMAT;
diff --git a/src/lib/formats/roland_dsk.h b/src/lib/formats/roland_dsk.h
new file mode 100644
index 00000000000..d7142384e4f
--- /dev/null
+++ b/src/lib/formats/roland_dsk.h
@@ -0,0 +1,27 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+
+#ifndef MAME_FORMATS_ROLAND_DSK_H
+#define MAME_FORMATS_ROLAND_DSK_H
+
+#pragma once
+
+#include "wd177x_dsk.h"
+
+class roland_sdisk_format : public wd177x_format
+{
+public:
+ roland_sdisk_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const format formats[];
+};
+
+
+extern const roland_sdisk_format FLOPPY_ROLAND_SDISK_FORMAT;
+
+#endif // MAME_FORMATS_ROLAND_DSK_H
diff --git a/src/lib/formats/rpk.cpp b/src/lib/formats/rpk.cpp
new file mode 100644
index 00000000000..6171b6a4cb0
--- /dev/null
+++ b/src/lib/formats/rpk.cpp
@@ -0,0 +1,474 @@
+// license:LGPL-2.1+
+// copyright-holders:Michael Zapf
+/***************************************************************************
+
+ rpk.cpp
+
+ RPK format support
+
+ A RPK file ("rompack") contains a collection of dump files and a layout
+ file that defines the kind of circuit board (PCB) used in the cartridge
+ and the mapping of dumps to sockets on the board.
+
+Example:
+ <?xml version="1.0" encoding="utf-8"?>
+ <romset>
+ <resources>
+ <rom id="gromimage" file="ed-assmg.bin" />
+ </resources>
+ <configuration>
+ <pcb type="standard">
+ <socket id="grom_socket" uses="gromimage"/>
+ </pcb>
+ </configuration>
+ </romset>
+
+DTD:
+ <!ELEMENT romset (resources, configuration)>
+ <!ELEMENT resources (rom|ram)+>
+ <!ELEMENT rom EMPTY>
+ <!ELEMENT ram EMPTY>
+ <!ELEMENT configuration (pcb)>
+ <!ELEMENT pcb (socket)+>
+ <!ELEMENT socket EMPTY>
+ <!ATTLIST romset version CDATA #IMPLIED>
+ <!ATTLIST rom id ID #REQUIRED
+ <!ATTLIST rom file CDATA #REQUIRED>
+ <!ATTLIST rom crc CDATA #IMPLIED>
+ <!ATTLIST rom sha1 CDATA #IMPLIED>
+ <!ATTLIST ram id ID #REQUIRED>
+ <!ATTLIST ram type (volatile|persistent) #IMPLIED>
+ <!ATTLIST ram store (internal|external) #IMPLIED>
+ <!ATTLIST ram file CDATA #IMPLIED>
+ <!ATTLIST ram length CDATA #REQUIRED>
+ <!ATTLIST pcb type CDATA #REQUIRED>
+ <!ATTLIST socket id ID #REQUIRED>
+ <!ATTLIST socket uses IDREF #REQUIRED>
+
+***************************************************************************/
+
+#include "rpk.h"
+#include "xmlfile.h"
+
+
+namespace
+{
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class rpk_category_impl : public std::error_category
+{
+public:
+ virtual char const *name() const noexcept override { return "rpk"; }
+ virtual std::string message(int condition) const override;
+};
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+rpk_category_impl const f_rpk_category_instance;
+};
+
+
+/***************************************************************************
+ RPK READER
+***************************************************************************/
+
+//-------------------------------------------------
+// ctor
+//-------------------------------------------------
+
+rpk_reader::rpk_reader(char const *const *pcb_types, bool supports_ram)
+ : m_pcb_types(pcb_types)
+ , m_supports_ram(supports_ram)
+{
+}
+
+
+//-------------------------------------------------
+// read
+//-------------------------------------------------
+
+std::error_condition rpk_reader::read(std::unique_ptr<util::random_read> &&stream, rpk_file::ptr &result) const
+{
+ // open the RPK (as a zip file)
+ util::archive_file::ptr zipfile;
+ std::error_condition ziperr = util::archive_file::open_zip(std::move(stream), zipfile);
+ if (ziperr)
+ return ziperr;
+
+ // open the layout XML
+ if (zipfile->search("layout.xml", false) < 0)
+ return error::MISSING_LAYOUT;
+
+ // determine the uncompressed length
+ uint64_t uncompressed_length_uint64 = zipfile->current_uncompressed_length();
+ size_t uncompressed_length = (size_t)uncompressed_length_uint64;
+ if (uncompressed_length != uncompressed_length_uint64)
+ return std::errc::not_enough_memory;
+
+ // prepare a buffer for the layout XML
+ std::unique_ptr<char[]> layout_xml_text(new (std::nothrow) char[uncompressed_length + 1]);
+ if (!layout_xml_text)
+ return std::errc::not_enough_memory;
+
+ // and decompress it
+ ziperr = zipfile->decompress(&layout_xml_text[0], uncompressed_length);
+ if (ziperr)
+ return ziperr;
+ layout_xml_text[uncompressed_length] = 0;
+
+ // parse the layout text
+ util::xml::file::ptr const layout_xml = util::xml::file::string_read(&layout_xml_text[0], nullptr);
+ if (!layout_xml)
+ return error::XML_ERROR;
+ layout_xml_text.reset();
+
+ // now we work within the XML tree
+
+ // romset is the root node
+ util::xml::data_node const *const romset_node = layout_xml->get_child("romset");
+ if (!romset_node)
+ return error::INVALID_LAYOUT; // document element must be <romset>
+
+ // resources is a child of romset
+ util::xml::data_node const *const resources_node = romset_node->get_child("resources");
+ if (!resources_node)
+ return error::INVALID_LAYOUT; // <romset> must have a <resources> child
+
+ // configuration is a child of romset; we're actually interested in ...
+ util::xml::data_node const *const configuration_node = romset_node->get_child("configuration");
+ if (!configuration_node)
+ return error::INVALID_LAYOUT; // <romset> must have a <configuration> child
+
+ // ... pcb, which is a child of configuration
+ util::xml::data_node const *const pcb_node = configuration_node->get_child("pcb");
+ if (!pcb_node)
+ return error::INVALID_LAYOUT; // <configuration> must have a <pcb> child
+
+ // we'll try to find the PCB type on the provided type list.
+ std::string const *const pcb_type_string = pcb_node->get_attribute_string_ptr("type");
+ if (!pcb_type_string)
+ return error::INVALID_LAYOUT; // <pcb> must have a 'type' attribute";
+ int pcb_type = 0;
+ while (m_pcb_types[pcb_type] && strcmp(m_pcb_types[pcb_type], pcb_type_string->c_str()))
+ pcb_type++;
+ if (!m_pcb_types[pcb_type])
+ return error::UNKNOWN_PCB_TYPE;
+
+ // create the rpk_file object
+ rpk_file::ptr file = std::make_unique<rpk_file>(std::move(zipfile), pcb_type);
+
+ // find the sockets and load their respective resource
+ for (util::xml::data_node const *socket_node = pcb_node->get_first_child(); socket_node; socket_node = socket_node->get_next_sibling())
+ {
+ if (strcmp(socket_node->get_name(), "socket") != 0)
+ return error::INVALID_LAYOUT; // <pcb> element has only <socket> children
+
+ std::string const *const id = socket_node->get_attribute_string_ptr("id");
+ if (!id)
+ return error::INVALID_LAYOUT; // <socket> must have an 'id' attribute
+
+ std::string const *const uses_name = socket_node->get_attribute_string_ptr("uses");
+ if (!uses_name)
+ return error::INVALID_LAYOUT; // <socket> must have a 'uses' attribute"
+
+ // locate the resource node
+ util::xml::data_node const *resource_node = nullptr;
+ for (util::xml::data_node const *this_resource_node = resources_node->get_first_child(); this_resource_node; this_resource_node = this_resource_node->get_next_sibling())
+ {
+ std::string const *const resource_name = this_resource_node->get_attribute_string_ptr("id");
+ if (!resource_name)
+ return error::INVALID_LAYOUT; // resource node must have an 'id' attribute
+
+ if (*resource_name == *uses_name)
+ {
+ resource_node = this_resource_node;
+ break;
+ }
+ }
+ if (!resource_node)
+ return error::INVALID_RESOURCE_REF; // *uses_name
+
+ // process the resource
+ if (!strcmp(resource_node->get_name(), "rom"))
+ {
+ std::error_condition err = file->add_rom_socket(std::string(*id), *resource_node);
+ if (err)
+ return err;
+ }
+ else if (!strcmp(resource_node->get_name(), "ram"))
+ {
+ if (!m_supports_ram)
+ return error::UNSUPPORTED_RPK_FEATURE; // <ram> is not supported by this system
+ std::error_condition err = file->add_ram_socket(std::string(*id), *resource_node);
+ if (err)
+ return err;
+ }
+ else
+ return error::INVALID_LAYOUT; // resource node must be <rom> or <ram>
+ }
+
+ // and we're done!
+ result = std::move(file);
+ return std::error_condition();
+}
+
+
+/***************************************************************************
+ RPK FILE
+***************************************************************************/
+
+//-------------------------------------------------
+// ctor
+//-------------------------------------------------
+
+rpk_file::rpk_file(util::archive_file::ptr &&zipfile, int pcb_type)
+ : m_zipfile(std::move(zipfile))
+ , m_pcb_type(pcb_type)
+{
+}
+
+
+
+//-------------------------------------------------
+// dtor
+//-------------------------------------------------
+
+rpk_file::~rpk_file()
+{
+}
+
+
+//-------------------------------------------------
+// add_rom_socket
+//-------------------------------------------------
+
+std::error_condition rpk_file::add_rom_socket(std::string &&id, const util::xml::data_node &rom_resource_node)
+{
+ // find the file attribute (required)
+ std::string const *const file = rom_resource_node.get_attribute_string_ptr("file");
+ if (!file)
+ return rpk_reader::error::INVALID_LAYOUT; // <rom> must have a 'file' attribute
+
+ // check for crc (optional)
+ std::optional<util::hash_collection> hashes;
+ std::string const *const crcstr = rom_resource_node.get_attribute_string_ptr("crc");
+ if (crcstr)
+ {
+ if (!hashes)
+ hashes.emplace();
+ hashes->add_from_string(util::hash_collection::HASH_CRC, *crcstr);
+ }
+
+ // check for sha1 (optional)
+ std::string const *const sha1 = rom_resource_node.get_attribute_string_ptr("sha1");
+ if (sha1)
+ {
+ if (!hashes.has_value())
+ hashes.emplace();
+ hashes->add_from_string(util::hash_collection::HASH_SHA1, *sha1);
+ }
+
+ // finally add the socket
+ m_sockets.emplace_back(*this, std::move(id), rpk_socket::socket_type::ROM, std::string(*file), std::move(hashes));
+ return std::error_condition();
+}
+
+
+//-------------------------------------------------
+// add_ram_socket
+//-------------------------------------------------
+
+std::error_condition rpk_file::add_ram_socket(std::string &&id, const util::xml::data_node &ram_resource_node)
+{
+ // find the length attribute
+ std::string const *const length_string = ram_resource_node.get_attribute_string_ptr("length");
+ if (!length_string)
+ return rpk_reader::error::MISSING_RAM_LENGTH;
+
+ // parse it
+ unsigned int length;
+ char suffix;
+ switch (sscanf(length_string->c_str(), "%u%c", &length, &suffix))
+ {
+ case 1:
+ // fall through
+ break;
+
+ case 2:
+ switch (tolower(suffix))
+ {
+ case 'k':
+ // kilobytes
+ length *= 1024;
+ break;
+
+ case 'm':
+ // megabytes
+ length *= 1024 * 1024;
+ break;
+
+ default: // failed
+ return rpk_reader::error::INVALID_RAM_SPEC;
+ }
+ break;
+
+ default:
+ return rpk_reader::error::INVALID_RAM_SPEC;
+ }
+
+ // determine the type of RAM
+ rpk_socket::socket_type type;
+ std::string const *const ram_type = ram_resource_node.get_attribute_string_ptr("type");
+ if (ram_type && *ram_type == "persistent")
+ type = rpk_socket::socket_type::PERSISTENT_RAM;
+ else
+ type = rpk_socket::socket_type::RAM;
+
+ // persistent RAM needs a file name
+ std::string file;
+ if (type == rpk_socket::socket_type::PERSISTENT_RAM)
+ {
+ std::string const *const ram_filename = ram_resource_node.get_attribute_string_ptr("file");
+ if (ram_filename == nullptr)
+ return rpk_reader::error::INVALID_RAM_SPEC; // <ram type='persistent'> must have a 'file' attribute
+ file = *ram_filename;
+ }
+
+ // finally add the socket
+ m_sockets.emplace_back(*this, std::move(id), type, std::move(file), std::nullopt, length);
+ return std::error_condition();
+}
+
+
+/***************************************************************************
+ RPK SOCKET
+***************************************************************************/
+
+//-------------------------------------------------
+// ctor
+//-------------------------------------------------
+
+rpk_socket::rpk_socket(rpk_file &rpk, std::string &&id, socket_type type, std::string &&filename, std::optional<util::hash_collection> &&hashes, std::uint32_t length)
+ : m_rpk(rpk)
+ , m_id(id)
+ , m_type(type)
+ , m_filename(filename)
+ , m_hashes(std::move(hashes))
+ , m_length(length)
+{
+}
+
+
+//-------------------------------------------------
+// dtor
+//-------------------------------------------------
+
+rpk_socket::~rpk_socket()
+{
+}
+
+
+//-------------------------------------------------
+// read_file
+//-------------------------------------------------
+
+std::error_condition rpk_socket::read_file(std::vector<std::uint8_t> &result) const
+{
+ // find the file
+ if (m_rpk.zipfile().search(m_filename, false) < 0)
+ return rpk_reader::error::INVALID_FILE_REF;
+
+ // prepare a buffer
+ result.clear();
+ try
+ {
+ result.resize(m_rpk.zipfile().current_uncompressed_length());
+ }
+ catch (std::bad_alloc const &)
+ {
+ return std::errc::not_enough_memory;
+ }
+
+ // read the file
+ std::error_condition const ziperr = m_rpk.zipfile().decompress(&result[0], m_rpk.zipfile().current_uncompressed_length());
+ if (ziperr)
+ return ziperr;
+
+ // perform hash checks, if appropriate
+ if (m_hashes.has_value())
+ {
+ util::hash_collection actual_hashes;
+ actual_hashes.compute(&result[0], result.size(), m_hashes->hash_types().c_str());
+
+ if (actual_hashes != m_hashes)
+ return rpk_reader::error::INVALID_FILE_REF; // Hash check failed
+ }
+
+ // success!
+ return std::error_condition();
+}
+
+
+/***************************************************************************
+ RPK EXCEPTION HANDLING
+***************************************************************************/
+
+//-------------------------------------------------
+// rpk_category - gets the RPK error category instance
+//-------------------------------------------------
+
+std::error_category const &rpk_category() noexcept
+{
+ return f_rpk_category_instance;
+}
+
+//-------------------------------------------------
+// error_message
+//-------------------------------------------------
+
+std::string rpk_category_impl::message(int condition) const
+{
+ using namespace std::literals;
+
+ std::string result;
+ switch ( condition)
+ {
+ case 0:
+ result = "No error"s;
+ break;
+ case (int)rpk_reader::error::XML_ERROR:
+ result = "XML format error"s;
+ break;
+ case (int)rpk_reader::error::MISSING_RAM_LENGTH:
+ result = "Missing RAM length"s;
+ break;
+ case (int)rpk_reader::error::INVALID_RAM_SPEC:
+ result = "Invalid RAM specification"s;
+ break;
+ case (int)rpk_reader::error::INVALID_RESOURCE_REF:
+ result = "Invalid resource reference"s;
+ break;
+ case (int)rpk_reader::error::INVALID_LAYOUT:
+ result = "layout.xml not valid"s;
+ break;
+ case (int)rpk_reader::error::MISSING_LAYOUT:
+ result = "Missing layout"s;
+ break;
+ case (int)rpk_reader::error::UNKNOWN_PCB_TYPE:
+ result = "Unknown pcb type"s;
+ break;
+ case (int)rpk_reader::error::UNSUPPORTED_RPK_FEATURE:
+ result = "RPK feature not supported"s;
+ break;
+ default:
+ result = "Unknown error"s;
+ break;
+ }
+ return result;
+}
diff --git a/src/lib/formats/rpk.h b/src/lib/formats/rpk.h
new file mode 100644
index 00000000000..f6e6360c21c
--- /dev/null
+++ b/src/lib/formats/rpk.h
@@ -0,0 +1,143 @@
+// license:LGPL-2.1+
+// copyright-holders:Michael Zapf
+/***************************************************************************
+
+ rpk.h
+
+ RPK format support
+
+***************************************************************************/
+
+#ifndef MAME_FORMATS_RPK_H
+#define MAME_FORMATS_RPK_H
+
+#pragma once
+
+#include "hash.h"
+#include "unzip.h"
+
+#include <cassert>
+#include <list>
+#include <optional>
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class rpk_reader;
+class rpk_file;
+
+// ======================> rpk_socket
+
+class rpk_socket
+{
+ friend class rpk_reader;
+public:
+ enum class socket_type
+ {
+ ROM,
+ RAM,
+ PERSISTENT_RAM
+ };
+
+ // ctor/dtor
+ rpk_socket(rpk_file &rpk, std::string &&id, socket_type type, std::string &&filename, std::optional<util::hash_collection> &&hashes, std::uint32_t length = ~0);
+ rpk_socket(const rpk_socket &) = delete;
+ rpk_socket(rpk_socket &&) = delete;
+ ~rpk_socket();
+
+ // accessors
+ const std::string &id() const noexcept { return m_id; }
+ socket_type type() const noexcept { return m_type; }
+ const std::string &filename() const noexcept { return m_filename; }
+ std::uint32_t length() const noexcept { assert(m_type == socket_type::RAM || m_type == socket_type::PERSISTENT_RAM); return m_length; }
+
+ // methods
+ std::error_condition read_file(std::vector<std::uint8_t> &result) const;
+
+private:
+ rpk_file & m_rpk;
+ std::string m_id;
+ socket_type m_type;
+ std::string m_filename;
+ std::optional<util::hash_collection> m_hashes;
+ std::uint32_t m_length;
+};
+
+
+// ======================> rpk_file
+
+class rpk_file
+{
+ friend class rpk_reader;
+ friend class rpk_socket;
+
+public:
+ typedef std::unique_ptr<rpk_file> ptr;
+
+ // ctor/dtor
+ rpk_file(util::archive_file::ptr &&zipfile, int pcb_type);
+ rpk_file(const rpk_file &) = delete;
+ rpk_file(rpk_file &&) = delete;
+ ~rpk_file();
+
+ // accessors
+ int pcb_type() const { return m_pcb_type; }
+ const std::list<rpk_socket> &sockets() const { return m_sockets; }
+
+private:
+ util::archive_file::ptr m_zipfile;
+ int m_pcb_type;
+ std::list<rpk_socket> m_sockets;
+
+ // accesors
+ util::archive_file &zipfile() { return *m_zipfile; }
+
+ // methods
+ std::error_condition add_rom_socket(std::string &&id, const util::xml::data_node &rom_resource_node);
+ std::error_condition add_ram_socket(std::string &&id, const util::xml::data_node &ram_resource_node);
+};
+
+
+// ======================> rpk_reader
+
+class rpk_reader
+{
+public:
+ enum class error
+ {
+ XML_ERROR = 1,
+ INVALID_FILE_REF,
+ MISSING_RAM_LENGTH,
+ INVALID_RAM_SPEC,
+ INVALID_RESOURCE_REF,
+ INVALID_LAYOUT,
+ MISSING_LAYOUT,
+ UNKNOWN_PCB_TYPE,
+ UNSUPPORTED_RPK_FEATURE
+ };
+
+ // ctor/dtor
+ rpk_reader(char const *const *pcb_types, bool supports_ram);
+ rpk_reader(const rpk_reader &) = delete;
+ rpk_reader(rpk_reader &&) = delete;
+
+ // methods
+ std::error_condition read(std::unique_ptr<util::random_read> &&stream, rpk_file::ptr &result) const;
+
+private:
+ char const *const * m_pcb_types;
+ bool m_supports_ram;
+};
+
+
+// error category for RPK errors
+std::error_category const &rpk_category() noexcept;
+inline std::error_condition make_error_condition(rpk_reader::error err) noexcept { return std::error_condition(int(err), rpk_category()); }
+
+namespace std {
+ template <> struct is_error_condition_enum<rpk_reader::error> : public std::true_type { };
+} // namespace std
+
+#endif // MAME_FORMATS_RPK_H
diff --git a/src/lib/formats/rx50_dsk.cpp b/src/lib/formats/rx50_dsk.cpp
index a58a8c5e59c..853caf0dbc1 100644
--- a/src/lib/formats/rx50_dsk.cpp
+++ b/src/lib/formats/rx50_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Karl-Ludwig Deisenhofer
/**********************************************************************
- formats/rx50_dsk.c
+ formats/rx50_dsk.cpp
Floppies used by Rainbow 100 and 190.
The RX50 drive: 5.25" format; 300 rpm; MFM 250 kbps; 96 - 100 tpi
@@ -29,11 +29,11 @@
FORMAT A: /F:160 on DOS; turn MEDIACHK ON
************************************************************************/
-#include <assert.h>
-
-#include "flopimg.h"
#include "formats/rx50_dsk.h"
+#include "ioprocs.h"
+
+
// Controller: WD1793
// TRACK LAYOUT IS UNVERIFED. SEE SOURCES:
// - 'esq_dsk16' (uses WD 1772)
@@ -75,32 +75,37 @@ rx50img_format::rx50img_format()
{
}
-const char *rx50img_format::name() const
+const char *rx50img_format::name() const noexcept
{
- return "img";
+ return "rx50";
}
-const char *rx50img_format::description() const
+const char *rx50img_format::description() const noexcept
{
return "DEC Rainbow 100 floppy image";
}
-const char *rx50img_format::extensions() const
+const char *rx50img_format::extensions() const noexcept
{
return "img";
}
-bool rx50img_format::supports_save() const
+bool rx50img_format::supports_save() const noexcept
{
return true;
}
-void rx50img_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
+void rx50img_format::find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
{
head_count = 1;
uint32_t expected_size = 0;
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ {
+ track_count = head_count = sector_count = 0;
+ return;
+ }
track_count = 80;
sector_count = 10;
@@ -124,18 +129,18 @@ void rx50img_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &he
track_count = head_count = sector_count = 0;
}
-int rx50img_format::identify(io_generic *io, uint32_t form_factor)
+int rx50img_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
if(track_count)
- return 50;
+ return FIFID_SIZE;
return 0;
}
- // /* Sectors are numbered 1 to 10 */
-bool rx50img_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+// Sectors are numbered 1 to 10
+bool rx50img_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
@@ -153,17 +158,17 @@ bool rx50img_format::load(io_generic *io, uint32_t form_factor, floppy_image *im
int track_size = sector_count*512;
for(int track=0; track < track_count; track++) {
for(int head=0; head < head_count; head++) {
- io_generic_read(io, sectdata, (track*head_count + head)*track_size, track_size);
+ /*auto const [err, actual] =*/ read_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF
generate_track(rx50_10_desc, track, head, sectors, sector_count, 102064, image); // 98480
}
}
- image->set_variant(floppy_image::SSQD);
+ image.set_variant(floppy_image::SSQD);
return true;
}
-bool rx50img_format::save(io_generic *io, floppy_image *image)
+bool rx50img_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
int track_count, head_count, sector_count;
get_geometry_mfm_pc(image, 2000, track_count, head_count, sector_count);
@@ -199,13 +204,13 @@ bool rx50img_format::save(io_generic *io, floppy_image *image)
for(int track=0; track < track_count; track++) {
for(int head=0; head < head_count; head++) {
get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata);
- io_generic_write(io, sectdata, (track*head_count + head)*track_size, track_size);
+ /*auto const [err, actual] =*/ write_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors
}
}
return true;
}
-const floppy_format_type FLOPPY_RX50IMG_FORMAT = &floppy_image_format_creator<rx50img_format>;
+const rx50img_format FLOPPY_RX50IMG_FORMAT;
/*
diff --git a/src/lib/formats/rx50_dsk.h b/src/lib/formats/rx50_dsk.h
index 318e0d02902..f3591a8c7cc 100644
--- a/src/lib/formats/rx50_dsk.h
+++ b/src/lib/formats/rx50_dsk.h
@@ -23,21 +23,21 @@ class rx50img_format : public floppy_image_format_t
public:
rx50img_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
static const desc_e rx50_10_desc[];
private:
- void find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
+ static void find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
};
-extern const floppy_format_type FLOPPY_RX50IMG_FORMAT;
+extern const rx50img_format FLOPPY_RX50IMG_FORMAT;
#endif // MAME_FORMATS_RX50_DSK_H
diff --git a/src/lib/formats/sap_dsk.cpp b/src/lib/formats/sap_dsk.cpp
new file mode 100644
index 00000000000..ef51cb8b772
--- /dev/null
+++ b/src/lib/formats/sap_dsk.cpp
@@ -0,0 +1,169 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+
+#include "sap_dsk.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+static constexpr unsigned HEADER_LENGTH = 66;
+static constexpr uint8_t MAGIC_XOR = 0xb3;
+
+const sap_dsk_format FLOPPY_SAP_FORMAT;
+
+inline uint16_t sap_dsk_format::accumulate_crc(uint16_t crc, uint8_t data) noexcept
+{
+ // Almost CRC16-CCITT, but not quite...
+ for (int shift : {0, 4})
+ crc = ((crc >> 4) & 0xfff) ^ 0x1081 * ((crc ^ (data >> shift)) & 0xf);
+ return crc;
+}
+
+const char *sap_dsk_format::name() const noexcept
+{
+ return "sap";
+}
+
+const char *sap_dsk_format::description() const noexcept
+{
+ return "Thomson SAP disk image (Systeme d'Archivage Pukall)";
+}
+
+const char *sap_dsk_format::extensions() const noexcept
+{
+ return "sap";
+}
+
+bool sap_dsk_format::supports_save() const noexcept
+{
+ return false;
+}
+
+int sap_dsk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint8_t buffer[HEADER_LENGTH];
+ auto [err, actual] = read_at(io, 0, buffer, HEADER_LENGTH);
+ if (err || actual != HEADER_LENGTH)
+ return 0;
+
+ // Check ID string
+ if (memcmp(&buffer[1], "SYSTEME D'ARCHIVAGE PUKALL S.A.P. (c) Alexandre PUKALL Avril 1998", HEADER_LENGTH - 1) != 0)
+ return 0;
+
+ // Check disk format
+ if (form_factor == floppy_image::FF_35)
+ {
+ // 3.5" disks must have 80 double-density tracks
+ if ((buffer[0] & 0x7f) != 0x01)
+ return 0;
+ }
+ else if (form_factor == floppy_image::FF_525)
+ {
+ // 5.25" disks must have 40 tracks
+ if ((buffer[0] & 0x7e) != 0x02)
+ return 0;
+ }
+ else if (form_factor != floppy_image::FF_UNKNOWN)
+ return 0;
+
+ uint32_t expected_size = HEADER_LENGTH + uint32_t((buffer[0] & 0x01) ? 256 + 6 : 128 + 6) * ((buffer[0] & 0x02) ? 40 : 80) * 16 * ((buffer[0] & 0x80) ? 2 : 1);
+ uint64_t actual_size;
+ if (!io.length(actual_size) && expected_size == actual_size)
+ return FIFID_SIGN | FIFID_SIZE;
+ else
+ return FIFID_SIGN;
+}
+
+bool sap_dsk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ uint8_t disk_type;
+ if (auto [err, actual] = read_at(io, 0, &disk_type, 1); err || actual != 1)
+ return false;
+
+ int track_count = (disk_type & 0x02) ? 40 : 80;
+ int head_count = (disk_type & 0x80) ? 2 : 1;
+ int max_tracks, max_heads;
+ image.get_maximal_geometry(max_tracks, max_heads);
+ if (max_heads < head_count || max_tracks < track_count)
+ return false;
+
+ switch (disk_type & 0x03)
+ {
+ case 0x01:
+ image.set_form_variant(floppy_image::FF_35, (disk_type & 0x80) ? floppy_image::DSDD : floppy_image::SSDD);
+ break;
+
+ case 0x02:
+ image.set_form_variant(floppy_image::FF_525, (disk_type & 0x80) ? floppy_image::DSSD : floppy_image::SSSD);
+ break;
+
+ case 0x03:
+ image.set_form_variant(floppy_image::FF_525, (disk_type & 0x80) ? floppy_image::DSDD : floppy_image::SSDD);
+ break;
+
+ default:
+ return false;
+ }
+
+ uint8_t sector_buf[16 * 1024];
+ uint8_t sector_header[4];
+ uint8_t sector_crc[2];
+ desc_pc_sector sectors[16];
+
+ size_t read_offset = HEADER_LENGTH;
+ for (int head = 0; head < head_count; head++)
+ {
+ for (int track = 0; track < track_count; track++)
+ {
+ uint8_t *bufptr = &sector_buf[0];
+ uint8_t track_size = 16;
+ int sector_count = 0;
+ bool is_mfm = disk_type & 0x01;
+ while (track_size != 0)
+ {
+ if (auto [err, actual] = read_at(io, read_offset, &sector_header[0], 4); err || actual != 4)
+ return false;
+ if (sector_count == 0 && sector_header[0] == 0x01)
+ is_mfm = false;
+ uint8_t sector_size = (sector_header[0] & 0x02) ? 4 >> (sector_header[0] & 0x01) : 1;
+ if (track_size < sector_size)
+ return false;
+ uint16_t sector_octets = sector_size * (is_mfm ? 256 : 128);
+ if (auto [err, actual] = read_at(io, read_offset + 4, bufptr, sector_octets); err || actual != sector_octets)
+ return false;
+ if (auto [err, actual] = read_at(io, read_offset + 4 + sector_octets, &sector_crc[0], 2); err || actual != 2)
+ return false;
+
+ uint16_t crc = 0xffff;
+ for (uint8_t data : sector_header)
+ crc = accumulate_crc(crc, data);
+ for (uint16_t i = 0; i < sector_octets; i++)
+ {
+ bufptr[i] ^= MAGIC_XOR;
+ crc = accumulate_crc(crc, bufptr[i]);
+ }
+
+ sectors[sector_count].track = sector_header[2];
+ sectors[sector_count].head = head;
+ sectors[sector_count].sector = sector_header[3];
+ sectors[sector_count].size = sector_size;
+ sectors[sector_count].actual_size = sector_octets;
+ sectors[sector_count].data = bufptr;
+ // Teo claims this flag is set for protection with "all the gap bytes set to 0xF7"; actual purpose is a bit unclear
+ sectors[sector_count].deleted = bool(sector_header[0] & 0x04);
+ sectors[sector_count].bad_crc = crc != get_u16be(sector_crc);
+
+ read_offset += sector_octets + 6;
+ bufptr += sector_octets;
+ track_size -= sector_size;
+ sector_count++;
+ }
+ if (is_mfm)
+ build_pc_track_mfm(track, head, image, 100000, sector_count, sectors, calc_default_pc_gap3_size(form_factor, sectors[0].actual_size));
+ else
+ build_pc_track_fm(track, head, image, 100000 / 2, sector_count, sectors, calc_default_pc_gap3_size(form_factor, sectors[0].actual_size));
+ }
+ }
+
+ return true;
+}
diff --git a/src/lib/formats/sap_dsk.h b/src/lib/formats/sap_dsk.h
new file mode 100644
index 00000000000..5bc86a2d6ef
--- /dev/null
+++ b/src/lib/formats/sap_dsk.h
@@ -0,0 +1,30 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+
+#ifndef MAME_FORMATS_SAP_DSK_H
+#define MAME_FORMATS_SAP_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+
+class sap_dsk_format : public floppy_image_format_t
+{
+public:
+ sap_dsk_format() = default;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+
+private:
+ static inline uint16_t accumulate_crc(uint16_t crc, uint8_t data) noexcept;
+};
+
+extern const sap_dsk_format FLOPPY_SAP_FORMAT;
+
+#endif // MAME_FORMATS_SAP_DSK_H
diff --git a/src/lib/formats/sc3000_bit.cpp b/src/lib/formats/sc3000_bit.cpp
index c656b54ee95..6044d9eec50 100644
--- a/src/lib/formats/sc3000_bit.cpp
+++ b/src/lib/formats/sc3000_bit.cpp
@@ -8,9 +8,9 @@
*********************************************************************/
-#include <assert.h>
-
#include "sc3000_bit.h"
+#include "tzx_cas.h"
+
/***************************************************************************
PARAMETERS
@@ -26,9 +26,9 @@
CassetteModulation sc3000_bit_modulation
-------------------------------------------------*/
-static const struct CassetteModulation sc3000_bit_modulation =
+static const cassette_image::Modulation sc3000_bit_modulation =
{
- CASSETTE_MODULATION_SINEWAVE,
+ cassette_image::MODULATION_SINEWAVE,
1200.0 - 300, 1200.0, 1200.0 + 300,
2400.0 - 600, 2400.0, 2400.0 + 600
};
@@ -37,9 +37,9 @@ static const struct CassetteModulation sc3000_bit_modulation =
sc3000_bit_identify - identify cassette
-------------------------------------------------*/
-static cassette_image::error sc3000_bit_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error sc3000_bit_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_modulation_identify( cassette, &sc3000_bit_modulation, opts);
+ return cassette->modulation_identify(sc3000_bit_modulation, opts);
}
/*-------------------------------------------------
@@ -48,7 +48,7 @@ static cassette_image::error sc3000_bit_identify(cassette_image *cassette, struc
#define MODULATE(_value) \
for (int i = 0; i < (_value ? 2 : 1); i++) { \
- err = cassette_put_modulated_data_bit(cassette, 0, time_index, _value, &sc3000_bit_modulation, &time_displacement);\
+ err = cassette->put_modulated_data_bit(0, time_index, _value, sc3000_bit_modulation, &time_displacement);\
if (err != cassette_image::error::SUCCESS) return err;\
time_index += time_displacement;\
}
@@ -56,15 +56,13 @@ static cassette_image::error sc3000_bit_identify(cassette_image *cassette, struc
static cassette_image::error sc3000_bit_load(cassette_image *cassette)
{
cassette_image::error err;
- uint64_t image_size = cassette_image_size(cassette);
- uint64_t image_pos = 0;
+ uint64_t image_size = cassette->image_size();
double time_index = 0.0;
double time_displacement;
- uint8_t data;
- while (image_pos < image_size)
+ for (uint64_t image_pos = 0; image_pos < image_size; image_pos++)
{
- cassette_image_read(cassette, &data, image_pos, 1);
+ uint8_t data = cassette->image_read_byte(image_pos);
switch (data)
{
@@ -77,13 +75,11 @@ static cassette_image::error sc3000_bit_load(cassette_image *cassette)
break;
case ' ':
- err = cassette_put_sample( cassette, 0, time_index, 1/1200.0, 0);
+ err = cassette->put_sample(0, time_index, 1/1200.0, 0);
if (err != cassette_image::error::SUCCESS) return err;
time_index += 1/1200.0;
break;
}
-
- image_pos++;
}
return cassette_image::error::SUCCESS;
@@ -93,7 +89,7 @@ static cassette_image::error sc3000_bit_load(cassette_image *cassette)
CassetteFormat sc3000_bit_cassette_format
-------------------------------------------------*/
-const struct CassetteFormat sc3000_bit_format =
+const cassette_image::Format sc3000_bit_format =
{
"bit",
sc3000_bit_identify,
@@ -103,4 +99,5 @@ const struct CassetteFormat sc3000_bit_format =
CASSETTE_FORMATLIST_START( sc3000_cassette_formats )
CASSETTE_FORMAT(sc3000_bit_format)
+ CASSETTE_FORMAT(tzx_cassette_format)
CASSETTE_FORMATLIST_END
diff --git a/src/lib/formats/sdd_dsk.cpp b/src/lib/formats/sdd_dsk.cpp
new file mode 100644
index 00000000000..e16e2621ba9
--- /dev/null
+++ b/src/lib/formats/sdd_dsk.cpp
@@ -0,0 +1,64 @@
+// license:BSD-3-Clause
+// copyright-holders:MetalliC
+/*********************************************************************
+
+ formats/sdd_dsk.cpp
+
+ Speccy-DOS SDD disk images
+
+*********************************************************************/
+
+#include "formats/sdd_dsk.h"
+
+#include "ioprocs.h"
+
+
+sdd_format::sdd_format() : wd177x_format(formats)
+{
+}
+
+const char *sdd_format::name() const noexcept
+{
+ return "sdd";
+}
+
+const char *sdd_format::description() const noexcept
+{
+ return "SDD floppy disk image";
+}
+
+const char *sdd_format::extensions() const noexcept
+{
+ return "sdd";
+}
+
+int sdd_format::get_image_offset(const format &f, int head, int track) const
+{
+ return (f.track_count * head + track) * compute_track_size(f);
+}
+
+const sdd_format::format sdd_format::formats[] = {
+ { // 5"25 640K 80 track double sided double density
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 16, 80, 2, 256, {}, -1, {1,12,7,2,13,8,3,14,9,4,15,10,5,16,11,6}, 60, 22, 24
+ },
+ { // 5"25 140K 35 track single sided double density
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000, 16, 35, 1, 256, {}, -1, {1,12,7,2,13,8,3,14,9,4,15,10,5,16,11,6}, 60, 22, 24
+ },
+ { // 5"25 400K 80 track double sided single density
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {1,8,5,2,9,6,3,10,7,4}, 40, 11, 10
+ },
+ { // 3'5 640K 80 track double sided double density
+ floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 80, 2, 256, {}, -1, {1,12,7,2,13,8,3,14,9,4,15,10,5,16,11,6}, 60, 22, 24
+ },
+ { // 3'5 400K 80 track double sided single density
+ floppy_image::FF_35, floppy_image::DSDD, floppy_image::FM,
+ 4000, 10, 80, 2, 256, {}, -1, {1,8,5,2,9,6,3,10,7,4}, 40, 11, 10
+ },
+ {}
+};
+
+const sdd_format FLOPPY_SDD_FORMAT;
diff --git a/src/lib/formats/sdd_dsk.h b/src/lib/formats/sdd_dsk.h
new file mode 100644
index 00000000000..ac95570a1fa
--- /dev/null
+++ b/src/lib/formats/sdd_dsk.h
@@ -0,0 +1,32 @@
+// license:BSD-3-Clause
+// copyright-holders:MetalliC
+/*********************************************************************
+
+ formats/sdd_dsk.h
+
+ Speccy-DOS SDD disk images
+
+*********************************************************************/
+#ifndef MAME_FORMATS_SDD_DSK_H
+#define MAME_FORMATS_SDD_DSK_H
+
+#pragma once
+
+#include "wd177x_dsk.h"
+
+class sdd_format : public wd177x_format {
+public:
+ sdd_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const format formats[];
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+};
+
+extern const sdd_format FLOPPY_SDD_FORMAT;
+
+#endif // MAME_FORMATS_SDD_DSK_H
diff --git a/src/lib/formats/sdf_dsk.cpp b/src/lib/formats/sdf_dsk.cpp
index b2cd4a4e7ab..92a6b9fb653 100644
--- a/src/lib/formats/sdf_dsk.cpp
+++ b/src/lib/formats/sdf_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Wilbert Pol, tim lindner
/*********************************************************************
- formats/sdf_dsk.h
+ formats/sdf_dsk.cpp
SDF disk images. Format created by Darren Atkinson for use with
his CoCoSDC floppy disk emulator.
@@ -12,43 +12,49 @@
*********************************************************************/
#include "sdf_dsk.h"
-#include <assert.h>
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
sdf_format::sdf_format()
{
}
-const char *sdf_format::name() const
+const char *sdf_format::name() const noexcept
{
return "sdf";
}
-const char *sdf_format::description() const
+const char *sdf_format::description() const noexcept
{
return "SDF disk image";
}
-const char *sdf_format::extensions() const
+const char *sdf_format::extensions() const noexcept
{
return "sdf";
}
-int sdf_format::identify(io_generic *io, uint32_t form_factor)
+int sdf_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t header[HEADER_SIZE];
- uint64_t size = io_generic_size(io);
-
- if (size < HEADER_SIZE)
+ uint64_t size;
+ if (io.length(size) || (size < HEADER_SIZE))
{
return 0;
}
- io_generic_read(io, header, 0, HEADER_SIZE);
+ auto const [err, actual] = read_at(io, 0, header, HEADER_SIZE);
+ if (err || (HEADER_SIZE != actual))
+ {
+ return 0;
+ }
int tracks = header[4];
int heads = header[5];
@@ -67,31 +73,31 @@ int sdf_format::identify(io_generic *io, uint32_t form_factor)
if (size == HEADER_SIZE + heads * tracks * TOTAL_TRACK_SIZE)
{
- return 100;
+ return FIFID_SIGN|FIFID_SIZE|FIFID_STRUCT;
}
return 0;
}
-bool sdf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool sdf_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t header[HEADER_SIZE];
std::vector<uint8_t> track_data(TOTAL_TRACK_SIZE);
std::vector<uint32_t> raw_track_data;
- io_generic_read(io, header, 0, HEADER_SIZE);
+ read_at(io, 0, header, HEADER_SIZE); // FIXME: check for errors and premature EOF
const int tracks = header[4];
const int heads = header[5];
if (heads == 2)
{
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
}
else
{
- image->set_variant(floppy_image::SSDD);
+ image.set_variant(floppy_image::SSDD);
}
for (int track = 0; track < tracks; track++)
@@ -104,7 +110,7 @@ bool sdf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
raw_track_data.clear();
// Read track
- io_generic_read(io, &track_data[0], HEADER_SIZE + ( heads * track + head ) * TOTAL_TRACK_SIZE, TOTAL_TRACK_SIZE);
+ read_at(io, HEADER_SIZE + (heads * track + head) * TOTAL_TRACK_SIZE, &track_data[0], TOTAL_TRACK_SIZE); // FIXME: check for errors and premature EOF
int sector_count = track_data[0];
@@ -115,8 +121,8 @@ bool sdf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
if (i < sector_count )
{
- idam_location[i] = ((track_data[ 8 * (i+1) + 1] << 8 | track_data[ 8 * (i+1)]) & 0x3FFF) - 4;
- dam_location[i] = ((track_data[ 8 * (i+1) + 1 + 2] << 8 | track_data[ 8 * (i+1) + 2]) & 0x3FFF) - 4;
+ idam_location[i] = (get_u16le(&track_data[ 8 * (i+1)]) & 0x3FFF) - 4;
+ dam_location[i] = (get_u16le(&track_data[ 8 * (i+1) + 2]) & 0x3FFF) - 4;
if (idam_location[i] > TOTAL_TRACK_SIZE) return false;
if (dam_location[i] > TOTAL_TRACK_SIZE) return false;
@@ -181,16 +187,10 @@ bool sdf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
}
-bool sdf_format::save(io_generic *io, floppy_image *image)
-{
- return false;
-}
-
-
-bool sdf_format::supports_save() const
+bool sdf_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_SDF_FORMAT = &floppy_image_format_creator<sdf_format>;
+const sdf_format FLOPPY_SDF_FORMAT;
diff --git a/src/lib/formats/sdf_dsk.h b/src/lib/formats/sdf_dsk.h
index 46b3905d6e7..149a3b2bcba 100644
--- a/src/lib/formats/sdf_dsk.h
+++ b/src/lib/formats/sdf_dsk.h
@@ -22,14 +22,13 @@ class sdf_format : public floppy_image_format_t
public:
sdf_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
protected:
static constexpr int HEADER_SIZE = 512;
@@ -41,6 +40,6 @@ protected:
static constexpr int SECTOR_SLOT_COUNT = 31;
};
-extern const floppy_format_type FLOPPY_SDF_FORMAT;
+extern const sdf_format FLOPPY_SDF_FORMAT;
#endif // MAME_FORMATS_SDF_DSK_H
diff --git a/src/lib/formats/sf7000_dsk.cpp b/src/lib/formats/sf7000_dsk.cpp
index 31d0c37ab15..cdbb94e48f7 100644
--- a/src/lib/formats/sf7000_dsk.cpp
+++ b/src/lib/formats/sf7000_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/sf7000_dsk.c
+ formats/sf7000_dsk.cpp
sf7000 format
*********************************************************************/
-#include <assert.h>
-
#include "formats/sf7000_dsk.h"
sf7000_format::sf7000_format() : upd765_format(formats)
{
}
-const char *sf7000_format::name() const
+const char *sf7000_format::name() const noexcept
{
return "sf7";
}
-const char *sf7000_format::description() const
+const char *sf7000_format::description() const noexcept
{
return "SF7 disk image";
}
-const char *sf7000_format::extensions() const
+const char *sf7000_format::extensions() const noexcept
{
return "sf7";
}
@@ -44,4 +42,4 @@ const sf7000_format::format sf7000_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_SF7000_FORMAT = &floppy_image_format_creator<sf7000_format>;
+const sf7000_format FLOPPY_SF7000_FORMAT;
diff --git a/src/lib/formats/sf7000_dsk.h b/src/lib/formats/sf7000_dsk.h
index ae74985c240..97a5a466936 100644
--- a/src/lib/formats/sf7000_dsk.h
+++ b/src/lib/formats/sf7000_dsk.h
@@ -19,14 +19,14 @@ class sf7000_format : public upd765_format
public:
sf7000_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_SF7000_FORMAT;
+extern const sf7000_format FLOPPY_SF7000_FORMAT;
#endif // MAME_FORMATS_SF7000_DSK_H
diff --git a/src/lib/formats/smx_dsk.cpp b/src/lib/formats/smx_dsk.cpp
index 9158a586c13..86cf07c4bf9 100644
--- a/src/lib/formats/smx_dsk.cpp
+++ b/src/lib/formats/smx_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/smx_dsk.c
+ formats/smx_dsk.cpp
Specialist MX format
*********************************************************************/
-#include <assert.h>
-
#include "formats/smx_dsk.h"
smx_format::smx_format() : wd177x_format(formats)
{
}
-const char *smx_format::name() const
+const char *smx_format::name() const noexcept
{
return "smx";
}
-const char *smx_format::description() const
+const char *smx_format::description() const noexcept
{
return "Specialist MX/Orion/B2M disk image";
}
-const char *smx_format::extensions() const
+const char *smx_format::extensions() const noexcept
{
return "odi,cpm,img";
}
@@ -45,4 +43,4 @@ const smx_format::format smx_format::formats[] =
{}
};
-const floppy_format_type FLOPPY_SMX_FORMAT = &floppy_image_format_creator<smx_format>;
+const smx_format FLOPPY_SMX_FORMAT;
diff --git a/src/lib/formats/smx_dsk.h b/src/lib/formats/smx_dsk.h
index f6fe998c1c3..e31628fec0a 100644
--- a/src/lib/formats/smx_dsk.h
+++ b/src/lib/formats/smx_dsk.h
@@ -19,14 +19,14 @@ class smx_format : public wd177x_format
public:
smx_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_SMX_FORMAT;
+extern const smx_format FLOPPY_SMX_FORMAT;
#endif // MAME_FORMATS_SMX_DSK_H
diff --git a/src/lib/formats/sol_cas.cpp b/src/lib/formats/sol_cas.cpp
index ac668535b51..1a793fb26a8 100644
--- a/src/lib/formats/sol_cas.cpp
+++ b/src/lib/formats/sol_cas.cpp
@@ -34,17 +34,18 @@ SVT - The full explanation may be found on the Solace web site,
********************************************************************/
-#include <assert.h>
-
#include "sol_cas.h"
+#include "multibyte.h"
+
+
#define WAVEENTRY_LOW -32768
#define WAVEENTRY_HIGH 32767
#define SOL20_WAV_FREQUENCY 4800
// image size
-static uint32_t sol20_image_size;
+static uint32_t sol20_image_size; // FIXME: global variable prevent multiple instances
static bool level;
static uint8_t sol20_cksm_byte;
static uint32_t sol20_byte_num;
@@ -83,17 +84,16 @@ static int sol20_output_bit(int16_t *buffer, int sample_pos, bool bit)
static int sol20_output_byte(int16_t *buffer, int sample_pos, uint8_t byte)
{
int samples = 0;
- uint8_t i;
/* start */
samples += sol20_output_bit (buffer, sample_pos + samples, 0);
/* data */
- for (i = 0; i<8; i++)
+ for (uint8_t i = 0; i<8; i++)
samples += sol20_output_bit (buffer, sample_pos + samples, (byte >> i) & 1);
/* stop */
- for (i = 0; i<2; i++)
+ for (uint8_t i = 0; i<2; i++)
samples += sol20_output_bit (buffer, sample_pos + samples, 1);
return samples;
@@ -158,11 +158,10 @@ static void sol20_scan_to_hex(const uint8_t *bytes)
static int sol20_read_hex(const uint8_t *bytes, uint8_t numdigits)
{
int data = 0;
- uint8_t i,chr;
- for (i = 0; i < numdigits; i++)
+ for (uint8_t i = 0; i < numdigits; i++)
{
- chr = bytes[sol20_byte_num];
+ uint8_t chr = bytes[sol20_byte_num];
if ((chr >= '0') && (chr <= '9'))
{
data = (data << 4) | (chr-48);
@@ -197,7 +196,7 @@ static int sol20_read_dec(const uint8_t *bytes)
static int sol20_handle_cassette(int16_t *buffer, const uint8_t *bytes)
{
uint32_t sample_count = 0;
- uint32_t i = 0,t = 0;
+ uint32_t t = 0;
uint16_t cc = 0;
sol20_byte_num = 1;
bool process_d = 0;
@@ -235,7 +234,7 @@ static int sol20_handle_cassette(int16_t *buffer, const uint8_t *bytes)
sol20_byte_num+=2; // bump to parameter
t = sol20_read_dec(bytes) * 140; // convert 10th of seconds to number of ones
- for (i = 0; i < t; i++)
+ for (uint32_t i = 0; i < t; i++)
sample_count += sol20_output_bit(buffer, sample_count, 1);
sol20_scan_to_eol(bytes);
break;
@@ -249,9 +248,9 @@ static int sol20_handle_cassette(int16_t *buffer, const uint8_t *bytes)
}
sol20_byte_num+=2; // bump to file name
- for (i = 0; i < 5; i++)
+ for (uint32_t i = 0; i < 5; i++)
sol20_header[i] = 0x20;
- for (i = 0; i < 5; i++)
+ for (uint32_t i = 0; i < 5; i++)
{
sol20_header[i] = bytes[sol20_byte_num++];
if (sol20_header[i] == 0x20)
@@ -262,29 +261,24 @@ static int sol20_handle_cassette(int16_t *buffer, const uint8_t *bytes)
sol20_header[6] = sol20_read_hex(bytes, 2);
sol20_scan_to_hex(bytes); // bump to length
length = sol20_read_hex(bytes, 4);
- sol20_header[7] = length;
- sol20_header[8] = length >> 8;
+ put_u16le(&sol20_header[7], length);
sol20_scan_to_hex(bytes); // bump to load-address
- i = sol20_read_hex(bytes, 4);
- sol20_header[9] = i;
- sol20_header[10] = i >> 8;
+ put_u16le(&sol20_header[9], sol20_read_hex(bytes, 4));
sol20_scan_to_hex(bytes); // bump to exec-address
- i = sol20_read_hex(bytes, 4);
- sol20_header[11] = i;
- sol20_header[12] = i >> 8;
+ put_u16le(&sol20_header[11], sol20_read_hex(bytes, 4));
sol20_header[13] = 0;
sol20_header[14] = 0;
sol20_header[15] = 0;
sol20_cksm_byte = 0;
- for (i = 0; i < 16; i++)
+ for (uint32_t i = 0; i < 16; i++)
sol20_cksm_byte = sol20_calc_cksm(sol20_cksm_byte, sol20_header[i]);
// write leader
- for (i = 0; i < 100; i++)
+ for (uint32_t i = 0; i < 100; i++)
sample_count += sol20_output_byte(buffer, sample_count, 0);
// write SOH
sample_count += sol20_output_byte(buffer, sample_count, 1);
// write Header
- for (i = 0; i < 16; i++)
+ for (uint32_t i = 0; i < 16; i++)
sample_count += sol20_output_byte(buffer, sample_count, sol20_header[i]);
// write checksum
sample_count += sol20_output_byte(buffer, sample_count, sol20_cksm_byte);
@@ -322,6 +316,7 @@ static int sol20_handle_cassette(int16_t *buffer, const uint8_t *bytes)
sol20_scan_to_hex(bytes);
}
}
+ [[fallthrough]];
default: // everything else is ignored
sol20_scan_to_eol(bytes);
break;
@@ -340,7 +335,7 @@ static int sol20_handle_cassette(int16_t *buffer, const uint8_t *bytes)
Generate samples for the tape image
********************************************************************/
-static int sol20_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int sol20_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return sol20_handle_cassette(buffer, bytes);
}
@@ -356,7 +351,7 @@ static int sol20_cassette_calculate_size_in_samples(const uint8_t *bytes, int le
return sol20_handle_cassette(nullptr, bytes);
}
-static const struct CassetteLegacyWaveFiller sol20_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller sol20_legacy_fill_wave =
{
sol20_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -367,17 +362,17 @@ static const struct CassetteLegacyWaveFiller sol20_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error sol20_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error sol20_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &sol20_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &sol20_legacy_fill_wave);
}
static cassette_image::error sol20_cassette_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &sol20_legacy_fill_wave);
+ return cassette->legacy_construct(&sol20_legacy_fill_wave);
}
-static const struct CassetteFormat sol20_cassette_image_format =
+static const cassette_image::Format sol20_cassette_image_format =
{
"svt",
sol20_cassette_identify,
diff --git a/src/lib/formats/sorc_cas.cpp b/src/lib/formats/sorc_cas.cpp
index 10fb26197f1..316fca7eaf4 100644
--- a/src/lib/formats/sorc_cas.cpp
+++ b/src/lib/formats/sorc_cas.cpp
@@ -25,10 +25,9 @@ header and leader bytes.
********************************************************************/
-#include <assert.h>
-
#include "sorc_cas.h"
+
#define WAVEENTRY_LOW -32768
#define WAVEENTRY_HIGH 32767
@@ -108,7 +107,7 @@ static int sorcerer_handle_cassette(int16_t *buffer, const uint8_t *bytes)
Generate samples for the tape image
********************************************************************/
-static int sorcerer_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int sorcerer_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return sorcerer_handle_cassette(buffer, bytes);
}
@@ -124,7 +123,7 @@ static int sorcerer_cassette_calculate_size_in_samples(const uint8_t *bytes, int
return sorcerer_handle_cassette(nullptr, bytes);
}
-static const struct CassetteLegacyWaveFiller sorcerer_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller sorcerer_legacy_fill_wave =
{
sorcerer_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -135,17 +134,17 @@ static const struct CassetteLegacyWaveFiller sorcerer_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error sorcerer_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error sorcerer_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &sorcerer_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &sorcerer_legacy_fill_wave);
}
static cassette_image::error sorcerer_cassette_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &sorcerer_legacy_fill_wave);
+ return cassette->legacy_construct(&sorcerer_legacy_fill_wave);
}
-static const struct CassetteFormat sorcerer_cassette_image_format =
+static const cassette_image::Format sorcerer_cassette_image_format =
{
"tape",
sorcerer_cassette_identify,
diff --git a/src/lib/formats/sorc_dsk.cpp b/src/lib/formats/sorc_dsk.cpp
index 7a1b734f780..4788b4f4bc0 100644
--- a/src/lib/formats/sorc_dsk.cpp
+++ b/src/lib/formats/sorc_dsk.cpp
@@ -8,12 +8,11 @@
*********************************************************************/
-#include <string.h>
-#include <assert.h>
-
#include "sorc_dsk.h"
#include "basicdsk.h"
+#include <cstring>
+
static FLOPPY_IDENTIFY(sorc_dsk_identify)
{
*vote = (floppy_image_size(floppy) == 332640) ? 100 : 0;
@@ -32,22 +31,20 @@ static int sorc_get_tracks_per_disk(floppy_image_legacy *floppy)
static uint64_t sorc_translate_offset(floppy_image_legacy *floppy, int track, int head, int sector)
{
- return 270*(16*track+sector);
+ return 270 * ((16 * uint64_t(track)) + sector);
}
static floperr_t get_offset(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, uint64_t *offset)
{
- uint64_t 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 = sorc_translate_offset(floppy, track, head, sector);
+ uint64_t offs = sorc_translate_offset(floppy, track, head, sector);
if (offset)
*offset = offs;
return FLOPPY_ERROR_SUCCESS;
diff --git a/src/lib/formats/sorc_dsk.h b/src/lib/formats/sorc_dsk.h
index 6988fa08b52..db98ea424ff 100644
--- a/src/lib/formats/sorc_dsk.h
+++ b/src/lib/formats/sorc_dsk.h
@@ -12,7 +12,7 @@
#pragma once
-#include "flopimg.h"
+#include "flopimg_legacy.h"
/**************************************************************************/
diff --git a/src/lib/formats/sord_cas.cpp b/src/lib/formats/sord_cas.cpp
index bc11c388915..acf89476e88 100644
--- a/src/lib/formats/sord_cas.cpp
+++ b/src/lib/formats/sord_cas.cpp
@@ -8,33 +8,26 @@
**************************************************************************/
-#include <string.h>
-#include <assert.h>
#include "sord_cas.h"
+#include <cstring>
+
#define SORDM5_WAVESAMPLES_HEADER 1
#define SORDM5_WAVESAMPLES_TRAILER 1
#define SORDM5_WAVESAMPLES_BLOCK 1
static const uint8_t SORDM5_CAS_HEADER[6] = { 'S', 'O', 'R', 'D', 'M', '5'};
-static const struct CassetteModulation sordm5_cas_modulation =
+static const cassette_image::Modulation sordm5_cas_modulation =
{
- CASSETTE_MODULATION_SINEWAVE,
+ cassette_image::MODULATION_SINEWAVE,
1575.0 - 300, 1575.0, 1575.0 + 300,
3150.0 - 600, 3150.0, 3150.0 + 600
};
-static uint8_t cassette_image_read_uint8( cassette_image *cassette, uint64_t offset)
-{
- uint8_t data;
- cassette_image_read( cassette, &data, offset, 1);
- return data;
-}
-
-static cassette_image::error sordm5_tap_identify( cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error sordm5_tap_identify( cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_modulation_identify( cassette, &sordm5_cas_modulation, opts);
+ return cassette->modulation_identify(sordm5_cas_modulation, opts);
}
static cassette_image::error sordm5_tap_load( cassette_image *cassette)
@@ -49,44 +42,44 @@ static cassette_image::error sordm5_tap_load( cassette_image *cassette)
uint32_t block_size, i, j;
size_t filler_length;
// init
- image_size = cassette_image_size(cassette);
+ image_size = cassette->image_size();
image_pos = 0;
// read/check header
if (image_size < 16) return cassette_image::error::INVALID_IMAGE;
- cassette_image_read( cassette, &header, image_pos, 16);
+ cassette->image_read(&header, image_pos, 16);
image_pos += 16;
if (memcmp( header, SORDM5_CAS_HEADER, sizeof(SORDM5_CAS_HEADER)) != 0) return cassette_image::error::INVALID_IMAGE;
// add silence (header)
- err = cassette_put_sample( cassette, 0, time_index, SORDM5_WAVESAMPLES_HEADER, 0);
+ err = cassette->put_sample(0, time_index, SORDM5_WAVESAMPLES_HEADER, 0);
if (err != cassette_image::error::SUCCESS) 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);
+ block_type = cassette->image_read_byte(image_pos + 0);
if ((block_type != 'H') && (block_type != 'D')) return cassette_image::error::INVALID_IMAGE;
// read block size
- block_size = cassette_image_read_uint8( cassette, image_pos + 1);
+ block_size = cassette->image_read_byte(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);
+ err = cassette->put_sample(0, time_index, SORDM5_WAVESAMPLES_BLOCK, 0);
if (err != cassette_image::error::SUCCESS) 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);
+ err = cassette->put_modulated_filler(0, time_index, 0xFF, filler_length, sordm5_cas_modulation, &time_displacement);
if (err != cassette_image::error::SUCCESS) 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);
+ byte = cassette->image_read_byte(image_pos + i);
// calc/check checksum
#if 0
if (i == block_size)
@@ -108,27 +101,27 @@ static cassette_image::error sordm5_tap_load( cassette_image *cassette)
bit = (byte >> (j-2)) & 1;
}
// add bit
- err = cassette_put_modulated_data_bit( cassette, 0, time_index, bit, &sordm5_cas_modulation, &time_displacement);
+ err = cassette->put_modulated_data_bit(0, time_index, bit, sordm5_cas_modulation, &time_displacement);
if (err != cassette_image::error::SUCCESS) 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);
+ err = cassette->put_modulated_data_bit(0, time_index, 1, sordm5_cas_modulation, &time_displacement);
if (err != cassette_image::error::SUCCESS) 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);
+ err = cassette->put_sample(0, time_index, SORDM5_WAVESAMPLES_TRAILER, 0);
if (err != cassette_image::error::SUCCESS) return err;
time_index += SORDM5_WAVESAMPLES_TRAILER;
//
return cassette_image::error::SUCCESS;
}
-static const struct CassetteFormat sordm5_cas_format =
+static const cassette_image::Format sordm5_cas_format =
{
"cas",
sordm5_tap_identify,
diff --git a/src/lib/formats/spc1000_cas.cpp b/src/lib/formats/spc1000_cas.cpp
index d13021b0047..a3830c4a679 100644
--- a/src/lib/formats/spc1000_cas.cpp
+++ b/src/lib/formats/spc1000_cas.cpp
@@ -21,17 +21,16 @@ IPL: This seems a quickload format containing RAM dump, not a real tape
********************************************************************/
-#include <assert.h>
-
#include "spc1000_cas.h"
+
#define WAVEENTRY_LOW -32768
#define WAVEENTRY_HIGH 32767
#define SPC1000_WAV_FREQUENCY 17000
// image size
-static int spc1000_image_size;
+static int spc1000_image_size; // FIXME: global variable prevents multiple instances
static int spc1000_put_samples(int16_t *buffer, int sample_pos, int count, int level)
{
@@ -90,12 +89,12 @@ static int spc1000_handle_cas(int16_t *buffer, const uint8_t *bytes)
Generate samples for the tape image
********************************************************************/
-static int spc1000_tap_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int spc1000_tap_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return spc1000_handle_tap(buffer, bytes);
}
-static int spc1000_cas_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int spc1000_cas_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return spc1000_handle_cas(buffer, bytes);
}
@@ -125,7 +124,7 @@ static int spc1000_cas_calculate_size_in_samples(const uint8_t *bytes, int lengt
// TAP
-static const struct CassetteLegacyWaveFiller spc1000_tap_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller spc1000_tap_legacy_fill_wave =
{
spc1000_tap_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -136,17 +135,17 @@ static const struct CassetteLegacyWaveFiller spc1000_tap_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error spc1000_tap_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error spc1000_tap_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &spc1000_tap_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &spc1000_tap_legacy_fill_wave);
}
static cassette_image::error spc1000_tap_cassette_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &spc1000_tap_legacy_fill_wave);
+ return cassette->legacy_construct(&spc1000_tap_legacy_fill_wave);
}
-static const struct CassetteFormat spc1000_tap_cassette_image_format =
+static const cassette_image::Format spc1000_tap_cassette_image_format =
{
"tap",
spc1000_tap_cassette_identify,
@@ -156,7 +155,7 @@ static const struct CassetteFormat spc1000_tap_cassette_image_format =
// CAS
-static const struct CassetteLegacyWaveFiller spc1000_cas_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller spc1000_cas_legacy_fill_wave =
{
spc1000_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -167,17 +166,17 @@ static const struct CassetteLegacyWaveFiller spc1000_cas_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error spc1000_cas_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error spc1000_cas_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &spc1000_cas_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &spc1000_cas_legacy_fill_wave);
}
static cassette_image::error spc1000_cas_cassette_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &spc1000_cas_legacy_fill_wave);
+ return cassette->legacy_construct(&spc1000_cas_legacy_fill_wave);
}
-static const struct CassetteFormat spc1000_cas_cassette_image_format =
+static const cassette_image::Format spc1000_cas_cassette_image_format =
{
"cas",
spc1000_cas_cassette_identify,
diff --git a/src/lib/formats/st_dsk.cpp b/src/lib/formats/st_dsk.cpp
index 9808dc56ff9..fc105de6fcb 100644
--- a/src/lib/formats/st_dsk.cpp
+++ b/src/lib/formats/st_dsk.cpp
@@ -2,62 +2,68 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/st_dsk.c
+ formats/st_dsk.cpp
All usual Atari ST formats
*********************************************************************/
-#include <assert.h>
-
#include "formats/st_dsk.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <cstring>
+
+
st_format::st_format()
{
}
-const char *st_format::name() const
+const char *st_format::name() const noexcept
{
return "st";
}
-const char *st_format::description() const
+const char *st_format::description() const noexcept
{
return "Atari ST floppy disk image";
}
-const char *st_format::extensions() const
+const char *st_format::extensions() const noexcept
{
return "st";
}
-bool st_format::supports_save() const
+bool st_format::supports_save() const noexcept
{
return true;
}
-void st_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
+void st_format::find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
{
- uint64_t size = io_generic_size(io);
- for(track_count=80; track_count <= 82; track_count++)
- for(head_count=1; head_count <= 2; head_count++)
- for(sector_count=9; sector_count <= 11; sector_count++)
- if(size == (uint32_t)512*track_count*head_count*sector_count)
- return;
+ uint64_t size;
+ if(!io.length(size)) {
+ for(track_count=80; track_count <= 82; track_count++)
+ for(head_count=1; head_count <= 2; head_count++)
+ for(sector_count=9; sector_count <= 11; sector_count++)
+ if(size == (uint32_t)512*track_count*head_count*sector_count)
+ return;
+ }
track_count = head_count = sector_count = 0;
}
-int st_format::identify(io_generic *io, uint32_t form_factor)
+int st_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
if(track_count)
- return 50;
+ return FIFID_SIZE;
return 0;
}
-bool st_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool st_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint8_t track_count, head_count, sector_count;
find_size(io, track_count, head_count, sector_count);
@@ -73,18 +79,18 @@ bool st_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
int track_size = sector_count*512;
for(int track=0; track < track_count; track++) {
for(int head=0; head < head_count; head++) {
- io_generic_read(io, sectdata, (track*head_count + head)*track_size, track_size);
+ /*auto const [err, actual] =*/ read_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF
generate_track(atari_st_fcp_get_desc(track, head, head_count, sector_count),
track, head, sectors, sector_count, 100000, image);
}
}
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
return true;
}
-bool st_format::save(io_generic *io, floppy_image *image)
+bool st_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
int track_count, head_count, sector_count;
get_geometry_mfm_pc(image, 2000, track_count, head_count, sector_count);
@@ -109,7 +115,7 @@ bool st_format::save(io_generic *io, floppy_image *image)
for(int track=0; track < track_count; track++) {
for(int head=0; head < head_count; head++) {
get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata);
- io_generic_write(io, sectdata, (track*head_count + head)*track_size, track_size);
+ /*auto const [err, actual] =*/ write_at(io, (track*head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors
}
}
@@ -121,35 +127,35 @@ msa_format::msa_format()
{
}
-const char *msa_format::name() const
+const char *msa_format::name() const noexcept
{
return "msa";
}
-const char *msa_format::description() const
+const char *msa_format::description() const noexcept
{
return "Atari MSA floppy disk image";
}
-const char *msa_format::extensions() const
+const char *msa_format::extensions() const noexcept
{
return "msa";
}
-bool msa_format::supports_save() const
+bool msa_format::supports_save() const noexcept
{
return true;
}
-void msa_format::read_header(io_generic *io, uint16_t &sign, uint16_t &sect, uint16_t &head, uint16_t &strack, uint16_t &etrack)
+void msa_format::read_header(util::random_read &io, uint16_t &sign, uint16_t &sect, uint16_t &head, uint16_t &strack, uint16_t &etrack)
{
uint8_t h[10];
- io_generic_read(io, h, 0, 10);
- sign = (h[0] << 8) | h[1];
- sect = (h[2] << 8) | h[3];
- head = (h[4] << 8) | h[5];
- strack = (h[6] << 8) | h[7];
- etrack = (h[8] << 8) | h[9];
+ /*auto const [err, actual] =*/ read_at(io, 0, h, 10); // FIXME: check for errors and premature EOF
+ sign = get_u16be(&h[0]);
+ sect = get_u16be(&h[2]);
+ head = get_u16be(&h[4]);
+ strack = get_u16be(&h[6]);
+ etrack = get_u16be(&h[8]);
}
bool msa_format::uncompress(uint8_t *buffer, int csize, int usize)
@@ -162,7 +168,7 @@ bool msa_format::uncompress(uint8_t *buffer, int csize, int usize)
if(csize-src < 3)
return false;
c = buffer[src++];
- int count = (buffer[src] << 8) | buffer[src+1];
+ int count = get_u16be(&buffer[src]);
src += 2;
if(usize-dst < count)
return false;
@@ -193,8 +199,8 @@ bool msa_format::compress(const uint8_t *buffer, int usize, uint8_t *dest, int &
return false;
dest[dst++] = 0xe5;
dest[dst++] = c;
- dest[dst++] = ncopy >> 8;
- dest[dst++] = ncopy;
+ put_u16be(&dest[dst], ncopy);
+ dst += 2;
} else {
src -= ncopy-1;
dest[dst++] = c;
@@ -206,7 +212,7 @@ bool msa_format::compress(const uint8_t *buffer, int usize, uint8_t *dest, int &
return dst < usize;
}
-int msa_format::identify(io_generic *io, uint32_t form_factor)
+int msa_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint16_t sign, sect, head, strack, etrack;
read_header(io, sign, sect, head, strack, etrack);
@@ -216,11 +222,11 @@ int msa_format::identify(io_generic *io, uint32_t form_factor)
(head == 0 || head == 1) &&
strack <= etrack &&
etrack < 82)
- return 100;
+ return FIFID_SIGN | FIFID_STRUCT;
return 0;
}
-bool msa_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool msa_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
uint16_t sign, sect, heads, strack, etrack;
read_header(io, sign, sect, heads, strack, etrack);
@@ -228,7 +234,7 @@ bool msa_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
uint8_t sectdata[11*512];
desc_s sectors[11];
for(int i=0; i<sect; i++) {
- sectors[i].data = sectdata + 512*1;
+ sectors[i].data = sectdata + 512*i;
sectors[i].size = 512;
sectors[i].sector_id = i + 1;
}
@@ -239,10 +245,10 @@ bool msa_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
for(int track=strack; track <= etrack; track++) {
for(int head=0; head <= heads; head++) {
uint8_t th[2];
- io_generic_read(io, th, pos, 2);
+ read_at(io, pos, th, 2); // FIXME: check for errors and premature EOF
pos += 2;
- int tsize = (th[0] << 8) | th[1];
- io_generic_read(io, sectdata, pos, tsize);
+ int tsize = get_u16be(th);
+ read_at(io, pos, sectdata, tsize); // FIXME: check for errors and premature EOF
pos += tsize;
if(tsize < track_size) {
if(!uncompress(sectdata, tsize, track_size))
@@ -253,12 +259,12 @@ bool msa_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
}
}
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
return true;
}
-bool msa_format::save(io_generic *io, floppy_image *image)
+bool msa_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
int track_count, head_count, sector_count;
get_geometry_mfm_pc(image, 2000, track_count, head_count, sector_count);
@@ -289,9 +295,9 @@ bool msa_format::save(io_generic *io, floppy_image *image)
header[8] = 0;
header[9] = track_count-1;
- io_generic_write(io, header, 0, 10);
-
- int pos = 10;
+ if(io.seek(0, SEEK_SET))
+ return false;
+ write(io, header, 10); // FIXME: check for errors
uint8_t sectdata[11*512];
uint8_t compdata[11*512];
@@ -303,18 +309,14 @@ bool msa_format::save(io_generic *io, floppy_image *image)
int csize;
if(compress(sectdata, track_size, compdata, csize)) {
uint8_t th[2];
- th[0] = csize >> 8;
- th[1] = csize;
- io_generic_write(io, th, pos, 2);
- io_generic_write(io, compdata, pos+2, csize);
- pos += 2+csize;
+ put_u16be(th, csize);
+ write(io, th, 2); // FIXME: check for errors
+ write(io, compdata, csize); // FIXME: check for errors
} else {
uint8_t th[2];
- th[0] = track_size >> 8;
- th[1] = track_size;
- io_generic_write(io, th, pos, 2);
- io_generic_write(io, sectdata, pos+2, track_size);
- pos += 2+track_size;
+ put_u16be(th, track_size);
+ write(io, th, 2); // FIXME: check for errors
+ write(io, sectdata, track_size); // FIXME: check for errors
}
}
}
@@ -322,5 +324,5 @@ bool msa_format::save(io_generic *io, floppy_image *image)
return true;
}
-const floppy_format_type FLOPPY_ST_FORMAT = &floppy_image_format_creator<st_format>;
-const floppy_format_type FLOPPY_MSA_FORMAT = &floppy_image_format_creator<msa_format>;
+const st_format FLOPPY_ST_FORMAT;
+const msa_format FLOPPY_MSA_FORMAT;
diff --git a/src/lib/formats/st_dsk.h b/src/lib/formats/st_dsk.h
index 44a0827757a..2026a1f37ea 100644
--- a/src/lib/formats/st_dsk.h
+++ b/src/lib/formats/st_dsk.h
@@ -19,17 +19,17 @@ class st_format : public floppy_image_format_t
public:
st_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
private:
- void find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
+ static void find_size(util::random_read &io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count);
};
class msa_format : public floppy_image_format_t
@@ -37,22 +37,22 @@ class msa_format : public floppy_image_format_t
public:
msa_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
private:
- bool uncompress(uint8_t *buffer, int csize, int usize);
- bool compress(const uint8_t *src, int usize, uint8_t *dest, int &csize);
- void read_header(io_generic *io, uint16_t &sign, uint16_t &sect, uint16_t &head, uint16_t &strack, uint16_t &etrack);
+ static bool uncompress(uint8_t *buffer, int csize, int usize);
+ static bool compress(const uint8_t *src, int usize, uint8_t *dest, int &csize);
+ static void read_header(util::random_read &io, uint16_t &sign, uint16_t &sect, uint16_t &head, uint16_t &strack, uint16_t &etrack);
};
-extern const floppy_format_type FLOPPY_ST_FORMAT;
-extern const floppy_format_type FLOPPY_MSA_FORMAT;
+extern const st_format FLOPPY_ST_FORMAT;
+extern const msa_format FLOPPY_MSA_FORMAT;
#endif // MAME_FORMATS_ST_DSK_H
diff --git a/src/lib/formats/svi_cas.cpp b/src/lib/formats/svi_cas.cpp
index c681c36d9dd..6e9a59e6ae9 100644
--- a/src/lib/formats/svi_cas.cpp
+++ b/src/lib/formats/svi_cas.cpp
@@ -1,9 +1,9 @@
// license:BSD-3-Clause
// copyright-holders:Sean Young
-#include <assert.h>
-
#include "svi_cas.h"
+#include <cstring>
+
#define CAS_PERIOD_0 (37)
#define CAS_PERIOD_1 (18)
#define CAS_HEADER_PERIODS (1600)
@@ -19,12 +19,12 @@ static const uint8_t CasHeader[17] =
#define SMPLO -32768
#define SMPHI 32767
-static int cas_size;
+static int cas_size; // FIXME: global variable prevents multiple instances
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int svi_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int svi_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
int cas_pos, samples_pos, n, i;
@@ -171,7 +171,7 @@ static int svi_cas_to_wav_size(const uint8_t *casdata, int caslen)
}
-static const struct CassetteLegacyWaveFiller svi_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller svi_legacy_fill_wave =
{
svi_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -184,21 +184,21 @@ static const struct CassetteLegacyWaveFiller svi_legacy_fill_wave =
-static cassette_image::error svi_cas_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error svi_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &svi_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &svi_legacy_fill_wave);
}
static cassette_image::error svi_cas_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &svi_legacy_fill_wave);
+ return cassette->legacy_construct(&svi_legacy_fill_wave);
}
-static const struct CassetteFormat svi_cas_format =
+static const cassette_image::Format svi_cas_format =
{
"cas",
svi_cas_identify,
diff --git a/src/lib/formats/svi_dsk.cpp b/src/lib/formats/svi_dsk.cpp
index aae08ba329d..4d326542ad5 100644
--- a/src/lib/formats/svi_dsk.cpp
+++ b/src/lib/formats/svi_dsk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -10,40 +10,47 @@
#include "svi_dsk.h"
+#include "ioprocs.h"
+
+
svi_format::svi_format()
{
}
-const char *svi_format::name() const
+const char *svi_format::name() const noexcept
{
return "svi";
}
-const char *svi_format::description() const
+const char *svi_format::description() const noexcept
{
return "SVI-318/328 disk image";
}
-const char *svi_format::extensions() const
+const char *svi_format::extensions() const noexcept
{
return "dsk";
}
-int svi_format::identify(io_generic *io, uint32_t form_factor)
+int svi_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if (io.length(size))
+ return 0;
if (size == 172032 || size == 346112)
- return 50;
+ return FIFID_SIZE;
return 0;
}
-bool svi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool svi_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- uint64_t size = io_generic_size(io);
- int head_count;
+ uint64_t size;
+ if (io.length(size))
+ return false;
+ int head_count;
switch (size)
{
case 172032: head_count = 1; break;
@@ -51,7 +58,8 @@ bool svi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
default: return false;
}
- int file_offset = 0;
+ if (io.seek(0, SEEK_SET))
+ return false;
for (int track = 0; track < 40; track++)
{
@@ -75,10 +83,9 @@ bool svi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
sectors[i].bad_crc = false;
sectors[i].data = &sector_data[sector_offset];
- io_generic_read(io, sectors[i].data, file_offset, sector_size);
+ /*auto const [err, actual] =*/ read(io, sectors[i].data, sector_size); // FIXME: check for errors and premature EOF
sector_offset += sector_size;
- file_offset += sector_size;
}
if (track == 0 && head == 0)
@@ -91,25 +98,21 @@ bool svi_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-bool svi_format::save(io_generic *io, floppy_image *image)
+bool svi_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
- uint8_t bitstream[500000/8];
- uint8_t sector_data[50000];
- desc_xs sectors[256];
- int track_size;
- uint64_t file_offset = 0;
+ if (io.seek(0, SEEK_SET))
+ return false;
int track_count, head_count;
- image->get_actual_geometry(track_count, head_count);
+ image.get_actual_geometry(track_count, head_count);
// initial fm track
- generate_bitstream_from_track(0, 0, 4000, bitstream, track_size, image);
- extract_sectors_from_bitstream_fm_pc(bitstream, track_size, sectors, sector_data, sizeof(sector_data));
+ auto bitstream = generate_bitstream_from_track(0, 0, 4000, image);
+ auto sectors = extract_sectors_from_bitstream_fm_pc(bitstream);
for (int i = 0; i < 18; i++)
{
- io_generic_write(io, sectors[i + 1].data, file_offset, 128);
- file_offset += 128;
+ /*auto const [err, actual] =*/ write(io, sectors[i + 1].data(), 128); // FIXME: check for errors
}
// rest are mfm tracks
@@ -120,13 +123,12 @@ bool svi_format::save(io_generic *io, floppy_image *image)
// skip track 0, head 0
if (track == 0) { if (head_count == 1) break; else head++; }
- generate_bitstream_from_track(track, head, 2000, bitstream, track_size, image);
- extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sector_data, sizeof(sector_data));
+ bitstream = generate_bitstream_from_track(track, head, 2000, image);
+ sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
for (int i = 0; i < 17; i++)
{
- io_generic_write(io, sectors[i + 1].data, file_offset, 256);
- file_offset += 256;
+ /*auto const [err, actual] =*/ write(io, sectors[i + 1].data(), 256); // FIXME: check for errors
}
}
}
@@ -134,9 +136,9 @@ bool svi_format::save(io_generic *io, floppy_image *image)
return true;
}
-bool svi_format::supports_save() const
+bool svi_format::supports_save() const noexcept
{
return true;
}
-const floppy_format_type FLOPPY_SVI_FORMAT = &floppy_image_format_creator<svi_format>;
+const svi_format FLOPPY_SVI_FORMAT;
diff --git a/src/lib/formats/svi_dsk.h b/src/lib/formats/svi_dsk.h
index bc1487a48fe..1fd0c5daa0e 100644
--- a/src/lib/formats/svi_dsk.h
+++ b/src/lib/formats/svi_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -19,16 +19,16 @@ class svi_format : public floppy_image_format_t
public:
svi_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_SVI_FORMAT;
+extern const svi_format FLOPPY_SVI_FORMAT;
#endif // MAME_FORMATS_SVI_DSK_H
diff --git a/src/lib/formats/swd_dsk.cpp b/src/lib/formats/swd_dsk.cpp
new file mode 100644
index 00000000000..ea578a7ed03
--- /dev/null
+++ b/src/lib/formats/swd_dsk.cpp
@@ -0,0 +1,50 @@
+// license:BSD-3-Clause
+// copyright-holders:MetalliC
+/*********************************************************************
+
+ formats/swd_dsk.cpp
+
+ Swift Disc disk images
+
+*********************************************************************/
+
+#include "formats/swd_dsk.h"
+
+swd_format::swd_format() : wd177x_format(formats)
+{
+}
+
+const char *swd_format::name() const noexcept
+{
+ return "swd";
+}
+
+const char *swd_format::description() const noexcept
+{
+ return "SWD floppy disk image";
+}
+
+const char *swd_format::extensions() const noexcept
+{
+ return "swd";
+}
+
+int swd_format::get_image_offset(const format &f, int head, int track) const
+{
+ return (f.track_count * head + track) * compute_track_size(f);
+}
+
+const swd_format::format swd_format::formats[] = {
+ // note: original formatted disks use side# 1/2 instead of usual 0/1
+ { // 3'5 640K 80 track double sided double density
+ floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 16, 80, 2, 256, {}, 1, {}, 60, 22, 56
+ },
+ { // 5"25 640K 80 track double sided double density
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
+ 2000, 16, 80, 2, 256, {}, 1, {}, 60, 22, 56
+ },
+ {}
+};
+
+const swd_format FLOPPY_SWD_FORMAT;
diff --git a/src/lib/formats/swd_dsk.h b/src/lib/formats/swd_dsk.h
new file mode 100644
index 00000000000..2102fa7f234
--- /dev/null
+++ b/src/lib/formats/swd_dsk.h
@@ -0,0 +1,32 @@
+// license:BSD-3-Clause
+// copyright-holders:MetalliC
+/*********************************************************************
+
+ formats/swd_dsk.h
+
+ Swift Disc disk images
+
+*********************************************************************/
+#ifndef MAME_FORMATS_SWD_DSK_H
+#define MAME_FORMATS_SWD_DSK_H
+
+#pragma once
+
+#include "wd177x_dsk.h"
+
+class swd_format : public wd177x_format {
+public:
+ swd_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const format formats[];
+ virtual int get_image_offset(const format &f, int head, int track) const override;
+};
+
+extern const swd_format FLOPPY_SWD_FORMAT;
+
+#endif // MAME_FORMATS_SWD_DSK_H
diff --git a/src/lib/formats/tandy2k_dsk.cpp b/src/lib/formats/tandy2k_dsk.cpp
index e08865c9493..5d11ddd95b7 100644
--- a/src/lib/formats/tandy2k_dsk.cpp
+++ b/src/lib/formats/tandy2k_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/tandy2k_dsk.c
+ formats/tandy2k_dsk.cpp
Tandy 2000 disk format
*********************************************************************/
-#include <assert.h>
-
#include "formats/tandy2k_dsk.h"
tandy2k_format::tandy2k_format() : upd765_format(formats)
{
}
-const char *tandy2k_format::name() const
+const char *tandy2k_format::name() const noexcept
{
return "tandy2k";
}
-const char *tandy2k_format::description() const
+const char *tandy2k_format::description() const noexcept
{
return "Tandy 2000 disk image";
}
-const char *tandy2k_format::extensions() const
+const char *tandy2k_format::extensions() const noexcept
{
return "dsk";
}
@@ -39,4 +37,4 @@ const tandy2k_format::format tandy2k_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_TANDY_2000_FORMAT = &floppy_image_format_creator<tandy2k_format>;
+const tandy2k_format FLOPPY_TANDY_2000_FORMAT;
diff --git a/src/lib/formats/tandy2k_dsk.h b/src/lib/formats/tandy2k_dsk.h
index 35833ecba6d..92396a9c1fe 100644
--- a/src/lib/formats/tandy2k_dsk.h
+++ b/src/lib/formats/tandy2k_dsk.h
@@ -19,14 +19,14 @@ class tandy2k_format : public upd765_format
public:
tandy2k_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_TANDY_2000_FORMAT;
+extern const tandy2k_format FLOPPY_TANDY_2000_FORMAT;
#endif // MAME_FORMATS_TANDY2K_DSK_H
diff --git a/src/lib/formats/td0_dsk.cpp b/src/lib/formats/td0_dsk.cpp
index b7c418af53c..956bbd002c7 100644
--- a/src/lib/formats/td0_dsk.cpp
+++ b/src/lib/formats/td0_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Miodrag Milanovic
/*********************************************************************
- formats/td0_dsk.c
+ formats/td0_dsk.cpp
TD0 disk images
@@ -14,10 +14,14 @@
* Edited and translated to English by Kenji RIKITAKE
*/
-#include <string.h>
-#include <assert.h>
-#include "pool.h"
-#include "flopimg.h"
+#include "flopimg_legacy.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <cstring>
+#include <tuple>
+
#define BUFSZ 512 // new input buffer
@@ -49,17 +53,26 @@ struct td0dsk_tag
};
struct tdlzhuf {
- uint16_t r,
- bufcnt,bufndx,bufpos, // string buffer
+ uint16_t r = 0,
+ bufcnt = 0, bufndx = 0, bufpos = 0, // string buffer
// the following to allow block reads from input in next_word()
- ibufcnt,ibufndx; // input buffer counters
- uint8_t inbuf[BUFSZ]; // input buffer
+ ibufcnt = 0, ibufndx = 0; // input buffer counters
+ uint8_t inbuf[BUFSZ]{}; // input buffer
};
struct td0dsk_t
{
- io_generic *floppy_file;
+public:
+ td0dsk_t(util::random_read &f) : floppy_file(f) { }
+
+ void set_floppy_file_offset(uint64_t o) { floppy_file_offset = o; }
+
+ void init_Decode();
+ int Decode(uint8_t *buf, int len);
+
+private:
+ util::random_read &floppy_file;
uint64_t floppy_file_offset;
struct tdlzhuf tdctl;
@@ -79,7 +92,7 @@ struct td0dsk_t
uint8_t getlen;
int data_read(uint8_t *buf, uint16_t size);
- int next_word();
+ int next_word(int needed);
int GetBit();
int GetByte();
void StartHuff();
@@ -87,33 +100,10 @@ struct td0dsk_t
void update(int c);
int16_t DecodeChar();
int16_t DecodePosition();
- void init_Decode();
- int Decode(uint8_t *buf, int len);
};
//static td0dsk_t td0dsk;
-struct floppy_image_legacy
-{
- struct io_generic io;
-
- const struct FloppyFormat *floppy_option;
- struct FloppyCallbacks format;
-
- /* loaded track stuff */
- int loaded_track_head;
- int loaded_track_index;
- uint32_t loaded_track_size;
- void *loaded_track_data;
- uint8_t loaded_track_status;
- uint8_t flags;
-
- /* tagging system */
- object_pool *tags;
- void *tag_data;
-};
-
-
static struct td0dsk_tag *get_tag(floppy_image_legacy *floppy)
{
struct td0dsk_tag *tag;
@@ -186,7 +176,7 @@ static floperr_t get_offset(floppy_image_legacy *floppy, int head, int track, in
offs+= 6;
if ((header[4] & 0x30)==0) {
offs+= 2;
- offs+= header[6] + (header[7]<<8);
+ offs+= get_u16le(&header[6]);
}
}
// read size of sector
@@ -225,7 +215,7 @@ static floperr_t internal_td0_read_sector(floppy_image_legacy *floppy, int head,
offset+=3;
// take data size
- size = header[6] + (header[7]<<8)-1;
+ size = get_u16le(&header[6])-1;
// take real sector size
realsize = 1 << (header[3] + 7);
@@ -246,7 +236,7 @@ static floperr_t internal_td0_read_sector(floppy_image_legacy *floppy, int head,
// - 2 bytes of data
// data is reapeted specified number of times
while(buff_pos<realsize) {
- for (i=0;i<data[data_pos]+(data[data_pos+1] << 8);i++) {
+ for (i=0;i<get_u16le(&data[data_pos]);i++) {
buf[buff_pos] = data[data_pos+2];buff_pos++;
buf[buff_pos] = data[data_pos+3];buff_pos++;
}
@@ -336,11 +326,12 @@ static floperr_t td0_get_indexed_sector_info(floppy_image_legacy *floppy, int he
int td0dsk_t::data_read(uint8_t *buf, uint16_t size)
{
- uint64_t image_size = io_generic_size(floppy_file);
+ uint64_t image_size = 0;
+ floppy_file.length(image_size);
if (size > image_size - floppy_file_offset) {
size = image_size - floppy_file_offset;
}
- io_generic_read(floppy_file,buf,floppy_file_offset,size);
+ /*auto const [err, actual] =*/ read_at(floppy_file, floppy_file_offset, buf, size); // FIXME: check for errors and premature EOF
floppy_file_offset += size;
return size;
}
@@ -422,16 +413,15 @@ static const uint8_t d_len[256] = {
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
};
-int td0dsk_t::next_word()
+int td0dsk_t::next_word(int needed)
{
- if(tdctl.ibufndx >= tdctl.ibufcnt)
- {
- tdctl.ibufndx = 0;
- tdctl.ibufcnt = data_read(tdctl.inbuf,BUFSZ);
- if(tdctl.ibufcnt <= 0)
- return(-1);
- }
while (getlen <= 8) { // typically reads a word at a time
+ if(tdctl.ibufndx >= tdctl.ibufcnt) {
+ tdctl.ibufndx = 0;
+ tdctl.ibufcnt = data_read(tdctl.inbuf,BUFSZ);
+ if(tdctl.ibufcnt <= 0)
+ return(getlen >= needed ? 0 : -1);
+ }
getbuf |= tdctl.inbuf[tdctl.ibufndx++] << (8 - getlen);
getlen += 8;
}
@@ -442,7 +432,7 @@ int td0dsk_t::next_word()
int td0dsk_t::GetBit() /* get one bit */
{
int16_t i;
- if(next_word() < 0)
+ if(next_word(1) < 0)
return(-1);
i = getbuf;
getbuf <<= 1;
@@ -456,7 +446,7 @@ int td0dsk_t::GetBit() /* get one bit */
int td0dsk_t::GetByte() /* get a byte */
{
uint16_t i;
- if(next_word() != 0)
+ if(next_word(8) != 0)
return(-1);
i = getbuf;
getbuf <<= 8;
@@ -653,7 +643,7 @@ int td0dsk_t::Decode(uint8_t *buf, int len) /* Decoding/Uncompressing */
}
else {
if((pos = DecodePosition()) < 0)
- return(count); // fatal error
+ return(count); // fatal error
tdctl.bufpos = (tdctl.r - pos - 1) & (N - 1);
tdctl.bufcnt = c - 255 + THRESHOLD;
tdctl.bufndx = 0;
@@ -679,7 +669,6 @@ int td0dsk_t::Decode(uint8_t *buf, int len) /* Decoding/Uncompressing */
FLOPPY_CONSTRUCT( td0_dsk_construct )
{
- td0dsk_t state;
struct FloppyCallbacks *callbacks;
struct td0dsk_tag *tag;
uint8_t *header;
@@ -710,9 +699,9 @@ FLOPPY_CONSTRUCT( td0_dsk_construct )
int rd;
int off = 12;
int size = 0;
- state.floppy_file = &(floppy->io);
+ td0dsk_t state(floppy_get_io(floppy));
state.init_Decode();
- state.floppy_file_offset = 12;
+ state.set_floppy_file_offset(12);
do
{
if((rd = state.Decode(obuf, BUFSZ)) > 0) size += rd;
@@ -724,7 +713,7 @@ FLOPPY_CONSTRUCT( td0_dsk_construct )
return FLOPPY_ERROR_OUTOFMEMORY;
}
memcpy(tag->data,obuf,12);
- state.floppy_file_offset = 12;
+ state.set_floppy_file_offset(12);
state.init_Decode();
do
{
@@ -743,7 +732,7 @@ FLOPPY_CONSTRUCT( td0_dsk_construct )
// header len + comment header + comment len
position = 12;
if (header[7] & 0x80) {
- position += 10 + header[14] + (header[15]<<8);
+ position += 10 + get_u16le(&header[14]);
}
tag->tracks = 0;
do {
@@ -764,7 +753,7 @@ FLOPPY_CONSTRUCT( td0_dsk_construct )
header = tag->data + position;
position+=2;
// skip sector data
- position+= header[0] + (header[1]<<8);
+ position+= get_u16le(&header[0]);
}
}
tag->tracks++;
@@ -792,7 +781,7 @@ FLOPPY_DESTRUCT( td0_dsk_destruct )
/*********************************************************************
- formats/td0_dsk.h
+ formats/td0_dsk.cpp
Teledisk disk images
@@ -804,60 +793,75 @@ td0_format::td0_format()
{
}
-const char *td0_format::name() const
+const char *td0_format::name() const noexcept
{
return "td0";
}
-const char *td0_format::description() const
+const char *td0_format::description() const noexcept
{
return "Teledisk disk image";
}
-const char *td0_format::extensions() const
+const char *td0_format::extensions() const noexcept
{
return "td0";
}
-int td0_format::identify(io_generic *io, uint32_t form_factor)
+int td0_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t h[7];
+ auto const [err, actual] = read_at(io, 0, h, 7); // FIXME: does this need to read 7 bytes? it only check 2 bytes. Also check for premature EOF.
+ if(err)
+ {
+ return 0;
+ }
- io_generic_read(io, h, 0, 7);
if(((h[0] == 'T') && (h[1] == 'D')) || ((h[0] == 't') && (h[1] == 'd')))
{
- return 100;
+ return FIFID_SIGN;
}
return 0;
}
-bool td0_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool td0_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
+ std::error_condition err;
+ size_t actual;
int track_count = 0;
int head_count = 0;
int track_spt;
int offset = 0;
const int max_size = 4*1024*1024; // 4MB ought to be large enough for any floppy
std::vector<uint8_t> imagebuf(max_size);
- uint8_t header[12];
- io_generic_read(io, header, 0, 12);
+ uint8_t header[12];
+ std::tie(err, actual) = read_at(io, 0, header, 12);
+ if(err || (actual != 12))
+ return false;
head_count = header[9];
if(header[0] == 't')
{
- td0dsk_t disk_decode;
+ td0dsk_t disk_decode(io);
- disk_decode.floppy_file = io;
disk_decode.init_Decode();
- disk_decode.floppy_file_offset = 12;
- disk_decode.Decode(&imagebuf[0], max_size);
+ disk_decode.set_floppy_file_offset(12);
+ actual = disk_decode.Decode(&imagebuf[0], max_size);
}
else
- io_generic_read(io, &imagebuf[0], 12, io_generic_size(io));
+ {
+ uint64_t image_size;
+ if(io.length(image_size))
+ return false;
+ std::tie(err, actual) = read_at(io, 12, &imagebuf[0], image_size - 12);
+ if(err || (actual != (image_size - 12)))
+ return false;
+ }
+ // skip optional comment section
if(header[7] & 0x80)
- offset = 10 + imagebuf[2] + (imagebuf[3] << 8);
+ offset = 10 + get_u16le(&imagebuf[2]);
track_spt = imagebuf[offset];
if(track_spt == 255) // Empty file?
@@ -869,44 +873,45 @@ bool td0_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
if((imagebuf[offset + 2] & 0x7f) == 2) // ?
{
if(head_count == 2)
- image->set_variant(floppy_image::DSHD);
+ image.set_variant(floppy_image::DSHD);
else
return false; // single side hd?
break;
}
- /* no break; could be qd, won't know until tracks are counted */
+ // could be qd, won't know until tracks are counted
+ [[fallthrough]];
case 1:
if(head_count == 2)
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
else
- image->set_variant(floppy_image::SSDD);
+ image.set_variant(floppy_image::SSDD);
break;
case 4:
if((imagebuf[offset + 2] & 0x7f) == 2) // ?
{
if(head_count == 2)
- image->set_variant(floppy_image::DSHD);
+ image.set_variant(floppy_image::DSHD);
else
return false; // single side 3.5?
break;
} else
- image->set_variant(floppy_image::SSDD);
- break;
+ image.set_variant(floppy_image::SSDD);
+ break; // FIXME: comment below says "no break" but this is a breal
/* no break */
case 3:
if(head_count == 2)
{
if(form_factor == floppy_image::FF_525)
- image->set_variant(floppy_image::DSQD);
+ image.set_variant(floppy_image::DSQD);
else
- image->set_variant(floppy_image::DSDD);
+ image.set_variant(floppy_image::DSDD);
}
else
{
if(form_factor == floppy_image::FF_525)
- image->set_variant(floppy_image::SSQD);
+ image.set_variant(floppy_image::SSQD);
else
- return false; // single side 3.5?
+ image.set_variant(floppy_image::SSDD);
}
break;
case 5:
@@ -922,6 +927,9 @@ bool td0_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
while(track_spt != 255)
{
+ if(actual < offset + 4)
+ return false;
+
desc_pc_sector sects[256];
uint8_t sect_data[65536];
int sdatapos = 0;
@@ -931,6 +939,9 @@ bool td0_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
offset += 4;
for(int i = 0; i < track_spt; i++)
{
+ if(actual < offset + 6)
+ return false;
+
uint8_t *hs = &imagebuf[offset];
uint16_t size;
offset += 6;
@@ -947,6 +958,8 @@ bool td0_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
else
{
offset += 3;
+ if(actual < offset)
+ return false;
size = 128 << hs[3];
int j, k;
switch(hs[8])
@@ -954,12 +967,16 @@ bool td0_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
default:
return false;
case 0:
+ if(actual < offset + size)
+ return false;
memcpy(&sect_data[sdatapos], &imagebuf[offset], size);
offset += size;
break;
case 1:
offset += 4;
- k = (hs[9] + (hs[10] << 8)) * 2;
+ if(actual < offset)
+ return false;
+ k = get_u16le(&hs[9]) * 2;
k = (k <= size) ? k : size;
for(j = 0; j < k; j += 2)
{
@@ -973,11 +990,15 @@ bool td0_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
k = 0;
while(k < size)
{
+ if(actual < offset + 2)
+ return false;
uint16_t len = imagebuf[offset];
uint16_t rep = imagebuf[offset + 1];
offset += 2;
if(!len)
{
+ if(actual < offset + rep)
+ return false;
memcpy(&sect_data[sdatapos + k], &imagebuf[offset], rep);
offset += rep;
k += rep;
@@ -985,6 +1006,8 @@ bool td0_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
else
{
len = (1 << len);
+ if(actual < offset + len)
+ return false;
rep = len * rep;
rep = ((rep + k) <= size) ? rep : (size - k);
for(j = 0; j < rep; j += len)
@@ -1014,27 +1037,24 @@ bool td0_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
else
build_pc_track_mfm(track, head, image, base_cell_count*2, track_spt, sects, calc_default_pc_gap3_size(form_factor, sects[0].actual_size));
+ if(actual <= offset)
+ return false;
track_spt = imagebuf[offset];
}
if((track_count > 50) && (form_factor == floppy_image::FF_525)) // ?
{
- if(image->get_variant() == floppy_image::DSDD)
- image->set_variant(floppy_image::DSQD);
- else if(image->get_variant() == floppy_image::SSDD)
- image->set_variant(floppy_image::SSQD);
+ if(image.get_variant() == floppy_image::DSDD)
+ image.set_variant(floppy_image::DSQD);
+ else if(image.get_variant() == floppy_image::SSDD)
+ image.set_variant(floppy_image::SSQD);
}
return true;
}
-bool td0_format::save(io_generic *io, floppy_image *image)
-{
- return false;
-}
-
-bool td0_format::supports_save() const
+bool td0_format::supports_save() const noexcept
{
return false;
}
-const floppy_format_type FLOPPY_TD0_FORMAT = &floppy_image_format_creator<td0_format>;
+const td0_format FLOPPY_TD0_FORMAT;
diff --git a/src/lib/formats/td0_dsk.h b/src/lib/formats/td0_dsk.h
index 96007fa4314..a6501054ff7 100644
--- a/src/lib/formats/td0_dsk.h
+++ b/src/lib/formats/td0_dsk.h
@@ -13,16 +13,15 @@ class td0_format : public floppy_image_format_t
public:
td0_format();
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
-
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
};
-extern const floppy_format_type FLOPPY_TD0_FORMAT;
+extern const td0_format FLOPPY_TD0_FORMAT;
#endif // MAME_FORMATS_TD0_DSK_H
diff --git a/src/lib/formats/thom_cas.cpp b/src/lib/formats/thom_cas.cpp
index 45f89ac2a29..f6f9e317d4f 100644
--- a/src/lib/formats/thom_cas.cpp
+++ b/src/lib/formats/thom_cas.cpp
@@ -8,13 +8,12 @@
**********************************************************************/
-#include <math.h>
-#include <assert.h>
-
-#include "pool.h"
-#include "cassimg.h"
#include "thom_cas.h"
+#include <cmath>
+#include <cstdio>
+#include <cstring>
+
/***************************** configuration **************************/
@@ -59,6 +58,8 @@ static uint8_t* to7_k7_bits;
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).
+ Many of these hacks use emulator-exclusive 6809 instructions and will
+ not work in MAME.
As we run the original BIOS routine, we need to hack back the k7 file
into the original stream.
@@ -86,18 +87,18 @@ static uint8_t* to7_k7_bits;
-static const struct CassetteModulation to7_k7_modulation =
+static const cassette_image::Modulation to7_k7_modulation =
{
- CASSETTE_MODULATION_SQUAREWAVE,
+ cassette_image::MODULATION_SQUAREWAVE,
4000.0, 4500.0, 5000.0,
5500.0, 6300.0, 7500.0
};
-static cassette_image::error to7_k7_identify ( cassette_image *cass, struct CassetteOptions *opts )
+static cassette_image::error to7_k7_identify ( cassette_image *cass, cassette_image::Options *opts )
{
- cassette_image::error e = cassette_modulation_identify( cass, &to7_k7_modulation, opts );
+ cassette_image::error e = cass->modulation_identify( to7_k7_modulation, opts );
return e;
}
@@ -109,9 +110,9 @@ static cassette_image::error to7_k7_load( cassette_image *cass )
static const int8_t square_wave[] = { -128, 127 };
double time = 0.;
#endif
- size_t size = cassette_image_size( cass ), pos = 0;
+ size_t size = cass->image_size( ), pos = 0;
int i, sz, sz2, bitmax = 1024, invalid = 0;
- uint8_t in, typ, block[264];
+ uint8_t 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 ));
@@ -133,8 +134,8 @@ static cassette_image::error to7_k7_load( cassette_image *cass )
do \
{ \
cassette_image::error err; \
- err = cassette_put_samples( cass, 0, time, (PERIOD), 2, 1, \
- square_wave, CASSETTE_WAVEFORM_8BIT ); \
+ err = cass->put_samples( 0, time, (PERIOD), 2, 1, \
+ square_wave, cassette_image::WAVEFORM_8BIT ); \
if ( err != cassette_image::error::SUCCESS ) \
return err; \
time += (PERIOD); \
@@ -202,7 +203,7 @@ static cassette_image::error to7_k7_load( cassette_image *cass )
} while (0)
/* check format */
- cassette_image_read( cass, block, 0, 64 );
+ cass->image_read( block, 0, 64 );
for ( i = 3; ; i++ )
{
if ( ( i >= size ) || ( i >= 64 ) )
@@ -227,8 +228,7 @@ static cassette_image::error to7_k7_load( cassette_image *cass )
/* skip to first 0xff filler */
for ( sz = 0; pos < size; pos++, sz++ )
{
- cassette_image_read( cass, &in, pos, 1 );
- if ( in == 0xff )
+ if ( cass->image_read_byte( pos ) == 0xff )
break;
}
if ( sz > 0 )
@@ -241,7 +241,7 @@ static cassette_image::error to7_k7_load( cassette_image *cass )
/* skip 0xff filler */
for ( sz = 0; pos < size; pos++, sz++ )
{
- cassette_image_read( cass, &in, pos, 1 );
+ uint8_t in = cass->image_read_byte( pos );
/* actually, we are bit laxist and treat as 0xff bytes with at least
5 bits out of 8 set to 1
*/
@@ -257,7 +257,7 @@ static cassette_image::error to7_k7_load( cassette_image *cass )
pos -= sz;
break;
}
- cassette_image_read( cass, block, pos, 4 );
+ cass->image_read( block, pos, 4 );
typ = block[2];
sz2 = block[3]+1;
if ( block[0] != 0x01 || block[1] != 0x3c || ( typ != 0x00 && typ != 0x01 && typ != 0xff ) )
@@ -268,7 +268,7 @@ static cassette_image::error to7_k7_load( cassette_image *cass )
pos += 4;
/* get block */
- cassette_image_read( cass, block+4, pos, sz2 );
+ cass->image_read( block+4, pos, sz2 );
pos += sz2;
/* 1-filler and 0xff-filler */
@@ -323,17 +323,16 @@ static cassette_image::error to7_k7_load( cassette_image *cass )
/* put block */
for (; pos < size; pos++ )
{
- cassette_image_read( cass, &in, pos, 1 );
+ uint8_t in = cass->image_read_byte( pos );
for ( sz = 0; pos < size && in == 0xff; sz++ )
{
pos++;
- cassette_image_read( cass, &in, pos, 1 );
+ in = cass->image_read_byte( pos );
}
if ( invalid < 10 && sz > 4 && in == 0x01 && pos + 4 <= size )
{
- uint8_t in1,in2;
- cassette_image_read( cass, &in1, pos+1, 1 );
- cassette_image_read( cass, &in2, pos+2, 1 );
+ uint8_t in1 = cass->image_read_byte( pos+1 );
+ uint8_t in2 = cass->image_read_byte( pos+2 );
if ( (in1 == 0x3c) && ((in2 == 0x00) || (in2 == 0x01) ) )
{
/* seems we have a regular block hidden in raw data => rebounce */
@@ -365,7 +364,7 @@ static cassette_image::error to7_k7_load( cassette_image *cass )
-static const struct CassetteFormat to7_k7 =
+static const cassette_image::Format to7_k7 =
{ "k7", to7_k7_identify, to7_k7_load, nullptr /* no save */ };
@@ -375,9 +374,9 @@ static const struct CassetteFormat to7_k7 =
static cassette_image::error to7_wav_identify ( cassette_image *cass,
- struct CassetteOptions *opts )
+ cassette_image::Options *opts )
{
- cassette_image::error e = wavfile_format.identify( cass, opts );
+ cassette_image::error e = cassette_image::wavfile_format.identify( cass, opts );
return e;
}
@@ -385,9 +384,7 @@ static cassette_image::error to7_wav_identify ( cassette_image *cass,
static cassette_image::error to7_wav_load ( cassette_image *cass )
{
- cassette_image::error e = wavfile_format.load( cass );
- struct CassetteInfo info;
- double len;
+ cassette_image::error e = cassette_image::wavfile_format.load( cass );
if ( to7_k7_bits )
{
@@ -398,9 +395,9 @@ static cassette_image::error to7_wav_load ( cassette_image *cass )
if ( e != cassette_image::error::SUCCESS )
return e;
- cassette_get_info( cass, &info );
+ cassette_image::Info info = cass->get_info( );
- len = (double) info.sample_count / info.sample_frequency;
+ double 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,
@@ -412,17 +409,17 @@ static cassette_image::error to7_wav_load ( cassette_image *cass )
-static cassette_image::error to7_wav_save ( cassette_image *cass, const struct CassetteInfo *info )
+static cassette_image::error to7_wav_save ( cassette_image *cass, const cassette_image::Info *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 );
+ return cassette_image::wavfile_format.save( cass, info );
}
/* overloaded wav: dump info */
-static const struct CassetteFormat to7_wav =
+static const cassette_image::Format to7_wav =
{ "wav", to7_wav_identify, to7_wav_load, to7_wav_save };
@@ -432,7 +429,7 @@ static const struct CassetteFormat to7_wav =
static cassette_image::error mo5_k5_identify ( cassette_image *cass,
- struct CassetteOptions *opts )
+ cassette_image::Options *opts )
{
opts -> bits_per_sample = 8;
opts -> channels = 1;
@@ -464,10 +461,11 @@ static cassette_image::error mo5_k5_identify ( cassette_image *cass,
static cassette_image::error mo5_k5_load( cassette_image *cass )
{
- size_t size = cassette_image_size( cass ), pos = 0;
+ size_t size = cass->image_size( ), pos = 0;
int i, sz, sz2, hbit = 0;
- uint8_t in, in2, in3, typ, block[264], sum;
- int invalid = 0, hbitsize = 0, dcmoto = 0;
+ uint8_t typ, block[264], sum;
+ int invalid = 0, hbitsize = 0;
+ bool dcmoto = false, dcmoto_old = false;
LOG (( "mo5_k5_load: start conversion, size=%li\n", (long)size ));
PRINT (( "mo5_k5_load: open cassette, length: %li bytes\n", (long) size ));
@@ -476,7 +474,7 @@ static cassette_image::error mo5_k5_load( cassette_image *cass )
#define K5_PUT_HBIT \
do \
{ \
- cassette_put_sample ( cass, 0, hbitsize * MO5_HBIT_LENGTH, MO5_HBIT_LENGTH, (hbit ? 1 : -1) << 30 ); \
+ cass->put_sample ( 0, hbitsize * MO5_HBIT_LENGTH, MO5_HBIT_LENGTH, (hbit ? 1 : -1) << 30 ); \
hbitsize++; \
} while ( 0 )
@@ -534,7 +532,7 @@ static cassette_image::error mo5_k5_load( cassette_image *cass )
} while (0)
/* check format */
- cassette_image_read( cass, block, 0, 64 );
+ cass->image_read( block, 0, 64 );
for ( i = 3; ; i++ )
{
if ( ( i >= size ) || ( i >= 64 ) )
@@ -556,9 +554,11 @@ static cassette_image::error mo5_k5_load( cassette_image *cass )
}
}
- cassette_image_read( cass, block, pos, 6 );
+ cass->image_read( block, pos, 6 );
if ( ! memcmp( block, "DCMOTO", 6 ) || ! memcmp( block, "DCMO5", 5 ) || ! memcmp( block, "DCMO6", 5 ) )
- dcmoto = 1;
+ dcmoto = true;
+ if ( block[0] == 0xdc && block[1] == 0x01 )
+ dcmoto_old = true;
/* loop over regular blocks */
while ( pos < size )
@@ -567,7 +567,7 @@ static cassette_image::error mo5_k5_load( cassette_image *cass )
/* skip DCMOTO header*/
if ( dcmoto )
{
- cassette_image_read( cass, block, pos, 6 );
+ cass->image_read( block, pos, 6 );
if ( ! memcmp( block, "DCMOTO", 6 ) )
{
LOG (( "mo5_k5_load: DCMOTO signature found at off=$%x\n", (int)pos ));
@@ -579,12 +579,17 @@ static cassette_image::error mo5_k5_load( cassette_image *cass )
pos += 5;
}
}
+ else if ( dcmoto_old )
+ {
+ cass->image_read( block, pos, 1 );
+ if ( block[0] == 0xdc )
+ pos++;
+ }
/* skip 0x01 filler */
for ( sz = 0; pos < size; pos++, sz++ )
{
- cassette_image_read( cass, &in, pos, 1 );
- if ( in != 0x01 )
+ if ( cass->image_read_byte(pos) != 0x01 )
break;
}
@@ -594,7 +599,7 @@ static cassette_image::error mo5_k5_load( cassette_image *cass )
pos -= sz;
break;
}
- cassette_image_read( cass, block, pos, 4 );
+ cass->image_read( block, pos, 4 );
typ = block[2];
sz2 = (uint8_t) (block[3]-1);
if ( block[0] != 0x3c || block[1] != 0x5a || ( typ != 0x00 && typ != 0x01 && typ != 0xff ) || pos+sz2 > size )
@@ -605,7 +610,7 @@ static cassette_image::error mo5_k5_load( cassette_image *cass )
pos += 4;
/* get block */
- cassette_image_read( cass, block+4, pos, sz2 );
+ cass->image_read( block+4, pos, sz2 );
pos += sz2;
/* 0-fillers and 0x01-fillers */
@@ -665,33 +670,33 @@ static cassette_image::error mo5_k5_load( cassette_image *cass )
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 );
+ uint8_t in = cass->image_read_byte( pos );
if ( dcmoto && in=='D' )
{
/* skip DCMOTO header*/
- cassette_image_read( cass, block, pos, 6 );
+ cass->image_read( 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 );
+ in = cass->image_read_byte( pos );
}
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 );
+ in = cass->image_read_byte( pos );
}
}
for ( sz = 0; pos < size && in == 0x01; sz++ )
{
pos++;
- cassette_image_read( cass, &in, pos, 1 );
+ in = cass->image_read_byte( pos );
}
if ( sz > 6 )
{
- cassette_image_read( cass, &in2, pos+1, 1 );
- cassette_image_read( cass, &in3, pos+2, 1 );
+ uint8_t in2 = cass->image_read_byte( pos+1 );
+ uint8_t in3 = cass->image_read_byte( pos+2 );
if ( invalid < 10 && in == 0x3c && in2 == 0x5a && (in3 == 0x00 || in3 == 0x01 || in3 == 0xff ) )
{
/* regular block found */
@@ -699,7 +704,12 @@ static cassette_image::error mo5_k5_load( cassette_image *cass )
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) ) )
+ if ( invalid < 10 && sz > 6 &&
+ ( (in == 0x3c && in2 == 0x5a) ||
+ (in == 0xc3 && in2 == 0x5a) ||
+ (in == 0xc3 && in2 == 0x3c) ||
+ (in == 0x87 && in2 == 0x4a) ||
+ (in == 0x3c && in2 == 0x55) ) ) // Androïdes
{
/* special block found */
K5_FILL_0( 1200 );
@@ -729,7 +739,7 @@ static cassette_image::error mo5_k5_load( cassette_image *cass )
-static const struct CassetteFormat mo5_k5 =
+static const cassette_image::Format mo5_k5 =
{ "k5,k7", mo5_k5_identify, mo5_k5_load, nullptr /* no save */ };
@@ -738,9 +748,9 @@ static const struct CassetteFormat mo5_k5 =
static cassette_image::error mo5_wav_identify ( cassette_image *cass,
- struct CassetteOptions *opts )
+ cassette_image::Options *opts )
{
- cassette_image::error e = wavfile_format.identify( cass, opts );
+ cassette_image::error e = cassette_image::wavfile_format.identify( cass, opts );
return e;
}
@@ -748,13 +758,11 @@ static cassette_image::error mo5_wav_identify ( cassette_image *cass,
static cassette_image::error mo5_wav_load ( cassette_image *cass )
{
- cassette_image::error e = wavfile_format.load( cass );
- struct CassetteInfo info;
- int len;
+ cassette_image::error e = cassette_image::wavfile_format.load( cass );
if ( e == cassette_image::error::SUCCESS )
{
- cassette_get_info( cass, &info );
- len = info.sample_count / info.sample_frequency;
+ cassette_image::Info info = cass->get_info( );
+ int 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;
@@ -762,17 +770,17 @@ static cassette_image::error mo5_wav_load ( cassette_image *cass )
-static cassette_image::error mo5_wav_save ( cassette_image *cass, const struct CassetteInfo *info )
+static cassette_image::error mo5_wav_save ( cassette_image *cass, const cassette_image::Info *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 );
+ return cassette_image::wavfile_format.save( cass, info );
}
/* overloaded wav: dump info */
-static const struct CassetteFormat mo5_wav =
+static const cassette_image::Format mo5_wav =
{ "wav", mo5_wav_identify, mo5_wav_load, mo5_wav_save };
@@ -780,9 +788,9 @@ static const struct CassetteFormat mo5_wav =
/********************* formats ************************/
-const struct CassetteFormat *const to7_cassette_formats[] =
+const cassette_image::Format *const to7_cassette_formats[] =
{ &to7_wav, &to7_k7, nullptr };
-const struct CassetteFormat *const mo5_cassette_formats[] =
+const cassette_image::Format *const mo5_cassette_formats[] =
{ &mo5_wav, &mo5_k5, nullptr };
diff --git a/src/lib/formats/thom_dsk.cpp b/src/lib/formats/thom_dsk.cpp
index c8563fc1135..3399a1aa487 100644
--- a/src/lib/formats/thom_dsk.cpp
+++ b/src/lib/formats/thom_dsk.cpp
@@ -1,444 +1,107 @@
// license:BSD-3-Clause
-// copyright-holders:Antoine Mine
-/*********************************************************************
+// copyright-holders:Olivier Galibert
- formats/thom_dsk.c
-
- Thomson disk images
-
- Based on work of Antoine Mine'
-
-*********************************************************************/
-
-#include <string.h>
-#include <assert.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_t sap_crc[] =
+thomson_525_format::thomson_525_format() : wd177x_format(formats)
{
- 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_t thom_sap_crc( uint8_t* data, int size )
-{
- int i;
- uint16_t 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_legacy *floppy)
+const char *thomson_525_format::name() const noexcept
{
- struct sap_dsk_tag *tag;
- tag = (sap_dsk_tag *)floppy_tag(floppy);
- return tag;
+ return "thomson_525";
}
-
-static FLOPPY_IDENTIFY(sap_dsk_identify)
+const char *thomson_525_format::description() const noexcept
{
- 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_legacy *floppy)
-{
- return 1;
+ return "Thomson 5.25 disk image";
}
-static int sap_get_tracks_per_disk(floppy_image_legacy *floppy)
+const char *thomson_525_format::extensions() const noexcept
{
- return get_tag(floppy)->tracks;
+ return "fd";
}
-
-static floperr_t get_offset(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, uint64_t *offset)
-{
- uint64_t offs;
- struct sap_dsk_tag *tag = get_tag(floppy);
- /* translate the sector to a raw sector */
- if (!sector_is_index)
+const thomson_525_format::format thomson_525_format::formats[] = {
{
- 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_legacy *floppy, int head, int track, int sector, bool sector_is_index, void *buffer, size_t buflen)
-{
- uint64_t offset;
- floperr_t err;
- int i;
- uint8_t *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_t*)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_legacy *floppy, int head, int track, int sector, bool sector_is_index, const void *buffer, size_t buflen, int ddam)
-{
- uint64_t offset;
- floperr_t err;
- uint8_t buf[256+6];
- uint16_t 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_t*)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_legacy *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_legacy *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_legacy *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_legacy *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_legacy *floppy, int head, int track, int sector, uint32_t *sector_length)
-{
- floperr_t err;
- err = get_offset(floppy, head, track, sector, false, nullptr);
- 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_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags)
-{
- floperr_t err;
- uint8_t header[4];
- uint64_t 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_legacy *floppy, util::option_resolution *params)
-{
- int track,sector;
- int pos;
- uint8_t 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 j;
- uint8_t 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 ( int i = 0; i < 80; i++ )
- for ( j = 0; j < 16; j++ )
- tag->sector_pos[i][j] = 0;
-
- /* count tracks & fill sector offset table */
- for ( uint64_t i = 0x42; i+4 < floppy_image_size(floppy); i += tag->sector_size + 6 ) // CRC 2 bytes + 4 bytes sector header
- {
- uint8_t 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_legacy *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] =
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
+ 4000,
+ 16, 40, 1,
+ 128, {},
+ 1, {},
+ 17, 22, 12
+ },
{
- { 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++ )
+ floppy_image::FF_525, floppy_image::SSDD, floppy_image::MFM,
+ 2000,
+ 16, 40, 1,
+ 256, {},
+ 1, {},
+ 31, 22, 44
+ },
{
- 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);
-}
-
-
-/* ----------------------------------------------------------------------- */
-
-
-static floperr_t fd_identify(floppy_image_legacy *floppy, int *vote, int tracks, int sector_size)
-{
- uint64_t expected_size;
- expected_size = sector_size;
- expected_size *= tracks;
- expected_size *= 16; /* secetors */
- *vote = (floppy_image_size(floppy) == expected_size) ? 100 : 50;
- return FLOPPY_ERROR_SUCCESS;
-}
+ floppy_image::FF_525, floppy_image::DSDD, floppy_image::MFM,
+ 2000,
+ 16, 40, 2,
+ 256, {},
+ 1, {},
+ 31, 22, 44
+ },
+ {}
+};
-static floperr_t fd_construct(floppy_image_legacy *floppy, int tracks, int sector_size)
+int thomson_525_format::get_image_offset(const format &f, int head, int track) const
{
- struct basicdsk_geometry geometry;
- memset(&geometry, 0, sizeof(geometry));
- geometry.heads = 1;
- geometry.first_sector_id = 1;
- geometry.sector_length = sector_size;
- geometry.tracks = tracks;
- geometry.sectors = 16;
- return basicdsk_construct(floppy, &geometry);
+ return (track + (head ? f.track_count : 0)) * compute_track_size(f);
}
-static FLOPPY_IDENTIFY(fd_80_256_identify)
-{
- return fd_identify(floppy, vote, 80, 256);
-}
-static FLOPPY_CONSTRUCT(fd_80_256_construct)
+thomson_35_format::thomson_35_format() : wd177x_format(formats)
{
- return fd_construct(floppy, 80, 256);
}
-static FLOPPY_IDENTIFY(fd_40_256_identify)
+const char *thomson_35_format::name() const noexcept
{
- return fd_identify(floppy, vote, 40, 256);
+ return "thomson_35";
}
-static FLOPPY_CONSTRUCT(fd_40_256_construct)
+const char *thomson_35_format::description() const noexcept
{
- return fd_construct(floppy, 40, 256);
+ return "Thomson 3.5 disk image";
}
-static FLOPPY_IDENTIFY(fd_80_128_identify)
+const char *thomson_35_format::extensions() const noexcept
{
- return fd_identify(floppy, vote, 80, 128);
+ return "fd";
}
-static FLOPPY_CONSTRUCT(fd_80_128_construct)
-{
- return fd_construct(floppy, 80, 128);
-}
-
-static FLOPPY_IDENTIFY(fd_40_128_identify)
-{
- return fd_identify(floppy, vote, 40, 128);
-}
+// 1280K .fd images exist but are not supported. They represent a notional type
+// of 4-sided disk that can be inserted into 2 drives at once.
+const thomson_35_format::format thomson_35_format::formats[] = {
+ {
+ floppy_image::FF_35, floppy_image::SSDD, floppy_image::MFM,
+ 2000,
+ 16, 80, 1,
+ 256, {},
+ 1, {},
+ 31, 22, 44
+ },
+ {
+ floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM,
+ 2000,
+ 16, 80, 2,
+ 256, {},
+ 1, {},
+ 31, 22, 44
+ },
+ {}
+};
-static FLOPPY_CONSTRUCT(fd_40_128_construct)
+int thomson_35_format::get_image_offset(const format &f, int head, int track) const
{
- return fd_construct(floppy, 40, 128);
+ return (track + (head ? f.track_count : 0)) * compute_track_size(f);
}
-
-/* ----------------------------------------------------------------------- */
-
-LEGACY_FLOPPY_OPTIONS_START(thomson)
-
-LEGACY_FLOPPY_OPTION(fdmfm2, "fd", "Thomson FD (MFM) 80 tracks disk image (3\"1/2 DD)",
- fd_80_256_identify, fd_80_256_construct, nullptr, nullptr)
-
-// Note: no way to distinguish between FD files for 5"1/4 DD and 3"1/2 SD actually, as they have the same size
-// however, we expect 3"1/2 SD to be rather rare, so, we simply put it after 5"1/4 DD
-
-LEGACY_FLOPPY_OPTION(fdmfm, "fd", "Thomson FD (MFM) 40 tracks disk image (5\"1/4 DD)",
- fd_40_256_identify, fd_40_256_construct, nullptr, nullptr)
-
-LEGACY_FLOPPY_OPTION(fd2, "fd", "Thomson FD (FM) 80 tracks disk image (3\"1/2 SD)",
- fd_80_128_identify, fd_80_128_construct, nullptr, nullptr)
-
-LEGACY_FLOPPY_OPTION(fd, "fd", "Thomson FD (FM) 40 tracks disk image (5\"1/4 SD)",
- fd_40_128_identify, fd_40_128_construct, nullptr, nullptr)
-
-LEGACY_FLOPPY_OPTION(sap,"sap", "Thomson SAP floppy disk image",
- sap_dsk_identify, sap_dsk_construct, nullptr, nullptr)
-
-LEGACY_FLOPPY_OPTION(qdd,"qd", "Thomson QDD floppy disk image (2\"8 SD)",
- qdd_dsk_identify, qdd_dsk_construct, nullptr, nullptr)
-
-LEGACY_FLOPPY_OPTIONS_END
+const thomson_525_format FLOPPY_THOMSON_525_FORMAT;
+const thomson_35_format FLOPPY_THOMSON_35_FORMAT;
diff --git a/src/lib/formats/thom_dsk.h b/src/lib/formats/thom_dsk.h
index 24bf68f3b38..09ea1cbec0c 100644
--- a/src/lib/formats/thom_dsk.h
+++ b/src/lib/formats/thom_dsk.h
@@ -1,18 +1,44 @@
// license:BSD-3-Clause
-// copyright-holders:Miodrag Milanovic
+// copyright-holders:Olivier Galibert
#ifndef MAME_FORMATS_THOM_DSK_H
#define MAME_FORMATS_THOM_DSK_H
#pragma once
-#include "flopimg.h"
+#include "wd177x_dsk.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
-*/
+class thomson_525_format : public wd177x_format
+{
+public:
+ thomson_525_format();
-LEGACY_FLOPPY_OPTIONS_EXTERN(thomson);
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+ int get_image_offset(const format &f, int head, int track) const override;
+
+private:
+ static const format formats[];
+};
+
+class thomson_35_format : public wd177x_format
+{
+public:
+ thomson_35_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+ int get_image_offset(const format &f, int head, int track) const override;
+
+private:
+ static const format formats[];
+};
+
+
+extern const thomson_525_format FLOPPY_THOMSON_525_FORMAT;
+extern const thomson_35_format FLOPPY_THOMSON_35_FORMAT;
#endif // MAME_FORMATS_THOM_DSK_H
diff --git a/src/lib/formats/ti99_dsk.cpp b/src/lib/formats/ti99_dsk.cpp
index b248c9dd594..4cca5bdb996 100644
--- a/src/lib/formats/ti99_dsk.cpp
+++ b/src/lib/formats/ti99_dsk.cpp
@@ -1,4 +1,4 @@
-// license:BSD-3-Clause
+// license:LGPL-2.1+
// copyright-holders:Michael Zapf
/*********************************************************************
@@ -17,7 +17,7 @@
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 16 sectors/track
+ DSDD). There are rare occurrences of 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 1,44 MiB (DSHD80).
@@ -40,31 +40,34 @@
********************************************************************/
-#include <string.h>
-#include <time.h>
-#include <assert.h>
-#include <iomanip>
-
-#include "emu.h" // osd_printf_* (in osdcore.h)
-#include "imageutl.h"
#include "ti99_dsk.h"
+#include "imageutl.h"
+
+#include "ioprocs.h"
+
+#include "osdcore.h" // osd_printf_* (in osdcore.h)
+
+#include <cstring>
+#include <ctime>
+#include <iomanip>
#define SECTOR_SIZE 256
-#undef LOG_OUTPUT_FUNC
#define LOG_OUTPUT_FUNC osd_printf_info
-#define LOG_WARN (1U<<1) // Warnings
-#define LOG_HEADER (1U<<2) // Header
-#define LOG_SHIFT (1U<<3) // Shift register
-#define LOG_INTERLEAVE (1U<<4) // Interleave information
-#define LOG_INFO (1U<<5) // Disk info
-#define LOG_DETAIL (1U<<6) // Details
-#define LOG_TRACK (1U<<7) // Track output
+#define LOG_WARN (1U << 1) // Warnings
+#define LOG_HEADER (1U << 2) // Header
+#define LOG_SHIFT (1U << 3) // Shift register
+#define LOG_INTERLEAVE (1U << 4) // Interleave information
+#define LOG_INFO (1U << 5) // Disk info
+#define LOG_DETAIL (1U << 6) // Details
+#define LOG_TRACK (1U << 7) // Track output
#define VERBOSE ( LOG_WARN )
-#include "logmacro.h"
+#define __EMU_H__ // logmacro wasn't really intended to be used outside stuff that uses libemu
+#include "../emu/logmacro.h"
+
// ====================================================
// Common methods for both formats.
@@ -78,13 +81,16 @@ int ti99_floppy_format::get_encoding(int cell_size)
/*
Load the image from disk and convert it into a sequence of flux levels.
*/
-bool ti99_floppy_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool ti99_floppy_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
+ uint64_t file_size;
+ if (io.length(file_size))
+ return false;
+
int cell_size = 0;
int sector_count = 0;
int heads = 0;
int log_track_count = 0;
- int file_size = io_generic_size(io);
bool img_high_tpi = false;
bool drive_high_tpi = false;
@@ -95,7 +101,7 @@ bool ti99_floppy_format::load(io_generic *io, uint32_t form_factor, floppy_image
// (the track count as defined by the file system)
determine_sizes(io, cell_size, sector_count, heads, log_track_count);
- image->get_maximal_geometry(track_count, head_count);
+ image.get_maximal_geometry(track_count, head_count);
drive_high_tpi = (track_count > 44);
// Depends on the image format (SDF or TDF)
@@ -209,11 +215,8 @@ bool ti99_floppy_format::load(io_generic *io, uint32_t form_factor, floppy_image
/*
Save all tracks to the image file.
*/
-bool ti99_floppy_format::save(io_generic *io, floppy_image *image)
+bool ti99_floppy_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
- int act_track_size = 0;
-
- uint8_t bitstream[500000/8];
uint8_t sectordata[9216]; // max size (36*256)
int cellsizes[] = { 2000, 4000, 1000, 2000 };
@@ -221,7 +224,7 @@ bool ti99_floppy_format::save(io_generic *io, floppy_image *image)
// Do we use double-stepping?
// If our image was loaded into a 80-track drive, we will always write 80 tracks.
int track_count, head_count;
- image->get_maximal_geometry(track_count, head_count);
+ image.get_maximal_geometry(track_count, head_count);
if (track_count > 80) track_count = 80;
else
@@ -244,13 +247,10 @@ bool ti99_floppy_format::save(io_generic *io, floppy_image *image)
for (int track = 0; track < track_count; track++)
{
// Retrieve the cells from the flux sequence. This also delivers the actual track size.
- generate_bitstream_from_track(track, head, cell_size, bitstream, act_track_size, image);
-
- // Maybe the track has become longer due to moving splices
- if (act_track_size > 200000000/cell_size) act_track_size = 200000000/cell_size;
+ auto bitstream = generate_bitstream_from_track(track, head, cell_size, image);
LOGMASKED(LOG_DETAIL, "[ti99_dsk] Getting sectors from track %d, head %d\n", track, head);
- seccount = get_sectors(bitstream, act_track_size, encoding, track, head, expected_sectors, sectordata, sector);
+ seccount = get_sectors(bitstream, encoding, track, head, expected_sectors, sectordata, sector);
LOGMASKED(LOG_DETAIL, "[ti99_dsk] Seccount = %d\n", seccount);
// We may have more sectors in MFM (18). This is OK in track 0; otherwise we need to restart the process.
@@ -329,7 +329,7 @@ enum
/*
Build a new track from sector contents.
*/
-void ti99_floppy_format::generate_fm_track_from_sectors(floppy_image *image, uint8_t *sectordata, int sector_count, int *sectornmb, int *secoffset, int track, int trackid, int head)
+void ti99_floppy_format::generate_fm_track_from_sectors(floppy_image &image, uint8_t *sectordata, int sector_count, int *sectornmb, int *secoffset, int track, int trackid, int head)
{
std::vector<uint32_t> buffer;
@@ -423,7 +423,7 @@ void ti99_floppy_format::generate_fm_track_from_sectors(floppy_image *image, uin
generate_track_from_levels(track, head, buffer, 0, image);
}
-void ti99_floppy_format::generate_mfm_track_from_sectors(floppy_image *image, uint8_t *sectordata, int sector_count, int *sectornmb, int *secoffset, int track, int trackid, int head)
+void ti99_floppy_format::generate_mfm_track_from_sectors(floppy_image &image, uint8_t *sectordata, int sector_count, int *sectornmb, int *secoffset, int track, int trackid, int head)
{
// MFM16: (80,50,22,50,4e,12)
// MFM18/36: (10,30,22,21,4e,12)
@@ -562,16 +562,15 @@ enum
The sectors are assumed to have a length of 256 byte, and their sequence
is stored in the secnumber array.
*/
-int ti99_floppy_format::get_sectors(const uint8_t *bitstream, int cell_count, int encoding, int track, int head, int sectors, uint8_t *sectordata, int *secnumber)
+int ti99_floppy_format::get_sectors(const std::vector<bool> &bitstream, int encoding, int track, int head, int sectors, uint8_t *sectordata, int *secnumber)
{
int bitpos = 0;
int lastpos = 0;
int spos = 0;
int seccount = 0;
uint16_t shift_reg = 0;
- int bytepos = 0;
uint8_t databyte = 0;
- uint8_t curbyte = 0;
+ [[maybe_unused]] uint8_t curbyte = 0;
int rep = 0;
int cursect = 0;
@@ -582,12 +581,10 @@ int ti99_floppy_format::get_sectors(const uint8_t *bitstream, int cell_count, in
int first = (encoding==floppy_image::MFM)? A1IDAM1 : FMIDAM;
int state = first;
- while (bitpos < cell_count)
+ while (bitpos < bitstream.size())
{
LOGMASKED(LOG_SHIFT, "[ti99_dsk] shift = %04x\n", shift_reg);
- shift_reg = (shift_reg << 1) & 0xffff;
- if ((bitpos & 0x07)==0) curbyte = bitstream[bytepos++];
- if ((curbyte & 0x80) != 0) shift_reg |= 1;
+ shift_reg = (shift_reg << 1) | bitstream[bitpos];
if (((bitpos - lastpos) & 1)==0)
{
@@ -908,24 +905,27 @@ uint8_t ti99_floppy_format::get_data_from_encoding(uint16_t raw)
bad sector map, we just check whether there are 3 more sectors and ignore
them.
*/
-const char *ti99_sdf_format::name() const
+const char *ti99_sdf_format::name() const noexcept
{
- return "sdf";
+ return "ti99";
}
-const char *ti99_sdf_format::description() const
+const char *ti99_sdf_format::description() const noexcept
{
return "TI99 sector dump floppy disk image";
}
-const char *ti99_sdf_format::extensions() const
+const char *ti99_sdf_format::extensions() const noexcept
{
return "dsk";
}
-int ti99_sdf_format::identify(io_generic *io, uint32_t form_factor)
+int ti99_sdf_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t file_size = io_generic_size(io);
+ uint64_t file_size;
+ if (io.length(file_size))
+ return 0;
+
int vote = 0;
// Adding support for another sector image format which adds 768 bytes
@@ -943,7 +943,7 @@ int ti99_sdf_format::identify(io_generic *io, uint32_t form_factor)
case 368640: // DSDD
case 737280: // DSDD80
case 1474560: // DSQD
- vote = 50;
+ vote = FIFID_SIZE;
break;
default:
vote = 0;
@@ -954,13 +954,13 @@ int ti99_sdf_format::identify(io_generic *io, uint32_t form_factor)
{
// Read first sector (Volume Information Block)
ti99vib vib;
- io_generic_read(io, &vib, 0, sizeof(ti99vib));
+ /*auto const [err, actual] =*/ read_at(io, 0, &vib, sizeof(ti99vib)); // FIXME: check for errors and premature EOF
// Check from contents
if ((vib.id[0]=='D')&&(vib.id[1]=='S')&&(vib.id[2]=='K'))
{
LOGMASKED(LOG_INFO, "[ti99_dsk] Found formatted SDF disk medium\n");
- vote = 100;
+ vote |= FIFID_SIGN;
}
else
{
@@ -971,10 +971,14 @@ int ti99_sdf_format::identify(io_generic *io, uint32_t form_factor)
return vote;
}
-void ti99_sdf_format::determine_sizes(io_generic *io, int& cell_size, int& sector_count, int& heads, int& tracks)
+void ti99_sdf_format::determine_sizes(util::random_read &io, int& cell_size, int& sector_count, int& heads, int& tracks) const
{
- uint64_t file_size = io_generic_size(io);
- ti99vib vib;
+ uint64_t file_size;
+ if (io.length(file_size))
+ {
+ cell_size = sector_count = heads = tracks = 0;
+ return;
+ }
cell_size = 0;
sector_count = 0;
@@ -987,7 +991,8 @@ void ti99_sdf_format::determine_sizes(io_generic *io, int& cell_size, int& secto
file_size -= 768;
// Read first sector
- io_generic_read(io, &vib, 0, sizeof(ti99vib));
+ ti99vib vib;
+ /*auto const [err, actual] =*/ read_at(io, 0, &vib, sizeof(ti99vib)); // FIXME: check for errors and premature EOF
// Check from contents
if ((vib.id[0]=='D')&&(vib.id[1]=='S')&&(vib.id[2]=='K'))
@@ -1061,18 +1066,18 @@ void ti99_sdf_format::determine_sizes(io_generic *io, int& cell_size, int& secto
}
}
-int ti99_sdf_format::get_track_size(int sector_count)
+int ti99_sdf_format::get_track_size(int sector_count) const
{
return sector_count * SECTOR_SIZE;
}
-void ti99_sdf_format::load_track(io_generic *io, uint8_t *sectordata, int *sector, int *secoffset, int head, int track, int sectorcount, int trackcount)
+void ti99_sdf_format::load_track(util::random_read &io, uint8_t *sectordata, int *sector, int *secoffset, int head, int track, int sectorcount, int trackcount) const
{
// Calculate the track offset from the beginning of the image file
int logicaltrack = (head==0)? track : (2*trackcount - track - 1);
int position = logicaltrack * get_track_size(sectorcount);
- io_generic_read(io, sectordata, position, sectorcount*SECTOR_SIZE);
+ /*auto const [err, actual] =*/ read_at(io, position, sectordata, sectorcount*SECTOR_SIZE); // FIXME: check for errors and premature EOF
// Interleave and skew
int interleave = 7;
@@ -1110,7 +1115,7 @@ void ti99_sdf_format::load_track(io_generic *io, uint8_t *sectordata, int *secto
/*
For debugging. Outputs the byte array in a xxd-like way.
*/
-void ti99_floppy_format::dumpbytes(uint8_t* trackdata, int length)
+void ti99_floppy_format::dumpbytes(const uint8_t* trackdata, int length)
{
for (int i=0; i < length; i+=16)
{
@@ -1118,7 +1123,7 @@ void ti99_floppy_format::dumpbytes(uint8_t* trackdata, int length)
}
}
-std::string ti99_floppy_format::dumpline(uint8_t* line, int address) const
+std::string ti99_floppy_format::dumpline(const uint8_t* line, int address)
{
std::ostringstream stream;
stream << std::hex << std::setfill('0') << std::setw(6) << unsigned(address);
@@ -1140,24 +1145,22 @@ std::string ti99_floppy_format::dumpline(uint8_t* line, int address) const
Write the data to the disk. We have a list of sector positions, so we
just need to go through that list and save each sector in the sector data.
*/
-void ti99_sdf_format::write_track(io_generic *io, uint8_t *sectordata, int *sector, int track, int head, int sector_count, int track_count)
+void ti99_sdf_format::write_track(util::random_read_write &io, uint8_t *sectordata, int *sector, int track, int head, int sector_count, int track_count) const
{
- uint8_t* buf;
-
int logicaltrack = head * track_count;
logicaltrack += ((head&1)==0)? track : (track_count - 1 - track);
int trackoffset = logicaltrack * sector_count * SECTOR_SIZE;
for (int i=0; i < sector_count; i++)
{
- buf = sectordata + i * SECTOR_SIZE;
+ uint8_t const *const buf = sectordata + i * SECTOR_SIZE;
LOGMASKED(LOG_DETAIL, "[ti99_dsk] Writing sector %d (offset %06x)\n", sector[i], sector[i] * SECTOR_SIZE);
- io_generic_write(io, buf, trackoffset + sector[i] * SECTOR_SIZE, SECTOR_SIZE);
+ /*auto const [err, actual] =*/ write_at(io, trackoffset + sector[i] * SECTOR_SIZE, buf, SECTOR_SIZE); // FIXME: check for errors
}
}
-const floppy_format_type FLOPPY_TI99_SDF_FORMAT = &floppy_image_format_creator<ti99_sdf_format>;
+const ti99_sdf_format FLOPPY_TI99_SDF_FORMAT;
/*
Track Dump Format
@@ -1198,17 +1201,17 @@ const floppy_format_type FLOPPY_TI99_SDF_FORMAT = &floppy_image_format_creator<t
(Head,Track): (0,0) (0,1) (0,2) ... (0,38) (0,39) (1,0) (1,1) ... (1,39)
*/
-const char *ti99_tdf_format::name() const
+const char *ti99_tdf_format::name() const noexcept
{
return "tdf";
}
-const char *ti99_tdf_format::description() const
+const char *ti99_tdf_format::description() const noexcept
{
return "TI99 track dump floppy disk image";
}
-const char *ti99_tdf_format::extensions() const
+const char *ti99_tdf_format::extensions() const noexcept
{
return "dsk,dtk";
}
@@ -1216,7 +1219,7 @@ const char *ti99_tdf_format::extensions() const
/*
Determine whether the image file can be interpreted as a track dump
*/
-int ti99_tdf_format::identify(io_generic *io, uint32_t form_factor)
+int ti99_tdf_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
int vote = 0;
uint8_t fulltrack[6872];
@@ -1232,14 +1235,14 @@ int ti99_tdf_format::identify(io_generic *io, uint32_t form_factor)
// Do we have a plausible image size? From the size alone we only give a 50 percent vote.
if (sector_count != 0)
- vote = 50;
+ vote = FIFID_SIZE;
if (vote > 0)
{
LOGMASKED(LOG_INFO, "[ti99_dsk] Image file length matches TDF\n");
// Fetch track 0
- io_generic_read(io, fulltrack, 0, get_track_size(sector_count));
+ /*auto const [err, actual] =*/ read_at(io, 0, fulltrack, get_track_size(sector_count)); // FIXME: check for errors and premature EOF
if (sector_count == 9)
{
@@ -1275,7 +1278,7 @@ int ti99_tdf_format::identify(io_generic *io, uint32_t form_factor)
else
{
LOGMASKED(LOG_INFO, "[ti99_dsk] Image format complies with TDF\n");
- vote = 100;
+ vote |= FIFID_STRUCT;
}
}
else LOGMASKED(LOG_INFO, "[ti99_dsk] Disk image is not a TDF image\n");
@@ -1283,52 +1286,63 @@ int ti99_tdf_format::identify(io_generic *io, uint32_t form_factor)
}
/*
- Find the proper format for a given image file.
+ Find the proper format for a given image file. Tracks are counted per side.
+ Note that only two formats are actually compatible with the PC99 emulator.
*/
-void ti99_tdf_format::determine_sizes(io_generic *io, int& cell_size, int& sector_count, int& heads, int& tracks)
+void ti99_tdf_format::determine_sizes(util::random_read &io, int& cell_size, int& sector_count, int& heads, int& tracks) const
{
- uint64_t file_size = io_generic_size(io);
- heads = 2; // TDF only supports two-sided recordings
- tracks = 40;
+ uint64_t file_size;
+ if (io.length(file_size))
+ {
+ cell_size = sector_count = heads = tracks = 0;
+ return;
+ }
+
// LOGMASKED(LOG_INFO, "[ti99_dsk] Image size = %ld\n", file_size); // doesn't compile
switch (file_size)
{
- case 260240: // used in PC99
- cell_size = 4000;
+ case 260240: // used in PC99
sector_count = 9;
+ tracks = 40;
break;
- case 491520: // 320K HX5102
- cell_size = 2000;
+ case 491520: // 320K HX5102
sector_count = 16;
+ tracks = 40;
break;
- case 549760: // used in PC99
- cell_size = 2000;
+ case 549760: // used in PC99
sector_count = 18;
+ tracks = 40;
break;
+ case 1003520:
+ sector_count = 36;
+ tracks = 40;
+ break;
+
case 520480:
- cell_size = 4000;
sector_count = 9;
+ tracks = 80;
break;
case 983040:
- cell_size = 2000;
sector_count = 16;
tracks = 80;
break;
case 1099520:
- cell_size = 2000;
sector_count = 18;
tracks = 80;
break;
case 2007040:
- cell_size = 1000;
- sector_count = 18;
+ sector_count = 36;
tracks = 80;
break;
+
default:
cell_size = 0;
sector_count = 0;
- break;
+ return;
}
+
+ heads = 2; // TDF only supports two-sided recordings
+ cell_size = (sector_count <= 9)? 4000 : ((sector_count <= 18)? 2000 : 1000);
}
/*
@@ -1337,13 +1351,13 @@ void ti99_tdf_format::determine_sizes(io_generic *io, int& cell_size, int& secto
track from scratch. TDF is not as flexible as it suggests, it does not
allow different gap lengths and so on.
*/
-void ti99_tdf_format::load_track(io_generic *io, uint8_t *sectordata, int *sector, int *secoffset, int head, int track, int sectorcount, int trackcount)
+void ti99_tdf_format::load_track(util::random_read &io, uint8_t *sectordata, int *sector, int *secoffset, int head, int track, int sectorcount, int trackcount) const
{
- uint8_t fulltrack[6872]; // space for a full TDF track
+ uint8_t fulltrack[12544]; // space for a full TDF track
// Read beginning of track 0. We need this to get the first gap, according
// to the format
- io_generic_read(io, fulltrack, 0, 100);
+ read_at(io, 0, fulltrack, 100); // FIXME: check for errors and premature EOF
int offset = 0;
int tracksize = get_track_size(sectorcount);
@@ -1384,7 +1398,7 @@ void ti99_tdf_format::load_track(io_generic *io, uint8_t *sectordata, int *secto
int base = (head * trackcount + track) * tracksize;
int position = 0;
- io_generic_read(io, fulltrack, base, tracksize);
+ read_at(io, base, fulltrack, tracksize); // FIXME: check for errors and premature EOF
for (int i=0; i < sectorcount; i++)
{
@@ -1432,9 +1446,9 @@ void ti99_tdf_format::load_track(io_generic *io, uint8_t *sectordata, int *secto
need the sector contents and the sector sequence, which are passed via
sectordata, sector, and sector_count.
*/
-void ti99_tdf_format::write_track(io_generic *io, uint8_t *sectordata, int *sector, int track, int head, int sector_count, int track_count)
+void ti99_tdf_format::write_track(util::random_read_write &io, uint8_t *sectordata, int *sector, int track, int head, int sector_count, int track_count) const
{
- uint8_t trackdata[14000];
+ uint8_t trackdata[12544];
int offset = ((track_count * head) + track) * get_track_size(sector_count);
int a1 = 0;
@@ -1486,10 +1500,10 @@ void ti99_tdf_format::write_track(io_generic *io, uint8_t *sectordata, int *sect
for (int i=0; i < param[WGAP3]; i++) trackdata[pos++] = param[WGAPBYTE];
}
for (int i=0; i < param[WGAP4]; i++) trackdata[pos++] = param[WGAPBYTE];
- io_generic_write(io, trackdata, offset, get_track_size(sector_count));
+ /*auto const [err, actual] =*/ write_at(io, offset, trackdata, get_track_size(sector_count)); // FIXME: check for errors
}
-int ti99_tdf_format::get_track_size(int sector_count)
+int ti99_tdf_format::get_track_size(int sector_count) const
{
switch (sector_count)
{
@@ -1505,4 +1519,4 @@ int ti99_tdf_format::get_track_size(int sector_count)
return 0;
}
-const floppy_format_type FLOPPY_TI99_TDF_FORMAT = &floppy_image_format_creator<ti99_tdf_format>;
+const ti99_tdf_format FLOPPY_TI99_TDF_FORMAT;
diff --git a/src/lib/formats/ti99_dsk.h b/src/lib/formats/ti99_dsk.h
index 04ba3e88a14..3fbf9b6bb1e 100644
--- a/src/lib/formats/ti99_dsk.h
+++ b/src/lib/formats/ti99_dsk.h
@@ -2,7 +2,7 @@
// copyright-holders:Michael Zapf
/*********************************************************************
- formats/ti99_dsk.c
+ formats/ti99_dsk.h
TI99 and Geneve disk images
@@ -22,30 +22,29 @@
class ti99_floppy_format : public floppy_image_format_t
{
public:
- bool supports_save() const override { return true; }
- bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- bool save(io_generic *io, floppy_image *image) override;
+ bool supports_save() const noexcept override { return true; }
+ bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
protected:
- int decode_bitstream(const uint8_t *bitstream, uint8_t *trackdata, int *sector, int cell_count, int encoding, uint8_t gapbytes, int track_size);
- uint8_t get_data_from_encoding(uint16_t raw);
- int get_sectors(const uint8_t *bitstream, int cell_count, int encoding, int track, int head, int sectors, uint8_t *sectordata, int *secnumber);
+ static uint8_t get_data_from_encoding(uint16_t raw);
+ static int get_sectors(const std::vector<bool> &bitstream, int encoding, int track, int head, int sectors, uint8_t *sectordata, int *secnumber);
- virtual int min_heads() =0;
+ virtual int min_heads() const =0;
- virtual void determine_sizes(io_generic *io, int& cell_size, int& sector_count, int& heads, int& tracks) =0;
- virtual int get_track_size(int sector_count) =0;
- virtual void load_track(io_generic *io, uint8_t *sectordata, int *sector, int *secoffset, int head, int track, int sectorcount, int trackcount) =0;
- virtual void write_track(io_generic *io, uint8_t *sectordata, int *sector, int track, int head, int sector_count, int track_count) =0;
+ virtual void determine_sizes(util::random_read &io, int& cell_size, int& sector_count, int& heads, int& tracks) const =0;
+ virtual int get_track_size(int sector_count) const=0;
+ virtual void load_track(util::random_read &io, uint8_t *sectordata, int *sector, int *secoffset, int head, int track, int sectorcount, int trackcount) const=0;
+ virtual void write_track(util::random_read_write &io, uint8_t *sectordata, int *sector, int track, int head, int sector_count, int track_count) const=0;
- int get_encoding(int cell_size);
+ static int get_encoding(int cell_size);
- void generate_fm_track_from_sectors(floppy_image *image, uint8_t *sectordata, int sector_count, int *sector, int *secoffset, int track, int trackid, int head);
- void generate_mfm_track_from_sectors(floppy_image *image, uint8_t *sectordata, int sector_count, int *sector, int *secoffset, int track, int trackid, int head);
+ static void generate_fm_track_from_sectors(floppy_image &image, uint8_t *sectordata, int sector_count, int *sector, int *secoffset, int track, int trackid, int head);
+ static void generate_mfm_track_from_sectors(floppy_image &image, uint8_t *sectordata, int sector_count, int *sector, int *secoffset, int track, int trackid, int head);
// Debugging
- void dumpbytes(uint8_t* trackdata, int length);
- std::string dumpline(uint8_t* line, int address) const;
+ static void dumpbytes(const uint8_t* trackdata, int length);
+ static std::string dumpline(const uint8_t* line, int address);
};
/*
@@ -54,19 +53,19 @@ protected:
class ti99_sdf_format : public ti99_floppy_format
{
public:
- int identify(io_generic *io, uint32_t form_factor) override;
- const char *name() const override;
- const char *description() const override;
- const char *extensions() const override;
+ int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ const char *name() const noexcept override;
+ const char *description() const noexcept override;
+ const char *extensions() const noexcept override;
private:
- void determine_sizes(io_generic *io, int& cell_size, int& sector_count, int& heads, int& tracks) override;
- int get_track_size(int sector_count) override;
- void write_track(io_generic *io, uint8_t *sectordata, int *sector, int track, int head, int sector_count, int track_count) override;
- void load_track(io_generic *io, uint8_t *sectordata, int *sector, int *secoffset, int head, int track, int sector_count, int track_count) override;
+ void determine_sizes(util::random_read &io, int& cell_size, int& sector_count, int& heads, int& tracks) const override;
+ int get_track_size(int sector_count) const override;
+ void write_track(util::random_read_write &io, uint8_t *sectordata, int *sector, int track, int head, int sector_count, int track_count) const override;
+ void load_track(util::random_read &io, uint8_t *sectordata, int *sector, int *secoffset, int head, int track, int sector_count, int track_count) const override;
// This format supports single-sided images
- int min_heads() override { return 1; }
+ int min_heads() const override { return 1; }
struct ti99vib
{
@@ -85,7 +84,7 @@ private:
};
};
-extern const floppy_format_type FLOPPY_TI99_SDF_FORMAT;
+extern const ti99_sdf_format FLOPPY_TI99_SDF_FORMAT;
/*
Implementation of the track dump format.
@@ -93,21 +92,21 @@ extern const floppy_format_type FLOPPY_TI99_SDF_FORMAT;
class ti99_tdf_format : public ti99_floppy_format
{
public:
- int identify(io_generic *io, uint32_t form_factor) override;
- const char *name() const override;
- const char *description() const override;
- const char *extensions() const override;
+ int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ const char *name() const noexcept override;
+ const char *description() const noexcept override;
+ const char *extensions() const noexcept override;
private:
- void determine_sizes(io_generic *io, int& cell_size, int& sector_count, int& heads, int& tracks) override;
- void load_track(io_generic *io, uint8_t *sectordata, int *sector, int *secoffset, int head, int track, int sectorcount, int trackcount) override;
- void write_track(io_generic *io, uint8_t *sectordata, int *sector, int track, int head, int sector_count, int track_count) override;
- int get_track_size(int sector_count) override;
+ void determine_sizes(util::random_read &io, int& cell_size, int& sector_count, int& heads, int& tracks) const override;
+ void load_track(util::random_read &io, uint8_t *sectordata, int *sector, int *secoffset, int head, int track, int sectorcount, int trackcount) const override;
+ void write_track(util::random_read_write &io, uint8_t *sectordata, int *sector, int track, int head, int sector_count, int track_count) const override;
+ int get_track_size(int sector_count) const override;
// This format only supports double-sided images
- int min_heads() override { return 2; }
+ int min_heads() const override { return 2; }
};
-extern const floppy_format_type FLOPPY_TI99_TDF_FORMAT;
+extern const ti99_tdf_format FLOPPY_TI99_TDF_FORMAT;
#endif // MAME_FORMATS_TI99_DSK_H
diff --git a/src/lib/formats/tibdd001_dsk.cpp b/src/lib/formats/tibdd001_dsk.cpp
new file mode 100644
index 00000000000..1404c4226e1
--- /dev/null
+++ b/src/lib/formats/tibdd001_dsk.cpp
@@ -0,0 +1,40 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/*********************************************************************
+
+ formats/tibdd001_dsk.cpp
+
+ TIB Disc Drive DD-001 disk images
+
+*********************************************************************/
+
+#include "formats/tibdd001_dsk.h"
+
+tib_dd_001_format::tib_dd_001_format() : upd765_format(formats)
+{
+}
+
+const char *tib_dd_001_format::name() const noexcept
+{
+ return "tibdd001";
+}
+
+const char *tib_dd_001_format::description() const noexcept
+{
+ return "TIB Disc Drive DD-001 floppy disk image";
+}
+
+const char *tib_dd_001_format::extensions() const noexcept
+{
+ return "img";
+}
+
+const tib_dd_001_format::format tib_dd_001_format::formats[] = {
+ { /* 720K 3 1/2 inch double density */
+ floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM,
+ 2000, 9, 80, 2, 512, {}, 1, {}, 80, 50, 22, 80
+ },
+ {}
+};
+
+const tib_dd_001_format FLOPPY_TIB_DD_001_FORMAT;
diff --git a/src/lib/formats/tibdd001_dsk.h b/src/lib/formats/tibdd001_dsk.h
new file mode 100644
index 00000000000..2830837c80e
--- /dev/null
+++ b/src/lib/formats/tibdd001_dsk.h
@@ -0,0 +1,33 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder
+/*********************************************************************
+
+ formats/tibdd001_dsk.h
+
+ TIB Disc Drive DD-001 disk images
+
+*********************************************************************/
+#ifndef MAME_FORMATS_TIB_DD_001_DSK_H
+#define MAME_FORMATS_TIB_DD_001_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+#include "upd765_dsk.h"
+
+class tib_dd_001_format : public upd765_format
+{
+public:
+ tib_dd_001_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const format formats[];
+};
+
+extern const tib_dd_001_format FLOPPY_TIB_DD_001_FORMAT;
+
+#endif // MAME_FORMATS_TIB_DD_001_DSK_H
diff --git a/src/lib/formats/tiki100_dsk.cpp b/src/lib/formats/tiki100_dsk.cpp
index 2258f29c47a..fcc4644e4c0 100644
--- a/src/lib/formats/tiki100_dsk.cpp
+++ b/src/lib/formats/tiki100_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/tiki100_dsk.c
+ formats/tiki100_dsk.cpp
TIKI 100 disk image format
*********************************************************************/
-#include <assert.h>
-
#include "formats/tiki100_dsk.h"
tiki100_format::tiki100_format() : wd177x_format(formats)
{
}
-const char *tiki100_format::name() const
+const char *tiki100_format::name() const noexcept
{
return "tiki100";
}
-const char *tiki100_format::description() const
+const char *tiki100_format::description() const noexcept
{
return "TIKI 100 disk image";
}
-const char *tiki100_format::extensions() const
+const char *tiki100_format::extensions() const noexcept
{
return "dsk";
}
@@ -116,4 +114,4 @@ const tiki100_format::format tiki100_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_TIKI100_FORMAT = &floppy_image_format_creator<tiki100_format>;
+const tiki100_format FLOPPY_TIKI100_FORMAT;
diff --git a/src/lib/formats/tiki100_dsk.h b/src/lib/formats/tiki100_dsk.h
index 197b114fad3..d5a2a03255e 100644
--- a/src/lib/formats/tiki100_dsk.h
+++ b/src/lib/formats/tiki100_dsk.h
@@ -19,14 +19,14 @@ class tiki100_format : public wd177x_format
public:
tiki100_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_TIKI100_FORMAT;
+extern const tiki100_format FLOPPY_TIKI100_FORMAT;
#endif // MAME_FORMATS_TIKI100_DSK_H
diff --git a/src/lib/formats/tim011_dsk.cpp b/src/lib/formats/tim011_dsk.cpp
new file mode 100644
index 00000000000..0afa2bb82d7
--- /dev/null
+++ b/src/lib/formats/tim011_dsk.cpp
@@ -0,0 +1,45 @@
+// license:BSD-3-Clause
+// copyright-holders:Miodrag Milanovic
+/*********************************************************************
+
+ formats/tim011_dsk.cpp
+
+ TIM 011 format
+
+*********************************************************************/
+
+#include "formats/tim011_dsk.h"
+
+tim011_format::tim011_format() : upd765_format(formats)
+{
+}
+
+const char *tim011_format::name() const noexcept
+{
+ return "tim011";
+}
+
+const char *tim011_format::description() const noexcept
+{
+ return "TIM 011 disk image";
+}
+
+const char *tim011_format::extensions() const noexcept
+{
+ return "img";
+}
+
+// Unverified gap sizes.
+const tim011_format::format tim011_format::formats[] = {
+ {
+ floppy_image::FF_35, floppy_image::DSDD, floppy_image::MFM,
+ 2000,
+ 5, 80, 2,
+ 1024, {},
+ 17, {},
+ 80, 50, 22, 80
+ },
+ {}
+};
+
+const tim011_format FLOPPY_TIM011_FORMAT;
diff --git a/src/lib/formats/tim011_dsk.h b/src/lib/formats/tim011_dsk.h
new file mode 100644
index 00000000000..a33fca51049
--- /dev/null
+++ b/src/lib/formats/tim011_dsk.h
@@ -0,0 +1,32 @@
+// license:BSD-3-Clause
+// copyright-holders:Miodrag Milanovic
+/*********************************************************************
+
+ formats/tim011_dsk.h
+
+ TIM 011 format
+
+*********************************************************************/
+#ifndef MAME_FORMATS_TIM011_DSK_H
+#define MAME_FORMATS_TIM011_DSK_H
+
+#pragma once
+
+#include "upd765_dsk.h"
+
+class tim011_format : public upd765_format
+{
+public:
+ tim011_format();
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+private:
+ static const format formats[];
+};
+
+extern const tim011_format FLOPPY_TIM011_FORMAT;
+
+#endif // MAME_FORMATS_TIM011_DSK_H
diff --git a/src/lib/formats/trd_dsk.cpp b/src/lib/formats/trd_dsk.cpp
index 4766a3426e3..cf1a7f00445 100644
--- a/src/lib/formats/trd_dsk.cpp
+++ b/src/lib/formats/trd_dsk.cpp
@@ -2,42 +2,117 @@
// copyright-holders:Miodrag Milanovic
/*********************************************************************
- formats/trd_dsk.c
+ formats/trd_dsk.cpp
TRD disk images
*********************************************************************/
-#include <assert.h>
-
#include "formats/trd_dsk.h"
+#include "ioprocs.h"
+
+#include <tuple>
+
+
trd_format::trd_format() : wd177x_format(formats)
{
}
-const char *trd_format::name() const
+const char *trd_format::name() const noexcept
{
return "trd";
}
-const char *trd_format::description() const
+const char *trd_format::description() const noexcept
{
return "TRD floppy disk image";
}
-const char *trd_format::extensions() const
+const char *trd_format::extensions() const noexcept
{
return "trd";
}
+int trd_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ int index = -1;
+ for (int i = 0; formats[i].form_factor; i++)
+ {
+ const format &f = formats[i];
+ if (form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
+ continue;
+
+ uint64_t format_size = f.track_count * f.head_count * compute_track_size(f);
+
+ if (size == format_size)
+ {
+ index = i; // at least size match, save it for the case if there will be no exact matches
+
+ std::error_condition err;
+ size_t actual;
+ uint8_t sectdata[0x100];
+ if (f.encoding == floppy_image::MFM)
+ std::tie(err, actual) = read_at(io, 0x800, sectdata, 0x100);
+ else
+ {
+ std::tie(err, actual) = read_at(io, 0x100, sectdata, 0x100);
+ for (int i = 0; i < 0x100; i++)
+ sectdata[i] ^= 0xff;
+ }
+ if (err || (0x100 != actual))
+ continue;
+
+ uint8_t disktype = sectdata[0xe3]; // 16 - DS80, 17 - DS40, 18 - SS80, 19 - SS40
+ if (disktype < 0x16 || disktype > 0x19)
+ continue;
+
+ uint8_t heads = ((disktype == 0x16) || disktype == 0x17) ? 2 : 1;
+ uint8_t tracks = ((disktype == 0x16) || disktype == 0x18) ? 80 : 40;
+
+ if (f.sector_count != sectdata[0xe7] || f.head_count != heads || f.track_count != tracks)
+ continue;
+
+ return i;
+ }
+ }
+ return index;
+}
+
const trd_format::format trd_format::formats[] = {
- { // 5"25 640K double density
+ // GAP4A should be 0 bytes
+ { // 5"25 640K 80 track double sided double density
floppy_image::FF_525, floppy_image::DSQD, floppy_image::MFM,
- // GAP4A - 0(!) bytes, GAP1 - 10 bytes, GAP2 - 22 bytes, GAP3 - 60 bytes, GAP4B - upto track end
- 2000, 16, 80, 2, 256, {}, 1, {}, 10, 22, 60
+ 2000, 16, 80, 2, 256, {}, -1, { 1,9,2,10,3,11,4,12,5,13,6,14,7,15,8,16 }, 10, 22, 60
+ },
+ { // 5"25 320K 80 track single sided double density
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::MFM,
+ 2000, 16, 80, 1, 256, {}, -1, { 1,9,2,10,3,11,4,12,5,13,6,14,7,15,8,16 }, 10, 22, 60
+ },
+ { // 5"25 320K 40 track double sided double density
+ floppy_image::FF_525, floppy_image::DSSD, floppy_image::MFM,
+ 2000, 16, 40, 2, 256, {}, -1, { 1,9,2,10,3,11,4,12,5,13,6,14,7,15,8,16 }, 10, 22, 60
+ },
+ { // 5"25 160K 40 track single sided double density
+ floppy_image::FF_525, floppy_image::SSSD, floppy_image::MFM,
+ 2000, 16, 40, 1, 256, {}, -1, { 1,9,2,10,3,11,4,12,5,13,6,14,7,15,8,16 }, 10, 22, 60
+ },
+
+ { // 5"25 400K 80 track double sided single density
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::FM,
+ // GAPs and interleave probably not correct
+ 4000, 10, 80, 2, 256, {}, 1, {}, 40, 10, 10
+ },
+ { // 5"25 280K 80 track double sided single density
+ floppy_image::FF_525, floppy_image::DSQD, floppy_image::FM,
+ // GAPs and interleave probably not correct
+ 4000, 7, 80, 2, 256, {}, 1, {}, 40, 10, 10
},
{}
};
-const floppy_format_type FLOPPY_TRD_FORMAT = &floppy_image_format_creator<trd_format>;
+const trd_format FLOPPY_TRD_FORMAT;
diff --git a/src/lib/formats/trd_dsk.h b/src/lib/formats/trd_dsk.h
index a33ff489878..70888b1e37a 100644
--- a/src/lib/formats/trd_dsk.h
+++ b/src/lib/formats/trd_dsk.h
@@ -18,14 +18,15 @@ class trd_format : public wd177x_format {
public:
trd_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
};
-extern const floppy_format_type FLOPPY_TRD_FORMAT;
+extern const trd_format FLOPPY_TRD_FORMAT;
#endif // MAME_FORMATS_TRD_DSK_H
diff --git a/src/lib/formats/trs80_dsk.cpp b/src/lib/formats/trs80_dsk.cpp
index 988c253a9b4..f670273a5af 100644
--- a/src/lib/formats/trs80_dsk.cpp
+++ b/src/lib/formats/trs80_dsk.cpp
@@ -1,45 +1,101 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
- TRS-80
+TRS-80
- JV1 disk image format
+JV1/3 disk image formats
- Used by Jeff Vavasour's TRS-80 Emulators
+Used by Jeff Vavasour's TRS-80 Emulators
- TODO:
- - Gap sizes unverified
+Description of JV1:
+ This format has no header or identifying characteristics. It is
+ simply a string of sectors one after the other in order. There's
+ no DAM or inter-sector bytes. The expected format is 10 sectors
+ per track, 256 bytes per sector, single-sided, single-density.
+ There's no limit on the number of tracks. It's up to the emulation
+ to decide how to represent things. This format is intended only
+ for the TRS-80 Model 1 with Level 2 basic. As it happens, the
+ format is readable by the Model 3, but it's probably not a good
+ idea to attempt writing on it.
+ BUGS: If you try to create a JV1 disk, MAME will crash.
+
+Description of JV3:
+ There's no header, but the format is obvious to the practised eye.
+ Firstly there's a 3 byte descriptor per sector, occurs 2901 times.
+ This is followed by a byte to indicate if the disk is read-only. The
+ total size of this area is 0x2200 bytes. The sector descriptors can
+ be in any order. Unused descriptors are FF FF (FC | size). These
+ can be intermixed with valid descriptors.
+ Byte 1 - track number (0xFF indicates unused entry).
+ Byte 2 - sector number.
+ Byte 3 - flag byte. The bits are assigned as follows:
+ Bits 0,1 - size in multiples of 128 bytes xor 1 for used tracks, or
+ xor 2 for unused tracks.
+ 128 bytes: 1 (used), 2 (unused)
+ 256 bytes: 0 (used), 3 (unused)
+ 512 bytes: 3 (used), 0 (unused)
+ 1024 bytes: 2 (used), 1 (unused)
+ bit 2: non-ibm flag. Some FDCs, such as the 1771, can have sector
+ sizes from 16 to 1024 in any multiple of 16. MAME doesn't
+ support this, and the format itself gives no indication of the
+ size. So, it's impossible to support it.
+ bit 3: sector has a CRC error.
+ bit 4: side (0 = side 0, 1 = side 1)
+ bits 5,6: DAM code:
+ 0xF8 (deleted): 0x60 (single-density), 0x20 (double-density)
+ 0xF9 (undefined): 0x40 (single-density), invalid/unused (double-density)
+ 0xFA (undefined): 0x20 (single-density), invalid/unused (double-density)
+ 0xFB (normal) : 0x00 (single and double-density)
+ bit 7: density (1 = dden/MFM). MAME doesn't support different densities in
+ a track, so it's defaulted to single-density unless at least 1 sector is
+ marked as double-density.
+ After 0x2200 comes the data. There's no DAM bytes or inter-sector bytes
+ included. If the last part of the descriptors is all unused, there's no
+ need for those sectors to be in the image. If 2901 entries is not enough,
+ the format allows another set of descriptors and data. However no TRS-80
+ disks need this extra area, so it's unsupported. This format is intended
+ only for the TRS-80 Models 3 and 4. Save/Create is currently unsupported.
+
+ What we support: up to 2 sides, 96 tracks, 18 sectors per track.
+ We support all the flags except the non-ibm flag.
+ We do not support multiple sets of descriptors and data.
+ We do not support multiple densities per track.
***************************************************************************/
#include "trs80_dsk.h"
-trs80_format::trs80_format() : wd177x_format(formats)
+#include "ioprocs.h"
+
+#include "osdcore.h" // osd_printf_*
+
+
+jv1_format::jv1_format() : wd177x_format(formats)
{
}
-const char *trs80_format::name() const
+const char *jv1_format::name() const noexcept
{
- return "trs80";
+ return "jv1";
}
-const char *trs80_format::description() const
+const char *jv1_format::description() const noexcept
{
return "TRS-80 JV1 disk image";
}
-const char *trs80_format::extensions() const
+const char *jv1_format::extensions() const noexcept
{
- return "dsk";
+ return "jv1,dsk";
}
-int trs80_format::get_track_dam_fm(const format &f, int head, int track)
+int jv1_format::get_track_dam_fm(const format &f, int head, int track) const
{
return (track == 17 && head == 0) ? FM_DDAM : FM_DAM;
}
-const trs80_format::format trs80_format::formats[] =
+const jv1_format::format jv1_format::formats[] =
{
{
floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
@@ -50,10 +106,352 @@ const trs80_format::format trs80_format::formats[] =
4000, 10, 40, 1, 256, {}, 0, {}, 14, 11, 12
},
{
- floppy_image::FF_525, floppy_image::SSDD, floppy_image::FM,
+ floppy_image::FF_525, floppy_image::SSQD, floppy_image::FM,
4000, 10, 80, 1, 256, {}, 0, {}, 14, 11, 12
},
{}
};
-const floppy_format_type FLOPPY_TRS80_FORMAT = &floppy_image_format_creator<trs80_format>;
+const jv1_format FLOPPY_JV1_FORMAT;
+
+
+#define MAX_SECTORS 19
+#define MAX_TRACKS 96
+
+jv3_format::jv3_format()
+{
+}
+
+const char *jv3_format::name() const noexcept
+{
+ return "jv3";
+}
+
+const char *jv3_format::description() const noexcept
+{
+ return "TRS-80 JV3 disk image";
+}
+
+const char *jv3_format::extensions() const noexcept
+{
+ return "jv3,dsk";
+}
+
+int jv3_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t image_size;
+ if (io.length(image_size))
+ return 0;
+
+ if (image_size < 0x2200)
+ return 0; // too small, silent return
+
+ auto const [err, data, actual] = read_at(io, 0, image_size);
+ if (err || (actual != image_size))
+ return 0;
+ const uint32_t entries = 2901;
+ const uint32_t header_size = entries *3 +1;
+
+ // check the readonly flag
+ if ((data[0x21ff] != 0) && (data[0x21ff] != 0xff))
+ return 0; // flag is incorrect, silent return
+
+ // Check for DMK
+ uint8_t testbyte = 0;
+ for (int i = 5; i < 16; i++)
+ testbyte |= data[i];
+ if (!testbyte)
+ return 0; // DMK, silent return
+
+ // Check for other disk formats
+ if (data[1] >= MAX_SECTORS)
+ return 0; // some other format, silent return
+
+ // Full validation of the disk
+ bool sector_array[2][MAX_TRACKS][MAX_SECTORS];
+ for (uint8_t side = 0; side < 2; side++)
+ for (uint8_t track = 0; track < MAX_TRACKS; track++)
+ for (uint8_t sector = 0; sector < MAX_SECTORS; sector++)
+ sector_array[side][track][sector] = false; // fill_n won't compile
+
+ uint32_t data_ptr = header_size, last_data = header_size, size = 0;
+
+ // printf errors may indicate it's another format, and so are ignorable.
+ for (uint32_t sect = 0; sect < entries; sect++)
+ {
+ uint8_t track = data[sect*3];
+ uint8_t sector = data[sect*3+1];
+ uint8_t flag_size = data[sect*3+2] & 3;
+ uint8_t flag_side = (data[sect*3+2] & 0x10) ? 1 : 0;
+
+ if (track < 0xff)
+ {
+ // validate so we don't overrun the array
+ if (track >= MAX_TRACKS)
+ {
+ osd_printf_info("jv3_format::identify - track %d exceeds maximum allowed (%d)\n",track,MAX_TRACKS-1);
+ return 0;
+ }
+
+ if (sector >= MAX_SECTORS)
+ {
+ osd_printf_info("jv3_format::identify - sector %d exceeds maximum allowed (%d)\n",sector,MAX_SECTORS-1);
+ return 0;
+ }
+
+ // check if sector already exists
+ if (sector_array[flag_side][track][sector])
+ {
+ osd_printf_info("jv3_format::identify - side %d track %d sector %d is duplicated\n",flag_side,track,sector);
+ return 0;
+ }
+
+ sector_array[flag_side][track][sector] = true;
+
+ size = 128 << (flag_size ^1);
+ data_ptr += size;
+ last_data = data_ptr;
+ }
+ else
+ {
+ size = 128 << (flag_size ^2);
+ data_ptr += size;
+ }
+ }
+
+ // Is all data in the image? (unused tracks at the end are optional)
+ if (last_data > image_size)
+ {
+ osd_printf_info("jv3_format::identify - disk is missing some data. Expected 0x%X, Actual = 0x%X\n",last_data,image_size);
+ return 0;
+ }
+
+ return FIFID_STRUCT;
+}
+
+bool jv3_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ // disk has already been validated in every way except if it exceeds drive tracks, we do that below
+ osd_printf_info("Disk detected as JV3\n");
+ int drive_tracks, drive_sides;
+ image.get_maximal_geometry(drive_tracks, drive_sides);
+ uint64_t image_size;
+ if (io.length(image_size))
+ return false;
+ auto const [err, data, actual] = read_at(io, 0, image_size);
+ if (err || (actual != image_size))
+ return 0;
+ const uint32_t entries = 2901;
+ const uint32_t header_size = entries *3 +1;
+ bool is_dd = false, is_ds = false;
+
+ for (uint8_t curr_side = 0; curr_side < 2; curr_side++)
+ {
+ for (uint8_t curr_track = 0; curr_track < MAX_TRACKS; curr_track++)
+ {
+ // Find out how many sectors are in this track
+ uint8_t max_sect = 0, min_sect = 0xff;
+ for (uint32_t sect = 0; sect < entries; sect++)
+ {
+ uint8_t track = data[sect*3];
+ uint8_t sector = data[sect*3+1];
+ uint8_t flags = data[sect*3+2];
+ uint8_t side = (flags & 0x10) ? 1 : 0;
+ if ((track == curr_track) && (side == curr_side))
+ {
+ if (sector > max_sect)
+ max_sect = sector;
+ if (sector < min_sect)
+ min_sect = sector;
+ }
+ }
+
+ // If there's any data for this track process it
+ if (max_sect >= min_sect)
+ {
+ // Protect against oversized disk
+ if ((curr_track >= drive_tracks) || (curr_side >= drive_sides))
+ {
+ osd_printf_error("Disk exceeds drive capabilities\n");
+ return false;
+ }
+
+ max_sect++; // now it is the max possible number of sectors on this track
+
+ desc_pc_sector sectors[MAX_SECTORS];
+ // set defaults in case some sectors are missing from the image
+ for (uint8_t i = 0; i < max_sect; i++)
+ {
+ sectors[i].track = curr_track;
+ sectors[i].head = curr_side;
+ sectors[i].sector = i;
+ sectors[i].actual_size = 256;
+ sectors[i].size = 1;
+ sectors[i].deleted = true;
+ sectors[i].bad_crc = false;
+ sectors[i].data = 0;
+ }
+
+ uint32_t data_ptr = header_size;
+ bool ddensity = false;
+ // Search for sectors on this track & side
+ for (uint32_t sect = 0; sect < entries; sect++)
+ {
+ uint8_t track = data[sect*3];
+ uint8_t sector = data[sect*3+1];
+ uint8_t flags = data[sect*3+2];
+ uint8_t side = (flags & 0x10) ? 1 : 0;
+ uint8_t size = (flags & 0x03);
+ if (track == 0xff)
+ size ^= 2;
+ else
+ size ^= 1;
+ uint16_t sector_size = 128 << size;
+ if ((track == curr_track) && (side == curr_side))
+ {
+ // process the sector
+ sectors[sector].track = curr_track;
+ sectors[sector].head = curr_side;
+ sectors[sector].sector = sector;
+ sectors[sector].actual_size = sector_size;
+ sectors[sector].size = size;
+ uint8_t dam = flags & 0xe0;
+ sectors[sector].deleted = ((dam == 0x60) || (dam == 0xa0)) ? true : false;
+ sectors[sector].bad_crc = (flags & 0x08) ? true : false;
+ sectors[sector].data = &data[data_ptr];
+ if (flags & 0x80)
+ ddensity = true;
+ }
+ data_ptr += sector_size;
+ }
+
+ //osd_printf_verbose("Side %d, Track %d, %s density\n",curr_side,curr_track,ddensity ? "Double" : "Single");
+ if (ddensity)
+ {
+ is_dd = true;
+ build_wd_track_mfm(curr_track, curr_side, image, 100000, max_sect, sectors, 32, 31, 22);
+ }
+ else
+ build_wd_track_fm(curr_track, curr_side, image, 50000, max_sect, sectors, 14, 12, 11);
+
+ if (curr_side)
+ is_ds = true;
+ }
+ }
+ }
+ if (is_dd)
+ {
+ if (is_ds)
+ image.set_variant(floppy_image::DSDD);
+ else
+ image.set_variant(floppy_image::SSDD);
+ }
+ else
+ {
+ if (is_ds)
+ image.set_variant(floppy_image::DSSD);
+ else
+ image.set_variant(floppy_image::SSSD);
+ }
+ return true;
+}
+
+bool jv3_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ int track_count, head_count;
+ image.get_actual_geometry(track_count, head_count);
+
+ if (track_count)
+ {
+ // If the disk already exists, find out if it's writable
+ uint64_t image_size;
+ if (!io.length(image_size) && (image_size >= 0x2200))
+ {
+ uint8_t flag;
+ auto const [err, actual] = read_at(io, 0x21ff, &flag, 1);
+ if (err || (1 != actual)) // TODO: should we save over the top if we couldn’t read the read-only flag?
+ return false;
+ if (flag == 0)
+ return false; // disk is read-only
+ }
+ }
+
+ uint32_t data_ptr = 0x2200, sect_ptr = 0;
+
+
+ // prepare default header, which we will partially overwrite later
+ uint8_t header[0x2200];
+ std::fill_n(header, std::size(header), 0xFF);
+
+ if (!track_count)
+ {
+ track_count = 35;
+ head_count = 1;
+ }
+
+ // write disk data
+ for (uint8_t head = 0; head < head_count; head++)
+ {
+ for (uint8_t track = 0; track < track_count; track++)
+ {
+ auto bitstream = generate_bitstream_from_track(track, head, 2000, image);
+ auto sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
+ bool ddensity = true;
+ if (sectors.empty())
+ {
+ bitstream = generate_bitstream_from_track(track, head, 4000, image);
+ sectors = extract_sectors_from_bitstream_fm_pc(bitstream);
+ ddensity = false;
+ }
+
+ if (sectors.empty())
+ {
+ // write dummy data. If we write nothing at all, mame hangs
+ uint8_t dummy[256];
+ std::fill_n(dummy, std::size(dummy), 0xE5);
+ for (uint8_t i = 0; i < 10; i++)
+ {
+ header[sect_ptr++] = track;
+ header[sect_ptr++] = i;
+ header[sect_ptr++] = head ? 0x10 : 0;
+ /*auto const [err, actual] =*/ write_at(io, data_ptr, &dummy[0], 256); // FIXME: check for errors
+ data_ptr += 256;
+ }
+ }
+ else
+ {
+ // How many sectors?
+ uint8_t count = 0;
+ for (const auto &s : sectors)
+ if (!s.empty())
+ count++;
+
+ // Save them
+ for (uint8_t i = 0; i < count; i++)
+ {
+ header[sect_ptr++] = track;
+ header[sect_ptr++] = i;
+ uint8_t flags = (ddensity) ? 0x80 : 0;
+ flags |= (sectors[i].size() >> 8) ^1;
+ flags |= head ? 0x10 : 0;
+ header[sect_ptr++] = flags;
+ /*auto const [err, actual] =*/ write_at(io, data_ptr, sectors[i].data(), sectors[i].size()); // FIXME: check for errors
+ data_ptr += sectors[i].size();
+ }
+ }
+ }
+ }
+ // Save the header
+ /*auto const [err, actual] =*/ write_at(io, 0, header, 0x2200); // FIXME: check for errors
+
+ return true;
+}
+
+
+bool jv3_format::supports_save() const noexcept
+{
+ return true;
+}
+
+const jv3_format FLOPPY_JV3_FORMAT;
+
diff --git a/src/lib/formats/trs80_dsk.h b/src/lib/formats/trs80_dsk.h
index c5b99a1366b..e13b12723af 100644
--- a/src/lib/formats/trs80_dsk.h
+++ b/src/lib/formats/trs80_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -14,22 +14,38 @@
#include "wd177x_dsk.h"
-class trs80_format : public wd177x_format
+class jv1_format : public wd177x_format
{
public:
- trs80_format();
+ jv1_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
protected:
- virtual int get_track_dam_fm(const format &f, int head, int track) override;
+ virtual int get_track_dam_fm(const format &f, int head, int track) const override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_TRS80_FORMAT;
+class jv3_format : public floppy_image_format_t
+{
+public:
+ jv3_format();
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual bool supports_save() const noexcept override;
+};
+
+extern const jv1_format FLOPPY_JV1_FORMAT;
+extern const jv3_format FLOPPY_JV3_FORMAT;
#endif // MAME_FORMATS_TRS80_DSK_H
diff --git a/src/lib/formats/trs_cas.cpp b/src/lib/formats/trs_cas.cpp
index a6701ed1263..2648c536320 100644
--- a/src/lib/formats/trs_cas.cpp
+++ b/src/lib/formats/trs_cas.cpp
@@ -3,79 +3,170 @@
/********************************************************************
Support for TRS80 .cas cassette images
+Types handled:
+- Model 1 Level I: 250 baud
+- Model 1 Level II: 500 baud
+- Model 3/4: 1500 baud.
-********************************************************************/
+Level I and II tape format is completely identical, apart from the
+baud rate. The contents are specific to each system though.
+The Model 3 and 4 can load either Level II tapes (by answering L
+to the Cass? prompt), or the fast format by hitting enter at Cass?
-#include <assert.h>
+********************************************************************/
#include "formats/trs_cas.h"
+
#define SILENCE 0
#define SMPLO -32768
#define SMPHI 32767
-static int cas_size;
+static int cas_size = 0; // FIXME: global variable prevents multiple instances
/*******************************************************************
Generate one high-low cycle of sample data
********************************************************************/
-static inline int trs80l2_cas_cycle(int16_t *buffer, int sample_pos, int silence, int high, int low)
+static inline int trs80m1_cas_cycle(int16_t *buffer, int sample_pos, bool bit)
{
- int i = 0;
+ uint8_t i;
if ( buffer )
{
- while( i < silence )
- {
- buffer[ sample_pos + i ] = SILENCE;
- i++;
- }
- while( i < silence + high)
+ for (i = 0; i < 32; i++)
+ buffer[ sample_pos++ ] = SILENCE;
+ for (i = 0; i < 6; i++)
+ buffer[ sample_pos++ ] = bit ? SMPHI : SILENCE;
+ for (i = 0; i < 6; i++)
+ buffer[ sample_pos++ ] = bit ? SMPLO : SILENCE;
+ }
+ return 44;
+}
+
+
+static int trs80m1_handle_cas(int16_t *buffer, const uint8_t *casdata)
+{
+ int data_pos = 0, sample_count = 0;
+ bool sw = false;
+
+ // Make sure this is a trs80 tape
+ // Should have some zero bytes then one 0xA5
+ while ((cas_size > data_pos) && (casdata[data_pos] == 0x00))
+ data_pos++;
+ if (casdata[data_pos] != 0xA5)
+ return 0;
+
+ data_pos = 0;
+ while( data_pos < cas_size )
+ {
+ uint8_t data = casdata[data_pos];
+
+ for (uint8_t i = 0; i < 8; i++ )
{
- buffer[ sample_pos + i ] = SMPHI;
- i++;
+ /* Signal code */
+ sample_count += trs80m1_cas_cycle( buffer, sample_count, true );
+
+ /* Bit code */
+ sample_count += trs80m1_cas_cycle( buffer, sample_count, data >> 7 );
+
+ data <<= 1;
}
- while( i < silence + high + low )
+ if (!sw && (casdata[data_pos] == 0xA5))
{
- buffer[ sample_pos + i ] = SMPLO;
- i++;
+ sw = true;
+ // Need 1ms silence here while rom is busy
+ sample_count += trs80m1_cas_cycle( buffer, sample_count, false );
}
+
+ data_pos++;
+ }
+
+ // Specification requires a short silence to indicate EOF
+ sample_count += trs80m1_cas_cycle( buffer, sample_count, false );
+ sample_count += trs80m1_cas_cycle( buffer, sample_count, false );
+ return sample_count;
+}
+
+static inline int trs80m3_cas_cycle(int16_t *buffer, int sample_pos, bool bit)
+{
+ uint8_t i, counts = bit ? 8 : 16;
+
+ if ( buffer )
+ {
+ for (i = 0; i < counts; i++)
+ buffer[ sample_pos++ ] = SMPHI;
+ for (i = 0; i < counts; i++)
+ buffer[ sample_pos++ ] = SMPLO;
}
- return silence + high + low;
+ return counts*2;
}
-static int trs80l2_handle_cas(int16_t *buffer, const uint8_t *casdata)
+static int trs80m3_handle_cas(int16_t *buffer, const uint8_t *casdata)
{
- int data_pos, sample_count;
+ int data_pos = 0, sample_count = 0;
+ uint8_t sw = 0, bitout = 0, byteout = 0;
+
+ // Make sure this is a trs80m3 tape
+ // Should have ~256 0x55 then one 0x7f
+ // It's possible that 0x57 could be encountered instead,
+ // but no working tapes with it have been found.
+ // Other bit-shifted variants might exist too.
+ while ((cas_size > data_pos) && (casdata[data_pos] == 0x55))
+ data_pos++;
+ if (casdata[data_pos] != 0x7f)
+ return 0;
data_pos = 0;
- sample_count = 0;
-
while( data_pos < cas_size )
{
- uint8_t data = casdata[data_pos];
- int i;
+ uint8_t data = casdata[data_pos];
- for ( i = 0; i < 8; i++ )
+ for (uint8_t 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 );
+ sample_count += trs80m3_cas_cycle( buffer, sample_count, data >> 7 );
+
+ // This paragraph unscrambles and prints the SYSTEM name.
+ // If the first character is U, type SYSTEM, then the next 6 characters; otherwise use CLOAD.
+ if ((sw == 1) && buffer)
+ {
+ if (bitout)
+ {
+ byteout = (byteout << 1) | (data >> 7);
+ if (bitout == 8)
+ {
+ if (byteout == 0)
+ sw = 2;
+ printf("%c",byteout);
+ byteout = 0;
+ bitout = 0;
+ }
+ else
+ bitout++;
+ }
+ else bitout++;
+ }
data <<= 1;
}
+ if (!sw && (casdata[data_pos] != 0x55))
+ {
+ sw = 1;
+ // This 1ms of silence isn't absolutely necessary, but the system
+ // writes it, so we may as well emulate it.
+ sample_count += trs80m1_cas_cycle( buffer, sample_count, false );
+ }
+
data_pos++;
}
+
+ // Specification requires a short silence to indicate EOF
+ sample_count += trs80m1_cas_cycle( buffer, sample_count, false );
+ sample_count += trs80m1_cas_cycle( buffer, sample_count, false );
return sample_count;
}
@@ -83,47 +174,85 @@ static int trs80l2_handle_cas(int16_t *buffer, const uint8_t *casdata)
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int trs80l2_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int trs80_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
- return trs80l2_handle_cas( buffer, bytes );
+ if (cas_size && (bytes[0] == 0x55))
+ return trs80m3_handle_cas( buffer, bytes );
+ else
+ return trs80m1_handle_cas( buffer, bytes );
}
/*******************************************************************
Calculate the number of samples needed for this tape image
********************************************************************/
-static int trs80l2_cas_to_wav_size(const uint8_t *casdata, int caslen)
+static int trs80_cas_to_wav_size(const uint8_t *casdata, int caslen)
{
cas_size = caslen;
- return trs80l2_handle_cas( nullptr, casdata );
+ if (cas_size && (casdata[0] == 0x55))
+ return trs80m3_handle_cas( nullptr, casdata );
+ else
+ return trs80m1_handle_cas( nullptr, casdata );
+}
+
+static const cassette_image::LegacyWaveFiller trs80l1_cas_legacy_fill_wave =
+{
+ trs80_cas_fill_wave, /* fill_wave */
+ -1, /* chunk_size */
+ 0, /* chunk_samples */
+ trs80_cas_to_wav_size, /* chunk_sample_calc */
+ 22050, /* sample_frequency */
+ 0, /* header_samples */
+ 0 /* trailer_samples */
+};
+
+
+static cassette_image::error trs80l1_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
+{
+ return cassette->legacy_identify(opts, &trs80l1_cas_legacy_fill_wave);
+}
+
+
+static cassette_image::error trs80l1_cas_load(cassette_image *cassette)
+{
+ return cassette->legacy_construct(&trs80l1_cas_legacy_fill_wave);
}
-static const struct CassetteLegacyWaveFiller trs80l2_cas_legacy_fill_wave =
+
+static const cassette_image::Format trs80l1_cas_format =
+{
+ "cas",
+ trs80l1_cas_identify,
+ trs80l1_cas_load,
+ nullptr
+};
+
+static const cassette_image::LegacyWaveFiller trs80l2_cas_legacy_fill_wave =
{
- trs80l2_cas_fill_wave, /* fill_wave */
+ trs80_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
0, /* chunk_samples */
- trs80l2_cas_to_wav_size, /* chunk_sample_calc */
+ trs80_cas_to_wav_size, /* chunk_sample_calc */
44100, /* sample_frequency */
0, /* header_samples */
0 /* trailer_samples */
};
-static cassette_image::error trs80l2_cas_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error trs80l2_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &trs80l2_cas_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &trs80l2_cas_legacy_fill_wave);
}
static cassette_image::error trs80l2_cas_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &trs80l2_cas_legacy_fill_wave);
+ return cassette->legacy_construct(&trs80l2_cas_legacy_fill_wave);
}
-static const struct CassetteFormat trs80l2_cas_format =
+static const cassette_image::Format trs80l2_cas_format =
{
"cas",
trs80l2_cas_identify,
@@ -132,6 +261,10 @@ static const struct CassetteFormat trs80l2_cas_format =
};
+CASSETTE_FORMATLIST_START(trs80l1_cassette_formats)
+ CASSETTE_FORMAT(trs80l1_cas_format)
+CASSETTE_FORMATLIST_END
+
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
index 57954faf30c..139c519106f 100644
--- a/src/lib/formats/trs_cas.h
+++ b/src/lib/formats/trs_cas.h
@@ -14,6 +14,7 @@
#include "cassimg.h"
+CASSETTE_FORMATLIST_EXTERN(trs80l1_cassette_formats);
CASSETTE_FORMATLIST_EXTERN(trs80l2_cassette_formats);
#endif // MAME_FORMATS_TRS_CAS_H
diff --git a/src/lib/formats/tvc_cas.cpp b/src/lib/formats/tvc_cas.cpp
index 9177491278c..2abde7b9a9b 100644
--- a/src/lib/formats/tvc_cas.cpp
+++ b/src/lib/formats/tvc_cas.cpp
@@ -8,10 +8,11 @@
********************************************************************/
-#include <assert.h>
-
#include "tvc_cas.h"
+#include "multibyte.h"
+
+
#define TVC64_BIT0_FREQ 1812
#define TVC64_BIT1_FREQ 2577
#define TVC64_PRE_FREQ 2128
@@ -24,23 +25,27 @@
static void tvc64_emit_level(cassette_image *cass, double &time, int freq, int level)
{
double period = 1.0 / freq;
- cassette_put_sample(cass, 0, time, period, level * WAVE_AMPLITUDE);
+ cass->put_sample(0, time, period, level * WAVE_AMPLITUDE);
time += period;
}
-static cassette_image::error tvc64_output_byte(cassette_image *cass, double &time, uint8_t byte)
+static cassette_image::error tvc64_output_bytes(cassette_image *cass, double &time, const uint8_t *buffer, int size)
{
- for (int i=0; i<8; i++)
+ while (size-- > 0)
{
- if ((byte>>i) & 0x01)
+ uint8_t byte = *buffer++;
+ for (int i=0; i<8; i++)
{
- tvc64_emit_level(cass, time, TVC64_BIT1_FREQ*2, +1);
- tvc64_emit_level(cass, time, TVC64_BIT1_FREQ*2, -1);
- }
- else
- {
- tvc64_emit_level(cass, time, TVC64_BIT0_FREQ*2, +1);
- tvc64_emit_level(cass, time, TVC64_BIT0_FREQ*2, -1);
+ if ((byte>>i) & 0x01)
+ {
+ tvc64_emit_level(cass, time, TVC64_BIT1_FREQ*2, +1);
+ tvc64_emit_level(cass, time, TVC64_BIT1_FREQ*2, -1);
+ }
+ else
+ {
+ tvc64_emit_level(cass, time, TVC64_BIT0_FREQ*2, +1);
+ tvc64_emit_level(cass, time, TVC64_BIT0_FREQ*2, -1);
+ }
}
}
@@ -89,8 +94,8 @@ static cassette_image::error tvc64_cassette_load(cassette_image *cassette)
double time = 0.0;
uint8_t header[TVC64_HEADER_BYTES];
- cassette_image_read(cassette, header, 0, TVC64_HEADER_BYTES);
- uint16_t cas_size = (header[0x83]<<8) | header[0x82];
+ cassette->image_read(header, 0, TVC64_HEADER_BYTES);
+ uint16_t cas_size = get_u16le(&header[0x82]);
// tape header
tmp_buff[buff_idx++] = 0x00;
@@ -132,8 +137,7 @@ static cassette_image::error tvc64_cassette_load(cassette_image *cassette)
tvc64_emit_level(cassette, time, TVC64_SYNC_FREQ*2, -1);
// header data
- for (int i=0; i<buff_idx; i++)
- tvc64_output_byte(cassette, time, tmp_buff[i]);
+ tvc64_output_bytes(cassette, time, tmp_buff, buff_idx);
// 5 post data cycles
tvc64_output_predata(cassette, time, 5);
@@ -171,8 +175,8 @@ static cassette_image::error tvc64_cassette_load(cassette_image *cassette)
// sector data
int sector_size = (i == sector_num) ? (cas_size % 256) : 256;
- for (int z=0; z < sector_size; z++)
- cassette_image_read(cassette, &tmp_buff[buff_idx++], TVC64_HEADER_BYTES + i*256 + z, 1);
+ cassette->image_read(&tmp_buff[buff_idx], TVC64_HEADER_BYTES + i*256, sector_size);
+ buff_idx += sector_size;
if (i == sector_num || ((i+1) == sector_num && (cas_size % 256 ) == 0))
tmp_buff[buff_idx++] = 0xff; // last sector
@@ -185,8 +189,7 @@ static cassette_image::error tvc64_cassette_load(cassette_image *cassette)
tmp_buff[buff_idx++] = (crc>>8) & 0xff;
// output the sector
- for (int z=0; z<buff_idx; z++)
- tvc64_output_byte(cassette, time, tmp_buff[z]);
+ tvc64_output_bytes(cassette, time, tmp_buff, buff_idx);
buff_idx = 0;
}
@@ -200,12 +203,9 @@ static cassette_image::error tvc64_cassette_load(cassette_image *cassette)
return cassette_image::error::SUCCESS;
}
-static cassette_image::error tvc64_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error tvc64_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- uint8_t byte;
- cassette_image_read(cassette, &byte, 0, 1);
-
- if (byte == 0x11)
+ if (cassette->image_read_byte(0) == 0x11)
{
opts->bits_per_sample = 16;
opts->channels = 1;
@@ -216,7 +216,7 @@ static cassette_image::error tvc64_cassette_identify(cassette_image *cassette, s
return cassette_image::error::INVALID_IMAGE;
}
-static const struct CassetteFormat tvc64_cassette_image_format =
+static const cassette_image::Format tvc64_cassette_image_format =
{
"cas",
tvc64_cassette_identify,
diff --git a/src/lib/formats/tvc_dsk.cpp b/src/lib/formats/tvc_dsk.cpp
index 8a2254cb7cc..00a8abbc3c0 100644
--- a/src/lib/formats/tvc_dsk.cpp
+++ b/src/lib/formats/tvc_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/tvc_dsk.c
+ formats/tvc_dsk.cpp
tvc format
*********************************************************************/
-#include <assert.h>
-
#include "formats/tvc_dsk.h"
tvc_format::tvc_format() : wd177x_format(formats)
{
}
-const char *tvc_format::name() const
+const char *tvc_format::name() const noexcept
{
return "tvc";
}
-const char *tvc_format::description() const
+const char *tvc_format::description() const noexcept
{
return "Videoton TVC HBF disk image";
}
-const char *tvc_format::extensions() const
+const char *tvc_format::extensions() const noexcept
{
return "img,dsk";
}
@@ -45,4 +43,4 @@ const tvc_format::format tvc_format::formats[] =
{}
};
-const floppy_format_type FLOPPY_TVC_FORMAT = &floppy_image_format_creator<tvc_format>;
+const tvc_format FLOPPY_TVC_FORMAT;
diff --git a/src/lib/formats/tvc_dsk.h b/src/lib/formats/tvc_dsk.h
index e94e3516953..35353f17d08 100644
--- a/src/lib/formats/tvc_dsk.h
+++ b/src/lib/formats/tvc_dsk.h
@@ -19,14 +19,14 @@ class tvc_format : public wd177x_format
public:
tvc_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_TVC_FORMAT;
+extern const tvc_format FLOPPY_TVC_FORMAT;
#endif // MAME_FORMATS_TVC_DSK_H
diff --git a/src/lib/formats/tzx_cas.cpp b/src/lib/formats/tzx_cas.cpp
index 28d363df433..f758c1b31e6 100644
--- a/src/lib/formats/tzx_cas.cpp
+++ b/src/lib/formats/tzx_cas.cpp
@@ -27,11 +27,13 @@ TODO:
*/
-#include <assert.h>
-
#include "tzx_cas.h"
-#include "formats/imageutl.h"
-#include "emu.h"
+#include "imageutl.h"
+
+#include "multibyte.h"
+
+#include <cmath>
+
#define TZX_WAV_FREQUENCY 44100
#define WAVE_LOW -0x5a9e
@@ -51,7 +53,7 @@ static const uint8_t TZX_HEADER[8] = { 'Z','X','T','a','p','e','!',0x1a };
Initialized by tzx_cas_get_wave_size, used (and cleaned up) by tzx_cas_fill_wave
*/
-static int16_t wave_data = 0;
+static int16_t wave_data = 0; // FIXME: global variables prevent multiple instances
static int block_count = 0;
static uint8_t** blocks = nullptr;
static float t_scale = 1; /* for scaling T-states to the 4MHz CPC */
@@ -70,7 +72,7 @@ static void toggle_wave_data(void)
static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen )
{
- int pos = sizeof(TZX_HEADER) + 2;
+ uint32_t pos = sizeof(TZX_HEADER) + 2;
int max_block_count = INITIAL_MAX_BLOCK_COUNT;
int loopcount = 0, loopoffset = 0;
blocks = (uint8_t**)malloc(max_block_count * sizeof(uint8_t*));
@@ -87,7 +89,7 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen )
void *old_blocks = blocks;
int old_max_block_count = max_block_count;
max_block_count = max_block_count + BLOCK_COUNT_INCREMENTS;
- blocks = (uint8_t**)malloc(max_block_count * sizeof(uint8_t*)); // SHOULD NOT BE USING auto_alloc_array()
+ blocks = (uint8_t**)malloc(max_block_count * sizeof(uint8_t*));
memset(blocks, 0, max_block_count);
memcpy(blocks, old_blocks, old_max_block_count * sizeof(uint8_t*));
free(old_blocks);
@@ -101,12 +103,12 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen )
{
case 0x10:
pos += 2;
- datasize = casdata[pos] + (casdata[pos + 1] << 8);
+ datasize = get_u16le(&casdata[pos]);
pos += 2 + datasize;
break;
case 0x11:
pos += 0x0f;
- datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16);
+ datasize = get_u24le(&casdata[pos]);
pos += 3 + datasize;
break;
case 0x12:
@@ -118,12 +120,12 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen )
break;
case 0x14:
pos += 7;
- datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16);
+ datasize = get_u24le(&casdata[pos]);
pos += 3 + datasize;
break;
case 0x15:
pos += 5;
- datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16);
+ datasize = get_u24le(&casdata[pos]);
pos += 3 + datasize;
break;
case 0x20: case 0x23:
@@ -131,7 +133,7 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen )
break;
case 0x24:
- loopcount = casdata[pos] + (casdata[pos + 1] << 8);
+ loopcount = get_u16le(&casdata[pos]);
pos +=2;
loopoffset = pos;
break;
@@ -152,11 +154,11 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen )
break;
case 0x26:
- datasize = casdata[pos] + (casdata[pos + 1] << 8);
+ datasize = get_u16le(&casdata[pos]);
pos += 2 + 2 * datasize;
break;
case 0x28: case 0x32:
- datasize = casdata[pos] + (casdata[pos + 1] << 8);
+ datasize = get_u16le(&casdata[pos]);
pos += 2 + datasize;
break;
case 0x31:
@@ -173,24 +175,31 @@ static void tzx_cas_get_blocks( const uint8_t *casdata, int caslen )
break;
case 0x35:
pos += 0x10;
- datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16) + (casdata[pos + 3] << 24);
+ datasize = get_u32le(&casdata[pos]);
pos += 4 + datasize;
break;
case 0x40:
pos += 1;
- datasize = casdata[pos] + (casdata[pos + 1] << 8) + (casdata[pos + 2] << 16);
+ datasize = get_u24le(&casdata[pos]);
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);
+ datasize = get_u32le(&casdata[pos]);
pos += 4 + datasize;
break;
}
- block_count++;
+ if (pos > caslen)
+ {
+ LOG_FORMATS("Block %d(ID=%d) with wrong length discarded\n", block_count, blocktype);
+ }
+ else
+ {
+ block_count++;
+ }
}
}
@@ -360,7 +369,7 @@ static inline int tzx_handle_symbol(int16_t **buffer, const uint8_t *symtable, u
for (int i = 0; i < maxp; i++)
{
- uint16_t pulse_length = cursymb[1 + (i*2)] | (cursymb[2 + (i*2)] << 8);
+ uint16_t pulse_length = get_u16le(&cursymb[1 + (i*2)]);
// shorter lists can be terminated with a pulse_length of 0
if (pulse_length != 0)
@@ -415,7 +424,7 @@ static int tzx_handle_generalized(int16_t **buffer, const uint8_t *bytes, int pa
for (int i = 0; i < totp*3; i+=3)
{
uint8_t symbol = table2[i + 0];
- uint16_t repetitions = table2[i + 1] + (table2[i + 2] << 8);
+ uint16_t repetitions = get_u16le(&table2[i + 1]);
//printf("symbol %02x repetitions %04x\n", symbol, repetitions); // does 1 mean repeat once, or that it only occurs once?
for (int j = 0; j < repetitions; j++)
@@ -435,7 +444,7 @@ static int tzx_handle_generalized(int16_t **buffer, const uint8_t *bytes, int pa
const uint8_t *symtable = bytes;
const uint8_t *table2 = bytes + (2 * npd + 1)*asd;
- int NB = ceil(compute_log2(asd)); // number of bits needed to represent each symbol
+ int NB = std::ceil(compute_log2(asd)); // number of bits needed to represent each symbol
uint8_t stream_bit = 0;
uint32_t stream_byte = 0;
@@ -523,28 +532,28 @@ static int tzx_cas_do_work( int16_t **buffer )
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);
+ pause_time = get_u16le(&cur_block[1]);
+ data_size = get_u16le(&cur_block[3]);
pilot_length = (cur_block[5] < 128) ? 8063 : 3223;
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);
+ pilot = get_u16le(&cur_block[1]);
+ sync1 = get_u16le(&cur_block[3]);
+ sync2 = get_u16le(&cur_block[5]);
+ bit0 = get_u16le(&cur_block[7]);
+ bit1 = get_u16le(&cur_block[9]);
+ pilot_length = get_u16le(&cur_block[11]);
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);
+ pause_time = get_u16le(&cur_block[14]);
+ data_size = get_u24le(&cur_block[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);
+ pilot = get_u16le(&cur_block[1]);
+ pilot_length = get_u16le(&cur_block[3]);
size += tzx_cas_handle_block(buffer, cur_block, 0, 0, pilot, pilot_length, 0, 0, 0, 0, 0);
current_block++;
break;
@@ -557,20 +566,20 @@ static int tzx_cas_do_work( int16_t **buffer )
current_block++;
break;
case 0x14: /* Pure Data Block */
- bit0 = cur_block[1] + (cur_block[2] << 8);
- bit1 = cur_block[3] + (cur_block[4] << 8);
+ bit0 = get_u16le(&cur_block[1]);
+ bit1 = get_u16le(&cur_block[3]);
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);
+ pause_time = get_u16le(&cur_block[6]);
+ data_size = get_u24le(&cur_block[8]);
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);
+ pause_time = get_u16le(&cur_block[1]);
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. */
+ in MAME 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);
@@ -606,7 +615,7 @@ static int tzx_cas_do_work( int16_t **buffer )
break;
case 0x32: /* Archive Info */
ascii_block_common_log("Archive Info Block", block_type);
- total_size = cur_block[1] + (cur_block[2] << 8);
+ total_size = get_u16le(&cur_block[1]);
text_size = 0;
for (data_size = 0; data_size < cur_block[3]; data_size++) // data_size = number of text blocks, in this case
{
@@ -644,7 +653,7 @@ static int tzx_cas_do_work( int16_t **buffer )
LOG_FORMATS("%c", cur_block[1 + data_size]);
}
LOG_FORMATS(":\n");
- text_size = cur_block[11] + (cur_block[12] << 8) + (cur_block[13] << 16) + (cur_block[14] << 24);
+ text_size = get_u32le(&cur_block[11]);
for (data_size = 0; data_size < text_size; data_size++)
LOG_FORMATS("%c", cur_block[15 + data_size]);
LOG_FORMATS("\n");
@@ -656,7 +665,7 @@ static int tzx_cas_do_work( int16_t **buffer )
current_block++;
break;
case 0x24: /* Loop Start */
- loopcount = cur_block[1] + (cur_block[2] << 8);
+ loopcount = get_u16le(&cur_block[1]);
current_block++;
loopoffset = current_block;
@@ -690,10 +699,10 @@ static int tzx_cas_do_work( int16_t **buffer )
case 0x15: /* Direct Recording */ // used on 'bombscar' in the cpc_cass list
// having this missing is fatal
- tstates = cur_block[1] + (cur_block[2] << 8);
- pause_time= cur_block[3] + (cur_block[4] << 8);
+ tstates = get_u16le(&cur_block[1]);
+ pause_time = get_u16le(&cur_block[3]);
bits_in_last_byte = cur_block[5];
- data_size = cur_block[6] + (cur_block[7] << 8) + (cur_block[8] << 16);
+ data_size = get_u24le(&cur_block[6]);
size += tzx_handle_direct(buffer, &cur_block[9], pause_time, data_size, tstates, bits_in_last_byte);
current_block++;
break;
@@ -708,15 +717,15 @@ static int tzx_cas_do_work( int16_t **buffer )
{
// having this missing is fatal
// used crudely by batmanc in spectrum_cass list (which is just a redundant encoding of batmane ?)
- data_size = cur_block[1] + (cur_block[2] << 8) + (cur_block[3] << 16) + (cur_block[4] << 24);
- pause_time= cur_block[5] + (cur_block[6] << 8);
+ data_size = get_u32le(&cur_block[1]);
+ pause_time = get_u16le(&cur_block[5]);
- uint32_t totp = cur_block[7] + (cur_block[8] << 8) + (cur_block[9] << 16) + (cur_block[10] << 24);
+ uint32_t totp = get_u32le(&cur_block[7]);
int npp = cur_block[11];
int asp = cur_block[12];
if (asp == 0 && totp > 0) asp = 256;
- uint32_t totd = cur_block[13] + (cur_block[14] << 8) + (cur_block[15] << 16) + (cur_block[16] << 24);
+ uint32_t totd = get_u32le(&cur_block[13]);
int npd = cur_block[17];
int asd = cur_block[18];
if (asd == 0 && totd > 0) asd = 256;
@@ -777,7 +786,7 @@ cleanup:
return -1;
}
-static int tzx_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
+static int tzx_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int )
{
int16_t *p = buffer;
int size = 0;
@@ -786,7 +795,7 @@ static int tzx_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
return size;
}
-static int cdt_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
+static int cdt_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int )
{
int16_t *p = buffer;
int size = 0;
@@ -795,92 +804,134 @@ static int cdt_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
return size;
}
-static int tap_cas_to_wav_size( const uint8_t *casdata, int caslen )
+static int tap_cas_to_wav_size(const uint8_t *casdata, int caslen)
{
+ /*
+ TAP Header:
+ | 0 | W | Length of Data Block (N) | -+
+ | 2 | B | Flag Byte | -+
+ | 3 | B*(N-2) | Data | | Data Block
+ | N+1 | B | XOR Checksum of [2..N] | -+
+ */
+
int size = 0;
const uint8_t *p = casdata;
- while (p < casdata + caslen)
+ while (caslen > 2)
{
- int data_size = p[0] + (p[1] << 8);
- int pilot_length = (p[2] == 0x00) ? 8063 : 3223;
- LOG_FORMATS("tap_cas_to_wav_size: Handling TAP block containing 0x%X bytes", data_size);
+ int data_size = get_u16le(&p[0]);
p += 2;
+ caslen -= 2;
+ if (caslen < data_size)
+ {
+ LOG_FORMATS("tap_cas_to_wav_size: Requested 0x%X bytes but only 0x%X available.\n", data_size, caslen);
+ data_size = caslen;
+ }
+ caslen -= data_size;
+
+ // Data Block
+ LOG_FORMATS("tap_cas_to_wav_size: Handling TAP block containing 0x%X bytes", data_size);
+ const int pilot_length = (p[0] == 0x00) ? 8063 : 3223;
size += tzx_cas_handle_block(nullptr, p, 1000, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8);
- LOG_FORMATS(", total size is now: %d\n", size);
+ LOG_FORMATS(", total size is now: %d", size);
+
+ // Validate Checksum
+ const uint8_t checksum = p[data_size - 1];
+ LOG_FORMATS(", checksum 0x%X\n", checksum);
+ uint8_t check = 0x00;
+ for(int i = 0; i < (data_size - 1); i++)
+ {
+ check ^= p[i];
+ }
+ if (check != checksum)
+ {
+ osd_printf_warning("tap_cas_to_wav_size: wrong checksum 0x%X\n", check);
+ }
+
p += data_size;
}
return size;
}
-static int tap_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
+static int tap_cas_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int bytes_length)
{
int16_t *p = buffer;
int size = 0;
- while (size < length)
+ int block = 0;
+ while (bytes_length > 2)
{
- int data_size = bytes[0] + (bytes[1] << 8);
- int pilot_length = (bytes[2] == 0x00) ? 8063 : 3223;
- LOG_FORMATS("tap_cas_fill_wave: Handling TAP block containing 0x%X bytes\n", data_size);
+ int data_size = get_u16le(&bytes[0]);
bytes += 2;
+ bytes_length -= 2;
+
+ LOG_FORMATS("tap_cas_fill_wave: Handling TAP block containing 0x%X bytes\n", data_size);
+ if (bytes_length < data_size)
+ {
+ osd_printf_warning("BAD Image: block #%d required %d byte(s), but only %d available\n", block, data_size, bytes_length);
+ data_size = bytes_length; // Take as much as we can.
+ }
+ bytes_length -= data_size;
+
+ int pilot_length = (bytes[0] == 0x00) ? 8063 : 3223;
size += tzx_cas_handle_block(&p, bytes, 1000, data_size, 2168, pilot_length, 667, 735, 855, 1710, 8);
bytes += data_size;
+ ++block;
}
return size;
}
-static const struct CassetteLegacyWaveFiller tzx_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller 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 */
+ 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 =
+static const cassette_image::LegacyWaveFiller 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 */
+ 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 =
+static const cassette_image::LegacyWaveFiller 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 */
+ 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 cassette_image::error tzx_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts )
+static cassette_image::error tzx_cassette_identify( cassette_image *cassette, cassette_image::Options *opts )
{
- return cassette_legacy_identify(cassette, opts, &tzx_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &tzx_legacy_fill_wave);
}
-static cassette_image::error tap_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts )
+static cassette_image::error tap_cassette_identify( cassette_image *cassette, cassette_image::Options *opts )
{
- return cassette_legacy_identify(cassette, opts, &tap_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &tap_legacy_fill_wave);
}
-static cassette_image::error cdt_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts )
+static cassette_image::error cdt_cassette_identify( cassette_image *cassette, cassette_image::Options *opts )
{
- return cassette_legacy_identify(cassette, opts, &cdt_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &cdt_legacy_fill_wave);
}
static cassette_image::error tzx_cassette_load( cassette_image *cassette )
{
- cassette_image::error err = cassette_legacy_construct(cassette, &tzx_legacy_fill_wave);
+ cassette_image::error err = cassette->legacy_construct(&tzx_legacy_fill_wave);
free(blocks);
blocks = nullptr;
return err;
@@ -888,18 +939,18 @@ static cassette_image::error tzx_cassette_load( cassette_image *cassette )
static cassette_image::error tap_cassette_load( cassette_image *cassette )
{
- return cassette_legacy_construct(cassette, &tap_legacy_fill_wave);
+ return cassette->legacy_construct(&tap_legacy_fill_wave);
}
static cassette_image::error cdt_cassette_load( cassette_image *cassette )
{
- cassette_image::error err = cassette_legacy_construct(cassette, &cdt_legacy_fill_wave);
+ cassette_image::error err = cassette->legacy_construct(&cdt_legacy_fill_wave);
free(blocks);
blocks = nullptr;
return err;
}
-const struct CassetteFormat tzx_cassette_format =
+const cassette_image::Format tzx_cassette_format =
{
"tzx",
tzx_cassette_identify,
@@ -907,7 +958,7 @@ const struct CassetteFormat tzx_cassette_format =
nullptr
};
-static const struct CassetteFormat tap_cassette_format =
+static const cassette_image::Format tap_cassette_format =
{
"tap,blk",
tap_cassette_identify,
@@ -915,7 +966,7 @@ static const struct CassetteFormat tap_cassette_format =
nullptr
};
-static const struct CassetteFormat cdt_cassette_format =
+static const cassette_image::Format cdt_cassette_format =
{
"cdt",
cdt_cassette_identify,
diff --git a/src/lib/formats/tzx_cas.h b/src/lib/formats/tzx_cas.h
index 2410ea57523..b7e620b1683 100644
--- a/src/lib/formats/tzx_cas.h
+++ b/src/lib/formats/tzx_cas.h
@@ -13,7 +13,7 @@
#include "cassimg.h"
-extern const struct CassetteFormat tzx_cassette_format;
+extern const cassette_image::Format tzx_cassette_format;
CASSETTE_FORMATLIST_EXTERN(tzx_cassette_formats);
CASSETTE_FORMATLIST_EXTERN(cdt_cassette_formats);
diff --git a/src/lib/formats/uef_cas.cpp b/src/lib/formats/uef_cas.cpp
index 78247887eb5..02f150915a7 100644
--- a/src/lib/formats/uef_cas.cpp
+++ b/src/lib/formats/uef_cas.cpp
@@ -17,13 +17,15 @@ the size of memory to alloc for the decoding.
Not nice, but it works...
*/
+#include "uef_cas.h"
+#include "imageutl.h"
-#include <string.h>
-#include <math.h>
-#include <assert.h>
+#include "multibyte.h"
#include <zlib.h>
-#include "uef_cas.h"
+
+#include <cmath>
+#include <cstring>
#define UEF_WAV_FREQUENCY 4800
@@ -64,7 +66,7 @@ static const uint8_t* skip_gz_header( const uint8_t *p ) {
/* Skip the extra field */
if ( ( flags & EXTRA_FIELD ) != 0 ) {
- int len = ( p[1] << 8 ) | p[0];
+ int len = get_u16le( &p[0] );
p += 2 + len;
}
/* Skip the original file name */
@@ -94,13 +96,13 @@ static float get_uef_float( const uint8_t *Float)
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;
+ Mantissa = get_u24le(&Float[0]) | 0x800000;
Result = (float)Mantissa;
Result = (float)ldexp(Result, -23);
/* decode exponent */
- Exponent = ((Float[2]&0x80) >> 7) | (Float[3]&0x7f) << 1;
+ Exponent = (get_u16le(&Float[2])&0x7f80) >> 7;
Exponent -= 127;
Result = (float)ldexp(Result, Exponent);
@@ -118,7 +120,7 @@ static int uef_cas_to_wav_size( const uint8_t *casdata, int caslen ) {
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 ];
+ int inflate_size = get_u32le( &casdata[ caslen - 4 ] );
const uint8_t *in_ptr = skip_gz_header( casdata );
if ( in_ptr == nullptr ) {
@@ -166,8 +168,8 @@ static int uef_cas_to_wav_size( const uint8_t *casdata, int caslen ) {
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 chunk_type = get_u16le( &casdata[pos] );
+ int chunk_length = get_u32le( &casdata[pos+2] );
int baud_length;
pos += 6;
@@ -187,14 +189,14 @@ static int uef_cas_to_wav_size( const uint8_t *casdata, int caslen ) {
size += ( chunk_length * 10 ) * 4;
break;
case 0x0110: /* carrier tone (previously referred to as 'high tone') */
- baud_length = ( casdata[pos+1] << 8 ) | casdata[pos];
+ baud_length = get_u16le( &casdata[pos] );
size += baud_length * 2;
break;
case 0x0111:
LOG_FORMATS( "Unsupported chunk type: %04x\n", chunk_type );
break;
case 0x0112: /* integer gap */
- baud_length = ( casdata[pos+1] << 8 ) | casdata[pos];
+ baud_length = get_u16le( &casdata[pos] );
size += baud_length * 2 ;
break;
case 0x0116: /* floating point gap */
@@ -245,7 +247,7 @@ static int16_t* uef_cas_fill_bit( uint8_t loops, int16_t *buffer, bool bit )
return buffer;
}
-static int uef_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
+static int uef_cas_fill_wave( int16_t *buffer, int length, const uint8_t *bytes, int )
{
if ( bytes[0] == 0x1f && bytes[1] == 0x8b ) {
if ( gz_ptr == nullptr ) {
@@ -259,19 +261,19 @@ static int uef_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
uint32_t 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 chunk_type = get_u16le( &bytes[pos] );
+ int chunk_length = get_u32le( &bytes[pos+2] );
- uint32_t baud_length, j;
- uint8_t i, *c;
+ uint32_t baud_length;
+ const uint8_t *c;
pos += 6;
switch( chunk_type ) {
case 0x0100: /* implicit start/stop bit data block */
case 0x0104: // used by atom dumps, looks like normal data
- for( j = 0; j < chunk_length; j++ ) {
+ for( uint32_t j = 0; j < chunk_length; j++ ) {
uint8_t byte = bytes[pos+j];
p = uef_cas_fill_bit( loops, p, 0 );
- for( i = 0; i < 8; i++ ) {
+ for( uint8_t i = 0; i < 8; i++ ) {
p = uef_cas_fill_bit( loops, p, (byte >> i) & 1 );
}
p = uef_cas_fill_bit( loops, p, 1 );
@@ -283,11 +285,10 @@ static int uef_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
LOG_FORMATS( "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 ) {
+ for( uint32_t j = ( chunk_length * 10 ) - bytes[pos]; j; ) {
uint8_t byte = *c;
- for( i = 0; i < 8 && i < j; i++ ) {
+ for( uint8_t i = 0; i < 8 && i < j; i++ ) {
p = uef_cas_fill_bit( loops, p, (byte >> i) & 1 );
j--;
}
@@ -295,27 +296,27 @@ static int uef_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
}
break;
case 0x0110: /* carrier tone (previously referred to as 'high tone') */
- for( baud_length = ( ( bytes[pos+1] << 8 ) | bytes[pos] ) ; baud_length; baud_length-- ) {
+ for( baud_length = get_u16le( &bytes[pos] ) ; baud_length; baud_length-- ) {
*p = WAVE_LOW; p++;
*p = WAVE_HIGH; p++;
length -= 2;
}
break;
case 0x0112: /* integer gap */
- for( baud_length = ( ( bytes[pos+1] << 8 ) | bytes[pos] ) ; baud_length; baud_length-- ) {
+ for( baud_length = get_u16le( &bytes[pos] ) ; baud_length; baud_length-- ) {
*p = WAVE_NULL; p++;
*p = WAVE_NULL; p++;
length -= 2;
}
break;
case 0x0116: /* floating point gap */
- for( baud_length = (get_uef_float(bytes+pos)*UEF_WAV_FREQUENCY); baud_length; baud_length-- ) {
+ for( baud_length = (get_uef_float(bytes + pos)*UEF_WAV_FREQUENCY); baud_length; baud_length-- ) {
*p = WAVE_NULL; p++;
length -= 1;
}
break;
case 0x0117: /* change baud rate */
- baud_length = ( bytes[pos+1] << 8 ) | bytes[pos];
+ baud_length = get_u16le( &bytes[pos] );
// These are the only supported numbers
if (baud_length == 300)
loops = 4;
@@ -334,7 +335,7 @@ static int uef_cas_fill_wave( int16_t *buffer, int length, uint8_t *bytes )
return p - buffer;
}
-static const struct CassetteLegacyWaveFiller uef_legacy_fill_wave = {
+static const cassette_image::LegacyWaveFiller uef_legacy_fill_wave = {
uef_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
0, /* chunk_samples */
@@ -344,21 +345,21 @@ static const struct CassetteLegacyWaveFiller uef_legacy_fill_wave = {
0 /* trailer_samples */
};
-static cassette_image::error uef_cassette_identify( cassette_image *cassette, struct CassetteOptions *opts ) {
+static cassette_image::error uef_cassette_identify( cassette_image *cassette, cassette_image::Options *opts ) {
uint8_t header[10];
- cassette_image_read(cassette, header, 0, sizeof(header));
+ cassette->image_read(header, 0, sizeof(header));
if (memcmp(&header[0], GZ_HEADER, sizeof(GZ_HEADER)) && memcmp(&header[0], UEF_HEADER, sizeof(UEF_HEADER))) {
return cassette_image::error::INVALID_IMAGE;
}
- return cassette_legacy_identify( cassette, opts, &uef_legacy_fill_wave );
+ return cassette->legacy_identify( opts, &uef_legacy_fill_wave );
}
static cassette_image::error uef_cassette_load( cassette_image *cassette ) {
- return cassette_legacy_construct( cassette, &uef_legacy_fill_wave );
+ return cassette->legacy_construct( &uef_legacy_fill_wave );
}
-const struct CassetteFormat uef_cassette_format = {
+const cassette_image::Format uef_cassette_format = {
"uef",
uef_cassette_identify,
uef_cassette_load,
diff --git a/src/lib/formats/uef_cas.h b/src/lib/formats/uef_cas.h
index 73aa2fe8ae2..f6bcff94ca1 100644
--- a/src/lib/formats/uef_cas.h
+++ b/src/lib/formats/uef_cas.h
@@ -12,7 +12,7 @@
#include "cassimg.h"
-extern const struct CassetteFormat uef_cassette_format;
+extern const cassette_image::Format uef_cassette_format;
CASSETTE_FORMATLIST_EXTERN(uef_cassette_formats);
diff --git a/src/lib/formats/uniflex_dsk.cpp b/src/lib/formats/uniflex_dsk.cpp
index 6b536aac3eb..063d3c877f9 100644
--- a/src/lib/formats/uniflex_dsk.cpp
+++ b/src/lib/formats/uniflex_dsk.cpp
@@ -1,7 +1,7 @@
// license:BSD-3-Clause
// copyright-holders:68bit
/*
- * uniflex_dsk.c - UniFLEX compatible disk images
+ * uniflex_dsk.cpp - UniFLEX compatible disk images
*
* The UniFLEX floppy disk format is distinctily different to the FLEX format,
* the sector size is 512 byte sectors versus 256 byte sectors and the format
@@ -10,58 +10,67 @@
*/
#include "uniflex_dsk.h"
-#include "formats/imageutl.h"
+#include "imageutl.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
uniflex_format::uniflex_format() : wd177x_format(formats)
{
}
-const char *uniflex_format::name() const
+const char *uniflex_format::name() const noexcept
{
return "uniflex";
}
-const char *uniflex_format::description() const
+const char *uniflex_format::description() const noexcept
{
return "UniFLEX compatible disk image";
}
-const char *uniflex_format::extensions() const
+const char *uniflex_format::extensions() const noexcept
{
return "dsk";
}
-int uniflex_format::identify(io_generic *io, uint32_t form_factor)
+int uniflex_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int const type = find_size(io, form_factor, variants);
if (type != -1)
- return 75;
+ return FIFID_SIZE;
+
return 0;
}
-int uniflex_format::find_size(io_generic *io, uint32_t form_factor)
+int uniflex_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
- uint8_t sir[192];
+ uint64_t size;
+ if (io.length(size))
+ return -1;
// Look at the SIR sector, the second sector.
- io_generic_read(io, sir, 1 * 512, sizeof(sir));
+ uint8_t sir[192];
+ auto const [err, actual] = read_at(io, 1 * 512, sir, sizeof(sir)); // FIXME: check for premature EOF
+ if (err)
+ return -1;
- uint16_t fdn_block_count = pick_integer_be(sir, 0x10, 2);
- uint32_t last_block_number = pick_integer_be(sir, 0x12, 3);
- uint32_t free_block_count = pick_integer_be(sir, 0x15, 3);
- uint16_t fdn_free_count = pick_integer_be(sir, 0x15, 2);
+ uint16_t fdn_block_count = get_u16be(&sir[0x10]);
+ uint32_t last_block_number = get_u24be(&sir[0x12]);
+ uint32_t free_block_count = get_u24be(&sir[0x15]);
+ uint16_t fdn_free_count = get_u16be(&sir[0x15]); // FIXME: offset should be different from previous?
- uint16_t volume_number = pick_integer_be(sir, 0x36, 2);
+ uint16_t volume_number = get_u16be(&sir[0x36]);
uint8_t disk_density = sir[0x3a];
uint8_t disk_side_info = sir[0x3b];
- uint32_t volbc_start_addr = pick_integer_be(sir, 0x3c, 3);
- uint16_t swap_size = pick_integer_be(sir, 0x3f, 2);
+ uint32_t volbc_start_addr = get_u24be(&sir[0x3c]);
+ uint16_t swap_size = get_u16be(&sir[0x3f]);
LOG_FORMATS("UniFLEX floppy dsk size %d\n", (uint32_t)size);
- LOG_FORMATS(" time = %u %u\n", (uint32_t) pick_integer_be(sir, 0x08, 4), (uint32_t) pick_integer_be(sir, 0x0c, 4));
+ LOG_FORMATS(" time = %u %u\n", get_u32be(&sir[0x08]), get_u32be(&sir[0x0c]));
LOG_FORMATS(" fdn_block_count: %d\n", fdn_block_count);
LOG_FORMATS(" file system name: ");
@@ -83,7 +92,7 @@ int uniflex_format::find_size(io_generic *io, uint32_t form_factor)
LOG_FORMATS(" swap_size: %d\n", swap_size);
// The first eight bytes appear to be zeros.
- if (pick_integer_be(sir, 0x00, 8) != 0)
+ if (get_u64be(&sir[0x00]) != 0)
return -1;
for(int i=0; formats[i].form_factor; i++) {
@@ -147,4 +156,4 @@ const uniflex_format::format uniflex_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_UNIFLEX_FORMAT = &floppy_image_format_creator<uniflex_format>;
+const uniflex_format FLOPPY_UNIFLEX_FORMAT;
diff --git a/src/lib/formats/uniflex_dsk.h b/src/lib/formats/uniflex_dsk.h
index cd3a3c515ab..5eab46e690c 100644
--- a/src/lib/formats/uniflex_dsk.h
+++ b/src/lib/formats/uniflex_dsk.h
@@ -16,17 +16,17 @@ class uniflex_format : public wd177x_format
public:
uniflex_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual int find_size(io_generic *io, uint32_t form_factor) override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
void build_sector_description(const format &f, uint8_t *sectdata, desc_s *sectors, int track, int head) const override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_UNIFLEX_FORMAT;
+extern const uniflex_format FLOPPY_UNIFLEX_FORMAT;
#endif // MAME_FORMATS_UNIFLEX_DSK_H
diff --git a/src/lib/formats/upd765_dsk.cpp b/src/lib/formats/upd765_dsk.cpp
index c8355b7b691..589ab1e1666 100644
--- a/src/lib/formats/upd765_dsk.cpp
+++ b/src/lib/formats/upd765_dsk.cpp
@@ -2,39 +2,50 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/upd765_dsk.h
+ formats/upd765_dsk.cpp
helper for simple upd765-formatted disk images
*********************************************************************/
-#include "emu.h" // emu_fatalerror
#include "formats/upd765_dsk.h"
-upd765_format::upd765_format(const format *_formats) : file_header_skip_bytes(0), file_footer_skip_bytes(0), formats(_formats)
+#include "ioprocs.h"
+
+#include "osdcore.h" // osd_printf_*
+
+#include <cstring>
+
+
+upd765_format::upd765_format(const format *_formats) : formats(_formats)
{
}
-int upd765_format::find_size(io_generic *io, uint32_t form_factor) const
+int upd765_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if(io.length(size))
+ return -1;
+
for(int i=0; formats[i].form_factor; i++) {
const format &f = formats[i];
if(form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
continue;
+ if(!variants.empty() && !has_variant(variants, f.variant))
+ continue;
- if(size == file_header_skip_bytes + (uint64_t) compute_track_size(f) * f.track_count * f.head_count + file_footer_skip_bytes)
+ if(size == compute_track_size(f) * f.track_count * f.head_count)
return i;
}
return -1;
}
-int upd765_format::identify(io_generic *io, uint32_t form_factor)
+int upd765_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int type = find_size(io, form_factor, variants);
if(type != -1)
- return 50;
+ return FIFID_SIZE;
return 0;
}
@@ -74,7 +85,7 @@ void upd765_format::build_sector_description(const format &f, uint8_t *sectdata,
}
}
-floppy_image_format_t::desc_e* upd765_format::get_desc_fm(const format &f, int &current_size, int &end_gap_index)
+floppy_image_format_t::desc_e* upd765_format::get_desc_fm(const format &f, int &current_size, int &end_gap_index) const
{
static floppy_image_format_t::desc_e desc[26] = {
/* 00 */ { FM, 0xff, f.gap_4a },
@@ -119,7 +130,7 @@ floppy_image_format_t::desc_e* upd765_format::get_desc_fm(const format &f, int &
return desc;
}
-floppy_image_format_t::desc_e* upd765_format::get_desc_mfm(const format &f, int &current_size, int &end_gap_index)
+floppy_image_format_t::desc_e* upd765_format::get_desc_mfm(const format &f, int &current_size, int &end_gap_index) const
{
static floppy_image_format_t::desc_e desc[29] = {
/* 00 */ { MFM, 0x4e, 0 },
@@ -173,16 +184,16 @@ floppy_image_format_t::desc_e* upd765_format::get_desc_mfm(const format &f, int
return desc;
}
-bool upd765_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool upd765_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- int type = find_size(io, form_factor);
+ int type = find_size(io, form_factor, variants);
if(type == -1)
return false;
// format shouldn't exceed image geometry
const format &f = formats[type];
int img_tracks, img_heads;
- image->get_maximal_geometry(img_tracks, img_heads);
+ image.get_maximal_geometry(img_tracks, img_heads);
if (f.track_count > img_tracks || f.head_count > img_heads)
return false;
@@ -190,8 +201,7 @@ bool upd765_format::load(io_generic *io, uint32_t form_factor, floppy_image *ima
int current_size;
int end_gap_index;
- switch (f.encoding)
- {
+ switch(f.encoding) {
case floppy_image::FM:
desc = get_desc_fm(f, current_size, end_gap_index);
break;
@@ -203,8 +213,10 @@ bool upd765_format::load(io_generic *io, uint32_t form_factor, floppy_image *ima
int total_size = 200000000/f.cell_size;
int remaining_size = total_size - current_size;
- if(remaining_size < 0)
- throw emu_fatalerror("upd765_format: Incorrect track layout, max_size=%d, current_size=%d", total_size, current_size);
+ if(remaining_size < 0) {
+ osd_printf_error("upd765_format: Incorrect track layout, max_size=%d, current_size=%d\n", total_size, current_size);
+ return false;
+ }
// Fixup the end gap
desc[end_gap_index].p2 = remaining_size / 16;
@@ -219,21 +231,21 @@ bool upd765_format::load(io_generic *io, uint32_t form_factor, floppy_image *ima
for(int track=0; track < f.track_count; track++)
for(int head=0; head < f.head_count; head++) {
build_sector_description(f, sectdata, sectors, track, head);
- io_generic_read(io, sectdata, file_header_skip_bytes + (track*f.head_count + head)*track_size, track_size);
+ /*auto const [err, actual] =*/ read_at(io, (track*f.head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors and premature EOF
generate_track(desc, track, head, sectors, f.sector_count, total_size, image);
}
- image->set_variant(f.variant);
+ image.set_form_variant(f.form_factor, f.variant);
return true;
}
-bool upd765_format::supports_save() const
+bool upd765_format::supports_save() const noexcept
{
return true;
}
-bool upd765_format::save(io_generic *io, floppy_image *image)
+bool upd765_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
// Count the number of formats
int formats_count;
@@ -253,8 +265,8 @@ bool upd765_format::save(io_generic *io, floppy_image *image)
int cur_cell_size = 0;
candidates.clear();
for(int i=0; i != formats_count; i++) {
- if(image->get_form_factor() == floppy_image::FF_UNKNOWN ||
- image->get_form_factor() == formats[i].form_factor) {
+ if(image.get_form_factor() == floppy_image::FF_UNKNOWN ||
+ image.get_form_factor() == formats[i].form_factor) {
if(formats[i].cell_size == cur_cell_size)
candidates.push_back(i);
else if((!cur_cell_size || formats[i].cell_size < cur_cell_size) &&
@@ -291,7 +303,7 @@ bool upd765_format::save(io_generic *io, floppy_image *image)
// Otherwise, find the best
int tracks, heads;
- image->get_actual_geometry(tracks, heads);
+ image.get_actual_geometry(tracks, heads);
chosen_candidate = candidates[0];
for(unsigned int i=1; i != candidates.size(); i++) {
const format &cc = formats[chosen_candidate];
@@ -332,40 +344,36 @@ bool upd765_format::save(io_generic *io, floppy_image *image)
if(chosen_candidate == -1)
chosen_candidate = 0;
-
const format &f = formats[chosen_candidate];
int track_size = compute_track_size(f);
uint8_t sectdata[40*512];
desc_s sectors[40];
- for(int track=0; track < f.track_count; track++)
+ for(int track=0; track < f.track_count; track++) {
for(int head=0; head < f.head_count; head++) {
build_sector_description(f, sectdata, sectors, track, head);
extract_sectors(image, f, sectors, track, head);
- io_generic_write(io, sectdata, (track*f.head_count + head)*track_size, track_size);
+ /*auto const [err, actual] =*/ write_at(io, (track*f.head_count + head)*track_size, sectdata, track_size); // FIXME: check for errors
}
+ }
return true;
}
-void upd765_format::check_compatibility(floppy_image *image, std::vector<int> &candidates)
+void upd765_format::check_compatibility(const floppy_image &image, std::vector<int> &candidates) const
{
- uint8_t bitstream[500000/8];
- uint8_t sectdata[50000];
- desc_xs sectors[256];
- int track_size;
-
// Extract the sectors
- generate_bitstream_from_track(0, 0, formats[candidates[0]].cell_size, bitstream, track_size, image);
+ auto bitstream = generate_bitstream_from_track(0, 0, formats[candidates[0]].cell_size, image);
+ std::vector<std::vector<uint8_t>> sectors;
switch (formats[candidates[0]].encoding)
{
case floppy_image::FM:
- extract_sectors_from_bitstream_fm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
+ sectors = extract_sectors_from_bitstream_fm_pc(bitstream);
break;
case floppy_image::MFM:
- extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
+ sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
break;
}
@@ -374,8 +382,8 @@ void upd765_format::check_compatibility(floppy_image *image, std::vector<int> &c
for(unsigned int i=0; i != candidates.size(); i++) {
const format &f = formats[candidates[i]];
int ns = 0;
- for(int j=0; j<256; j++)
- if(sectors[j].data) {
+ for(unsigned int j=0; j != sectors.size(); j++)
+ if(!sectors[j].empty()) {
int sid;
if(f.sector_base_id == -1) {
for(sid=0; sid < f.sector_count; sid++)
@@ -386,10 +394,10 @@ void upd765_format::check_compatibility(floppy_image *image, std::vector<int> &c
if(sid < 0 || sid > f.sector_count)
goto fail;
if(f.sector_base_size) {
- if(sectors[j].size != f.sector_base_size)
+ if(sectors[j].size() != f.sector_base_size)
goto fail;
} else {
- if(sectors[j].size != f.per_sector_size[sid])
+ if(sectors[j].size() != f.per_sector_size[sid])
goto fail;
}
ns++;
@@ -403,35 +411,32 @@ void upd765_format::check_compatibility(floppy_image *image, std::vector<int> &c
}
-void upd765_format::extract_sectors(floppy_image *image, const format &f, desc_s *sdesc, int track, int head)
+void upd765_format::extract_sectors(const floppy_image &image, const format &f, desc_s *sdesc, int track, int head) const
{
- uint8_t bitstream[500000/8];
- uint8_t sectdata[50000];
- desc_xs sectors[256];
- int track_size;
-
// Extract the sectors
- generate_bitstream_from_track(track, head, f.cell_size, bitstream, track_size, image);
+ auto bitstream = generate_bitstream_from_track(track, head, f.cell_size, image);
+ std::vector<std::vector<uint8_t>> sectors;
switch (f.encoding)
{
case floppy_image::FM:
- extract_sectors_from_bitstream_fm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
+ sectors = extract_sectors_from_bitstream_fm_pc(bitstream);
break;
case floppy_image::MFM:
- extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
+ sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
break;
}
for(int i=0; i<f.sector_count; i++) {
desc_s &ds = sdesc[i];
- desc_xs &xs = sectors[ds.sector_id];
- if(!xs.data)
+ if(ds.sector_id >= sectors.size() || sectors[ds.sector_id].empty())
memset((void *)ds.data, 0, ds.size);
- else if(xs.size < ds.size) {
- memcpy((void *)ds.data, xs.data, xs.size);
- memset((uint8_t *)ds.data + xs.size, 0, xs.size - ds.size);
+
+ else if(sectors[ds.sector_id].size() < ds.size) {
+ memcpy((void *)ds.data, sectors[ds.sector_id].data(), sectors[ds.sector_id].size());
+ memset((uint8_t *)ds.data + sectors[ds.sector_id].size(), 0, ds.size - sectors[ds.sector_id].size());
+
} else
- memcpy((void *)ds.data, xs.data, ds.size);
+ memcpy((void *)ds.data, sectors[ds.sector_id].data(), ds.size);
}
}
diff --git a/src/lib/formats/upd765_dsk.h b/src/lib/formats/upd765_dsk.h
index a1b3f776104..b2a1b3f8a0e 100644
--- a/src/lib/formats/upd765_dsk.h
+++ b/src/lib/formats/upd765_dsk.h
@@ -39,22 +39,19 @@ public:
// End the array with {}
upd765_format(const format *formats);
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
protected:
- uint64_t file_header_skip_bytes;
- uint64_t file_footer_skip_bytes;
-
- floppy_image_format_t::desc_e* get_desc_fm(const format &f, int &current_size, int &end_gap_index);
- floppy_image_format_t::desc_e* get_desc_mfm(const format &f, int &current_size, int &end_gap_index);
- int find_size(io_generic *io, uint32_t form_factor) const;
+ floppy_image_format_t::desc_e* get_desc_fm(const format &f, int &current_size, int &end_gap_index) const;
+ floppy_image_format_t::desc_e* get_desc_mfm(const format &f, int &current_size, int &end_gap_index) const;
+ int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const;
int compute_track_size(const format &f) const;
virtual void build_sector_description(const format &d, uint8_t *sectdata, desc_s *sectors, int track, int head) const;
- void check_compatibility(floppy_image *image, std::vector<int> &candidates);
- void extract_sectors(floppy_image *image, const format &f, desc_s *sdesc, int track, int head);
+ void check_compatibility(const floppy_image &image, std::vector<int> &candidates) const;
+ void extract_sectors(const floppy_image &image, const format &f, desc_s *sdesc, int track, int head) const;
private:
format const *const formats;
diff --git a/src/lib/formats/vdk_dsk.cpp b/src/lib/formats/vdk_dsk.cpp
index 308f544e2d3..02e25ec5df9 100644
--- a/src/lib/formats/vdk_dsk.cpp
+++ b/src/lib/formats/vdk_dsk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -10,49 +10,57 @@
***************************************************************************/
-#include "emu.h"
#include "vdk_dsk.h"
+#include "ioprocs.h"
+
+
vdk_format::vdk_format()
{
}
-const char *vdk_format::name() const
+const char *vdk_format::name() const noexcept
{
return "vdk";
}
-const char *vdk_format::description() const
+const char *vdk_format::description() const noexcept
{
return "VDK disk image";
}
-const char *vdk_format::extensions() const
+const char *vdk_format::extensions() const noexcept
{
return "vdk";
}
-int vdk_format::identify(io_generic *io, uint32_t form_factor)
+int vdk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
uint8_t id[2];
- io_generic_read(io, id, 0, 2);
+ auto const [err, actual] = read_at(io, 0, id, 2);
+ if (err || (2 != actual))
+ return 0;
if (id[0] == 'd' && id[1] == 'k')
- return 50;
+ return FIFID_SIGN;
else
return 0;
}
-bool vdk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool vdk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
+ if (io.seek(0, SEEK_SET))
+ return false;
+
uint8_t header[0x100];
- io_generic_read(io, header, 0, 0x100);
+ read(io, header, 0x100); // FIXME: check for errors and premature EOF
- int header_size = header[3] * 0x100 + header[2];
- int track_count = header[8];
- int head_count = header[9];
+ int const header_size = header[3] * 0x100 + header[2];
+ int const track_count = header[8];
+ int const head_count = header[9];
- int file_offset = header_size;
+ if (io.seek(header_size, SEEK_SET))
+ return false;
for (int track = 0; track < track_count; track++)
{
@@ -73,10 +81,9 @@ bool vdk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
sectors[i].bad_crc = false;
sectors[i].data = &sector_data[sector_offset];
- io_generic_read(io, sectors[i].data, file_offset, SECTOR_SIZE);
+ read(io, sectors[i].data, SECTOR_SIZE); // FIXME: check for errors and premature EOF
sector_offset += SECTOR_SIZE;
- file_offset += SECTOR_SIZE;
}
build_wd_track_mfm(track, head, image, 100000, SECTOR_COUNT, sectors, 22, 32, 24);
@@ -86,15 +93,13 @@ bool vdk_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
return true;
}
-bool vdk_format::save(io_generic *io, floppy_image *image)
+bool vdk_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
- uint8_t bitstream[500000/8];
- uint8_t sector_data[50000];
- desc_xs sectors[256];
- uint64_t file_offset = 0;
+ if (io.seek(0, SEEK_SET))
+ return false;
int track_count, head_count;
- image->get_actual_geometry(track_count, head_count);
+ image.get_actual_geometry(track_count, head_count);
// write header
uint8_t header[12];
@@ -112,32 +117,27 @@ bool vdk_format::save(io_generic *io, floppy_image *image)
header[10] = 0;
header[11] = 0;
- io_generic_write(io, header, file_offset, sizeof(header));
- file_offset += sizeof(header);
+ write(io, header, sizeof(header)); // FIXME: check for errors
// write disk data
for (int track = 0; track < track_count; track++)
{
for (int head = 0; head < head_count; head++)
{
- int track_size;
- generate_bitstream_from_track(track, head, 2000, bitstream, track_size, image);
- extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sector_data, sizeof(sector_data));
+ auto bitstream = generate_bitstream_from_track(track, head, 2000, image);
+ auto sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
for (int i = 0; i < SECTOR_COUNT; i++)
- {
- io_generic_write(io, sectors[FIRST_SECTOR_ID + i].data, file_offset, SECTOR_SIZE);
- file_offset += SECTOR_SIZE;
- }
+ write(io, sectors[FIRST_SECTOR_ID + i].data(), SECTOR_SIZE); // FIXME: check for errors
}
}
return true;
}
-bool vdk_format::supports_save() const
+bool vdk_format::supports_save() const noexcept
{
return true;
}
-const floppy_format_type FLOPPY_VDK_FORMAT = &floppy_image_format_creator<vdk_format>;
+const vdk_format FLOPPY_VDK_FORMAT;
diff --git a/src/lib/formats/vdk_dsk.h b/src/lib/formats/vdk_dsk.h
index abadc3bf2f6..58ec21f0531 100644
--- a/src/lib/formats/vdk_dsk.h
+++ b/src/lib/formats/vdk_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -21,14 +21,14 @@ class vdk_format : public floppy_image_format_t
public:
vdk_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
private:
static const int SECTOR_SIZE = 256;
@@ -36,6 +36,6 @@ private:
static const int FIRST_SECTOR_ID = 1;
};
-extern const floppy_format_type FLOPPY_VDK_FORMAT;
+extern const vdk_format FLOPPY_VDK_FORMAT;
#endif // MAME_FORMATS_VDK_DSK_H
diff --git a/src/lib/formats/vector06_dsk.cpp b/src/lib/formats/vector06_dsk.cpp
index 6601d84e7c7..67cd5c4c425 100644
--- a/src/lib/formats/vector06_dsk.cpp
+++ b/src/lib/formats/vector06_dsk.cpp
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -17,17 +17,17 @@ vector06_format::vector06_format() : wd177x_format(formats)
{
}
-const char *vector06_format::name() const
+const char *vector06_format::name() const noexcept
{
return "vector06";
}
-const char *vector06_format::description() const
+const char *vector06_format::description() const noexcept
{
return "Vector 06 disk image";
}
-const char *vector06_format::extensions() const
+const char *vector06_format::extensions() const noexcept
{
return "fdd";
}
@@ -45,4 +45,4 @@ const vector06_format::format vector06_format::formats[] =
{}
};
-const floppy_format_type FLOPPY_VECTOR06_FORMAT = &floppy_image_format_creator<vector06_format>;
+const vector06_format FLOPPY_VECTOR06_FORMAT;
diff --git a/src/lib/formats/vector06_dsk.h b/src/lib/formats/vector06_dsk.h
index a17cacfa04c..9e134b558ec 100644
--- a/src/lib/formats/vector06_dsk.h
+++ b/src/lib/formats/vector06_dsk.h
@@ -1,4 +1,4 @@
-// license:GPL-2.0+
+// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
@@ -19,14 +19,14 @@ class vector06_format : public wd177x_format
public:
vector06_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_VECTOR06_FORMAT;
+extern const vector06_format FLOPPY_VECTOR06_FORMAT;
#endif // MAME_FORMATS_VECTOR06_DSK_H
diff --git a/src/lib/formats/vg5k_cas.cpp b/src/lib/formats/vg5k_cas.cpp
index 898507e7139..724a4889229 100644
--- a/src/lib/formats/vg5k_cas.cpp
+++ b/src/lib/formats/vg5k_cas.cpp
@@ -5,17 +5,18 @@
Support for VG-5000 .k7 cassette images
********************************************************************/
-#include <assert.h>
#include "vg5k_cas.h"
+#include "multibyte.h"
+
#define SMPLO -32768
#define SILENCE 0
#define SMPHI 32767
-static int k7_size;
+static int k7_size; // FIXME: global variable prevents multiple instances
/*******************************************************************
Generate one high-low cycle of sample data
@@ -144,7 +145,7 @@ static int vg5k_handle_tap(int16_t *buffer, const uint8_t *casdata)
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;
+ block_size = get_u16le(&casdata[data_pos - 4]) + 20;
/* 10000 samples of silence before the data block */
sample_count += vg5k_cas_silence(buffer, sample_count, 10000);
@@ -183,7 +184,7 @@ static int vg5k_handle_tap(int16_t *buffer, const uint8_t *casdata)
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int vg5k_k7_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int vg5k_k7_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return vg5k_handle_tap(buffer, bytes);
}
@@ -200,7 +201,7 @@ static int vg5k_k7_to_wav_size(const uint8_t *casdata, int caslen)
}
-static const struct CassetteLegacyWaveFiller vg5k_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller vg5k_legacy_fill_wave =
{
vg5k_k7_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -211,19 +212,19 @@ static const struct CassetteLegacyWaveFiller vg5k_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error vg5k_k7_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error vg5k_k7_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &vg5k_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &vg5k_legacy_fill_wave);
}
static cassette_image::error vg5k_k7_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &vg5k_legacy_fill_wave);
+ return cassette->legacy_construct(&vg5k_legacy_fill_wave);
}
-static const struct CassetteFormat vg5k_k7_format =
+static const cassette_image::Format vg5k_k7_format =
{
"k7",
vg5k_k7_identify,
@@ -234,4 +235,5 @@ static const struct CassetteFormat vg5k_k7_format =
CASSETTE_FORMATLIST_START(vg5k_cassette_formats)
CASSETTE_FORMAT(vg5k_k7_format)
+ CASSETTE_FORMAT(cassette_image::wavfile_format)
CASSETTE_FORMATLIST_END
diff --git a/src/lib/formats/vgi_dsk.cpp b/src/lib/formats/vgi_dsk.cpp
new file mode 100644
index 00000000000..a86ffdefb75
--- /dev/null
+++ b/src/lib/formats/vgi_dsk.cpp
@@ -0,0 +1,146 @@
+// license:BSD-3-Clause
+// copyright-holders:Eric Anderson
+/*********************************************************************
+
+Micropolis VGI disk image format
+
+The format is essentially an HCS-ordered sector image, but with raw
+275 byte sectors. The sector format is: SYNC (0xFF), track, sector,
+10 byte "user data", 256 byte data, checksum, 4 byte ECC, ECC present
+flag.
+
+http://www.bitsavers.org/pdf/micropolis/metafloppy/1084-01_1040_1050_Users_Manual_Apr79.pdf
+
+*********************************************************************/
+
+#include "vgi_dsk.h"
+#include "ioprocs.h"
+
+#include <cstring>
+
+static constexpr int TRACK_SIZE = 100'000;
+static constexpr int HALF_BITCELL_SIZE = 2000;
+
+micropolis_vgi_format::micropolis_vgi_format() : floppy_image_format_t()
+{
+}
+
+struct format {
+ int head_count;
+ int track_count;
+ uint32_t variant;
+};
+
+static const format formats[] = {
+ {1, 35, floppy_image::SSDD16}, // MOD-I
+ {2, 35, floppy_image::DSDD16},
+ {1, 77, floppy_image::SSQD16}, // MOD-II
+ {2, 77, floppy_image::DSQD16},
+ {}
+};
+
+static format find_format(int file_size)
+{
+ for (int i = 0; formats[i].head_count; i++)
+ if (file_size == formats[i].head_count * formats[i].track_count * 16 * 275)
+ return formats[i];
+ return {};
+}
+
+int micropolis_vgi_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t file_size;
+ if (io.length(file_size))
+ return 0;
+ format fmt = find_format(file_size);
+ if (!fmt.head_count)
+ return 0;
+ if (!has_variant(variants, fmt.variant))
+ return 0;
+ return FIFID_SIZE;
+}
+
+bool micropolis_vgi_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ uint64_t file_size;
+ if (io.length(file_size))
+ return false;
+ const format fmt = find_format(file_size);
+ if (!fmt.head_count)
+ return false;
+ image.set_variant(fmt.variant);
+
+ std::vector<uint32_t> buf;
+ uint8_t sector_bytes[275];
+ for (int head = 0; head < fmt.head_count; head++) {
+ for (int track = 0; track < fmt.track_count; track++) {
+ for (int sector = 0; sector < 16; sector++) {
+ for (int i = 0; i < 40; i++)
+ mfm_w(buf, 8, 0, HALF_BITCELL_SIZE);
+ auto const [err, actual] = read(io, sector_bytes, std::size(sector_bytes));
+ if (err || (actual != std::size(sector_bytes)))
+ return false;
+ for (int i = 0; i < std::size(sector_bytes); i++)
+ mfm_w(buf, 8, sector_bytes[i], HALF_BITCELL_SIZE);
+ while (buf.size() < TRACK_SIZE/16 * (sector+1))
+ mfm_w(buf, 8, 0, HALF_BITCELL_SIZE);
+ }
+ generate_track_from_levels(track, head, buf, 0, image);
+ buf.clear();
+ }
+ }
+ return true;
+}
+
+bool micropolis_vgi_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ uint32_t variant = image.get_variant();
+ format fmt = {};
+ for (int i = 0; formats[i].head_count; i++)
+ if (variant == formats[i].variant)
+ fmt = formats[i];
+ if (!fmt.head_count) {
+ int heads, tracks;
+ image.get_actual_geometry(tracks, heads);
+ if (heads == 0 && tracks == 0)
+ return false; // Brand-new image; we don't know the size yet
+ for (int i = 0; formats[i].head_count; i++)
+ if (heads <= formats[i].head_count && tracks <= formats[i].track_count)
+ fmt = formats[i];
+ }
+ if (!fmt.head_count)
+ return false;
+
+ if (io.seek(0, SEEK_SET))
+ return false;
+ uint8_t sector_bytes[275];
+ for (int head = 0; head < fmt.head_count; head++) {
+ for (int track = 0; track < fmt.track_count; track++) {
+ std::vector<bool> bitstream = generate_bitstream_from_track(track, head, HALF_BITCELL_SIZE, image);
+ for (int sector = 0; sector < 16; sector++) {
+ int sector_start = TRACK_SIZE/16 * sector;
+ uint32_t pos = sector_start + 512 - 16;
+ uint16_t shift_reg = 0;
+ while (pos < sector_start + 60*16 && pos < bitstream.size()) {
+ shift_reg = (shift_reg << 1) | bitstream[pos++];
+ if (shift_reg == 0x5554)
+ break;
+ }
+ if (shift_reg == 0x5554) {
+ pos--;
+ for (int i = 0; i < std::size(sector_bytes); i++)
+ sector_bytes[i] = sbyte_mfm_r(bitstream, pos);
+ } else {
+ memset(sector_bytes, 0, std::size(sector_bytes));
+ }
+
+ auto const [err, actual] = write(io, sector_bytes, std::size(sector_bytes));
+ if (err)
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+const micropolis_vgi_format FLOPPY_VGI_FORMAT;
diff --git a/src/lib/formats/vgi_dsk.h b/src/lib/formats/vgi_dsk.h
new file mode 100644
index 00000000000..fabdc10ec1c
--- /dev/null
+++ b/src/lib/formats/vgi_dsk.h
@@ -0,0 +1,34 @@
+// license:BSD-3-Clause
+// copyright-holders:Eric Anderson
+/*********************************************************************
+
+Micropolis VGI disk image format
+
+The Micropolis disk format was used in Vector Graphic machines.
+
+*********************************************************************/
+#ifndef MAME_FORMATS_VGI_DSK_H
+#define MAME_FORMATS_VGI_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+
+class micropolis_vgi_format : public floppy_image_format_t
+{
+public:
+ micropolis_vgi_format();
+
+ int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+
+ const char *name() const noexcept override { return "vgi"; }
+ const char *description() const noexcept override { return "Micropolis VGI disk image"; }
+ const char *extensions() const noexcept override { return "vgi"; }
+ bool supports_save() const noexcept override { return true; }
+};
+
+extern const micropolis_vgi_format FLOPPY_VGI_FORMAT;
+
+#endif // MAME_FORMATS_VGI_DSK_H
diff --git a/src/lib/formats/victor9k_dsk.cpp b/src/lib/formats/victor9k_dsk.cpp
index 1fbba7502be..803c75303d8 100644
--- a/src/lib/formats/victor9k_dsk.cpp
+++ b/src/lib/formats/victor9k_dsk.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Curt Coder
/*********************************************************************
- formats/victor9k_dsk.c
+ formats/victor9k_dsk.cpp
Victor 9000 sector disk image format
@@ -30,12 +30,18 @@
1 4-15 0-7 18 224.5 267
2 16-26 8-18 17 212.2 283
3 27-37 19-29 16 199.9 300
- 4 38-48 30-40 15 187.6 320
- 5 49-59 41-51 14 175.3 342
+ 4 38-47* 30-39* 15 187.6 320
+ 5 48-59 40-51 14 175.3 342
6 60-70 52-62 13 163.0 368
7 71-79 63-74 12 149.6 401
8 unused 75-79 11 144.0 417
+ * The documentation for the Victor lists Zone 4 as ending with Track 48
+ on side 1 and track 40 on side two. This is incorrect. The above table
+ reflects disks analyzed from various machines and matches the assembly
+ code in the floppy driver. Various written documents contain this
+ documentation bug.
+
Interleave factor 3
cell 2.13 usec
@@ -97,92 +103,126 @@
zone.
*/
-#include "emu.h" // osd_printf_verbose, BIT, emu_fatalerror
#include "formats/victor9k_dsk.h"
+#include "coretmpl.h" // util::BIT
+#include "ioprocs.h"
+
+#include "osdcore.h" // osd_printf_*
+
+#include <cstring>
+#include <iterator>
+
+
victor9k_format::victor9k_format()
{
}
-const char *victor9k_format::name() const
+const char *victor9k_format::name() const noexcept
{
return "victor9k";
}
-const char *victor9k_format::description() const
+const char *victor9k_format::description() const noexcept
{
return "Victor 9000 disk image";
}
-const char *victor9k_format::extensions() const
+const char *victor9k_format::extensions() const noexcept
{
return "img";
}
-int victor9k_format::find_size(io_generic *io, uint32_t form_factor)
+int victor9k_format::find_size(util::random_read &io)
+{
+ uint64_t size;
+ if (io.length(size))
+ return -1;
+
+ for (int i = 0; formats[i].sector_count; i++) {
+ const format &f = formats[i];
+ if(size == uint32_t(f.sector_count*f.sector_base_size))
+ return i;
+ }
+
+ return -1;
+}
+
+int victor9k_format::find_size(util::random_read &io, uint32_t form_factor)
+{
+ return find_size(io);
+}
+
+int victor9k_format::identify(const floppy_image &image)
{
- uint64_t size = io_generic_size(io);
- for(int i=0; formats[i].sector_count; i++) {
+ for (int i = 0; formats[i].form_factor; i++) {
const format &f = formats[i];
- if(size == (uint32_t) f.sector_count*f.sector_base_size*f.head_count)
+ if(f.variant == image.get_variant())
return i;
}
+
return -1;
}
-int victor9k_format::identify(io_generic *io, uint32_t form_factor)
+
+int victor9k_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
int type = find_size(io, form_factor);
if (type != -1)
- return 50;
+ return FIFID_SIZE;
return 0;
}
-void victor9k_format::log_boot_sector(uint8_t *data)
+void victor9k_format::log_boot_sector(const uint8_t *data)
{
// System disc ID
osd_printf_verbose("System disc: %s\n", ((data[0] == 0xff) && (data[1] == 0x00)) ? "yes" : "no");
// Load address
- osd_printf_verbose("Load address: %04x\n", (data[1] << 8) | data[2]);
+ osd_printf_verbose("Load address: %04x\n", (data[2] && data[3]));
// Length
- osd_printf_verbose("Length: %04x\n", (data[3] << 8) | data[4]);
+ osd_printf_verbose("Length: %04x\n", (data[4] <<8 | data[5]));
// Entry offset
- osd_printf_verbose("Entry offset: %04x\n", (data[5] << 8) | data[6]);
+ osd_printf_verbose("Entry offset: %04x\n", (data[6] << 8 | data[7]));
// Entry segment
- osd_printf_verbose("Entry segment: %04x\n", (data[7] << 8) | data[8]);
+ osd_printf_verbose("Entry segment: %04x\n", (data[8] << 8 | data[9]));
// I.D.
- //osd_printf_verbose("I.D.: %s\n", data[10]);
+ osd_printf_verbose("Disk I.D.: %s\n", (data[10] << 8 | data[17]));
// Part number
- //osd_printf_verbose("Part number: %s\n", data[18]);
+ osd_printf_verbose("Part number: %s\n", (data[18] << 8 | data[25]));
// Sector size
- osd_printf_verbose("Sector size: %04x\n", (data[25] << 8) | data[26]);
+ osd_printf_verbose("Sector size: %04d\n", (data[26] << 8 | data[27]));
// Data start
- osd_printf_verbose("Data start: %04x\n", (data[27] << 8) | data[28]);
+ osd_printf_verbose("Data start: %04x\n", (data[28] << 8 | data[29]));
// Boot start
- osd_printf_verbose("Boot start: %04x\n", (data[29] << 8) | data[30]);
+ osd_printf_verbose("Boot start: %04x\n", (data[30] << 8 | data[31]));
+
+ //Disk type - flags
+ osd_printf_verbose("%s sided\n", (data[32]) ? "Double" : "Single");
- // Flags
- osd_printf_verbose("%s sided\n", BIT(data[33], 0) ? "Double" : "Single");
- osd_printf_verbose("Interleave factor: %u\n", data[32] >> 4);
+ //Sector Interleave
+ osd_printf_verbose("Sector Interleave: %02x\n", data[33]);
// Disc type
switch (data[34]) {
- case 0x00: osd_printf_verbose("Disc type: CP/M\n"); break;
- case 0x01: osd_printf_verbose("Disc type: MS-DOS\n"); break;
- default: osd_printf_verbose("Disc type: unknown\n"); break;
+ case 0x01: osd_printf_verbose("Disc type: CP/M\n"); break;
+ case 0x10: osd_printf_verbose("Disc type: MS-DOS\n"); break;
+ default: osd_printf_verbose("Disc type: unknown\n"); break;
}
+ //reserved
+ osd_printf_verbose("Part number: %s\n", data[35] << 8 | data[37]);
+
// Speed table
osd_printf_verbose("Speed table: ");
for (int i = 38; i < 56; i++) {
@@ -210,7 +250,7 @@ floppy_image_format_t::desc_e* victor9k_format::get_sector_desc(const format &f,
static floppy_image_format_t::desc_e desc[] = {
/* 00 */ { SECTOR_INTERLEAVE_SKEW, 0, 0},
/* 01 */ { SECTOR_LOOP_START, 0, -1 },
- /* 02 */ { SYNC_GCR5, 9 },
+ /* 02 */ { SYNC_GCR5, 15 },
/* 03 */ { GCR5, 0x07, 1 },
/* 04 */ { CRC_VICTOR_HDR_START, 1 },
/* 05 */ { TRACK_ID_VICTOR_GCR5 },
@@ -231,13 +271,13 @@ floppy_image_format_t::desc_e* victor9k_format::get_sector_desc(const format &f,
/* 20 */ { END }
};
- current_size = 90 + (1+1+1+1)*10 + 8*8 + 50 + (1+f.sector_base_size+2)*10 + 8*8;
+ current_size = 150 + (1+1+1+1)*10 + 8*8 + 50 + (1+f.sector_base_size+2)*10 + 8*8;
current_size *= sector_count;
return desc;
}
-void victor9k_format::build_sector_description(const format &f, uint8_t *sectdata, uint32_t sect_offs, desc_s *sectors, int sector_count) const
+void victor9k_format::build_sector_description(const format &f, uint8_t *sectdata, uint32_t sect_offs, desc_s *sectors, int sector_count)
{
for (int i = 0; i < sector_count; i++) {
sectors[i].data = sectdata + sect_offs;
@@ -248,24 +288,32 @@ void victor9k_format::build_sector_description(const format &f, uint8_t *sectdat
}
}
-bool victor9k_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool victor9k_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- int type = find_size(io, form_factor);
- if(type == -1)
+ int const type = find_size(io, form_factor);
+ if (type == -1)
return false;
const format &f = formats[type];
- uint64_t size = io_generic_size(io);
- std::vector<uint8_t> img;
- img.resize(size);
+ osd_printf_verbose("Type: %d Head Count: %d Sector Count: %d\n", type,
+ f.head_count, f.sector_count);
- io_generic_read(io, &img[0], 0, size);
+ uint64_t size;
+ if (io.length(size))
+ return false;
+
+ auto const [err, img, actual] = read_at(io, 0, size);
+ if (err || (actual != size))
+ return false;
log_boot_sector(&img[0]);
int track_offset = 0;
+ osd_printf_verbose("load Heads: %01d Tracks: %02d Sectors/track head[1]track[1]: %02d\n ",
+ f.head_count, f.track_count, sectors_per_track[1][1]);
+
for (int head = 0; head < f.head_count; head++) {
for (int track = 0; track < f.track_count; track++) {
int current_size = 0;
@@ -276,8 +324,10 @@ bool victor9k_format::load(io_generic *io, uint32_t form_factor, floppy_image *i
floppy_image_format_t::desc_e *desc = get_sector_desc(f, current_size, sector_count);
int remaining_size = total_size - current_size;
- if(remaining_size < 0)
- throw emu_fatalerror("victor9k_format: Incorrect track layout, max_size=%d, current_size=%d", total_size, current_size);
+ if(remaining_size < 0) {
+ osd_printf_error("victor9k_format: Incorrect track layout, max_size=%d, current_size=%d\n", total_size, current_size);
+ return false;
+ }
// Fixup the end gap
desc[18].p2 = remaining_size / 8;
@@ -293,13 +343,14 @@ bool victor9k_format::load(io_generic *io, uint32_t form_factor, floppy_image *i
}
}
- image->set_variant(f.variant);
+ image.set_variant(f.variant);
return true;
}
int victor9k_format::get_rpm(int head, int track)
{
+ osd_printf_verbose("Head: %1d TracK: %02d \n ", head, track);
return rpm[speed_zone[head][track]];
}
@@ -307,8 +358,9 @@ int victor9k_format::get_image_offset(const format &f, int _head, int _track)
{
int offset = 0;
if (_head) {
+ int first_side = 0; //build up offset for first side
for (int track = 0; track < f.track_count; track++) {
- offset += compute_track_size(f, _head, track);
+ offset += compute_track_size(f, first_side, track);
}
}
for (int track = 0; track < _track; track++) {
@@ -327,7 +379,7 @@ const victor9k_format::format victor9k_format::formats[] = {
floppy_image::FF_525, floppy_image::SSDD, 1224, 80, 1, 512
},
{ //
- floppy_image::FF_525, floppy_image::DSDD, 2448, 80, 2, 512
+ floppy_image::FF_525, floppy_image::DSDD, 2391, 80, 2, 512
},
{}
};
@@ -353,8 +405,8 @@ const int victor9k_format::sectors_per_track[2][80] =
18, 18, 18, 18, 18, 18, 18, 18,
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
- 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+ 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+ 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
11, 11, 11, 11, 11
@@ -377,8 +429,8 @@ const int victor9k_format::speed_zone[2][80] =
1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
8, 8, 8, 8, 8
@@ -390,49 +442,67 @@ const int victor9k_format::rpm[9] =
252, 267, 283, 300, 321, 342, 368, 401, 417
};
-bool victor9k_format::save(io_generic *io, floppy_image *image)
+bool victor9k_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
- const format &f = formats[0];
+ int type = victor9k_format::identify(image);
+ uint64_t size;
+ io.length(size); // FIXME: check for errors
+ osd_printf_verbose("save type: %01d, size: %d\n", type, size);
- for(int head=0; head < f.head_count; head++) {
+ if (type == -1)
+ return false;
+
+ const format &f = formats[type];
+
+ osd_printf_verbose("save Heads: %01d Tracks: %02d Sectors/track head[1]track[1]: %02d\n ",
+ f.head_count, f.track_count, sectors_per_track[1][1]);
+ for (int head = 0; head < f.head_count; head++) {
for(int track=0; track < f.track_count; track++) {
int sector_count = sectors_per_track[head][track];
int track_size = compute_track_size(f, head, track);
uint8_t sectdata[40*512];
desc_s sectors[40];
int offset = get_image_offset(f, head, track);
+ osd_printf_verbose(">>type: %s, head: %d, track: %d, sector_count: %d, offset: %d, track_size: %d\n",
+ f.sector_base_size, head, track, sector_count, offset, track_size);
build_sector_description(f, sectdata, 0, sectors, sector_count);
extract_sectors(image, f, sectors, track, head, sector_count);
- io_generic_write(io, sectdata, offset, track_size);
+ /*auto const [err, actual] =*/ write_at(io, offset, sectdata, track_size); // FIXME: check for errors
}
}
return true;
}
-void victor9k_format::extract_sectors(floppy_image *image, const format &f, desc_s *sdesc, int track, int head, int sector_count)
+void victor9k_format::extract_sectors(const floppy_image &image, const format &f, desc_s *sdesc, int track, int head, int sector_count)
{
- uint8_t bitstream[500000/8];
- uint8_t sectdata[50000];
- desc_xs sectors[256];
- int track_size;
-
// Extract the sectors
- generate_bitstream_from_track(track, head, cell_size[speed_zone[head][track]], bitstream, track_size, image);
- extract_sectors_from_bitstream_victor_gcr5(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
+ auto bitstream = generate_bitstream_from_track(track, head, cell_size[speed_zone[head][track]], image);
+ auto sectors = extract_sectors_from_bitstream_victor_gcr5(bitstream);
+
+ if (sectors.size() == 0) {
+ for(int i=0; i<sector_count; i++) {
+ std::vector<unsigned char> sector;
+ sectors.push_back(sector);
+ }
+ }
- for(int i=0; i<sector_count; i++) {
+ for (int i = 0; i<sector_count; i++) {
desc_s &ds = sdesc[i];
- desc_xs &xs = sectors[ds.sector_id];
- if(!xs.data)
- memset((void *)ds.data, 0, ds.size);
- else if(xs.size < ds.size) {
- memcpy((void *)ds.data, xs.data, xs.size);
- memset((uint8_t *)ds.data + xs.size, 0, xs.size - ds.size);
+ const auto &data = sectors[ds.sector_id];
+ osd_printf_verbose("Head: %01d TracK: %02d Total Sectors: %02d Current Sector: %02d ",
+ head, track, sector_count, i);
+ if (data.empty()) {
+ memset((uint8_t *)ds.data, 0, ds.size);
+ } else if (data.size() < ds.size) {
+ memcpy((uint8_t *)ds.data, data.data(), data.size() - 1);
+ memset((uint8_t *)ds.data + data.size() - 1, 0, data.size() - ds.size);
+ osd_printf_verbose("data.size(): %01d\n", data.size());
} else
- memcpy((void *)ds.data, xs.data, ds.size);
+ memcpy((uint8_t *)ds.data, data.data(), ds.size);
+ osd_printf_verbose("data.size(): %01d\n", data.size());
}
}
-const floppy_format_type FLOPPY_VICTOR_9000_FORMAT = &floppy_image_format_creator<victor9k_format>;
+const victor9k_format FLOPPY_VICTOR_9000_FORMAT;
diff --git a/src/lib/formats/victor9k_dsk.h b/src/lib/formats/victor9k_dsk.h
index 066e270e78f..192175a0e47 100644
--- a/src/lib/formats/victor9k_dsk.h
+++ b/src/lib/formats/victor9k_dsk.h
@@ -30,14 +30,14 @@ public:
victor9k_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
- virtual bool supports_save() const override { return true; }
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override { return true; }
static int get_rpm(int head, int track);
@@ -49,20 +49,18 @@ protected:
static const int speed_zone[2][80];
static const int rpm[9];
- int find_size(io_generic *io, uint32_t form_factor);
- void log_boot_sector(uint8_t *data);
- floppy_image_format_t::desc_e* get_sector_desc(const format &f, int &current_size, int sector_count);
- void build_sector_description(const format &f, uint8_t *sectdata, uint32_t sect_offs, desc_s *sectors, int sector_count) const;
- int get_image_offset(const format &f, int head, int track);
- int compute_track_size(const format &f, int head, int track);
- void extract_sectors(floppy_image *image, const format &f, desc_s *sdesc, int track, int head, int sector_count);
+ static int find_size(util::random_read &io);
+ static int find_size(util::random_read &io, uint32_t form_factor);
+ static int identify(const floppy_image &image);
+ static void log_boot_sector(const uint8_t *data);
+ static floppy_image_format_t::desc_e* get_sector_desc(const format &f, int &current_size, int sector_count);
+ static void build_sector_description(const format &f, uint8_t *sectdata, uint32_t sect_offs, desc_s *sectors, int sector_count);
+ static int get_image_offset(const format &f, int head, int track);
+ static int compute_track_size(const format &f, int head, int track);
+ static void extract_sectors(const floppy_image &image, const format &f, desc_s *sdesc, int track, int head, int sector_count);
};
-extern const floppy_format_type FLOPPY_VICTOR_9000_FORMAT;
+extern const victor9k_format FLOPPY_VICTOR_9000_FORMAT;
-FLOPPY_IDENTIFY( victor9k_dsk_identify );
-
-FLOPPY_CONSTRUCT( victor9k_dsk_construct );
-
#endif // MAME_FORMATS_VICTOR9K_DSK_H
diff --git a/src/lib/formats/vt_cas.cpp b/src/lib/formats/vt_cas.cpp
index 9dbc68f8074..2052647bfd8 100644
--- a/src/lib/formats/vt_cas.cpp
+++ b/src/lib/formats/vt_cas.cpp
@@ -1,7 +1,5 @@
// license:GPL-2.0+
// copyright-holders:Juergen Buchmueller
-#include <assert.h>
-
#include "formats/vt_cas.h"
/*********************************************************************
@@ -10,7 +8,7 @@
#define SILENCE 8000
-static int generic_fill_wave(int16_t *buffer, int length, uint8_t *code, int bitsamples, int bytesamples, int lo, int16_t *(*fill_wave_byte)(int16_t *buffer, int byte))
+static int generic_fill_wave(int16_t *buffer, int length, const uint8_t *code, int bitsamples, int bytesamples, int lo, int16_t *(*fill_wave_byte)(int16_t *buffer, int byte))
{
static int nullbyte;
@@ -92,33 +90,33 @@ static int16_t *vtech1_fill_wave_byte(int16_t *buffer, int byte)
return buffer;
}
-static int vtech1_cassette_fill_wave(int16_t *buffer, int length, uint8_t *code)
+static int vtech1_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *code, int)
{
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 =
+static const cassette_image::LegacyWaveFiller vtech1_legacy_fill_wave =
{
- vtech1_cassette_fill_wave, /* fill_wave */
- 1, /* chunk_size */
- V1_BYTESAMPLES, /* chunk_samples */
- nullptr, /* chunk_sample_calc */
- 600*V1_BITSAMPLES, /* sample_frequency */
- 600*V1_BITSAMPLES, /* header_samples */
- 600*V1_BITSAMPLES /* trailer_samples */
+ vtech1_cassette_fill_wave, // fill_wave
+ 1, // chunk_size
+ V1_BYTESAMPLES, // chunk_samples
+ nullptr, // chunk_sample_calc
+ 600*V1_BITSAMPLES, // sample_frequency
+ 600*V1_BITSAMPLES, // header_samples
+ 600*V1_BITSAMPLES // trailer_samples
};
-static cassette_image::error vtech1_cas_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error vtech1_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &vtech1_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &vtech1_legacy_fill_wave);
}
static cassette_image::error vtech1_cas_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &vtech1_legacy_fill_wave);
+ return cassette->legacy_construct(&vtech1_legacy_fill_wave);
}
-static const struct CassetteFormat vtech1_cas_format =
+static const cassette_image::Format vtech1_cas_format =
{
"cas",
vtech1_cas_identify,
@@ -187,12 +185,12 @@ static int16_t *vtech2_fill_wave_byte(int16_t *buffer, int byte)
return buffer;
}
-static int vtech2_cassette_fill_wave(int16_t *buffer, int length, uint8_t *code)
+static int vtech2_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *code, int)
{
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 =
+static const cassette_image::LegacyWaveFiller vtech2_legacy_fill_wave =
{
vtech2_cassette_fill_wave, /* fill_wave */
1, /* chunk_size */
@@ -203,17 +201,17 @@ static const struct CassetteLegacyWaveFiller vtech2_legacy_fill_wave =
600*VT2_BITSAMPLES /* trailer_samples */
};
-static cassette_image::error vtech2_cas_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error vtech2_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &vtech2_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &vtech2_legacy_fill_wave);
}
static cassette_image::error vtech2_cas_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &vtech2_legacy_fill_wave);
+ return cassette->legacy_construct(&vtech2_legacy_fill_wave);
}
-static const struct CassetteFormat vtech2_cas_format =
+static const cassette_image::Format vtech2_cas_format =
{
"cas",
vtech2_cas_identify,
diff --git a/src/lib/formats/vt_dsk.cpp b/src/lib/formats/vt_dsk.cpp
new file mode 100644
index 00000000000..da7e450b673
--- /dev/null
+++ b/src/lib/formats/vt_dsk.cpp
@@ -0,0 +1,420 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/*********************************************************************
+
+ formats/vt_dsk.cpp
+
+ VTech disk image formats
+
+*********************************************************************/
+
+#include "formats/vt_dsk.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+#include "osdcomm.h"
+
+
+// Zero = | 9187 |
+// One = | 2237 | 6950 |
+// 0.5us ~= 143
+
+void vtech_common_format::wbit(std::vector<uint32_t> &buffer, uint32_t &pos, bool bit)
+{
+ buffer.push_back(pos | floppy_image::MG_F);
+ if(bit) {
+ pos += 2237;
+ buffer.push_back(pos | floppy_image::MG_F);
+ pos += 6950;
+ } else
+ pos += 9187;
+}
+
+void vtech_common_format::wbyte(std::vector<uint32_t> &buffer, uint32_t &pos, uint8_t byte)
+{
+ for(int i = 7; i >= 0; i--)
+ wbit(buffer, pos, (byte >> i) & 1);
+}
+
+void vtech_common_format::image_to_flux(const uint8_t *bdata, size_t size, floppy_image &image)
+{
+ static const uint8_t sector_map[16] = {
+ 0x0, 0xb, 0x6, 0x1, 0xc, 0x7, 0x2, 0xd, 0x8, 0x3, 0xe, 0x9, 0x4, 0xf, 0xa, 0x5
+ };
+
+ for(int track = 0; track != 40; track++) {
+ uint32_t pos = 0;
+ std::vector<uint32_t> &buffer = image.get_buffer(track, 0);
+ buffer.clear();
+ image.set_write_splice_position(track, 0, 0);
+ // One window of pad at the start to avoid problems with the write splice
+ wbit(buffer, pos, 0);
+
+ for(int sector = 0; sector != 16; sector++) {
+ uint8_t sid = sector_map[sector];
+ for(int i=0; i != 7; i++)
+ wbyte(buffer, pos, 0x80);
+ wbyte(buffer, pos, 0x00);
+ wbyte(buffer, pos, 0xfe);
+ wbyte(buffer, pos, 0xe7);
+ wbyte(buffer, pos, 0x18);
+ wbyte(buffer, pos, 0xc3);
+ wbyte(buffer, pos, track);
+ wbyte(buffer, pos, sid);
+ wbyte(buffer, pos, track+sid);
+ for(int i=0; i != 5; i++)
+ wbyte(buffer, pos, 0x80);
+ wbyte(buffer, pos, 0x00);
+ wbyte(buffer, pos, 0xc3);
+ wbyte(buffer, pos, 0x18);
+ wbyte(buffer, pos, 0xe7);
+ wbyte(buffer, pos, 0xfe);
+ uint16_t chk = 0;
+ const uint8_t *src = &bdata[16*128*track + 128*sid];
+ for(int i=0; i != 128; i++) {
+ chk += src[i];
+ wbyte(buffer, pos, src[i]);
+ }
+ wbyte(buffer, pos, chk);
+ wbyte(buffer, pos, chk >> 8);
+ }
+ // Rest is just not formatted
+ buffer.push_back(pos | floppy_image::MG_N);
+ }
+}
+
+std::vector<uint8_t> vtech_common_format::flux_to_image(const floppy_image &image)
+{
+ std::vector<uint8_t> bdata(16*256*40, 0);
+
+ for(int track = 0; track != 40; track++) {
+ const auto &buffer = image.get_buffer(track, 0);
+ int sz = buffer.size();
+ if(sz < 128)
+ continue;
+
+ std::vector<bool> bitstream;
+ int cpos = 0;
+ while((buffer[cpos] & floppy_image::MG_MASK) != floppy_image::MG_F) {
+ cpos++;
+ if(cpos == sz) {
+ cpos = -1;
+ break;
+ }
+ }
+ if(cpos == -1)
+ continue;
+ for(;;) {
+ int npos = cpos;
+ for(;;) {
+ npos++;
+ if(npos == sz)
+ npos = 0;
+ if((buffer[npos] & floppy_image::MG_MASK) == floppy_image::MG_F)
+ break;
+ }
+ int dt = (buffer[npos] & floppy_image::TIME_MASK) - (buffer[cpos] & floppy_image::TIME_MASK);
+ if(dt < 0)
+ cpos += 200000000;
+ int t = dt >= 9187 - 143 ? 0 :
+ dt >= 2237 - 143 && dt <= 2237 + 143 ? 1 :
+ 2;
+ if(t <= 1)
+ bitstream.push_back(t);
+ if(npos <= cpos)
+ break;
+ cpos = npos;
+ }
+ int mode = 0;
+ int pos = 0;
+ int count = 0;
+ bool looped = false;
+ uint8_t *dest = nullptr;
+ [[maybe_unused]] uint16_t checksum = 0;
+ uint64_t buf = 0;
+ sz = bitstream.size();
+ if(sz < 128)
+ continue;
+
+ for(int i=0; i != 63; i++)
+ buf = (buf << 1) | bitstream[sz-64+i];
+ for(;;) {
+ buf = (buf << 1) | bitstream[pos];
+ count++;
+ switch(mode) {
+ case 0: // idle
+ if(buf == 0x80808000fee718c3)
+ mode = 1;
+ count = 0;
+ break;
+
+ case 1: // sector header
+ if(count == 24) {
+ uint8_t trk = buf >> 16;
+ uint8_t sector = buf >> 8;
+ uint8_t chk = buf;
+ if(chk != sector + trk) {
+ mode = 0;
+ break;
+ }
+ checksum = 0;
+ dest = bdata.data() + 128 * 16 * trk + 128 * (sector & 0xf);
+ mode = 2;
+ }
+ break;
+
+ case 2: // look for sector data
+ if(buf == 0x80808000fee718c3)
+ mode = 1;
+ else if(buf == 0x80808000c318e7fe)
+ mode = 3;
+ count = 0;
+ break;
+
+ case 3: // sector data
+ if(count <= 128*8 && !(count & 7)) {
+ uint8_t byte = buf;
+ checksum += byte;
+ *dest++ = byte;
+ } else if(count == 128*8+16) {
+ // uint16_t disk_checksum = buf;
+ // printf("sector checksum %04x %04x\n", checksum, disk_checksum);
+ mode = 0;
+ }
+ break;
+ }
+ if(mode == 0 && looped)
+ break;
+ pos++;
+ if(pos == sz) {
+ pos = 0;
+ looped = true;
+ }
+ }
+ }
+
+ return bdata;
+}
+
+
+const char *vtech_bin_format::name() const noexcept
+{
+ return "vtech_bin";
+}
+
+const char *vtech_bin_format::description() const noexcept
+{
+ return "VTech sector disk image";
+}
+
+const char *vtech_bin_format::extensions() const noexcept
+{
+ return "bin";
+}
+
+const char *vtech_dsk_format::name() const noexcept
+{
+ return "vtech_dsk";
+}
+
+const char *vtech_dsk_format::description() const noexcept
+{
+ return "VTech dsk image";
+}
+
+const char *vtech_dsk_format::extensions() const noexcept
+{
+ return "dsk,dvz";
+}
+
+int vtech_bin_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if(io.length(size))
+ return 0;
+
+ if(size == 40*16*256)
+ return FIFID_SIZE;
+
+ return 0;
+}
+
+int vtech_dsk_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
+{
+ uint64_t size;
+ if(io.length(size))
+ return 0;
+
+ if(size < 256)
+ return 0;
+
+ auto const [err, bdata, actual] = read_at(io, 0, size);
+ if(err || (actual != size))
+ return 0;
+
+ // Structurally validate the presence of sector headers and data
+ int count_sh = 0, count_sd = 0;
+ uint64_t buf = 0;
+ for(size_t i = 0; size > i; ++i) {
+ buf = (buf << 8) | bdata[i];
+ if(buf == 0x80808000fee718c3)
+ count_sh++;
+ else if(buf == 0x80808000c318e7fe)
+ count_sd++;
+ }
+
+ return count_sh >= 30*16 && count_sd >= 30*16 ? FIFID_STRUCT : 0;
+}
+
+bool vtech_bin_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ uint64_t size;
+ if(io.length(size) || (size != 40*16*256))
+ return false;
+
+ auto const [err, bdata, actual] = read_at(io, 0, size);
+ if(err || (actual != size))
+ return false;
+
+ image_to_flux(bdata.get(), size, image);
+ image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD);
+ return true;
+}
+
+bool vtech_dsk_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
+{
+ uint64_t size;
+ if(io.length(size))
+ return false;
+ auto const [err, bdata, actual] = read_at(io, 0, size);
+ if(err || (actual != size))
+ return false;
+
+ std::vector<uint8_t> bdatax(128*16*40, 0);
+
+ int mode = 0;
+ int count = 0;
+ [[maybe_unused]] uint16_t checksum = 0;
+ uint64_t buf = 0;
+ uint8_t *dest = nullptr;
+
+ for(size_t i = 0; size > i; ++i) {
+ buf = (buf << 8) | bdata[i];
+ count++;
+ switch(mode) {
+ case 0: // idle
+ if(buf == 0x80808000fee718c3)
+ mode = 1;
+ count = 0;
+ break;
+
+ case 1: // sector header
+ if(count == 3) {
+ const uint8_t trk = buf >> 16;
+ const uint8_t sector = buf >> 8;
+ const uint8_t chk = buf;
+ if(chk != sector + trk) {
+ mode = 0;
+ break;
+ }
+ dest = &bdatax[128*16*trk + sector*128];
+ checksum = 0;
+ mode = 2;
+ }
+ break;
+
+ case 2: // look for sector data
+ if(buf == 0x80808000fee718c3)
+ mode = 1;
+ else if(buf == 0x80808000c318e7fe)
+ mode = 3;
+ count = 0;
+ break;
+
+ case 3: // sector data
+ if(count <= 128) {
+ const uint8_t byte = buf;
+ checksum += byte;
+ *dest++ = byte;
+
+ } else if(count == 128+2) {
+ uint16_t disk_checksum = buf;
+ disk_checksum = swapendian_int16(disk_checksum);
+ mode = 0;
+ }
+ break;
+ }
+ }
+
+ image_to_flux(bdatax.data(), bdatax.size(), image);
+ image.set_form_variant(floppy_image::FF_525, floppy_image::SSSD);
+ return true;
+}
+
+bool vtech_bin_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ int tracks, heads;
+ image.get_maximal_geometry(tracks, heads);
+ if(tracks < 40)
+ return false;
+
+ auto bdata = flux_to_image(image);
+ /*auto const [err, actual] =*/ write_at(io, 0, bdata.data(), bdata.size()); // FIXME: check for errors
+ return true;
+}
+
+bool vtech_dsk_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
+{
+ int tracks, heads;
+ image.get_maximal_geometry(tracks, heads);
+ if(tracks < 40)
+ return false;
+
+ auto bdata = flux_to_image(image);
+ std::vector<uint8_t> bdatax(0x9b*16*40);
+
+ // Format is essentially an idealized version of what's written on the disk
+
+ static const uint8_t sector_map[16] = {
+ 0x0, 0xb, 0x6, 0x1, 0xc, 0x7, 0x2, 0xd, 0x8, 0x3, 0xe, 0x9, 0x4, 0xf, 0xa, 0x5
+ };
+
+ int pos = 0;
+ for(int track = 0; track != 40; track++) {
+ for(int sector = 0; sector != 16; sector++) {
+ uint8_t sid = sector_map[sector];
+ for(int i=0; i != 7; i++)
+ bdatax[pos++] = 0x80;
+ bdatax[pos++] = 0x00;
+ bdatax[pos++] = 0xfe;
+ bdatax[pos++] = 0xe7;
+ bdatax[pos++] = 0x18;
+ bdatax[pos++] = 0xc3;
+ bdatax[pos++] = track;
+ bdatax[pos++] = sid;
+ bdatax[pos++] = track+sid;
+ for(int i=0; i != 5; i++)
+ bdatax[pos++] = 0x80;
+ bdatax[pos++] = 0x00;
+ bdatax[pos++] = 0xc3;
+ bdatax[pos++] = 0x18;
+ bdatax[pos++] = 0xe7;
+ bdatax[pos++] = 0xfe;
+ uint16_t chk = 0;
+ const uint8_t *src = bdata.data() + 16*128*track + 128*sid;
+ for(int i=0; i != 128; i++) {
+ chk += src[i];
+ bdatax[pos++] = src[i];
+ }
+ put_u16le(&bdatax[pos], chk);
+ pos += 2;
+ }
+ }
+
+ /*auto const [err, actual] =*/ write_at(io, 0, bdatax.data(), bdatax.size()); // FIXME: check for errors
+ return true;
+}
+
+
+const vtech_bin_format FLOPPY_VTECH_BIN_FORMAT;
+const vtech_dsk_format FLOPPY_VTECH_DSK_FORMAT;
diff --git a/src/lib/formats/vt_dsk.h b/src/lib/formats/vt_dsk.h
new file mode 100644
index 00000000000..1348f9dbd4d
--- /dev/null
+++ b/src/lib/formats/vt_dsk.h
@@ -0,0 +1,58 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert
+/*********************************************************************
+
+ formats/vt_dsk.h
+
+ VTech disk image formats
+
+*********************************************************************/
+#ifndef MAME_FORMATS_VT_DSK_H
+#define MAME_FORMATS_VT_DSK_H
+
+#pragma once
+
+#include "flopimg.h"
+
+class vtech_common_format : public floppy_image_format_t {
+public:
+ virtual bool supports_save() const noexcept override { return true; }
+
+protected:
+ static void image_to_flux(const uint8_t *bdata, size_t size, floppy_image &image);
+ static std::vector<uint8_t> flux_to_image(const floppy_image &image);
+
+ static void wbit(std::vector<uint32_t> &buffer, uint32_t &pos, bool bit);
+ static void wbyte(std::vector<uint32_t> &buffer, uint32_t &pos, uint8_t byte);
+};
+
+class vtech_bin_format : public vtech_common_format {
+public:
+ vtech_bin_format() = default;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+};
+
+class vtech_dsk_format : public vtech_common_format {
+public:
+ vtech_dsk_format() = default;
+
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
+
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+};
+
+extern const vtech_bin_format FLOPPY_VTECH_BIN_FORMAT;
+extern const vtech_dsk_format FLOPPY_VTECH_DSK_FORMAT;
+
+#endif // MAME_FORMATS_VT_DSK_H
diff --git a/src/lib/formats/wavfile.cpp b/src/lib/formats/wavfile.cpp
index 072be6c92a2..6cce494f1c8 100644
--- a/src/lib/formats/wavfile.cpp
+++ b/src/lib/formats/wavfile.cpp
@@ -8,11 +8,13 @@
*********************************************************************/
-#include <stdio.h>
-#include <assert.h>
-
#include "wavfile.h"
-#include "cassimg.h"
+
+#include "osdcomm.h" // little_endianize_int16
+
+#include <cassert>
+#include <cstdio>
+#include <cstring>
static const char magic1[4] = { 'R', 'I', 'F', 'F' };
static const char magic2[4] = { 'W', 'A', 'V', 'E' };
@@ -57,7 +59,7 @@ static void put_leuint16(void *ptr, uint16_t value)
-static cassette_image::error wavfile_process(cassette_image *cassette, struct CassetteOptions *opts,
+static cassette_image::error wavfile_process(cassette_image *cassette, cassette_image::Options *opts,
bool read_waveform)
{
uint8_t file_header[12];
@@ -76,7 +78,7 @@ static cassette_image::error wavfile_process(cassette_image *cassette, struct Ca
int waveform_flags = 0;
/* read header */
- cassette_image_read(cassette, file_header, 0, sizeof(file_header));
+ cassette->image_read(file_header, 0, sizeof(file_header));
offset = sizeof(file_header);
/* check magic numbers */
@@ -87,13 +89,13 @@ static cassette_image::error wavfile_process(cassette_image *cassette, struct Ca
/* read and sanity check size */
stated_size = get_leuint32(&file_header[4]) + 8;
- file_size = cassette_image_size(cassette);
+ file_size = cassette->image_size();
if (stated_size > file_size)
stated_size = (uint32_t) file_size;
while(offset < stated_size)
{
- cassette_image_read(cassette, tag_header, offset, sizeof(tag_header));
+ cassette->image_read(tag_header, offset, sizeof(tag_header));
tag_size = get_leuint32(&tag_header[4]);
offset += sizeof(tag_header);
@@ -104,7 +106,7 @@ static cassette_image::error wavfile_process(cassette_image *cassette, struct Ca
return cassette_image::error::INVALID_IMAGE;
format_specified = true;
- cassette_image_read(cassette, format_tag, offset, sizeof(format_tag));
+ cassette->image_read(format_tag, offset, sizeof(format_tag));
format_type = get_leuint16(&format_tag[0]);
opts->channels = get_leuint16(&format_tag[2]);
@@ -121,13 +123,13 @@ static cassette_image::error wavfile_process(cassette_image *cassette, struct Ca
switch(opts->bits_per_sample)
{
case 8:
- waveform_flags = CASSETTE_WAVEFORM_8BIT | CASSETTE_WAVEFORM_UNSIGNED; // 8-bits wav are stored unsigned
+ waveform_flags = cassette_image::WAVEFORM_8BIT | cassette_image::WAVEFORM_UNSIGNED; // 8-bits wav are stored unsigned
break;
case 16:
- waveform_flags = CASSETTE_WAVEFORM_16BITLE;
+ waveform_flags = cassette_image::WAVEFORM_16BITLE;
break;
case 32:
- waveform_flags = CASSETTE_WAVEFORM_32BITLE;
+ waveform_flags = cassette_image::WAVEFORM_32BITLE;
break;
default:
return cassette_image::error::INVALID_IMAGE;
@@ -142,7 +144,7 @@ static cassette_image::error wavfile_process(cassette_image *cassette, struct Ca
if (read_waveform)
{
tag_samples = tag_size / (opts->bits_per_sample / 8) / opts->channels;
- cassette_read_samples(cassette, opts->channels, 0.0, tag_samples / ((double) opts->sample_frequency),
+ cassette->read_samples(opts->channels, 0.0, tag_samples / ((double) opts->sample_frequency),
tag_samples, offset, waveform_flags);
}
}
@@ -158,7 +160,7 @@ static cassette_image::error wavfile_process(cassette_image *cassette, struct Ca
-static cassette_image::error wavfile_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error wavfile_identify(cassette_image *cassette, cassette_image::Options *opts)
{
return wavfile_process(cassette, opts, false);
}
@@ -167,14 +169,13 @@ static cassette_image::error wavfile_identify(cassette_image *cassette, struct C
static cassette_image::error wavfile_load(cassette_image *cassette)
{
- struct CassetteOptions opts;
- memset(&opts, 0, sizeof(opts));
+ cassette_image::Options opts;
return wavfile_process(cassette, &opts, true);
}
-static cassette_image::error wavfile_save(cassette_image *cassette, const struct CassetteInfo *info)
+static cassette_image::error wavfile_save(cassette_image *cassette, const cassette_image::Info *info)
{
cassette_image::error err;
uint8_t consolidated_header[12 + 8 + 16 + 8];
@@ -187,7 +188,7 @@ static cassette_image::error wavfile_save(cassette_image *cassette, const struct
uint16_t bits_per_sample;
uint32_t data_size;
size_t bytes_per_sample = 2;
- int waveform_flags = CASSETTE_WAVEFORM_16BITLE;
+ int waveform_flags = cassette_image::WAVEFORM_16BITLE;
uint16_t block_align;
bits_per_sample = (uint16_t) (bytes_per_sample * 8);
@@ -216,10 +217,10 @@ static cassette_image::error wavfile_save(cassette_image *cassette, const struct
put_leuint32(&data_tag_header[4], data_size);
/* write consolidated header */
- cassette_image_write(cassette, consolidated_header, 0, sizeof(consolidated_header));
+ cassette->image_write(consolidated_header, 0, sizeof(consolidated_header));
/* write out the actual data */
- err = cassette_write_samples(cassette, info->channels, 0.0, info->sample_count
+ err = cassette->write_samples(info->channels, 0.0, info->sample_count
/ (double) info->sample_frequency, info->sample_count, sizeof(consolidated_header),
waveform_flags);
if (err != cassette_image::error::SUCCESS)
@@ -230,7 +231,7 @@ static cassette_image::error wavfile_save(cassette_image *cassette, const struct
-const struct CassetteFormat wavfile_format =
+const cassette_image::Format cassette_image::wavfile_format =
{
"wav",
wavfile_identify,
@@ -250,41 +251,36 @@ const struct CassetteFormat wavfile_format =
This code has already identified some rounding errors
*********************************************************************/
-#ifdef UNUSED_FUNCTION
-void wavfile_testload(const char *fname)
+[[maybe_unused]] void wavfile_testload(const char *fname)
{
- cassette_image *cassette;
- FILE *f;
- long offset;
- int freq, samples, i;
- int32_t cassamp;
- int16_t wavsamp;
-
- f = fopen(fname, "rb");
+ FILE *f = fopen(fname, "rb");
if (!f)
return;
- if (cassette_open(f, &stdio_ioprocs, &wavfile_format, CASSETTE_FLAG_READONLY, &cassette))
+ cassette_image::ptr cassette;
+ if (cassette_image::open(util::stdio_read_write_noclose(f, 0x00), &cassette_image::wavfile_format, cassette_image::FLAG_READONLY, cassette) != cassette_image::error::SUCCESS)
{
fclose(f);
return;
}
- offset = 44;
- freq = 44100;
- samples = 5667062;
+ constexpr long offset = 44;
+ constexpr int freq = 44100;
+ constexpr int samples = 5667062;
- for (i = 0; i < samples; i++)
+ for (int i = 0; i < samples; i++)
{
- cassette_get_sample(cassette, 0, i / (double) freq, 0.0, &cassamp);
+ int32_t cassamp;
+ cassette->get_sample(0, i / double(freq), 0.0, &cassamp);
+ int16_t wavsamp;
fseek(f, offset + i * 2, SEEK_SET);
fread(&wavsamp, 1, 2, f);
- assert(cassamp == (((uint32_t) wavsamp) << 16));
+
+ assert(cassamp == (uint32_t(wavsamp) << 16));
}
- cassette_close(cassette);
+ cassette.reset();
fclose(f);
}
-#endif
diff --git a/src/lib/formats/wavfile.h b/src/lib/formats/wavfile.h
index 0f48b8378b0..397d502a0ec 100644
--- a/src/lib/formats/wavfile.h
+++ b/src/lib/formats/wavfile.h
@@ -14,6 +14,4 @@
#include "cassimg.h"
-extern const struct CassetteFormat wavfile_format;
-
#endif // MAME_FORMATS_WAVFILE_H
diff --git a/src/lib/formats/wd177x_dsk.cpp b/src/lib/formats/wd177x_dsk.cpp
index 69915f0f396..e5b02f02e32 100644
--- a/src/lib/formats/wd177x_dsk.cpp
+++ b/src/lib/formats/wd177x_dsk.cpp
@@ -1,48 +1,79 @@
// license:BSD-3-Clause
-// copyright-holders:Olivier Galibert
+// copyright-holders:Olivier Galibert, 68bit
/*********************************************************************
- formats/wd177x_dsk.h
+ formats/wd177x_dsk.cpp
helper for simple wd177x-formatted disk images
*********************************************************************/
-#include "emu.h" // emu_fatalerror
#include "formats/wd177x_dsk.h"
+#include "ioprocs.h"
+
+#include "osdcore.h" // osd_printf_*
+
+#include <cstring>
+
+
wd177x_format::wd177x_format(const format *_formats)
{
formats = _formats;
}
+// Default implementation. May be overwritten by subclasses to handle tracks
+// that vary from the default, such as a FM encoded track on a largely MFM
+// encoded disk, or a track with an different sector IDs etc. Only the track
+// encoding is used from the returned format, the number of track_count is not
+// accessed.
+const wd177x_format::format &wd177x_format::get_track_format(const format &f, int head, int track) const
+{
+ return f;
+}
+
/*
Default implementation for find_size. May be overwritten by subclasses.
*/
-int wd177x_format::find_size(io_generic *io, uint32_t form_factor)
+int wd177x_format::find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- uint64_t size = io_generic_size(io);
+ uint64_t size;
+ if(io.length(size))
+ return -1;
+
for(int i=0; formats[i].form_factor; i++) {
const format &f = formats[i];
if(form_factor != floppy_image::FF_UNKNOWN && form_factor != f.form_factor)
continue;
+ if(!variants.empty() && !has_variant(variants, f.variant))
+ continue;
+ uint64_t format_size = 0;
+ for(int track=0; track < f.track_count; track++) {
+ for(int head=0; head < f.head_count; head++) {
+ const format &tf = get_track_format(f, head, track);
+ format_size += compute_track_size(tf);
+ }
+ }
- if(size == (uint64_t)compute_track_size(f) * f.track_count * f.head_count)
+ if(size == format_size)
return i;
}
+
return -1;
}
-int wd177x_format::identify(io_generic *io, uint32_t form_factor)
+int wd177x_format::identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const
{
- int type = find_size(io, form_factor);
+ int const type = find_size(io, form_factor, variants);
if(type != -1)
- return 50;
+ return FIFID_SIZE;
+
return 0;
}
-int wd177x_format::compute_track_size(const format &f) const
+// A track specific format is to be supplied.
+int wd177x_format::compute_track_size(const format &f)
{
int track_size;
if(f.sector_base_size)
@@ -55,6 +86,7 @@ int wd177x_format::compute_track_size(const format &f) const
return track_size;
}
+// A track specific format is to be supplied.
void wd177x_format::build_sector_description(const format &f, uint8_t *sectdata, desc_s *sectors, int track, int head) const
{
if(f.sector_base_id == -1) {
@@ -78,7 +110,8 @@ void wd177x_format::build_sector_description(const format &f, uint8_t *sectdata,
}
}
-floppy_image_format_t::desc_e* wd177x_format::get_desc_fm(const format &f, int &current_size, int &end_gap_index)
+// A track specific format is to be supplied.
+floppy_image_format_t::desc_e* wd177x_format::get_desc_fm(const format &f, int &current_size, int &end_gap_index) const
{
static floppy_image_format_t::desc_e desc[23] = {
/* 00 */ { FM, 0xff, 0 },
@@ -125,7 +158,8 @@ floppy_image_format_t::desc_e* wd177x_format::get_desc_fm(const format &f, int &
return desc;
}
-floppy_image_format_t::desc_e* wd177x_format::get_desc_mfm(const format &f, int &current_size, int &end_gap_index)
+// A track specific format is to be supplied.
+floppy_image_format_t::desc_e* wd177x_format::get_desc_mfm(const format &f, int &current_size, int &end_gap_index) const
{
static floppy_image_format_t::desc_e desc[25] = {
/* 00 */ { MFM, 0x4e, 0 },
@@ -174,66 +208,75 @@ floppy_image_format_t::desc_e* wd177x_format::get_desc_mfm(const format &f, int
return desc;
}
-bool wd177x_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
+bool wd177x_format::load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const
{
- int type = find_size(io, form_factor);
+ int const type = find_size(io, form_factor, variants);
if(type == -1)
return false;
const format &f = formats[type];
- floppy_image_format_t::desc_e *desc;
- int current_size;
- int end_gap_index;
+ int max_tracks, max_heads;
+ image.get_maximal_geometry(max_tracks, max_heads);
- switch (f.encoding)
- {
- case floppy_image::FM:
- desc = get_desc_fm(f, current_size, end_gap_index);
- break;
- case floppy_image::MFM:
- default:
- desc = get_desc_mfm(f, current_size, end_gap_index);
- break;
+ if(f.track_count > max_tracks) {
+ osd_printf_error("wd177x_format: Number of tracks in image file too high for floppy drive (%d > %d)\n", f.track_count, max_tracks);
+ return false;
}
- int total_size = 200000000/f.cell_size;
- int remaining_size = total_size - current_size;
- if(remaining_size < 0)
- throw emu_fatalerror("wd177x_format: Incorrect track layout, max_size=%d, current_size=%d", total_size, current_size);
-
- // Fixup the end gap
- desc[end_gap_index].p2 = remaining_size / 16;
- desc[end_gap_index + 1].p2 = remaining_size & 15;
- desc[end_gap_index + 1].p1 >>= 16-(remaining_size & 15);
+ for(int track=0; track < f.track_count; track++)
+ for(int head=0; head < f.head_count; head++) {
+ uint8_t sectdata[40*512];
+ desc_s sectors[40];
+ floppy_image_format_t::desc_e *desc;
+ int current_size;
+ int end_gap_index;
+ const format &tf = get_track_format(f, head, track);
+
+ switch (tf.encoding)
+ {
+ case floppy_image::FM:
+ desc = get_desc_fm(tf, current_size, end_gap_index);
+ break;
+ case floppy_image::MFM:
+ default:
+ desc = get_desc_mfm(tf, current_size, end_gap_index);
+ break;
+ }
- int track_size = compute_track_size(f);
+ int total_size = 200000000/tf.cell_size;
+ int remaining_size = total_size - current_size;
+ if(remaining_size < 0) {
+ osd_printf_error("wd177x_format: Incorrect track layout, max_size=%d, current_size=%d\n", total_size, current_size);
+ return false;
+ }
- uint8_t sectdata[40*512];
- desc_s sectors[40];
+ // Fixup the end gap
+ desc[end_gap_index].p2 = remaining_size / 16;
+ desc[end_gap_index + 1].p2 = remaining_size & 15;
+ desc[end_gap_index + 1].p1 >>= 16-(remaining_size & 15);
- for(int track=0; track < f.track_count; track++)
- for(int head=0; head < f.head_count; head++) {
- if (f.encoding == floppy_image::FM)
- desc[14].p1 = get_track_dam_fm(f, head, track);
+ if (tf.encoding == floppy_image::FM)
+ desc[14].p1 = get_track_dam_fm(tf, head, track);
else
- desc[16].p1 = get_track_dam_mfm(f, head, track);
+ desc[16].p1 = get_track_dam_mfm(tf, head, track);
- build_sector_description(f, sectdata, sectors, track, head);
- io_generic_read(io, sectdata, get_image_offset(f, head, track), track_size);
- generate_track(desc, track, head, sectors, f.sector_count, total_size, image);
+ build_sector_description(tf, sectdata, sectors, track, head);
+ int track_size = compute_track_size(tf);
+ /*auto const [err, actual] =*/ read_at(io, get_image_offset(f, head, track), sectdata, track_size); // FIXME: check for errors and premature EOF
+ generate_track(desc, track, head, sectors, tf.sector_count, total_size, image);
}
- image->set_variant(f.variant);
+ image.set_form_variant(f.form_factor, f.variant);
return true;
}
-bool wd177x_format::supports_save() const
+bool wd177x_format::supports_save() const noexcept
{
return true;
}
-bool wd177x_format::save(io_generic *io, floppy_image *image)
+bool wd177x_format::save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const
{
// Count the number of formats
int formats_count;
@@ -252,9 +295,9 @@ bool wd177x_format::save(io_generic *io, floppy_image *image)
// Build the list of all formats for the immediately superior cell size
int cur_cell_size = 0;
candidates.clear();
- for(int i=0; i != formats_count; i++) {
- if(image->get_form_factor() == floppy_image::FF_UNKNOWN ||
- image->get_form_factor() == formats[i].form_factor) {
+ for(int i=0; i < formats_count; i++) {
+ if(image.get_form_factor() == floppy_image::FF_UNKNOWN ||
+ image.get_form_factor() == formats[i].form_factor) {
if(formats[i].cell_size == cur_cell_size)
candidates.push_back(i);
else if((!cur_cell_size || formats[i].cell_size < cur_cell_size) &&
@@ -273,7 +316,7 @@ bool wd177x_format::save(io_generic *io, floppy_image *image)
if(candidates.empty())
break;
- // Filter with track 0 head 0
+ // Filter
check_compatibility(image, candidates);
// Nobody matches, try with the next cell size
@@ -291,9 +334,9 @@ bool wd177x_format::save(io_generic *io, floppy_image *image)
// Otherwise, find the best
int tracks, heads;
- image->get_actual_geometry(tracks, heads);
+ image.get_actual_geometry(tracks, heads);
chosen_candidate = candidates[0];
- for(unsigned int i=1; i != candidates.size(); i++) {
+ for(unsigned int i=1; i < candidates.size(); i++) {
const format &cc = formats[chosen_candidate];
const format &cn = formats[candidates[i]];
@@ -334,19 +377,20 @@ bool wd177x_format::save(io_generic *io, floppy_image *image)
if(chosen_candidate == -1)
chosen_candidate = 0;
-
const format &f = formats[chosen_candidate];
- int track_size = compute_track_size(f);
uint8_t sectdata[40*512];
desc_s sectors[40];
- for(int track=0; track < f.track_count; track++)
+ for(int track=0; track < f.track_count; track++) {
for(int head=0; head < f.head_count; head++) {
- build_sector_description(f, sectdata, sectors, track, head);
- extract_sectors(image, f, sectors, track, head);
- io_generic_write(io, sectdata, get_image_offset(f, head, track), track_size);
+ const format &tf = get_track_format(f, head, track);
+ build_sector_description(tf, sectdata, sectors, track, head);
+ extract_sectors(image, tf, sectors, track, head);
+ int track_size = compute_track_size(tf);
+ /*auto const [err, actual] =*/ write_at(io, get_image_offset(f, head, track), sectdata, track_size); // FIXME: check for errors
}
+ }
return true;
}
@@ -355,105 +399,135 @@ bool wd177x_format::save(io_generic *io, floppy_image *image)
Default implementation of the image offset computation. May be overwritten
by subclasses.
*/
-int wd177x_format::get_image_offset(const format &f, int head, int track)
+int wd177x_format::get_image_offset(const format &f, int head, int track) const
{
- return (track * f.head_count + head) * compute_track_size(f);
+ int offset = 0;
+
+ for(int trk=0; trk < track; trk++) {
+ for(int hd=0; hd < f.head_count; hd++) {
+ const format &tf = get_track_format(f, hd, trk);
+ offset += compute_track_size(tf);
+ }
+ }
+
+ for(int hd=0; hd < head; hd++) {
+ const format &tf = get_track_format(f, hd, track);
+ offset += compute_track_size(tf);
+ }
+
+
+ return offset;
}
-int wd177x_format::get_track_dam_fm(const format &f, int head, int track)
+// A track specific format is to be supplied.
+int wd177x_format::get_track_dam_fm(const format &f, int head, int track) const
{
// everything marked as data by default
return FM_DAM;
}
-int wd177x_format::get_track_dam_mfm(const format &f, int head, int track)
+// A track specific format is to be supplied.
+int wd177x_format::get_track_dam_mfm(const format &f, int head, int track) const
{
// everything marked as data by default
return MFM_DAM;
}
-void wd177x_format::check_compatibility(floppy_image *image, std::vector<int> &candidates)
+void wd177x_format::check_compatibility(const floppy_image &image, std::vector<int> &candidates) const
{
- uint8_t bitstream[500000/8];
- uint8_t sectdata[50000];
- desc_xs sectors[256];
- int track_size;
-
- // Extract the sectors
- generate_bitstream_from_track(0, 0, formats[candidates[0]].cell_size, bitstream, track_size, image);
-
- switch (formats[candidates[0]].encoding)
- {
- case floppy_image::FM:
- extract_sectors_from_bitstream_fm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
- break;
- case floppy_image::MFM:
- extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
- break;
- }
-
// Check compatibility with every candidate, copy in-place
int *ok_cands = &candidates[0];
- for(unsigned int i=0; i != candidates.size(); i++) {
+ for(unsigned int i=0; i < candidates.size(); i++) {
const format &f = formats[candidates[i]];
- int ns = 0;
- for(int j=0; j<256; j++)
- if(sectors[j].data) {
- int sid;
- if(f.sector_base_id == -1) {
- for(sid=0; sid < f.sector_count; sid++)
- if(f.per_sector_id[sid] == j)
- break;
- } else
- sid = j - f.sector_base_id;
- if(sid < 0 || sid > f.sector_count)
- goto fail;
- if(f.sector_base_size) {
- if(sectors[j].size != f.sector_base_size)
- goto fail;
- } else {
- if(sectors[j].size != f.per_sector_size[sid])
- goto fail;
+
+ int max_tracks, max_heads;
+ image.get_maximal_geometry(max_tracks, max_heads);
+
+ // Fail if floppy drive can't handle track or head count
+ if(f.track_count > max_tracks || f.head_count > max_heads) {
+ goto fail;
+ }
+
+ for(int track=0; track < f.track_count; track++) {
+ for(int head=0; head < f.head_count; head++) {
+ const format &tf = get_track_format(f, head, track);
+
+ auto bitstream = generate_bitstream_from_track(track, head, tf.cell_size, image);
+ std::vector<std::vector<uint8_t>> sectors;
+
+ switch (tf.encoding)
+ {
+ case floppy_image::FM:
+ sectors = extract_sectors_from_bitstream_fm_pc(bitstream);
+ break;
+ case floppy_image::MFM:
+ sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
+ break;
}
- ns++;
+ int ns = 0;
+ for(int j=0; j<int(sectors.size()); j++)
+ if(!sectors[j].empty()) {
+ int sid;
+ if(tf.sector_base_id == -1) {
+ for(sid=0; sid < tf.sector_count; sid++)
+ if(tf.per_sector_id[sid] == j)
+ break;
+ } else
+ sid = j - tf.sector_base_id;
+ if(sid < 0 || sid > tf.sector_count)
+ goto fail;
+ if(tf.sector_base_size) {
+ if(sectors[j].size() != tf.sector_base_size)
+ goto fail;
+ } else {
+ if(sectors[j].size() != tf.per_sector_size[sid])
+ goto fail;
+ }
+ ns++;
+ }
+
+ if(ns > tf.sector_count)
+ goto fail;
+
+ // Be permissive of some missing sectors in later tracks
+ if(ns < tf.sector_count && track < 2)
+ goto fail;
}
- if(ns == f.sector_count)
- *ok_cands++ = candidates[i];
+ }
+ *ok_cands++ = candidates[i];
fail:
;
}
candidates.resize(ok_cands - &candidates[0]);
}
-void wd177x_format::extract_sectors(floppy_image *image, const format &f, desc_s *sdesc, int track, int head)
+// A track specific format is to be supplied.
+void wd177x_format::extract_sectors(const floppy_image &image, const format &f, desc_s *sdesc, int track, int head)
{
- uint8_t bitstream[500000/8];
- uint8_t sectdata[50000];
- desc_xs sectors[256];
- int track_size;
-
// Extract the sectors
- generate_bitstream_from_track(track, head, f.cell_size, bitstream, track_size, image);
+ auto bitstream = generate_bitstream_from_track(track, head, f.cell_size, image);
+ std::vector<std::vector<uint8_t>> sectors;
switch (f.encoding)
{
case floppy_image::FM:
- extract_sectors_from_bitstream_fm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
+ sectors = extract_sectors_from_bitstream_fm_pc(bitstream);
break;
case floppy_image::MFM:
- extract_sectors_from_bitstream_mfm_pc(bitstream, track_size, sectors, sectdata, sizeof(sectdata));
+ sectors = extract_sectors_from_bitstream_mfm_pc(bitstream);
break;
}
for(int i=0; i<f.sector_count; i++) {
desc_s &ds = sdesc[i];
- desc_xs &xs = sectors[ds.sector_id];
- if(!xs.data)
+ if(ds.sector_id >= sectors.size() || sectors[ds.sector_id].empty())
memset((void *)ds.data, 0, ds.size);
- else if(xs.size < ds.size) {
- memcpy((void *)ds.data, xs.data, xs.size);
- memset((uint8_t *)ds.data + xs.size, 0, xs.size - ds.size);
+
+ else if(sectors[ds.sector_id].size() < ds.size) {
+ memcpy((void *)ds.data, sectors[ds.sector_id].data(), sectors[ds.sector_id].size());
+ memset((uint8_t *)ds.data + sectors[ds.sector_id].size(), 0, ds.size - sectors[ds.sector_id].size());
+
} else
- memcpy((void *)ds.data, xs.data, ds.size);
+ memcpy((void *)ds.data, sectors[ds.sector_id].data(), ds.size);
}
}
diff --git a/src/lib/formats/wd177x_dsk.h b/src/lib/formats/wd177x_dsk.h
index 95431b3aaaa..7a4dc84921f 100644
--- a/src/lib/formats/wd177x_dsk.h
+++ b/src/lib/formats/wd177x_dsk.h
@@ -38,27 +38,28 @@ public:
// End the array with {}
wd177x_format(const format *formats);
- virtual int identify(io_generic *io, uint32_t form_factor) override;
- virtual bool load(io_generic *io, uint32_t form_factor, floppy_image *image) override;
- virtual bool save(io_generic *io, floppy_image *image) override;
- virtual bool supports_save() const override;
+ virtual int identify(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const override;
+ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants, floppy_image &image) const override;
+ virtual bool save(util::random_read_write &io, const std::vector<uint32_t> &variants, const floppy_image &image) const override;
+ virtual bool supports_save() const noexcept override;
protected:
enum { FM_DAM = 0xf56f, FM_DDAM = 0xf56a, MFM_DAM = 0xfb, MFM_DDAM = 0xf8 };
const format *formats;
- virtual floppy_image_format_t::desc_e* get_desc_fm(const format &f, int &current_size, int &end_gap_index);
- virtual floppy_image_format_t::desc_e* get_desc_mfm(const format &f, int &current_size, int &end_gap_index);
- virtual int find_size(io_generic *io, uint32_t form_factor);
- virtual int get_image_offset(const format &f, int head, int track);
- virtual int get_track_dam_fm(const format &f, int head, int track);
- virtual int get_track_dam_mfm(const format &f, int head, int track);
+ virtual const wd177x_format::format &get_track_format(const format &f, int head, int track) const;
+ virtual floppy_image_format_t::desc_e* get_desc_fm(const format &f, int &current_size, int &end_gap_index) const;
+ virtual floppy_image_format_t::desc_e* get_desc_mfm(const format &f, int &current_size, int &end_gap_index) const;
+ virtual int find_size(util::random_read &io, uint32_t form_factor, const std::vector<uint32_t> &variants) const;
+ virtual int get_image_offset(const format &f, int head, int track) const;
+ virtual int get_track_dam_fm(const format &f, int head, int track) const;
+ virtual int get_track_dam_mfm(const format &f, int head, int track) const;
- int compute_track_size(const format &f) const;
+ static int compute_track_size(const format &f);
virtual void build_sector_description(const format &d, uint8_t *sectdata, desc_s *sectors, int track, int head) const;
- virtual void check_compatibility(floppy_image *image, std::vector<int> &candidates);
- void extract_sectors(floppy_image *image, const format &f, desc_s *sdesc, int track, int head);
+ virtual void check_compatibility(const floppy_image &image, std::vector<int> &candidates) const;
+ static void extract_sectors(const floppy_image &image, const format &f, desc_s *sdesc, int track, int head);
};
#endif // MAME_FORMATS_WD177X_DSK_H
diff --git a/src/lib/formats/x07_cas.cpp b/src/lib/formats/x07_cas.cpp
index b49d7c8d643..945e1ee5c1a 100644
--- a/src/lib/formats/x07_cas.cpp
+++ b/src/lib/formats/x07_cas.cpp
@@ -6,8 +6,6 @@
********************************************************************/
-#include <assert.h>
-
#include "x07_cas.h"
#define WAVEENTRY_LOW -32768
@@ -19,7 +17,7 @@
#define X07_HEADER_BYTES 16
// image size
-static int x07_image_size;
+static int x07_image_size; // FIXME: global variable prevents multiple instances
static int x07_put_samples(int16_t *buffer, int sample_pos, int count, int level)
{
@@ -124,7 +122,7 @@ static int x07_handle_cassette(int16_t *buffer, const uint8_t *bytes)
Generate samples for the tape image
********************************************************************/
-static int x07_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
+static int x07_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
return x07_handle_cassette(buffer, bytes);
}
@@ -140,7 +138,7 @@ static int x07_cassette_calculate_size_in_samples(const uint8_t *bytes, int leng
return x07_handle_cassette(nullptr, bytes);
}
-static const struct CassetteLegacyWaveFiller x07_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller x07_legacy_fill_wave =
{
x07_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -151,17 +149,17 @@ static const struct CassetteLegacyWaveFiller x07_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error x07_cassette_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error x07_cassette_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &x07_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &x07_legacy_fill_wave);
}
static cassette_image::error x07_cassette_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &x07_legacy_fill_wave);
+ return cassette->legacy_construct(&x07_legacy_fill_wave);
}
-static const struct CassetteFormat x07_cassette_image_format =
+static const cassette_image::Format x07_cassette_image_format =
{
"k7,lst,cas",
x07_cassette_identify,
diff --git a/src/lib/formats/x1_tap.cpp b/src/lib/formats/x1_tap.cpp
index b4fe65b8e8c..f730fe9fb21 100644
--- a/src/lib/formats/x1_tap.cpp
+++ b/src/lib/formats/x1_tap.cpp
@@ -20,15 +20,18 @@
* 0x00: Sampling rate (4 bytes)
*
*/
+#include "x1_tap.h"
+#include "imageutl.h"
-#include <assert.h>
+#include "multibyte.h"
+
+#include <cstring>
-#include "x1_tap.h"
#define WAVE_HIGH 0x5a9e
#define WAVE_LOW -0x5a9e
-static int cas_size;
+static int cas_size; // FIXME: global variables prevent multiple instances
static int samplerate;
static int new_format;
@@ -80,7 +83,7 @@ static int x1_cas_to_wav_size (const uint8_t *casdata, int caslen)
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);
+ samplerate = get_u32le(&casdata[0x1c]);
new_format = 1;
}
else // old TAP format
@@ -88,7 +91,7 @@ static int x1_cas_to_wav_size (const uint8_t *casdata, int caslen)
ret = (caslen - 4) * 8; // each byte = 8 samples
cas_size = ret;
- samplerate = casdata[0x00] | (casdata[0x01] << 8) | (casdata[0x02] << 16) | (casdata[0x03] << 24);
+ samplerate = get_u32le(&casdata[0x00]);
new_format = 0;
}
@@ -98,12 +101,12 @@ static int x1_cas_to_wav_size (const uint8_t *casdata, int caslen)
/*******************************************************************
Generate samples for the tape image
********************************************************************/
-static int x1_cas_fill_wave(int16_t *buffer, int sample_count, uint8_t *bytes)
+static int x1_cas_fill_wave(int16_t *buffer, int sample_count, const uint8_t *bytes, int)
{
return x1_handle_tap(buffer,bytes);
}
-static const struct CassetteLegacyWaveFiller x1_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller x1_legacy_fill_wave =
{
x1_cas_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -114,20 +117,20 @@ static const struct CassetteLegacyWaveFiller x1_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error x1_cas_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error x1_cas_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &x1_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &x1_legacy_fill_wave);
}
static cassette_image::error x1_cas_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &x1_legacy_fill_wave);
+ return cassette->legacy_construct(&x1_legacy_fill_wave);
}
-static const struct CassetteFormat x1_cassette_format = {
+static const cassette_image::Format x1_cassette_format = {
"tap",
x1_cas_identify,
x1_cas_load,
diff --git a/src/lib/formats/xdf_dsk.cpp b/src/lib/formats/xdf_dsk.cpp
index c77d685d9be..cea4c7e4e72 100644
--- a/src/lib/formats/xdf_dsk.cpp
+++ b/src/lib/formats/xdf_dsk.cpp
@@ -2,31 +2,29 @@
// copyright-holders:Olivier Galibert
/*********************************************************************
- formats/xdf_dsk.c
+ formats/xdf_dsk.cpp
x68k bare-bones formats
*********************************************************************/
-#include <assert.h>
-
#include "formats/xdf_dsk.h"
xdf_format::xdf_format() : upd765_format(formats)
{
}
-const char *xdf_format::name() const
+const char *xdf_format::name() const noexcept
{
return "xdf";
}
-const char *xdf_format::description() const
+const char *xdf_format::description() const noexcept
{
return "XDF disk image";
}
-const char *xdf_format::extensions() const
+const char *xdf_format::extensions() const noexcept
{
return "xdf,hdm,2hd";
}
@@ -44,4 +42,4 @@ const xdf_format::format xdf_format::formats[] = {
{}
};
-const floppy_format_type FLOPPY_XDF_FORMAT = &floppy_image_format_creator<xdf_format>;
+const xdf_format FLOPPY_XDF_FORMAT;
diff --git a/src/lib/formats/xdf_dsk.h b/src/lib/formats/xdf_dsk.h
index 6e8dc3b8f34..5f56a0fd0e3 100644
--- a/src/lib/formats/xdf_dsk.h
+++ b/src/lib/formats/xdf_dsk.h
@@ -19,14 +19,14 @@ class xdf_format : public upd765_format
public:
xdf_format();
- virtual const char *name() const override;
- virtual const char *description() const override;
- virtual const char *extensions() const override;
+ virtual const char *name() const noexcept override;
+ virtual const char *description() const noexcept override;
+ virtual const char *extensions() const noexcept override;
private:
static const format formats[];
};
-extern const floppy_format_type FLOPPY_XDF_FORMAT;
+extern const xdf_format FLOPPY_XDF_FORMAT;
#endif // MAME_FORMATS_XDF_DSK_H
diff --git a/src/lib/formats/zx81_p.cpp b/src/lib/formats/zx81_p.cpp
index 9fa5814385f..515f5b78581 100644
--- a/src/lib/formats/zx81_p.cpp
+++ b/src/lib/formats/zx81_p.cpp
@@ -31,8 +31,6 @@ medium transfer rate is approx. 307 bps (38 bytes/sec) for files that contain
*****************************************************************************/
-#include <assert.h>
-
#include "zx81_p.h"
#include "tzx_cas.h"
@@ -56,7 +54,7 @@ medium transfer rate is approx. 307 bps (38 bytes/sec) for files that contain
#define ZX81_DATA_LENGTH_OFFSET 0x0b
#define ZX80_DATA_LENGTH_OFFSET 0x04
-static uint8_t zx_file_name[128];
+static uint8_t zx_file_name[128]; // FIXME: global variables prevent multiple instances
static uint16_t real_data_length = 0;
static uint8_t zx_file_name_length = 0;
@@ -64,9 +62,7 @@ static uint8_t zx_file_name_length = 0;
static int16_t *zx81_emit_level(int16_t *p, int count, int level)
{
- int i;
-
- for (i=0; i<count; i++) *(p++) = level;
+ for (int i=0; i<count; i++) *(p++) = level;
return p;
}
@@ -177,7 +173,7 @@ static int zx81_cassette_calculate_size_in_samples(const uint8_t *bytes, int len
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_t *buffer, int length, uint8_t *bytes)
+static int zx81_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
int16_t * p = buffer;
int i;
@@ -196,7 +192,7 @@ static int zx81_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
return p - buffer;
}
-static const struct CassetteLegacyWaveFiller zx81_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller zx81_legacy_fill_wave =
{
zx81_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -207,9 +203,9 @@ static const struct CassetteLegacyWaveFiller zx81_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error zx81_p_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error zx81_p_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &zx81_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &zx81_legacy_fill_wave);
}
static cassette_image::error zx81_p_load(cassette_image *cassette)
@@ -219,10 +215,10 @@ static cassette_image::error zx81_p_load(cassette_image *cassette)
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);
+ return cassette->legacy_construct(&zx81_legacy_fill_wave);
}
-static const struct CassetteFormat zx81_p_image_format =
+static const cassette_image::Format zx81_p_image_format =
{
"p,81",
zx81_p_identify,
@@ -254,7 +250,7 @@ static int zx80_cassette_calculate_size_in_samples(const uint8_t *bytes, int len
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_t *buffer, int length, uint8_t *bytes)
+static int zx80_cassette_fill_wave(int16_t *buffer, int length, const uint8_t *bytes, int)
{
int16_t * p = buffer;
int i;
@@ -269,7 +265,7 @@ static int zx80_cassette_fill_wave(int16_t *buffer, int length, uint8_t *bytes)
return p - buffer;
}
-static const struct CassetteLegacyWaveFiller zx80_legacy_fill_wave =
+static const cassette_image::LegacyWaveFiller zx80_legacy_fill_wave =
{
zx80_cassette_fill_wave, /* fill_wave */
-1, /* chunk_size */
@@ -280,17 +276,17 @@ static const struct CassetteLegacyWaveFiller zx80_legacy_fill_wave =
0 /* trailer_samples */
};
-static cassette_image::error zx80_o_identify(cassette_image *cassette, struct CassetteOptions *opts)
+static cassette_image::error zx80_o_identify(cassette_image *cassette, cassette_image::Options *opts)
{
- return cassette_legacy_identify(cassette, opts, &zx80_legacy_fill_wave);
+ return cassette->legacy_identify(opts, &zx80_legacy_fill_wave);
}
static cassette_image::error zx80_o_load(cassette_image *cassette)
{
- return cassette_legacy_construct(cassette, &zx80_legacy_fill_wave);
+ return cassette->legacy_construct(&zx80_legacy_fill_wave);
}
-static const struct CassetteFormat zx80_o_image_format =
+static const cassette_image::Format zx80_o_image_format =
{
"o,80",
zx80_o_identify,