| 12
 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
 
 | // license:BSD-3-Clause
// copyright-holders:Fabio Priuli
/*********************************************************************
    formats/nfd_dsk.h
    PC98 NFD disk images (info from: http://www.geocities.jp/t98next/dev.html )
    Revision 0
    ==========
    header structure (variable length > 0x120, header length = DWORD at 0x110)
    0x000-0x00F = T98FDDIMAGE.R* followed by 2 0x00 bytes, * = format revision (0 or 1 so far)
    0x010-0x10F = space for image info / comments
    0x110-0x113 = header length (DWORD)
    0x114       = write protect (any value > 0 means not writeable)
    0x115       = number of heads
    0x116-0x11F = reserved
    0x120-EOHeader = sector map (0x10 for each sector of the disk!)
    last 0x10 are fixed to 0x00, probably it marks the end of sector map?
    sector map structure
    0x0     = track number
    0x1     = head
    0x2     = sector number
    0x3     = sector size (in 128byte chunks)
    0x4     = MFM/FM (1 = MFM, 0 = FM)?
    0x5     = DDAM/DAM (1 = DDAM, 0 = DAM)
    0x6-0x9 = READ DATA (FDDBIOS) Results (Status, St0, St1, St2) ??
    0xA     = PDA (disk type)
    0xB-0xF = reserved and equal to 0x00 (possibly available for future format extensions?)
    Revision 1
    ==========
    header structure (variable length > 0x120, header length = DWORD at 0x110)
    0x000-0x11F = same as Rev. 0 format
    0x120-0x3AF = 164 DWORDs containing, for each track, the absolute position of the sector maps
                  for sectors of the track. for unformatted/unused tracks 0 is used
    0x3B0-0x3B3 = absolute position of addintional info in the header, if any
    0x3B4-0x3BF = reserved
    0x120-EOHeader = sector map + special data for each track:
                     first 0x10 of each track = #sectors (WORD), #extra data (WORD), reserved 0xc bytes zeroed
                     then 0x10 for each sector of this track and 0x10 for each extra data chunk
    sector map structure
    0x0     = track number
    0x1     = head
    0x2     = sector number
    0x3     = sector size (in 128byte chunks)
    0x4     = MFM/FM (1 = MFM, 0 = FM)?
    0x5     = DDAM/DAM (1 = DDAM, 0 = DAM)
    0x6-0x9 = READ DATA (FDDBIOS) Results (Status, St0, St1, St2) ??
    0xA     = RETRY DATA (1 = Yes, 0 = No)
    0xB     = PDA (disk type)
    0xC-0xF = reserved and equal to 0x00 (possibly available for future format extensions?)
    extra data map structure
    0x0     = command
    0x1     = track number
    0x2     = head
    0x3     = sector number
    0x4     = sector size (in 128byte chunks)
    0x5-0x8 = READ DATA (FDDBIOS) Results (Status, St0, St1, St2) ??
    0x9     = Number of times to RETRY loading data
    0xA-0xD = length of RETRY DATA
    0xE     = PDA (disk type)
    0xF     = reserved and equal to 0x00 (possibly available for future format extensions?)
    TODO:
    - add support for write protect header bit? apparently, some disks try to write and
      fail to boot if they succeed which is the reason this bit was added
    - add support for DDAM in Rev. 0 (need an image which set it in some sector)
    - investigate the READ DATA bytes of sector headers
    - investigate RETRY DATA chunks
 *********************************************************************/
	#include <assert.h>
