ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/1541job.cpp
Revision: 1.4
Committed: 2005-06-27T19:55:48Z (19 years, 5 months ago) by cebix
Branch: MAIN
CVS Tags: VERSION_4_2, HEAD
Changes since 1.3: +1 -1 lines
Log Message:
updated copyright dates

File Contents

# User Rev Content
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     }