1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert
3 /*********************************************************************
4 
5     formats/st_dsk.c
6 
7     All usual Atari ST formats
8 
9 *********************************************************************/
10 
11 #include <cassert>
12 
13 #include "formats/st_dsk.h"
14 
st_format()15 st_format::st_format()
16 {
17 }
18 
name() const19 const char *st_format::name() const
20 {
21 	return "st";
22 }
23 
description() const24 const char *st_format::description() const
25 {
26 	return "Atari ST floppy disk image";
27 }
28 
extensions() const29 const char *st_format::extensions() const
30 {
31 	return "st";
32 }
33 
supports_save() const34 bool st_format::supports_save() const
35 {
36 	return true;
37 }
38 
find_size(io_generic * io,uint8_t & track_count,uint8_t & head_count,uint8_t & sector_count)39 void st_format::find_size(io_generic *io, uint8_t &track_count, uint8_t &head_count, uint8_t &sector_count)
40 {
41 	uint64_t size = io_generic_size(io);
42 	for(track_count=80; track_count <= 82; track_count++)
43 		for(head_count=1; head_count <= 2; head_count++)
44 			for(sector_count=9; sector_count <= 11; sector_count++)
45 				if(size == (uint32_t)512*track_count*head_count*sector_count)
46 					return;
47 	track_count = head_count = sector_count = 0;
48 }
49 
identify(io_generic * io,uint32_t form_factor)50 int st_format::identify(io_generic *io, uint32_t form_factor)
51 {
52 	uint8_t track_count, head_count, sector_count;
53 	find_size(io, track_count, head_count, sector_count);
54 
55 	if(track_count)
56 		return 50;
57 	return 0;
58 }
59 
load(io_generic * io,uint32_t form_factor,floppy_image * image)60 bool st_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
61 {
62 	uint8_t track_count, head_count, sector_count;
63 	find_size(io, track_count, head_count, sector_count);
64 
65 	uint8_t sectdata[11*512];
66 	desc_s sectors[11];
67 	for(int i=0; i<sector_count; i++) {
68 		sectors[i].data = sectdata + 512*i;
69 		sectors[i].size = 512;
70 		sectors[i].sector_id = i + 1;
71 	}
72 
73 	int track_size = sector_count*512;
74 	for(int track=0; track < track_count; track++) {
75 		for(int head=0; head < head_count; head++) {
76 			io_generic_read(io, sectdata, (track*head_count + head)*track_size, track_size);
77 			generate_track(atari_st_fcp_get_desc(track, head, head_count, sector_count),
78 							track, head, sectors, sector_count, 100000, image);
79 		}
80 	}
81 
82 	image->set_variant(floppy_image::DSDD);
83 
84 	return true;
85 }
86 
save(io_generic * io,floppy_image * image)87 bool st_format::save(io_generic *io, floppy_image *image)
88 {
89 	int track_count, head_count, sector_count;
90 	get_geometry_mfm_pc(image, 2000, track_count, head_count, sector_count);
91 
92 	if(track_count < 80)
93 		track_count = 80;
94 	else if(track_count > 82)
95 		track_count = 82;
96 
97 	// Happens for a fully unformatted floppy
98 	if(!head_count)
99 		head_count = 1;
100 
101 	if(sector_count > 11)
102 		sector_count = 11;
103 	else if(sector_count < 9)
104 		sector_count = 9;
105 
106 	uint8_t sectdata[11*512];
107 	int track_size = sector_count*512;
108 
109 	for(int track=0; track < track_count; track++) {
110 		for(int head=0; head < head_count; head++) {
111 			get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata);
112 			io_generic_write(io, sectdata, (track*head_count + head)*track_size, track_size);
113 		}
114 	}
115 
116 	return true;
117 }
118 
119 
msa_format()120 msa_format::msa_format()
121 {
122 }
123 
name() const124 const char *msa_format::name() const
125 {
126 	return "msa";
127 }
128 
description() const129 const char *msa_format::description() const
130 {
131 	return "Atari MSA floppy disk image";
132 }
133 
extensions() const134 const char *msa_format::extensions() const
135 {
136 	return "msa";
137 }
138 
supports_save() const139 bool msa_format::supports_save() const
140 {
141 	return true;
142 }
143 
read_header(io_generic * io,uint16_t & sign,uint16_t & sect,uint16_t & head,uint16_t & strack,uint16_t & etrack)144 void msa_format::read_header(io_generic *io, uint16_t &sign, uint16_t &sect, uint16_t &head, uint16_t &strack, uint16_t &etrack)
145 {
146 	uint8_t h[10];
147 	io_generic_read(io, h, 0, 10);
148 	sign = (h[0] << 8) | h[1];
149 	sect = (h[2] << 8) | h[3];
150 	head = (h[4] << 8) | h[5];
151 	strack = (h[6] << 8) | h[7];
152 	etrack = (h[8] << 8) | h[9];
153 }
154 
uncompress(uint8_t * buffer,int csize,int usize)155 bool msa_format::uncompress(uint8_t *buffer, int csize, int usize)
156 {
157 	uint8_t sectdata[11*512];
158 	int src=0, dst=0;
159 	while(src<csize && dst<usize) {
160 		unsigned char c = buffer[src++];
161 		if(c == 0xe5) {
162 			if(csize-src < 3)
163 				return false;
164 			c = buffer[src++];
165 			int count = (buffer[src] << 8) | buffer[src+1];
166 			src += 2;
167 			if(usize-dst < count)
168 				return false;
169 			for(int i=0; i<count; i++)
170 				sectdata[dst++] = c;
171 		} else
172 			sectdata[dst++] = c;
173 	}
174 
175 	if(src != csize || dst != usize)
176 		return false;
177 	memcpy(buffer, sectdata, usize);
178 	return true;
179 }
180 
compress(const uint8_t * buffer,int usize,uint8_t * dest,int & csize)181 bool msa_format::compress(const uint8_t *buffer, int usize, uint8_t *dest, int &csize)
182 {
183 	int src=0, dst=0;
184 	while(src<usize && dst<usize) {
185 		unsigned char c = buffer[src++];
186 		int ncopy = 1;
187 		while(src < usize && buffer[src] == c) {
188 			src++;
189 			ncopy++;
190 		}
191 		if(ncopy > 4 || c == 0xe5) {
192 			if(dst+3 > usize)
193 				return false;
194 			dest[dst++] = 0xe5;
195 			dest[dst++] = c;
196 			dest[dst++] = ncopy >> 8;
197 			dest[dst++] = ncopy;
198 		} else {
199 			src -= ncopy-1;
200 			dest[dst++] = c;
201 		}
202 	}
203 
204 	csize = dst;
205 
206 	return dst < usize;
207 }
208 
identify(io_generic * io,uint32_t form_factor)209 int msa_format::identify(io_generic *io, uint32_t form_factor)
210 {
211 	uint16_t sign, sect, head, strack, etrack;
212 	read_header(io, sign, sect, head, strack, etrack);
213 
214 	if(sign == 0x0e0f &&
215 		(sect >= 9 && sect <= 11) &&
216 		(head == 0 || head == 1) &&
217 		strack <= etrack &&
218 		etrack < 82)
219 		return 100;
220 	return 0;
221 }
222 
load(io_generic * io,uint32_t form_factor,floppy_image * image)223 bool msa_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
224 {
225 	uint16_t sign, sect, heads, strack, etrack;
226 	read_header(io, sign, sect, heads, strack, etrack);
227 
228 	uint8_t sectdata[11*512];
229 	desc_s sectors[11];
230 	for(int i=0; i<sect; i++) {
231 		sectors[i].data = sectdata + 512*1;
232 		sectors[i].size = 512;
233 		sectors[i].sector_id = i + 1;
234 	}
235 
236 	int pos = 10;
237 	int track_size = sect*512;
238 
239 	for(int track=strack; track <= etrack; track++) {
240 		for(int head=0; head <= heads; head++) {
241 			uint8_t th[2];
242 			io_generic_read(io, th, pos, 2);
243 			pos += 2;
244 			int tsize = (th[0] << 8) | th[1];
245 			io_generic_read(io, sectdata, pos, tsize);
246 			pos += tsize;
247 			if(tsize < track_size) {
248 				if(!uncompress(sectdata, tsize, track_size))
249 					return false;
250 			}
251 			generate_track(atari_st_fcp_get_desc(track, head, head+1, sect),
252 							track, head, sectors, sect, 100000, image);
253 		}
254 	}
255 
256 	image->set_variant(floppy_image::DSDD);
257 	return true;
258 }
259 
260 
save(io_generic * io,floppy_image * image)261 bool msa_format::save(io_generic *io, floppy_image *image)
262 {
263 	int track_count, head_count, sector_count;
264 	get_geometry_mfm_pc(image, 2000, track_count, head_count, sector_count);
265 
266 	if(track_count < 80)
267 		track_count = 80;
268 	else if(track_count > 82)
269 		track_count = 82;
270 
271 	// Happens for a fully unformatted floppy
272 	if(!head_count)
273 		head_count = 1;
274 
275 	if(sector_count > 11)
276 		sector_count = 11;
277 	else if(sector_count < 9)
278 		sector_count = 9;
279 
280 	uint8_t header[10];
281 	header[0] = 0x0e;
282 	header[1] = 0x0f;
283 	header[2] = 0;
284 	header[3] = sector_count;
285 	header[4] = 0;
286 	header[5] = head_count-1;
287 	header[6] = 0;
288 	header[7] = 0;
289 	header[8] = 0;
290 	header[9] = track_count-1;
291 
292 	io_generic_write(io, header, 0, 10);
293 
294 	int pos = 10;
295 
296 	uint8_t sectdata[11*512];
297 	uint8_t compdata[11*512];
298 	int track_size = sector_count*512;
299 
300 	for(int track=0; track < track_count; track++) {
301 		for(int head=0; head < head_count; head++) {
302 			get_track_data_mfm_pc(track, head, image, 2000, 512, sector_count, sectdata);
303 			int csize;
304 			if(compress(sectdata, track_size, compdata, csize)) {
305 				uint8_t th[2];
306 				th[0] = csize >> 8;
307 				th[1] = csize;
308 				io_generic_write(io, th, pos, 2);
309 				io_generic_write(io, compdata, pos+2, csize);
310 				pos += 2+csize;
311 			} else {
312 				uint8_t th[2];
313 				th[0] = track_size >> 8;
314 				th[1] = track_size;
315 				io_generic_write(io, th, pos, 2);
316 				io_generic_write(io, sectdata, pos+2, track_size);
317 				pos += 2+track_size;
318 			}
319 		}
320 	}
321 
322 	return true;
323 }
324 
325 const floppy_format_type FLOPPY_ST_FORMAT = &floppy_image_format_creator<st_format>;
326 const floppy_format_type FLOPPY_MSA_FORMAT = &floppy_image_format_creator<msa_format>;
327