#include "nfd_dsk.h"
nfd_format::nfd_format()
{
}
const char *nfd_format::name() const
{
	return "nfd";
}
const char *nfd_format::description() const
{
	return "NFD disk image";
}
const char *nfd_format::extensions() const
{
	return "nfd";
}
int nfd_format::identify(io_generic *io, uint32_t form_factor)
{
	uint8_t h[16];
	io_generic_read(io, h, 0, 16);
	if (strncmp((const char *)h, "T98FDDIMAGE.R0", 14) == 0 || strncmp((const char *)h, "T98FDDIMAGE.R1", 14) == 0)
		return 100;
	return 0;
}
bool nfd_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
{
	uint64_t size = io_generic_size(io);
	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;
	// 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;
	uint8_t num_secs[164];
	uint8_t num_specials[164];
	uint32_t track_sizes[164];
	uint8_t tracks[164 * 26];
	uint8_t heads[164 * 26];
	uint8_t secs[164 * 26];
	uint8_t mfm[164 * 26];
	uint8_t sec_sizes[164 * 26];
	uint32_t hsize = little_endianize_int32(*(uint32_t *)(h+0x110));
	int pos = 0x120;
	// set up sector map
	if (format_version == 1)
	{
		for (int track = 0; track < 164; track++)
		{
			int curr_track_size = 0;
			// read sector map absolute location
			io_generic_read(io, hsec, pos, 4);
			pos += 4;
			uint32_t secmap_addr = little_endianize_int32(*(uint32_t *)(hsec));
			if (secmap_addr)
			{
				// 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);
				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);
					if (track == 0 && sect == 0)
						disk_type = hsec[0xb];  // can this change across the disk? I don't think so...
					secmap_addr += 0x10;
					tracks[(track * 26) + sect] = hsec[0];
					heads[(track * 26) + sect] = hsec[1];
					secs[(track * 26) + sect] = hsec[2];
					sec_sizes[(track * 26) + sect] = hsec[3];
					mfm[(track * 26) + sect] = hsec[4];
					curr_track_size += (128 << hsec[3]);
				}
				if (num_specials[track] > 0)
				{
					for (int sect = 0; sect < num_specials[track]; sect++)
					{
						io_generic_read(io, hsec, secmap_addr, 0x10);
						secmap_addr += 0x10;
						curr_track_size += (hsec[9] + 1) * little_endianize_int32(*(uint32_t *)(hsec + 0x0a));
					}
				}
			}
			else
			{
				num_secs[track] = 0;
				num_specials[track] = 0;
			}
			track_sizes[track] = curr_track_size;
		}
	}
	else
	{
		for (int track = 0; track < 163 && pos < hsize; track++)
		{
			int curr_num_sec = 0, curr_track_size = 0;
			for (int sect = 0; sect < 26; sect++)
			{
				// read sector map for this sector
				// for rev.0 format each sector uses 0x10 bytes
				io_generic_read(io, hsec, pos, 0x10);
				if (track == 0 && sect == 0)
					disk_type = hsec[0xa];  // can this change across the disk? I don't think so...
				pos += 0x10;
				if (hsec[0] == 0xff)    // unformatted/unused sector
					continue;
				tracks[(track * 26) + sect] = hsec[0];
				heads[(track * 26) + sect] = hsec[1];
				secs[(track * 26) + sect] = hsec[2];
				sec_sizes[(track * 26) + sect] = hsec[3];
				mfm[(track * 26) + sect] = hsec[4];
				curr_track_size += (128 << hsec[3]);
				curr_num_sec++;
			}
			num_secs[track] = curr_num_sec;
			track_sizes[track] = curr_track_size;
		}
	}
	// shouln't this be set-up depending on disk_type? gaplus does not like having less than 166666 cells
	int cell_count = form_factor == floppy_image::FF_35 ? 200000 : 166666;
	switch (disk_type)
	{
		case 0x10:  // 640K disk, 2DD
			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);
			break;
	}
	desc_pc_sector sects[256];
	uint8_t sect_data[65536];
	int cur_sec_map = 0, sector_size;
	pos = hsize;
	for (int track = 0; track < 163 && pos < size; track++)
	{
		io_generic_read(io, sect_data, pos, track_sizes[track]);
		for (int i = 0; i < num_secs[track]; i++)
		{
			cur_sec_map = track * 26 + i;
			sector_size = 128 << sec_sizes[cur_sec_map];
			sects[i].track       = tracks[cur_sec_map];
			sects[i].head        = heads[cur_sec_map];
			sects[i].sector      = secs[cur_sec_map];
			sects[i].size        = sec_sizes[cur_sec_map];
			sects[i].actual_size = sector_size;
			sects[i].deleted     = false;
			sects[i].bad_crc     = false;
			sects[i].data        = sect_data + i * sector_size;
		}
		pos += track_sizes[track];
		// notice that the operation below might fail if sectors of the same track have variable sec_sizes,
		// because the gap3 calculation would account correctly only for the first sector...
		// examined images had constant sec_sizes in the each track, so probably this is not an issue
		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])));
	}
	return true;
}
bool nfd_format::supports_save() const
{
	return false;
}
const floppy_format_type FLOPPY_NFD_FORMAT = &floppy_image_format_creator<nfd_format>;
 |