-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrx50p2l.c
324 lines (295 loc) · 8.19 KB
/
rx50p2l.c
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
/*
* Quick program to read a phyiscal RX-50 dump and turn it into a
* logical dump that can be used. -- knows how to fix DOS --
*
* Tracks 0 and 1 have no interleave.
* Tracks 2-79 have an interlave of 2, so the logical tracks are
* stored in the following physical tracks:
* Physical Logical
* 1 1
* 2 6
* 3 2
* 4 7
* 5 3
* 6 8
* 7 4
* 8 9
* 9 5
* 10 10
*
* Physical Logical
* 1 1
* 3 2
* 5 3
* 7 4
* 9 5
* 2 6
* 4 7
* 6 8
* 8 9
* 10 10
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>
#define NSECTORS 10
#define SECTOR_LEN 512
#define TOTAL_TRACKS 80
#define Z80_DI 0xf3 /* Z80 encoding for DI mnemonic */
#define RX50_BLANK 0xe5 /* bytes on a blank RX-50 */
#define RB_MEDIA_ID 0xfa /* Rainbow used media ID 0xfa for its media ID */
/*
* Identity mapping
*/
int same[NSECTORS] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
/*
* If we have a logical dump, how do we turn it into a physical one?
*/
int p2l[NSECTORS] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
/*
* If we have a physical dump, how do we turn it into a logical one?
*/
int l2p[NSECTORS] = {1, 6, 2, 7, 3, 8, 4, 9, 5, 10};
uint8_t track_buffer[SECTOR_LEN * NSECTORS];
struct iovec twiddle_vect[NSECTORS];
int span = 0;
int skip_tracks = 0;
int identity_tracks = 2;
int do_dos = 0;
int do_cpm = 0;
/*
* The MS-DOS media check routine reads sector 2 and checks ot see if
* the first byte is 0xfa and declares it Rainbow formatted if it
* finds that. There's a hack for 0xe5 for blank media I don't think
* we need since DOS's FORMAT program always put a boot loader onto
* the floppy, even if it was the crappy 'This is not a system disk'
* one. MS-DOS needed to do this so it could format blank floppies,
* a situation we won't encounter.
*/
uint8_t sec2magic[4] = { 0xfa, /* cli -- also the rainbow media ID */
0xe9, 0xd6, 0x00 }; /* jmp .+0xd6 -- start of secondary boot loader */
uint8_t fat12_start[3] = { RB_MEDIA_ID, 0xff, 0xff }; /* Standard FAT start */
uint8_t fat12_startb[3] = { 0xfc, 0xff, 0xff }; /* Standard FAT start */
struct pc_dos_bootsector {
uint8_t jump[3]; /* Jump instruction E9 xx xx */
uint8_t oem_name[8]; /* Name of OEM + Version */
uint8_t bpb_bytes_per_sec[2]; /* Bytes per sector */
uint8_t bpb_sec_per_clust; /* Sectors in each cluster */
uint8_t bpb_res_sec[2]; /* Number of reserved sectors */
uint8_t bpb_n_fat; /* Number of FATs total */
uint8_t bpb_root_ents[2]; /* Number of entries in root dir */
uint8_t bpb_secs[2]; /* Total number of sectors */
uint8_t bpb_media; /* Media ID */
uint8_t bpb_fat_sec[2]; /* Number of sectors per FAT */
uint8_t bpb_sec_per_track[2]; /* Sectors per track */
uint8_t bpb_heads[2]; /* Number of heads */
uint8_t bpb_hidden_sec[2]; /* Number of hidden sectors */
uint8_t drive_no; /* drive number (0x80) */
uint8_t boot_code[479]; /* Who cares -- boot code / data */
uint8_t magic_55;
uint8_t magic_aa;
};
struct pc_dos_bootsector dummy_bs = {
.jump = { 0xe9, 'r', 'b' }, /* Jump to boot code */
/* not really, but easy to ID */
.oem_name = { 'D', 'E', 'C', ' ', '3', '.', '1', '0' },
/*
* Values taken from Disk definition tables for MSDOS 2.00, page 1-4 in
* Microsoft MS-DOS Operating System BIOS Listing
* AA-X432A-TV
* June 1983
* Published in the Digital Rainbow 100 MS-DOS Technical Reference Manual
*/
.bpb_bytes_per_sec = { 0x00, 0x02 }, /* 512 bytes per sector */
.bpb_sec_per_clust = 0x01, /* 1 sector per cluster */
.bpb_res_sec = { 2 * 10, 0 }, /* 2 tracks (20 sectors) reserved */
.bpb_n_fat = 2, /* 2 FATs */
.bpb_root_ents = { 96, 0 }, /* 96 root directory entries */
.bpb_secs = { 0x20, 0x03 }, /* 800 sectors total */
.bpb_media = RB_MEDIA_ID, /* Rainbow media ID */
.bpb_fat_sec = { 3, 0 }, /* 3 sectors for each FAT */
/* End of MS-DOS 2.0 BPB values from trm */
.bpb_sec_per_track = { 10, 0 }, /* 10 sectors per track */
.bpb_heads = { 1, 0 }, /* Single sided */
.bpb_hidden_sec = { 0, 0 }, /* Unused in FreeBSD, but nothing hidden */
/* End of MS-DOS 3.3 BPB */
.drive_no = 0x80, /* This is a lie */
.magic_55 = 0x55, /* Last two bytes are 0x55 and */
.magic_aa = 0xaa, /* 0xaa per DOS signature */
};
_Static_assert(sizeof(struct pc_dos_bootsector) == SECTOR_LEN, "pc_dos_bootsector wrong size");
void
setup_twiddle_buf(struct iovec *iov, int *xlate, int n, int offset)
{
int i, new;
for (i = 0; i < n; i++) {
/* Sectors are 1-based */
new = xlate[(i + offset) % n] - 1;
// fprintf(stderr, "Moving %d to %d\n", i, new);
iov[new].iov_base = track_buffer + i * SECTOR_LEN;
iov[new].iov_len = SECTOR_LEN;
// fprintf(stderr, "%d offset %llx\n", new, (long long) ((uint8_t *)iov[new].iov_base - track_buffer));
}
}
void
usage(void)
{
errx(1, "[-i infile] [-o outfile] [-l]");
}
/*
* Read the entire track in so we work in pipelines that write < 5k at a time.
*/
size_t
read_track(int in)
{
size_t len, tot, rv;
tot = 0;
len = sizeof(track_buffer);
do {
rv = read(in, track_buffer, len);
if (rv == -1) {
if (tot == 0)
tot = -1;
break;
}
len -= rv;
tot += rv;
} while (len > 0);
return tot;
}
void
print_disk_type(int in)
{
/*
* OK look at track 0
*/
read_track(in); // Track 0
if (track_buffer[0] == Z80_DI)
printf("Found Z80 boot code in sector 0\n");
else if (track_buffer[0] == RX50_BLANK)
printf("Found Blank RX-50 media\n");
if (memcmp(track_buffer + SECTOR_LEN, sec2magic, sizeof(sec2magic)) == 0)
printf("Found MS-DOS secondary boot loader\n");
if (track_buffer[SECTOR_LEN] == 0xe9)
printf("Found CP/M 86 secondary boot loader\n");
/*
* Look at track 2
*/
read_track(in); // Track 1
read_track(in); // Track 2
if (memcmp(track_buffer, fat12_start, sizeof(fat12_start)) == 0)
printf("Found MS-DOS FAT in the right place\n");
if (memcmp(track_buffer, fat12_startb, sizeof(fat12_startb)) == 0)
printf("Found FC MS-DOS FAT in the right place\n");
else if (track_buffer[0] == 0x00 && track_buffer[1] != 0xe5)
printf("Possible CP/M directory\n");
}
int
main(int argc, char **argv)
{
int ch, i, in, out, lflag, wflag;
ssize_t len;
int offset;
in = STDIN_FILENO;
out = STDOUT_FILENO;
lflag = 1;
wflag = 0;
while ((ch = getopt(argc, argv, "cdfI:i:lo:ps:w")) != -1) {
switch (ch) {
case 'c':
do_cpm = 1;
do_dos = 0;
break;
case 'd':
do_dos = 1;
do_cpm = 0;
break;
case 'f':
dummy_bs.bpb_media = 0xfc;
break;
case 'i':
if (in != STDIN_FILENO)
close(in);
in = open(optarg, O_RDONLY);
if (in == -1)
err(1, "Can't open %s for reading\n", optarg);
break;
case 'I':
/* Number of tracks to identity map */
identity_tracks = strtol(optarg, NULL, 0);
break;
case 'l':
lflag = 1;
break;
case 'o':
if (out != STDOUT_FILENO)
close(out);
out = open(optarg, O_WRONLY| O_CREAT);
if (out == -1)
err(1, "Can't open %s for reading\n", optarg);
break;
case 'p':
lflag = 0;
break;
case 's':
/* Span per track, 0 DOS or CP/M 7 Venix */
span = strtol(optarg, NULL, 0);
break;
case 't':
skip_tracks = strtol(optarg, NULL, 0);
break;
case 'w':
wflag = 1;
break;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (argc > 0)
usage();
if (wflag) {
print_disk_type(in);
exit(0);
}
fprintf(stderr, "Starting\n");
for (i = 0; i < skip_tracks; i++) {
fprintf(stderr, "skipping track %d\n", i);
read_track(in);
}
offset = 0;
for (; i < TOTAL_TRACKS; i++) {
len = read_track(in);
if (len == -1)
err(1, "bad read");
else if (len != sizeof(track_buffer))
errx(1, "short read track %d (%zd bytes)", i, len);
if (i == 0 && do_dos && lflag) {
/*
* When we have MS-DOS, we have to hack the first sector so
* mtools works. At present, this is non-reversable.
*/
memcpy(track_buffer, &dummy_bs, SECTOR_LEN);
}
setup_twiddle_buf(twiddle_vect,
i < identity_tracks ? same : (lflag ? l2p : p2l),
NSECTORS, offset);
len = writev(out, twiddle_vect, NSECTORS);
if (len == -1)
err(1, "bad write");
else if (len != sizeof(track_buffer))
errx(1, "short write");
offset += span;
}
if (in != STDIN_FILENO)
close(in);
if (out != STDOUT_FILENO)
close(out);
}