1 |
cebix |
1.1 |
/* |
2 |
|
|
* 1541job.cpp - Emulation of 1541 GCR disk reading/writing |
3 |
|
|
* |
4 |
cebix |
1.4 |
* Frodo (C) 1994-1997,2002-2005 Christian Bauer |
5 |
cebix |
1.1 |
* |
6 |
|
|
* This program is free software; you can redistribute it and/or modify |
7 |
|
|
* it under the terms of the GNU General Public License as published by |
8 |
|
|
* the Free Software Foundation; either version 2 of the License, or |
9 |
|
|
* (at your option) any later version. |
10 |
|
|
* |
11 |
|
|
* This program 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 |
14 |
|
|
* GNU General Public License for more details. |
15 |
|
|
* |
16 |
|
|
* You should have received a copy of the GNU General Public License |
17 |
|
|
* along with this program; if not, write to the Free Software |
18 |
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 |
|
|
*/ |
20 |
|
|
|
21 |
|
|
/* |
22 |
|
|
* Notes: |
23 |
|
|
* ------ |
24 |
|
|
* |
25 |
|
|
* - This is only used for processor-level 1541 emulation. |
26 |
|
|
* It simulates the 1541 disk controller hardware (R/W head, |
27 |
|
|
* GCR reading/writing). |
28 |
|
|
* - The preferences settings for drive 8 are used to |
29 |
|
|
* specify the .d64 file |
30 |
|
|
* |
31 |
|
|
* Incompatibilities: |
32 |
|
|
* ------------------ |
33 |
|
|
* |
34 |
|
|
* - No GCR writing possible (WriteSector is a ROM patch) |
35 |
|
|
* - Programs depending on the exact timing of head movement/disk |
36 |
|
|
* rotation don't work |
37 |
|
|
* - The .d64 error info is unused |
38 |
|
|
*/ |
39 |
|
|
|
40 |
|
|
#include "sysdeps.h" |
41 |
|
|
|
42 |
|
|
#include "1541job.h" |
43 |
|
|
#include "CPU1541.h" |
44 |
|
|
#include "Prefs.h" |
45 |
|
|
|
46 |
|
|
|
47 |
|
|
// Number of tracks/sectors |
48 |
|
|
const int NUM_TRACKS = 35; |
49 |
|
|
const int NUM_SECTORS = 683; |
50 |
|
|
|
51 |
|
|
// Size of GCR encoded data |
52 |
|
|
const int GCR_SECTOR_SIZE = 1+10+9+1+325+8; // SYNC Header Gap SYNC Data Gap (should be 5 SYNC bytes each) |
53 |
|
|
const int GCR_TRACK_SIZE = GCR_SECTOR_SIZE * 21; // Each track in gcr_data has 21 sectors |
54 |
|
|
const int GCR_DISK_SIZE = GCR_TRACK_SIZE * NUM_TRACKS; |
55 |
|
|
|
56 |
|
|
// Job return codes |
57 |
|
|
const int RET_OK = 1; // No error |
58 |
|
|
const int RET_NOT_FOUND = 2; // Block not found |
59 |
|
|
const int RET_NOT_READY = 15; // Drive not ready |
60 |
|
|
|
61 |
|
|
|
62 |
|
|
// Number of sectors of each track |
63 |
|
|
const int num_sectors[36] = { |
64 |
|
|
0, |
65 |
|
|
21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, |
66 |
|
|
19,19,19,19,19,19,19, |
67 |
|
|
18,18,18,18,18,18, |
68 |
|
|
17,17,17,17,17 |
69 |
|
|
}; |
70 |
|
|
|
71 |
|
|
// Sector offset of start of track in .d64 file |
72 |
|
|
const int sector_offset[36] = { |
73 |
|
|
0, |
74 |
|
|
0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336, |
75 |
|
|
357,376,395,414,433,452,471, |
76 |
|
|
490,508,526,544,562,580, |
77 |
|
|
598,615,632,649,666 |
78 |
|
|
}; |
79 |
|
|
|
80 |
|
|
|
81 |
|
|
/* |
82 |
|
|
* Constructor: Open .d64 file if processor-level 1541 |
83 |
|
|
* emulation is enabled |
84 |
|
|
*/ |
85 |
|
|
|
86 |
|
|
Job1541::Job1541(uint8 *ram1541) : ram(ram1541) |
87 |
|
|
{ |
88 |
|
|
the_file = NULL; |
89 |
|
|
|
90 |
|
|
gcr_data = gcr_ptr = gcr_track_start = new uint8[GCR_DISK_SIZE]; |
91 |
|
|
gcr_track_end = gcr_track_start + GCR_TRACK_SIZE; |
92 |
|
|
current_halftrack = 2; |
93 |
|
|
|
94 |
|
|
disk_changed = true; |
95 |
|
|
|
96 |
|
|
if (ThePrefs.Emul1541Proc) |
97 |
|
|
open_d64_file(ThePrefs.DrivePath[0]); |
98 |
|
|
} |
99 |
|
|
|
100 |
|
|
|
101 |
|
|
/* |
102 |
|
|
* Destructor: Close .d64 file |
103 |
|
|
*/ |
104 |
|
|
|
105 |
|
|
Job1541::~Job1541() |
106 |
|
|
{ |
107 |
|
|
close_d64_file(); |
108 |
|
|
delete[] gcr_data; |
109 |
|
|
} |
110 |
|
|
|
111 |
|
|
|
112 |
|
|
/* |
113 |
|
|
* Preferences may have changed |
114 |
|
|
*/ |
115 |
|
|
|
116 |
|
|
void Job1541::NewPrefs(Prefs *prefs) |
117 |
|
|
{ |
118 |
|
|
// 1541 emulation turned off? |
119 |
|
|
if (!prefs->Emul1541Proc) |
120 |
|
|
close_d64_file(); |
121 |
|
|
|
122 |
|
|
// 1541 emulation turned on? |
123 |
|
|
else if (!ThePrefs.Emul1541Proc && prefs->Emul1541Proc) |
124 |
|
|
open_d64_file(prefs->DrivePath[0]); |
125 |
|
|
|
126 |
|
|
// .d64 file name changed? |
127 |
|
|
else if (strcmp(ThePrefs.DrivePath[0], prefs->DrivePath[0])) { |
128 |
|
|
close_d64_file(); |
129 |
|
|
open_d64_file(prefs->DrivePath[0]); |
130 |
|
|
disk_changed = true; |
131 |
|
|
} |
132 |
|
|
} |
133 |
|
|
|
134 |
|
|
|
135 |
|
|
/* |
136 |
|
|
* Open .d64 file |
137 |
|
|
*/ |
138 |
|
|
|
139 |
|
|
void Job1541::open_d64_file(char *filepath) |
140 |
|
|
{ |
141 |
|
|
long size; |
142 |
|
|
uint8 magic[4]; |
143 |
|
|
uint8 bam[256]; |
144 |
|
|
|
145 |
|
|
// Clear GCR buffer |
146 |
|
|
memset(gcr_data, 0x55, GCR_DISK_SIZE); |
147 |
|
|
|
148 |
|
|
// Try opening the file for reading/writing first, then for reading only |
149 |
|
|
write_protected = false; |
150 |
|
|
the_file = fopen(filepath, "rb+"); |
151 |
|
|
if (the_file == NULL) { |
152 |
|
|
write_protected = true; |
153 |
|
|
the_file = fopen(filepath, "rb"); |
154 |
|
|
} |
155 |
|
|
if (the_file != NULL) { |
156 |
|
|
|
157 |
|
|
// Check length |
158 |
|
|
fseek(the_file, 0, SEEK_END); |
159 |
|
|
if ((size = ftell(the_file)) < NUM_SECTORS * 256) { |
160 |
|
|
fclose(the_file); |
161 |
|
|
the_file = NULL; |
162 |
|
|
return; |
163 |
|
|
} |
164 |
|
|
|
165 |
|
|
// x64 image? |
166 |
|
|
fseek(the_file, 0, SEEK_SET); |
167 |
|
|
fread(&magic, 4, 1, the_file); |
168 |
|
|
if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64) |
169 |
|
|
image_header = 64; |
170 |
|
|
else |
171 |
|
|
image_header = 0; |
172 |
|
|
|
173 |
|
|
// Preset error info (all sectors no error) |
174 |
|
|
memset(error_info, 1, NUM_SECTORS); |
175 |
|
|
|
176 |
|
|
// Load sector error info from .d64 file, if present |
177 |
|
|
if (!image_header && size == NUM_SECTORS * 257) { |
178 |
|
|
fseek(the_file, NUM_SECTORS * 256, SEEK_SET); |
179 |
|
|
fread(&error_info, NUM_SECTORS, 1, the_file); |
180 |
|
|
}; |
181 |
|
|
|
182 |
|
|
// Read BAM and get ID |
183 |
|
|
read_sector(18, 0, bam); |
184 |
|
|
id1 = bam[162]; |
185 |
|
|
id2 = bam[163]; |
186 |
|
|
|
187 |
|
|
// Create GCR encoded disk data from image |
188 |
|
|
disk2gcr(); |
189 |
|
|
} |
190 |
|
|
} |
191 |
|
|
|
192 |
|
|
|
193 |
|
|
/* |
194 |
|
|
* Close .d64 file |
195 |
|
|
*/ |
196 |
|
|
|
197 |
|
|
void Job1541::close_d64_file(void) |
198 |
|
|
{ |
199 |
|
|
if (the_file != NULL) { |
200 |
|
|
fclose(the_file); |
201 |
|
|
the_file = NULL; |
202 |
|
|
} |
203 |
|
|
} |
204 |
|
|
|
205 |
|
|
|
206 |
|
|
/* |
207 |
|
|
* Write sector to disk (1541 ROM patch) |
208 |
|
|
*/ |
209 |
|
|
|
210 |
|
|
void Job1541::WriteSector(void) |
211 |
|
|
{ |
212 |
|
|
int track = ram[0x18]; |
213 |
|
|
int sector = ram[0x19]; |
214 |
|
|
uint16 buf = ram[0x30] | (ram[0x31] << 8); |
215 |
|
|
|
216 |
|
|
if (buf <= 0x0700) |
217 |
|
|
if (write_sector(track, sector, ram + buf)) |
218 |
|
|
sector2gcr(track, sector); |
219 |
|
|
} |
220 |
|
|
|
221 |
|
|
|
222 |
|
|
/* |
223 |
|
|
* Format one track (1541 ROM patch) |
224 |
|
|
*/ |
225 |
|
|
|
226 |
|
|
void Job1541::FormatTrack(void) |
227 |
|
|
{ |
228 |
|
|
int track = ram[0x51]; |
229 |
|
|
|
230 |
|
|
// Get new ID |
231 |
|
|
uint8 bufnum = ram[0x3d]; |
232 |
|
|
id1 = ram[0x12 + bufnum]; |
233 |
|
|
id2 = ram[0x13 + bufnum]; |
234 |
|
|
|
235 |
|
|
// Create empty block |
236 |
|
|
uint8 buf[256]; |
237 |
|
|
memset(buf, 1, 256); |
238 |
|
|
buf[0] = 0x4b; |
239 |
|
|
|
240 |
|
|
// Write block to all sectors on track |
241 |
|
|
for(int sector=0; sector<num_sectors[track]; sector++) { |
242 |
|
|
write_sector(track, sector, buf); |
243 |
|
|
sector2gcr(track, sector); |
244 |
|
|
} |
245 |
|
|
|
246 |
|
|
// Clear error info (all sectors no error) |
247 |
|
|
if (track == 35) |
248 |
|
|
memset(error_info, 1, NUM_SECTORS); |
249 |
|
|
// Write error_info to disk? |
250 |
|
|
} |
251 |
|
|
|
252 |
|
|
|
253 |
|
|
/* |
254 |
|
|
* Read sector (256 bytes) |
255 |
|
|
* true: success, false: error |
256 |
|
|
*/ |
257 |
|
|
|
258 |
|
|
bool Job1541::read_sector(int track, int sector, uint8 *buffer) |
259 |
|
|
{ |
260 |
|
|
int offset; |
261 |
|
|
|
262 |
|
|
// Convert track/sector to byte offset in file |
263 |
|
|
if ((offset = offset_from_ts(track, sector)) < 0) |
264 |
|
|
return false; |
265 |
|
|
|
266 |
|
|
#ifdef AMIGA |
267 |
|
|
if (offset != ftell(the_file)) |
268 |
|
|
fseek(the_file, offset + image_header, SEEK_SET); |
269 |
|
|
#else |
270 |
|
|
fseek(the_file, offset + image_header, SEEK_SET); |
271 |
|
|
#endif |
272 |
|
|
fread(buffer, 256, 1, the_file); |
273 |
|
|
return true; |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
|
277 |
|
|
/* |
278 |
|
|
* Write sector (256 bytes) !! -> GCR |
279 |
|
|
* true: success, false: error |
280 |
|
|
*/ |
281 |
|
|
|
282 |
|
|
bool Job1541::write_sector(int track, int sector, uint8 *buffer) |
283 |
|
|
{ |
284 |
|
|
int offset; |
285 |
|
|
|
286 |
|
|
// Convert track/sector to byte offset in file |
287 |
|
|
if ((offset = offset_from_ts(track, sector)) < 0) |
288 |
|
|
return false; |
289 |
|
|
|
290 |
|
|
#ifdef AMIGA |
291 |
|
|
if (offset != ftell(the_file)) |
292 |
|
|
fseek(the_file, offset + image_header, SEEK_SET); |
293 |
|
|
#else |
294 |
|
|
fseek(the_file, offset + image_header, SEEK_SET); |
295 |
|
|
#endif |
296 |
|
|
fwrite(buffer, 256, 1, the_file); |
297 |
|
|
return true; |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
|
301 |
|
|
/* |
302 |
|
|
* Convert track/sector to offset |
303 |
|
|
*/ |
304 |
|
|
|
305 |
|
|
int Job1541::secnum_from_ts(int track, int sector) |
306 |
|
|
{ |
307 |
|
|
return sector_offset[track] + sector; |
308 |
|
|
} |
309 |
|
|
|
310 |
|
|
int Job1541::offset_from_ts(int track, int sector) |
311 |
|
|
{ |
312 |
|
|
if ((track < 1) || (track > NUM_TRACKS) || |
313 |
|
|
(sector < 0) || (sector >= num_sectors[track])) |
314 |
|
|
return -1; |
315 |
|
|
|
316 |
|
|
return (sector_offset[track] + sector) << 8; |
317 |
|
|
} |
318 |
|
|
|
319 |
|
|
|
320 |
|
|
/* |
321 |
|
|
* Convert 4 bytes to 5 GCR encoded bytes |
322 |
|
|
*/ |
323 |
|
|
|
324 |
|
|
const uint16 gcr_table[16] = { |
325 |
|
|
0x0a, 0x0b, 0x12, 0x13, 0x0e, 0x0f, 0x16, 0x17, |
326 |
|
|
0x09, 0x19, 0x1a, 0x1b, 0x0d, 0x1d, 0x1e, 0x15 |
327 |
|
|
}; |
328 |
|
|
|
329 |
|
|
void Job1541::gcr_conv4(uint8 *from, uint8 *to) |
330 |
|
|
{ |
331 |
|
|
uint16 g; |
332 |
|
|
|
333 |
|
|
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; |
334 |
|
|
*to++ = g >> 2; |
335 |
|
|
*to = (g << 6) & 0xc0; |
336 |
|
|
from++; |
337 |
|
|
|
338 |
|
|
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; |
339 |
|
|
*to++ |= (g >> 4) & 0x3f; |
340 |
|
|
*to = (g << 4) & 0xf0; |
341 |
|
|
from++; |
342 |
|
|
|
343 |
|
|
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; |
344 |
|
|
*to++ |= (g >> 6) & 0x0f; |
345 |
|
|
*to = (g << 2) & 0xfc; |
346 |
|
|
from++; |
347 |
|
|
|
348 |
|
|
g = (gcr_table[*from >> 4] << 5) | gcr_table[*from & 15]; |
349 |
|
|
*to++ |= (g >> 8) & 0x03; |
350 |
|
|
*to = g; |
351 |
|
|
} |
352 |
|
|
|
353 |
|
|
|
354 |
|
|
/* |
355 |
|
|
* Create GCR encoded disk data from image |
356 |
|
|
*/ |
357 |
|
|
|
358 |
|
|
void Job1541::sector2gcr(int track, int sector) |
359 |
|
|
{ |
360 |
|
|
uint8 block[256]; |
361 |
|
|
uint8 buf[4]; |
362 |
|
|
uint8 *p = gcr_data + (track-1) * GCR_TRACK_SIZE + sector * GCR_SECTOR_SIZE; |
363 |
|
|
|
364 |
|
|
read_sector(track, sector, block); |
365 |
|
|
|
366 |
|
|
// Create GCR header |
367 |
|
|
*p++ = 0xff; // SYNC |
368 |
|
|
buf[0] = 0x08; // Header mark |
369 |
|
|
buf[1] = sector ^ track ^ id2 ^ id1; // Checksum |
370 |
|
|
buf[2] = sector; |
371 |
|
|
buf[3] = track; |
372 |
|
|
gcr_conv4(buf, p); |
373 |
|
|
buf[0] = id2; |
374 |
|
|
buf[1] = id1; |
375 |
|
|
buf[2] = 0x0f; |
376 |
|
|
buf[3] = 0x0f; |
377 |
|
|
gcr_conv4(buf, p+5); |
378 |
|
|
p += 10; |
379 |
|
|
memset(p, 0x55, 9); // Gap |
380 |
|
|
p += 9; |
381 |
|
|
|
382 |
|
|
// Create GCR data |
383 |
|
|
uint8 sum; |
384 |
|
|
*p++ = 0xff; // SYNC |
385 |
|
|
buf[0] = 0x07; // Data mark |
386 |
|
|
sum = buf[1] = block[0]; |
387 |
|
|
sum ^= buf[2] = block[1]; |
388 |
|
|
sum ^= buf[3] = block[2]; |
389 |
|
|
gcr_conv4(buf, p); |
390 |
|
|
p += 5; |
391 |
|
|
for (int i=3; i<255; i+=4) { |
392 |
|
|
sum ^= buf[0] = block[i]; |
393 |
|
|
sum ^= buf[1] = block[i+1]; |
394 |
|
|
sum ^= buf[2] = block[i+2]; |
395 |
|
|
sum ^= buf[3] = block[i+3]; |
396 |
|
|
gcr_conv4(buf, p); |
397 |
|
|
p += 5; |
398 |
|
|
} |
399 |
|
|
sum ^= buf[0] = block[255]; |
400 |
|
|
buf[1] = sum; // Checksum |
401 |
|
|
buf[2] = 0; |
402 |
|
|
buf[3] = 0; |
403 |
|
|
gcr_conv4(buf, p); |
404 |
|
|
p += 5; |
405 |
|
|
memset(p, 0x55, 8); // Gap |
406 |
|
|
} |
407 |
|
|
|
408 |
|
|
void Job1541::disk2gcr(void) |
409 |
|
|
{ |
410 |
|
|
// Convert all tracks and sectors |
411 |
|
|
for (int track=1; track<=NUM_TRACKS; track++) |
412 |
|
|
for(int sector=0; sector<num_sectors[track]; sector++) |
413 |
|
|
sector2gcr(track, sector); |
414 |
|
|
} |
415 |
|
|
|
416 |
|
|
|
417 |
|
|
/* |
418 |
|
|
* Move R/W head out (lower track numbers) |
419 |
|
|
*/ |
420 |
|
|
|
421 |
|
|
void Job1541::MoveHeadOut(void) |
422 |
|
|
{ |
423 |
|
|
if (current_halftrack == 2) |
424 |
|
|
return; |
425 |
|
|
current_halftrack--; |
426 |
|
|
#ifndef __riscos__ |
427 |
|
|
printf("Head move %d\n", current_halftrack); |
428 |
|
|
#endif |
429 |
|
|
gcr_ptr = gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE; |
430 |
|
|
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE; |
431 |
|
|
} |
432 |
|
|
|
433 |
|
|
|
434 |
|
|
/* |
435 |
|
|
* Move R/W head in (higher track numbers) |
436 |
|
|
*/ |
437 |
|
|
|
438 |
|
|
void Job1541::MoveHeadIn(void) |
439 |
|
|
{ |
440 |
|
|
if (current_halftrack == NUM_TRACKS*2) |
441 |
|
|
return; |
442 |
|
|
current_halftrack++; |
443 |
|
|
#ifndef __riscos__ |
444 |
|
|
printf("Head move %d\n", current_halftrack); |
445 |
|
|
#endif |
446 |
|
|
gcr_ptr = gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE; |
447 |
|
|
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE; |
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
|
451 |
|
|
/* |
452 |
|
|
* Get state |
453 |
|
|
*/ |
454 |
|
|
|
455 |
|
|
void Job1541::GetState(Job1541State *state) |
456 |
|
|
{ |
457 |
|
|
state->current_halftrack = current_halftrack; |
458 |
|
|
state->gcr_ptr = gcr_ptr - gcr_data; |
459 |
|
|
state->write_protected = write_protected; |
460 |
|
|
state->disk_changed = disk_changed; |
461 |
|
|
} |
462 |
|
|
|
463 |
|
|
|
464 |
|
|
/* |
465 |
|
|
* Set state |
466 |
|
|
*/ |
467 |
|
|
|
468 |
|
|
void Job1541::SetState(Job1541State *state) |
469 |
|
|
{ |
470 |
|
|
current_halftrack = state->current_halftrack; |
471 |
|
|
gcr_ptr = gcr_data + state->gcr_ptr; |
472 |
|
|
gcr_track_start = gcr_data + ((current_halftrack >> 1) - 1) * GCR_TRACK_SIZE; |
473 |
|
|
gcr_track_end = gcr_track_start + num_sectors[current_halftrack >> 1] * GCR_SECTOR_SIZE; |
474 |
|
|
write_protected = state->write_protected; |
475 |
|
|
disk_changed = state->disk_changed; |
476 |
|
|
} |