1 // license:BSD-3-Clause
2 // copyright-holders:Sergey Svishchev
3 /*********************************************************************
4 
5     formats/ibmxdf_dsk.c
6 
7     IBM Extended Density Format
8 
9     References:
10     - http://www.os2museum.com/wp/the-xdf-diskette-format/
11     - https://fdutils.linux.lu/Fdutils.html#SEC47 and xdfcopy.c
12 
13     Mapping XDF disk image to physical sectors on a 3.5" disk:
14 
15     First track uses standard 512-byte sectors:
16     - lbn 0 (boot sector) = 0.129 (head.sector)
17     - lbn 1-11 (FAT) = 0.130-0.139, then 1.129
18     - lbn 12-19 (aux FS) = 0.1-0.8
19     - lbn 20-22 = padding, not written to disk
20     - lbn 23-36 (root directory) = 1.130-1.143
21     - lbn 37-41 = padding, not written to disk
22     - lbn 42-45 (start of data area) = 1.144-1.147
23 
24     All other tracks use mixed size sectors, sequenced like so:
25     0.131, 0.132, 1.134, 0.130, 1.130, 0.134, 1.132, 1.131
26 
27     To do:
28     - 5.25 HD and 3.5 ED formats
29     - replace magic numbers
30 
31 *********************************************************************/
32 
33 #include "ibmxdf_dsk.h"
34 
35 
ibmxdf_format()36 ibmxdf_format::ibmxdf_format() : wd177x_format(formats)
37 {
38 }
39 
name() const40 const char *ibmxdf_format::name() const
41 {
42 	return "xdf";
43 }
44 
description() const45 const char *ibmxdf_format::description() const
46 {
47 	return "IBM XDF disk image";
48 }
49 
extensions() const50 const char *ibmxdf_format::extensions() const
51 {
52 	return "xdf,img";
53 }
54 
identify(io_generic * io,uint32_t form_factor)55 int ibmxdf_format::identify(io_generic *io, uint32_t form_factor)
56 {
57 	int type = find_size(io, form_factor);
58 
59 	if (type != -1)
60 		return 75;
61 	return 0;
62 }
63 
find_size(io_generic * io,uint32_t form_factor)64 int ibmxdf_format::find_size(io_generic *io, uint32_t form_factor)
65 {
66 	uint64_t size = io_generic_size(io);
67 
68 	if (size != 1884160)
69 		return -1;
70 
71 	return 0;
72 }
73 
get_image_offset(const format & f,int head,int track)74 int ibmxdf_format::get_image_offset(const format &f, int head, int track)
75 {
76 	return (2 * track) * compute_track_size(formats[0]);
77 }
78 
get_track_format(const format & f,int head,int track)79 const wd177x_format::format &ibmxdf_format::get_track_format(const format &f, int head, int track)
80 {
81 	int n = -1;
82 
83 	for (int i = 0; formats[i].form_factor; i++) {
84 		if (&formats[i] == &f) {
85 			n = i;
86 			break;
87 		}
88 	}
89 
90 	if (n < 0) {
91 		LOG_FORMATS("Error format not found\n");
92 		return f;
93 	}
94 
95 	if (head >= f.head_count) {
96 		LOG_FORMATS("Error invalid head %d\n", head);
97 		return f;
98 	}
99 
100 	if (track >= f.track_count) {
101 		LOG_FORMATS("Error invalid track %d\n", track);
102 		return f;
103 	}
104 
105 	if (track > 0) {
106 		if (head == 1) {
107 			const format &fh1 = formats_head1[n];
108 			if (!fh1.form_factor) {
109 				LOG_FORMATS("Error expected a head 1 format\n");
110 				return f;
111 			}
112 			return fh1;
113 		}
114 		return f;
115 	}
116 
117 	// Track 0
118 
119 	if (head == 1) {
120 		const format &fh1t0 = formats_head1_track0[n];
121 		if (fh1t0.form_factor) {
122 			return fh1t0;
123 		}
124 		const format &fh1 = formats_head1[n];
125 		if (fh1.form_factor) {
126 			return fh1;
127 		}
128 		LOG_FORMATS("Error expected a head 1 format\n");
129 		return f;
130 	}
131 
132 	// Head 0
133 
134 	const format &ft0 = formats_track0[n];
135 	if (ft0.form_factor) {
136 		return ft0;
137 	}
138 
139 	return f;
140 }
141 
142 
143 // Unverified gap sizes
144 const ibmxdf_format::format ibmxdf_format::formats[] = {
145 	{
146 		floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
147 		1000, 4, 80, 2, 0, { 1024, 512, 2048, 8192 }, -1, { 131, 130, 132, 134 }, 50, 22, 74
148 	},
149 	{}
150 };
151 
152 const ibmxdf_format::format ibmxdf_format::formats_head1[] = {
153 	{
154 		floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
155 		1000, 4, 80, 2, 0, { 2048, 512, 1024, 8192 }, -1, { 132, 130, 131, 134 }, 50, 22, 74
156 	},
157 	{}
158 };
159 
160 const ibmxdf_format::format ibmxdf_format::formats_track0[] = {
161 	{
162 		floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
163 		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
164 	},
165 	{}
166 };
167 
168 const ibmxdf_format::format ibmxdf_format::formats_head1_track0[] = {
169 	{
170 		floppy_image::FF_35, floppy_image::DSHD, floppy_image::MFM,
171 		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
172 	},
173 	{}
174 };
175 
load(io_generic * io,uint32_t form_factor,floppy_image * image)176 bool ibmxdf_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
177 {
178 	int type = find_size(io, form_factor);
179 	if(type == -1)
180 		return false;
181 
182 	const format &f = formats[type];
183 
184 	for(int track=0; track < f.track_count; track++)
185 		for(int head=0; head < f.head_count; head++) {
186 			uint8_t sectdata[23 * 2 * 512]; // XXX magic
187 			desc_s sectors[40];
188 			floppy_image_format_t::desc_e *desc;
189 			int current_size;
190 			int end_gap_index;
191 			const format &tf = get_track_format(f, head, track);
192 
193 			desc = get_desc_mfm(tf, current_size, end_gap_index);
194 
195 			int total_size = 200000000/tf.cell_size;
196 			int remaining_size = total_size - current_size;
197 			if(remaining_size < 0) {
198 				osd_printf_error("ibmxdf_format: Incorrect track layout, max_size=%d, current_size=%d\n", total_size, current_size);
199 				return false;
200 			}
201 
202 			// Fixup the end gap
203 			desc[end_gap_index].p2 = remaining_size / 16;
204 			desc[end_gap_index + 1].p2 = remaining_size & 15;
205 			desc[end_gap_index + 1].p1 >>= 16-(remaining_size & 15);
206 
207 			desc[16].p1 = get_track_dam_mfm(tf, head, track);
208 
209 			build_sector_description(tf, sectdata, sectors, track, head);
210 			int track_size = compute_track_size(f) * 2; // read both sides at once
211 			io_generic_read(io, sectdata, get_image_offset(f, head, track), track_size);
212 			generate_track(desc, track, head, sectors, tf.sector_count, total_size, image);
213 		}
214 
215 	image->set_variant(f.variant);
216 
217 	return true;
218 }
219 
220 static const int offsets[2][4] = { { 0, 11264, 1024, 12288 }, { 20480, 11776, 22528, 3072 } }; // XXX magic
221 
build_sector_description(const format & f,uint8_t * sectdata,desc_s * sectors,int track,int head) const222 void ibmxdf_format::build_sector_description(const format &f, uint8_t *sectdata, desc_s *sectors, int track, int head) const
223 {
224 	switch (track)
225 	{
226 	case 0:
227 		if (head == 0) {
228 			for(int i=0; i<f.sector_count; i++) {
229 				if (f.per_sector_id[i] < 129) // aux fs
230 					sectors[i].data = sectdata + f.sector_base_size * (f.per_sector_id[i] + 11);
231 				else
232 					sectors[i].data = sectdata + f.sector_base_size * (f.per_sector_id[i] - 129);
233 				sectors[i].size = f.sector_base_size;
234 				sectors[i].sector_id = f.per_sector_id[i];
235 			}
236 		}
237 		else {
238 			for(int i=0; i<f.sector_count; i++) {
239 				if (f.per_sector_id[i] == 129)
240 					sectors[i].data = sectdata + f.sector_base_size * 11;
241 				else if (f.per_sector_id[i] < 144)
242 					sectors[i].data = sectdata + f.sector_base_size * (f.per_sector_id[i] - 130 + 23);
243 				else // skip 5 sectors marked bad in the FAT
244 					sectors[i].data = sectdata + f.sector_base_size * (f.per_sector_id[i] - 130 + 28);
245 				sectors[i].size = f.sector_base_size;
246 				sectors[i].sector_id = f.per_sector_id[i];
247 			}
248 		}
249 		break;
250 
251 	default:
252 		for(int i=0; i<f.sector_count; i++) {
253 			sectors[i].data = sectdata + offsets[head][i];
254 			sectors[i].size = f.per_sector_size[i];
255 			sectors[i].sector_id = f.per_sector_id[i];
256 		}
257 		break;
258 	}
259 }
260 
261 const floppy_format_type FLOPPY_IBMXDF_FORMAT = &floppy_image_format_creator<ibmxdf_format>;
262 
263