1 // license:BSD-3-Clause
2 // copyright-holders:Nathan Woods
3 /*********************************************************************
4 
5     formats/basicdsk.c
6 
7     Floppy format code for basic disks
8 
9 *********************************************************************/
10 
11 #include <cstdlib>
12 #include <cstring>
13 #include <cassert>
14 
15 #include "basicdsk.h"
16 
17 static floperr_t basicdsk_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen);
18 static floperr_t basicdsk_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
19 static floperr_t basicdsk_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen);
20 static floperr_t basicdsk_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam);
21 static floperr_t basicdsk_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length);
22 static floperr_t basicdsk_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);
23 static int basicdsk_get_heads_per_disk(floppy_image_legacy *floppy);
24 static int basicdsk_get_tracks_per_disk(floppy_image_legacy *floppy);
25 static floperr_t basicdsk_format_track(floppy_image_legacy *floppy, int head, int track, util::option_resolution *params);
26 
27 
28 
29 #define BASICDSK_TAG    "basicdsktag"
30 
31 struct basicdsk_tag
32 {
33 	struct basicdsk_geometry geometry;
34 };
35 
36 
37 
38 /********************************************************************/
39 
get_geometry(floppy_image_legacy * floppy)40 static const struct basicdsk_geometry *get_geometry(floppy_image_legacy *floppy)
41 {
42 	const struct basicdsk_tag *tag;
43 	tag = (const basicdsk_tag *)floppy_tag(floppy);
44 	return &tag->geometry;
45 }
46 
47 
48 
basicdsk_construct(floppy_image_legacy * floppy,const struct basicdsk_geometry * geometry)49 floperr_t basicdsk_construct(floppy_image_legacy *floppy, const struct basicdsk_geometry *geometry)
50 {
51 	struct basicdsk_tag *tag;
52 	struct FloppyCallbacks *format;
53 
54 	assert(geometry->heads);
55 	assert(geometry->tracks);
56 	assert(geometry->sectors);
57 
58 	tag = (struct basicdsk_tag *) floppy_create_tag(floppy, sizeof(struct basicdsk_tag));
59 	if (!tag)
60 		return FLOPPY_ERROR_OUTOFMEMORY;
61 	tag->geometry = *geometry;
62 
63 	/* set up format callbacks */
64 	format = floppy_callbacks(floppy);
65 	format->read_sector = basicdsk_read_sector;
66 	format->write_sector = basicdsk_write_sector;
67 	format->read_indexed_sector = basicdsk_read_indexed_sector;
68 	format->write_indexed_sector = basicdsk_write_indexed_sector;
69 	format->get_sector_length = basicdsk_get_sector_length;
70 	format->get_heads_per_disk = basicdsk_get_heads_per_disk;
71 	format->get_tracks_per_disk = basicdsk_get_tracks_per_disk;
72 	format->get_indexed_sector_info = basicdsk_get_indexed_sector_info;
73 	format->format_track = basicdsk_format_track;
74 
75 	return FLOPPY_ERROR_SUCCESS;
76 }
77 
78 
79 
get_offset(floppy_image_legacy * floppy,int head,int track,int sector,bool sector_is_index,uint64_t * offset)80 static floperr_t get_offset(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, uint64_t *offset)
81 {
82 	const struct basicdsk_geometry *geom;
83 	uint64_t offs;
84 
85 	geom = get_geometry(floppy);
86 
87 	/* translate the sector to a raw sector */
88 	if (!sector_is_index)
89 	{
90 		sector -= geom->first_sector_id;
91 	}
92 
93 	if (geom->translate_sector)
94 		sector = geom->translate_sector(floppy, sector);
95 
96 	/* check to see if we are out of range */
97 	if ((head < 0) || (head >= geom->heads) || (track < 0) || (track >= geom->tracks)
98 			|| (sector < 0) || (sector >= geom->sectors))
99 		return FLOPPY_ERROR_SEEKERROR;
100 
101 	if (geom->translate_offset)
102 		offs = geom->translate_offset(floppy, geom, track, head, sector);
103 	else
104 	{
105 		offs = 0;
106 		offs += track;
107 		offs *= geom->heads;
108 		offs += head;
109 		offs *= geom->sectors;
110 		offs += sector;
111 	}
112 	offs *= geom->sector_length;
113 	offs += geom->offset;
114 
115 	if (offset)
116 		*offset = offs;
117 	return FLOPPY_ERROR_SUCCESS;
118 }
119 
120 
121 
internal_basicdsk_translate_sector_interleave(floppy_image_legacy * floppy,int sector)122 static int internal_basicdsk_translate_sector_interleave(floppy_image_legacy *floppy, int sector)
123 {
124 	const struct basicdsk_geometry *geom = get_geometry(floppy);
125 	if (sector >= geom->sectors)
126 		return sector;
127 	return geom->sector_map[sector];
128 }
129 
130 
131 
internal_basicdsk_read_sector(floppy_image_legacy * floppy,int head,int track,int sector,bool sector_is_index,void * buffer,size_t buflen)132 static floperr_t internal_basicdsk_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, void *buffer, size_t buflen)
133 {
134 	uint64_t offset;
135 	floperr_t err;
136 
137 	err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
138 	if (err)
139 		return err;
140 	floppy_image_read(floppy, buffer, offset, buflen);
141 	return FLOPPY_ERROR_SUCCESS;
142 }
143 
144 
145 
internal_basicdsk_write_sector(floppy_image_legacy * floppy,int head,int track,int sector,bool sector_is_index,const void * buffer,size_t buflen,int ddam)146 static floperr_t internal_basicdsk_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, const void *buffer, size_t buflen, int ddam)
147 {
148 	uint64_t offset;
149 	floperr_t err;
150 
151 	err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
152 	if (err)
153 		return err;
154 
155 	floppy_image_write(floppy, buffer, offset, buflen);
156 	return FLOPPY_ERROR_SUCCESS;
157 }
158 
159 
160 
basicdsk_read_sector(floppy_image_legacy * floppy,int head,int track,int sector,void * buffer,size_t buflen)161 static floperr_t basicdsk_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
162 {
163 	return internal_basicdsk_read_sector(floppy, head, track, sector, false, buffer, buflen);
164 }
165 
basicdsk_write_sector(floppy_image_legacy * floppy,int head,int track,int sector,const void * buffer,size_t buflen,int ddam)166 static floperr_t basicdsk_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
167 {
168 	return internal_basicdsk_write_sector(floppy, head, track, sector, false, buffer, buflen, ddam);
169 }
170 
basicdsk_read_indexed_sector(floppy_image_legacy * floppy,int head,int track,int sector,void * buffer,size_t buflen)171 static floperr_t basicdsk_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
172 {
173 	return internal_basicdsk_read_sector(floppy, head, track, sector, true, buffer, buflen);
174 }
175 
basicdsk_write_indexed_sector(floppy_image_legacy * floppy,int head,int track,int sector,const void * buffer,size_t buflen,int ddam)176 static floperr_t basicdsk_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam)
177 {
178 	return internal_basicdsk_write_sector(floppy, head, track, sector, true, buffer, buflen, ddam);
179 }
180 
181 
182 
basicdsk_format_track(floppy_image_legacy * floppy,int head,int track,util::option_resolution * params)183 static floperr_t basicdsk_format_track(floppy_image_legacy *floppy, int head, int track, util::option_resolution *params)
184 {
185 	floperr_t err = FLOPPY_ERROR_SUCCESS;
186 	uint8_t local_buffer[512];
187 	void *alloc_buffer = nullptr;
188 	void *buffer;
189 	uint32_t sector_length;
190 	int sector;
191 	const struct basicdsk_geometry *geometry;
192 
193 	geometry = get_geometry(floppy);
194 
195 	sector_length = geometry->sector_length;
196 
197 	if (sector_length > sizeof(local_buffer))
198 	{
199 		alloc_buffer = malloc(sector_length);
200 		if (!alloc_buffer)
201 		{
202 			err = FLOPPY_ERROR_OUTOFMEMORY;
203 			goto done;
204 		}
205 		buffer = alloc_buffer;
206 	}
207 	else
208 	{
209 		alloc_buffer = nullptr;
210 		buffer = local_buffer;
211 	}
212 
213 	memset(buffer, floppy_get_filler(floppy), sector_length);
214 
215 	for (sector = 0; sector < geometry->sectors; sector++)
216 	{
217 		err = basicdsk_write_sector(floppy, head, track, sector + geometry->first_sector_id, buffer, sector_length, 0);
218 		if (err)
219 			goto done;
220 	}
221 
222 done:
223 	if (alloc_buffer)
224 		free(alloc_buffer);
225 	return err;
226 }
227 
228 
229 
basicdsk_get_heads_per_disk(floppy_image_legacy * floppy)230 static int basicdsk_get_heads_per_disk(floppy_image_legacy *floppy)
231 {
232 	return get_geometry(floppy)->heads;
233 }
234 
235 
236 
basicdsk_get_tracks_per_disk(floppy_image_legacy * floppy)237 static int basicdsk_get_tracks_per_disk(floppy_image_legacy *floppy)
238 {
239 	return get_geometry(floppy)->tracks;
240 }
241 
242 
243 
basicdsk_get_sector_length(floppy_image_legacy * floppy,int head,int track,int sector,uint32_t * sector_length)244 static floperr_t basicdsk_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length)
245 {
246 	floperr_t err;
247 
248 	err = get_offset(floppy, head, track, sector, false, nullptr);
249 	if (err)
250 		return err;
251 
252 	if (sector_length)
253 		*sector_length = get_geometry(floppy)->sector_length;
254 	return FLOPPY_ERROR_SUCCESS;
255 }
256 
257 
258 
basicdsk_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)259 static floperr_t basicdsk_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)
260 {
261 	const struct basicdsk_geometry *geom = get_geometry(floppy);
262 
263 	if (geom->translate_sector)
264 		sector_index = geom->translate_sector(floppy, sector_index);
265 
266 	sector_index += geom->first_sector_id;
267 
268 	if (cylinder)
269 		*cylinder = track;
270 	if (side)
271 		*side = head;
272 	if (sector)
273 		*sector = sector_index;
274 	if (flags) {
275 		/* TODO: read DAM or DDAM and determine flags */
276 		*flags = 0;
277 		if (geom->get_ddam)
278 			*flags = geom->get_ddam(floppy, geom, track, head, sector_index);
279 	}
280 	return basicdsk_get_sector_length(floppy, head, track, sector_index, sector_length);
281 }
282 
283 
284 
285 /********************************************************************
286  * Generic Basicdsk Constructors
287  ********************************************************************/
288 
basicdsk_default_geometry(const struct FloppyFormat * format,struct basicdsk_geometry * geometry)289 static void basicdsk_default_geometry(const struct FloppyFormat *format, struct basicdsk_geometry *geometry)
290 {
291 	int sector_length;
292 	memset(geometry, 0, sizeof(*geometry));
293 
294 	auto err = util::option_resolution::get_default(format->param_guidelines, PARAM_HEADS,           &geometry->heads);
295 	assert(err == util::option_resolution::error::SUCCESS);
296 	err = util::option_resolution::get_default(format->param_guidelines, PARAM_TRACKS,          &geometry->tracks);
297 	assert(err == util::option_resolution::error::SUCCESS);
298 	err = util::option_resolution::get_default(format->param_guidelines, PARAM_SECTORS,         &geometry->sectors);
299 	assert(err == util::option_resolution::error::SUCCESS);
300 	err = util::option_resolution::get_default(format->param_guidelines, PARAM_FIRST_SECTOR_ID, &geometry->first_sector_id);
301 	assert(err == util::option_resolution::error::SUCCESS);
302 	err = util::option_resolution::get_default(format->param_guidelines, PARAM_INTERLEAVE,      &geometry->interleave);
303 	if (err != util::option_resolution::error::SUCCESS) {
304 		geometry->interleave = 1;
305 	}
306 	err = util::option_resolution::get_default(format->param_guidelines, PARAM_SECTOR_LENGTH,   &sector_length);
307 	assert(err == util::option_resolution::error::SUCCESS);
308 	geometry->sector_length = sector_length;
309 
310 	if (geometry->interleave > 1)
311 	{
312 		int sector = 0;
313 
314 		for (int i = 0; i < geometry->sectors; i++)
315 		{
316 			geometry->sector_map[i] = sector;
317 
318 			sector += geometry->interleave;
319 
320 			if (sector >= geometry->sectors)
321 				sector -= geometry->sectors;
322 		}
323 
324 		geometry->translate_sector = internal_basicdsk_translate_sector_interleave;
325 	}
326 }
327 
328 
329 
FLOPPY_CONSTRUCT(basicdsk_construct_default)330 FLOPPY_CONSTRUCT(basicdsk_construct_default)
331 {
332 	struct basicdsk_geometry geometry;
333 	basicdsk_default_geometry(format, &geometry);
334 	return basicdsk_construct(floppy, &geometry);
335 }
336 
337 
338 
FLOPPY_IDENTIFY(basicdsk_identify_default)339 FLOPPY_IDENTIFY(basicdsk_identify_default)
340 {
341 	uint64_t expected_size;
342 	struct basicdsk_geometry geometry;
343 
344 	basicdsk_default_geometry(format, &geometry);
345 
346 	expected_size = geometry.sector_length;
347 	expected_size *= geometry.heads;
348 	expected_size *= geometry.tracks;
349 	expected_size *= geometry.sectors;
350 	*vote = (floppy_image_size(floppy) == expected_size) ? 100 : 50;
351 	return FLOPPY_ERROR_SUCCESS;
352 }
353