1 /***************************************************************************
2 * *
3 * LIBDSK: General floppy and diskimage access library *
4 * Copyright (C) 2006 John Elliott <seasip.webmaster@gmail.com> *
5 * *
6 * This library is free software; you can redistribute it and/or *
7 * modify it under the terms of the GNU Library General Public *
8 * License as published by the Free Software Foundation; either *
9 * version 2 of the License, or (at your option) any later version. *
10 * *
11 * This library is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Library General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Library General Public *
17 * License along with this library; if not, write to the Free *
18 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, *
19 * MA 02111-1307, USA *
20 * *
21 ***************************************************************************/
22
23 #include "apriboot.h"
24
25 /* This file deals with transforming an Apricot boot sector to/from PCDOS
26 * format. In an attempt to make repeated conversion easier, it saves
27 * the vital bits of the original boot sector in unused areas of the
28 * converted sector, and indicates this with a magic number. */
29
30 unsigned char apri_magic[12] =
31 {
32 0x04, 0x35, 'A', 'P', 'R', 'I', 'B', 'O', 'O', 'T', '=', '>'
33 };
34
35 unsigned char pcd_magic[12] =
36 {
37 0x05, 0x35, 'A', 'P', 'R', 'I', 'B', 'O', 'O', 'T', '=', '>'
38 };
39
40 unsigned char pcdos_dummy[] =
41 {
42 /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
43 0xE9, 0x40, 0x90, 'I', 'B', 'M', ' ', ' ', '3', '.', '3', 0x00, 0x02, 0x01, 0x01, 0x00,
44 0x02, 0x80, 0x00, 0x76, 0x02, 0xFC, 0x02, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
45 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
46 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47 0x90, 0x90, 0xCD, 0x18
48 };
49
50
51 unsigned char apricot_dummy[] =
52 {
53 /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */
54 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
55 0x09, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0e, 0x00,
56 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x1E, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59 0x00, 0x02, 0x02, 0x01, 0x00, 0x02, 0xB0, 0x00, 0xA0, 0x05, 0xFE, 0x03, 0x00, 0x02, 0x00, 0x00,
60 'F', 'O', 'N', 'T', '=', 'B', 'R', 'I', 'T', '0', '3', ' ', ' ', ' ', ' ', ' ',
61 'K', 'E', 'Y', 'S', '=', 'A', 'C', 'T', '0', '0', '2', ' ', ' ', ' ', ' ', ' ',
62 0x06, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x22, 0x14, 0x02, 0x36, 0x00, 0x12, 0x00, 0x26, 0x00,
63 0x08, 0x01, 0x19, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
64 0x01, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65 0x0E, 0x0E, 0x08, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x11, 0x13, 0x1E, 0x00, 0x00, 0x00, 0x00
66 };
67
68
pcdos_to_apricot(byte * buf,const char * filename,int verbose)69 static int pcdos_to_apricot(byte *buf, const char *filename, int verbose)
70 {
71 byte buf2[512];
72
73 memset(buf2, 0, sizeof(buf2));
74 /* Is there a saved copy of the PCDOS boot sector? If so, use it */
75 if (!memcmp(buf + 0x144, apri_magic, sizeof(apri_magic)))
76 {
77 memcpy(buf2, buf + 0x150, 0xA0); /* Copy in saved sector */
78 }
79 else /* If not, generate the Apricot sector from the PCDOS one */
80 {
81 int bootsecs = buf[14] + 256 * buf[15];
82 int fatcopies= buf[16];
83 int dirsize = buf[17] + 256 * buf[18];
84 long totsecs = buf[19] + 256 * buf[20];
85 int fatsize = buf[22] + 256 * buf[23];
86 int sectors = buf[24] + 256 * buf[25];
87 int heads = buf[26] + 256 * buf[27];
88 /* Might want to expand this later on to handle 32-bit sector counts */
89 long cylinders = totsecs / (heads * sectors);
90 long dataoff = bootsecs + (fatcopies * fatsize) + ((dirsize + 15) / 16);
91
92 memcpy(buf2, apricot_dummy, sizeof(apricot_dummy));
93 memcpy(buf2, buf + 3, 8); /* OEM ID */
94 buf2[0x10] = buf[0x18]; /* Count of sectors */
95 buf2[0x11] = buf[0x19];
96 buf2[0x12] = (byte)(cylinders & 0xFF);
97 buf2[0x13] = (byte)((cylinders >> 8) & 0xFF);
98 buf2[0x14] = (byte)((cylinders >> 16) & 0xFF);
99 buf2[0x15] = (byte)((cylinders >> 24) & 0xFF);
100 buf2[0x16] = buf[0x1A]; /* Count of heads */
101
102 /* Offset to data area */
103 buf2[0x28] = (byte)(dataoff & 0xFF);
104 buf2[0x29] = (byte)((dataoff >> 8) & 0xFF);
105 buf2[0x2a] = (byte)((dataoff >> 16) & 0xFF);
106 buf2[0x2b] = (byte)((dataoff >> 24) & 0xFF);
107
108 memcpy(buf2 + 0x50, buf + 0x0b, 13); /* BPB */
109 /* Disk type */
110 if (heads == 1)
111 {
112 if (cylinders == 70)
113 buf2[0x54] = 0;
114 else buf2[0x54] = 1;
115 }
116 else buf2[0x54] = 2;
117 }
118 /* Save the original PCDOS boot sector */
119 memcpy(buf2 + 0x144, pcd_magic, sizeof(pcd_magic));
120 memcpy(buf2 + 0x150, buf, 0xA0);
121
122 memcpy(buf, buf2, sizeof(buf2));
123 if (verbose) fprintf(stderr,"%s: Bootsector converted to Apricot format\n", filename);
124 return 0;
125 }
126
127
apricot_to_pcdos(byte * buf,const char * filename,int verbose)128 static int apricot_to_pcdos(byte *buf, const char *filename, int verbose)
129 {
130 byte buf2[512];
131
132 memset(buf2, 0, sizeof(buf2));
133 /* Is there a saved copy of the PCDOS boot sector? If so, use it */
134 if (!memcmp(buf + 0x144, pcd_magic, sizeof(pcd_magic)))
135 {
136 memcpy(buf2, buf + 0x150, 0xA0); /* Copy in saved sector */
137 memcpy(buf2, pcdos_dummy, 3); /* and patch its code to be */
138 memcpy(buf2 + 0x40, pcdos_dummy + 0x40, 4);
139 /* the dummy boot sequence */
140 }
141 else /* If not, generate the PCDOS sector from the Apricot one */
142 {
143 int heads = buf[0x16];
144 int sectors = buf[0x10] + 256 * buf[0x11];
145
146 memcpy(buf2, pcdos_dummy, sizeof(pcdos_dummy));
147 memcpy(buf2 + 3, buf, 8); /* OEM ID */
148 memcpy(buf2 + 0x0b, buf + 0x50, 13); /* BPB */
149 buf2[24] = sectors & 0xFF;
150 buf2[25] = sectors >> 8;
151 buf2[26] = heads & 0xFF;
152 buf2[27] = heads >> 8;
153 }
154 /* Save the original Apricot boot sector */
155 memcpy(buf2 + 0x144, apri_magic, sizeof(apri_magic));
156 memcpy(buf2 + 0x150, buf, 0xA0);
157 buf2[0x1FE] = 0x55;
158 buf2[0x1FF] = 0xAA; /* Boot record signature */
159
160 memcpy(buf, buf2, sizeof(buf2));
161 if (verbose) fprintf(stderr,"%s: Bootsector converted to PCDOS format\n", filename);
162 return 0;
163 }
164
165
166
167 /* Check for a PCDOS BPB. */
sector_is_pcdos(byte * buf)168 static int sector_is_pcdos(byte *buf)
169 {
170 if (buf[0] != 0 && buf[0] != 0xE9 && buf[0] != 0xEB)
171 {
172 return 0; /* Must start with an IBM or Atari jump */
173 }
174 if (buf[11] != 0 || buf[12] != 2)
175 {
176 return 0; /* Must have a 512-byte sector size */
177 }
178 return 1;
179 }
180
181
sector_is_apricot(byte * buf)182 static int sector_is_apricot(byte *buf)
183 {
184 /* Check that the OEM ID is either ASCII or zeroes */
185 int n;
186
187 for (n = 0; n < 8; n++)
188 {
189 if (buf[n] != 0 && (buf[n] < ' ' || buf[n] > '~'))
190 {
191 return 0;
192 }
193 }
194 /* Check that we have 512-byte sectors */
195 if (buf[80] != 0 || buf[81] != 2)
196 {
197 return 0;
198 }
199 /* XXX More validation here */
200 return 1;
201 }
202
203
204
transform(MODE md,byte * buf,const char * filename,int verbose)205 int transform(MODE md, byte *buf, const char *filename, int verbose)
206 {
207 int is_pcdos = 0;
208
209 /* OK. Firstly, determine whether this is a PC-DOS bootsector,
210 an Apricot bootsector, or something else. */
211 if (sector_is_pcdos(buf))
212 {
213 is_pcdos = 1;
214 if (md == FORCE_PCDOS) /* Nothing to do! */
215 {
216 fprintf(stderr, "%s: Is already in PCDOS format\n",
217 filename);
218 return 0;
219 }
220 }
221 else if (sector_is_apricot(buf))
222 {
223 if (md == FORCE_APRICOT)
224 {
225 fprintf(stderr, "%s: Is already in Apricot format\n",
226 filename);
227 return 0;
228 }
229 }
230 else
231 {
232 fprintf(stderr, "%s: Could not identify the boot sector\n",
233 filename);
234 return -1;
235 }
236 /* Need to swap. */
237 if (is_pcdos) return pcdos_to_apricot(buf, filename, verbose);
238 else return apricot_to_pcdos(buf, filename, verbose);
239 }
240