1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
|
// license:BSD-3-Clause
// copyright-holders:Dirk Best
/***************************************************************************
TRS-80
JV1/3 disk image formats
Used by Jeff Vavasour's TRS-80 Emulators
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"
#include "ioprocs.h"
#include "osdcore.h" // osd_printf_*
jv1_format::jv1_format() : wd177x_format(formats)
{
}
const char *jv1_format::name() const
{
return "jv1";
}
const char *jv1_format::description() const
{
return "TRS-80 JV1 disk image";
}
const char *jv1_format::extensions() const
{
return "jv1,dsk";
}
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 jv1_format::format jv1_format::formats[] =
{
{
floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
4000, 10, 35, 1, 256, {}, 0, {}, 14, 11, 12
},
{
floppy_image::FF_525, floppy_image::SSSD, floppy_image::FM,
4000, 10, 40, 1, 256, {}, 0, {}, 14, 11, 12
},
{
floppy_image::FF_525, floppy_image::SSQD, floppy_image::FM,
4000, 10, 80, 1, 256, {}, 0, {}, 14, 11, 12
},
{}
};
const jv1_format FLOPPY_JV1_FORMAT;
#define MAX_SECTORS 19
#define MAX_TRACKS 96
jv3_format::jv3_format()
{
}
const char *jv3_format::name() const
{
return "jv3";
}
const char *jv3_format::description() const
{
return "TRS-80 JV3 disk image";
}
const char *jv3_format::extensions() const
{
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
std::vector<uint8_t> data(image_size);
size_t actual;
io.read_at(0, data.data(), image_size, actual);
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;
std::vector<uint8_t> data(image_size);
size_t actual;
io.read_at(0, data.data(), data.size(), actual);
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, 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))
{
std::vector<uint8_t> data(image_size);
size_t actual;
io.read_at(0, data.data(), data.size(), actual);
if ((data.size() >= 0x2200) && (data[0x21ff] == 0))
return false; // disk is readonly
}
}
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;
size_t actual;
io.write_at(data_ptr, &dummy[0], 256, actual);
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;
size_t actual;
io.write_at(data_ptr, sectors[i].data(), sectors[i].size(), actual);
data_ptr += sectors[i].size();
}
}
}
}
// Save the header
size_t actual;
io.write_at(0, header, 0x2200, actual);
return true;
}
bool jv3_format::supports_save() const
{
return true;
}
const jv3_format FLOPPY_JV3_FORMAT;
|