Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
f011_core.c
Go to the documentation of this file.
1 /* F011 FDC (used by Commodore 65 and MEGA65) emulation.
2  Part of the Xemu project, please visit: https://github.com/lgblgblgb/xemu
3  Copyright (C)2016-2022 LGB (Gábor Lénárt) <lgblgblgb@gmail.com>
4 
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18 
19 /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
20  !! FDC F011 emulation is still a big mess, with bugs, and unplemented features. !!
21  !! It gives you only read only access currently, and important features like SWAP !!
22  !! bit is not handled at all. The first goal is to be usable with "DIR" and "LOAD" !!
23  !! commands on the C65, nothing too much more !!
24  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
25 
26 
27 #include "xemu/emutools.h"
28 #include "xemu/f011_core.h"
29 #include "xemu/cpu65.h"
30 
31 // #define DEBUG_FOR_PAUL
32 // #define SOME_DEBUG
33 
34 // Do NOT change these ever (unless you're very sure what you're doing):
35 #define FDC_PRIVATE_HEAD_SIDE
36 #define FDC_PRIVATE_HEAD_TRACK
37 #define FDC_PRIVATE_STATUS_A
38 #define FDC_PRIVATE_STATUS_B
39 
40 #ifndef FDC_PRIVATE_HEAD_TRACK
41 static Uint8 head_track_storage; // "physical" track, ie, what is the head is positioned at currently
42 #endif
43 #ifndef FDC_PRIVATE_HEAD_SIDE
44 static int head_side_storage;
45 #endif
46 #ifndef FDC_PRIVATE_STATUS_A
47 static Uint8 status_a_storage;
48 #endif
49 #ifndef FDC_PRIVATE_STATUS_B
50 static Uint8 status_b_storage;
51 #endif
52 static Uint8 track, sector, side; // parameters given for an operation ("find"), if track is not the same as head_track, you won't find your sector on that track probably ...
53 static Uint8 control;
54 static Uint8 cmd;
55 static int curcmd;
56 static Uint8 dskclock, step;
57 static int emulate_busy;
58 static int drive;
59 static Uint8 *cache; // 512 bytes cache FDC will use. This is a real 512byte RAM attached to the FDC controller for buffered operations on the C65
60 static int cache_p_cpu; // cache pointer if CPU accesses the FDC buffer cache. 0 ... 511!
61 static int cache_p_fdc; // cache pointer if FDC accesses the FDC buffer cache. 0 ... 511!
62 static int swap_mask = 0;
63 static int warn_disk = 1;
64 static int warn_swap_bit = 1;
65 static int allowed_disk = FDC_ALLOW_DISK_ACCESS; // provides a way to TEMPORARLY reject disk access (eg: avoid autoboot)
66 static struct {
68 #ifdef FDC_PRIVATE_STATUS_A
70 #endif
71 #ifdef FDC_PRIVATE_STATUS_B
73 #endif
74 #ifdef FDC_PRIVATE_HEAD_TRACK
76 #endif
77 #ifdef FDC_PRIVATE_HEAD_SIDE
79 #endif
80 } drives[8];
81 
82 #ifdef FDC_PRIVATE_STATUS_A
83 # define DRV_STATUS_A(drv_no) drives[drv_no].status_a_storage
84 #else
85 # define DRV_STATUS_A(drv_no) status_a_storage
86 #endif
87 
88 #ifdef FDC_PRIVATE_STATUS_B
89 # define DRV_STATUS_B(drv_no) drives[drv_no].status_b_storage
90 #else
91 # define DRV_STATUS_B(drv_no) status_b_storage
92 #endif
93 
94 #ifdef FDC_PRIVATE_HEAD_SIDE
95 # define DRV_HEAD_SIDE(drv_no) drives[drv_no].head_side_storage
96 #else
97 # define DRV_HEAD_SIDE(drv_no) head_side_storage
98 #endif
99 
100 #ifdef FDC_PRIVATE_HEAD_TRACK
101 # define DRV_HEAD_TRACK(drv_no) drives[drv_no].head_track_storage
102 #else
103 # define DRV_HEAD_TRACK(drv_no) head_track_storage
104 #endif
105 
106 
107 static void execute_command ( void );
108 
109 
110 void fdc_init ( Uint8 *cache_set )
111 {
112  DEBUG("FDC: init F011 emulation core" NL);
113  cache = cache_set;
114  control = 0;
115  track = 0;
116  sector = 0;
117  side = 0;
118  cmd = 0;
119  curcmd = -1;
120  dskclock = 0xFF;
121  step = 0xFF;
122  cache_p_cpu = 0;
123  cache_p_fdc = 0;
124  drive = 0;
125  for (int i = 0; i < 8; i++) {
126  DRV_HEAD_SIDE(i) = 0;
127  DRV_HEAD_TRACK(i) = 0;
128  DRV_STATUS_A(i) = 0;
129  DRV_STATUS_B(i) = 0;
130  fdc_set_disk(i, 0, 0);
131  DRV_STATUS_B(i) &= 0x7F; // at this point we don't want disk changed signal (bit 7) yet
132  }
133  allowed_disk = FDC_ALLOW_DISK_ACCESS;
134 }
135 
136 
138 {
139  return cache_p_fdc;
140 }
141 
142 
144 {
145  return cache_p_cpu;
146 }
147 
148 
149 int fdc_get_status_a ( const int which )
150 {
151  return DRV_STATUS_A(which >= 0 ? which : drive);
152 }
153 
154 
155 int fdc_get_status_b ( const int which )
156 {
157  return DRV_STATUS_B(which >= 0 ? which : drive);
158 }
159 
160 
161 int fdc_get_led_state ( int blink_inc )
162 {
163  static unsigned int blink_counter = 0;
164  int f011_motor = control & 32;
165  int f011_led = control & 64;
166  blink_counter += blink_inc;
167  return (!f011_led && f011_motor) || (f011_led && (blink_counter & 0x100));
168 }
169 
170 
171 void fdc_allow_disk_access ( int in )
172 {
173  allowed_disk = in;
174 }
175 
176 
177 void fdc_set_disk ( int which, int in_have_disk, int in_have_write )
178 {
179  drives[which].have_disk = in_have_disk;
180  drives[which].have_write = in_have_write;
181  DEBUG("FDC: init: set have_disk=%d, have_write=%d on drive %d" NL, in_have_disk, in_have_write, which);
182  DRV_STATUS_B(which) |= 0x01; // disk changed signal is set, since the purpose of this function is to set new disk
183  if (in_have_disk) {
184  DRV_STATUS_A(which) |= 1; // on track-0
185  DRV_STATUS_B(which) |= 8; // disk inserted
186  } else {
187  DRV_STATUS_A(which) &= ~1;
188  DRV_STATUS_B(which) &= ~8;
189  }
190  if (!in_have_write) {
191  DRV_STATUS_A(which) |= 2; // write protect flag, read-only mode
192  } else {
193  DRV_STATUS_A(which) &= ~2;
194  }
195  //allowed_disk = FDC_ALLOW_DISK_ACCESS; // FIXME: maybe should be deleted, as causes to revokation of access to be dismissed on calling this function!
196 }
197 
198 
199 /* Note: this is really not so nice, but faster and easier to emulate ;)
200  That is, we read 512 bytes at once. Not byte-by-byte and allow program
201  to see this with DRQ changing, pointer maintaince on each byte read/written, etc ... */
202 static void read_sector ( void )
203 {
204  int error = 0;
205  if (drives[drive].have_disk && allowed_disk == FDC_ALLOW_DISK_ACCESS) {
206  DEBUG("FDC: reading sector drive=%d track=%d sector=%d side=%d @ PC=$%04X" NL, drive, track, sector, side, cpu65.old_pc);
207  Uint8 read_buffer[512];
208  error = fdc_cb_rd_sec(drive, read_buffer, side, track, sector);
209  if (error)
210  DEBUG("FDC: sector read-callback returned with error!" NL);
211  else {
212  int n;
213  DEBUG("FDC: sector has been read." NL);
214  for (n = 0; n < 512; n++) {
215  cache[cache_p_fdc] = read_buffer[n];
216  cache_p_fdc = (cache_p_fdc + 1) & 511;
217  }
218  }
219  } else {
220  error = 1;
221  DEBUG("FDC: no disk in drive" NL);
222  if (warn_disk && allowed_disk == FDC_ALLOW_DISK_ACCESS) {
223  INFO_WINDOW("No disk image was given or can be loaded!");
224  warn_disk = 0;
225  }
226  }
227  if (error) {
228  DRV_STATUS_A(drive) |= 16; // record not found ...
229  DRV_STATUS_B(drive) &= 15; // RDREQ/WTREQ/RUN/GATE off
230  } else {
231  DRV_STATUS_A(drive) |= 64; // DRQ, for buffered reads indicates that FDC accessed the buffer last (DRQ should be cleared by CPU reads, set by FDC access)
232  DRV_STATUS_B(drive) |= 128 | 32 ; // RDREQ, RUN to set! (important: ROM waits for RDREQ to be high after issued read operation, also we must clear it SOME time later ...)
233  DRV_STATUS_A(drive) &= ~32; // clear EQ, missing this in general freezes DOS as it usually does not expect to have EQ set even before the first data read [??]
234  }
235  if (allowed_disk == FDC_DENY_DISK_ACCESS_ONCE)
236  allowed_disk = FDC_ALLOW_DISK_ACCESS;
237 }
238 
239 
240 static void write_sector ( void )
241 {
242  int error = 0;
243  if (drives[drive].have_disk && allowed_disk == FDC_ALLOW_DISK_ACCESS && drives[drive].have_write) {
244  DEBUG("FDC: writing sector drive=%d track=%d sector=%d side=%d @ PC=$%04X" NL, drive, track, sector, side, cpu65.old_pc);
245  Uint8 write_buffer[512];
246  int n;
247  for (n = 0; n < 512; n++) {
248  write_buffer[n] = cache[cache_p_fdc];
249  cache_p_fdc = (cache_p_fdc + 1) & 511;
250  }
251  error = fdc_cb_wr_sec(drive, write_buffer, side, track, sector);
252  if (error)
253  DEBUG("FDC: sector write-callback returned with error!" NL);
254  else
255  DEBUG("FDC: sector has been written." NL);
256  } else {
257  error = 1;
258  DEBUG("FDC: no disk in drive or write protected" NL);
259  if (warn_disk && allowed_disk == FDC_ALLOW_DISK_ACCESS) {
260  INFO_WINDOW("No disk image was given or can be loaded or write protected disk!");
261  warn_disk = 0;
262  }
263  }
264  if (error) {
265  DRV_STATUS_A(drive) |= 16; // record not found ...
266  DRV_STATUS_B(drive) &= 15; // RDREQ/WTREQ/RUN/GATE off
267  } else {
268  DRV_STATUS_A(drive) |= 64; // DRQ, for buffered reads indicates that FDC accessed the buffer last (DRQ should be cleared by CPU reads, set by FDC access)
269  DRV_STATUS_B(drive) |= 64 | 32 ; // WTREQ, RUN to set!
270  DRV_STATUS_A(drive) &= ~32; // clear EQ, missing this in general freezes DOS as it usually does not expect to have EQ set even before the first data read [??]
271  }
272  if (allowed_disk == FDC_DENY_DISK_ACCESS_ONCE)
273  allowed_disk = FDC_ALLOW_DISK_ACCESS;
274 }
275 
276 
278 {
279  DEBUG("FDC: writing register %d with data $%02X" NL, addr, data);
280 #ifdef DEBUG_FOR_PAUL
281  printf("PAUL: FDC register %d will be written now, data is $%02X buffer pointer is %d now" NL, addr, data, cache_p_cpu);
282 #endif
283  switch (addr) {
284  case 0:
285 #if 0
286  if (status_a & 128) {
287  DEBUG("FDC: WARN: trying to write control register ($%02X) while FDC is busy." NL, data);
288  return;
289  }
290 #endif
291  control = data;
292  if (curcmd == -1)
293  curcmd = 0x100; // "virtual" command, by writing the control register
294  if (drive != (data & 7)) { // active drive selection has been changed?
295  drive = data & 7;
296  DEBUG("FDC: disk change signal was cleared on drive selection (drive: %d)" NL, drive);
297  DRV_STATUS_B(drive) &= ~0x01; // clearing disk change signal (not correct implementation, as it needs only if the given drive deselected!)
298  }
299  DRV_STATUS_A(drive) |= 128; // writing control register also causes to set the BUSY flag for some time ... XXX FIXME: should it be done BEFORE drive selection??
300  DRV_HEAD_SIDE(drive) = (data >> 3) & 1;
301 #if 0
302  if (drive)
303  DEBUG("FDC: WARN: not drive-0 is selected: %d!" NL, drive);
304  else
305  DEBUG("FDC: great, drive-0 is selected" NL);
306 #endif
307  if (data & 16) {
308  DEBUG("FDC: WARN: SWAP bit emulation is experimental!" NL);
309  if (warn_swap_bit) {
310  DEBUGPRINT("FDC: warning: SWAP bit emulation is experimental! There will be no further warnings on this." NL);
311  warn_swap_bit = 0;
312  }
313  swap_mask = 0x100;
314  } else
315  swap_mask = 0;
316  break;
317  case 1:
318  // FIXME: I still don't what happens if a running operation (ie BUSY) is in progress and new command is tried to be given to the F011 FDC
319  DEBUG("FDC: command=$%02X (lower bits: $%X)" NL, data & 0xF8, data & 7);
320  if ((DRV_STATUS_A(drive) & 128) && ((data & 0xF8))) { // if BUSY, and command is not the cancel command ..
321  DEBUG("FDC: WARN: trying to issue another command ($%02X) while the previous ($%02X) is running." NL, data, cmd);
322  return;
323  }
324  cmd = data;
325  curcmd = data;
326  DRV_STATUS_A(drive) |= 128; // simulate busy status ...
327  DRV_STATUS_B(drive) &= 255 - 2; // turn IRQ flag OFF
328  DRV_STATUS_A(drive) &= 255 - (4 + 8 + 16); // turn RNF/CRC/LOST flags OFF
329  DRV_STATUS_B(drive) &= 255 - (128 + 64); // also turn RDREQ and WRREQ OFF [IS THIS REALLY NEEDED?]
330  break;
331  case 4:
332  track = data;
333  break;
334  case 5:
335  sector = data;
336  break;
337  case 6:
338  side = data;
339  break;
340  case 7:
341  // FIXME: this algorithm do not "enforce" the internals of F011, just if the software comply the rules otherwise ......
342  DRV_STATUS_A(drive) &= ~64; // clear DRQ
343  //status_b &= 127; // turn RDREQ off after the first access, this is somewhat incorrect :-P
344  if (DRV_STATUS_A(drive) & 32) // if EQ was already set and another byte passed ---> LOST
345  DRV_STATUS_A(drive) |= 4; // LOST!!!! but probably incorrect, since no new read is done by FDC since then just "wrapping" the read data, LOST remains till next command!
346  cache[cache_p_cpu ^ swap_mask] = data;
347  cache_p_cpu = (cache_p_cpu + 1) & 511;
348  // always check EQ-situation _after_ the operation! Since initially they are same and it won't be loved by C65 DOS at all, honoured with a freeze
349  if (cache_p_cpu == cache_p_fdc)
350  DRV_STATUS_A(drive) |= 32; // turn EQ on
351  else
352  DRV_STATUS_A(drive) &= ~32; // turn EQ off
353  break;
354  // TODO: write DATA register (7) [only for writing it is needed anyway]
355  case 8:
356  dskclock = data;
357  break;
358  case 9:
359  step = data;
360  break;
361  }
362  if (DRV_STATUS_A(drive) & 128) {
363  emulate_busy = 10;
364  execute_command(); // do it NOW!!!! With this setting now: there is no even BUSY state, everything happens within one OPC... seems to work still. Real F011 won't do this surely
365  }
366 #ifdef DEBUG_FOR_PAUL
367  printf("PAUL: FDC register %d has been written, data was $%02X buffer pointer is %d now" NL, addr, data, cache_p_cpu);
368 #endif
369 }
370 
371 
372 static void execute_command ( void )
373 {
374 #ifdef DEBUG_FOR_PAUL
375  printf("PAUL: issuing FDC command $%02X pointer was %d" NL, cmd, cache_p_cpu);
376 #endif
377  DRV_STATUS_A(drive) &= 127; // turn BUSY flag OFF
378  DRV_STATUS_B(drive) |= 2; // turn IRQ flag ON
379  if (control & 128)
380  INFO_WINDOW("Sorry, FDC-IRQ is not supported yet, by FDC emulation!");
381  if (curcmd < 0)
382  return; // no cmd was given?!
383  if (curcmd > 0xFF)
384  return; // only control register was written, not the command register
385 #ifdef SOME_DEBUG
386  printf("Command: $%02X" NL, cmd);
387 #endif
388  switch (cmd & 0xF8) { // high 5 bits of the command ...
389  case 0x40: // read sector
390  //status_a |= 16; // record not found for testing ...
391  DRV_STATUS_B(drive) |= 128; // RDREQ: if it's not here, you won't get a READY. prompt!
392  //status_b |= 32; // RUN?!
393  DRV_STATUS_A(drive) |= 64; // set DRQ
394  DRV_STATUS_A(drive) &= (255 - 32); // clear EQ
395  //status_a |= 32; // set EQ?!
396  //cache_p_cpu = cache_p_fdc; // yayy .... If it's not here we can't get READY. prompt!!
397  read_sector();
398  //cache_p_drive = (cache_p_drive + BLOCK_SIZE) & 511;
400 #ifdef SOME_DEBUG
401  printf("READ: cache_p_cpu=%d / cache_p_fdc=%d drive_selected=%d" NL, cache_p_cpu, cache_p_fdc, drive);
402 #endif
403  DEBUG("FDC: READ: head_track=%d need_track=%d head_side=%d need_side=%d need_sector=%d drive_selected=%d" NL,
404  DRV_HEAD_TRACK(drive), track, DRV_HEAD_SIDE(drive), side, sector, drive
405  );
406  break;
407  case 0x80: // write sector
408  if (!(DRV_STATUS_A(drive) & 2)) { // if not write protected ....
409  DRV_STATUS_A(drive) |= 64; // set DRQ
410  DRV_STATUS_A(drive) &= (255 - 32); // clear EQ
411  write_sector();
412 #ifdef SOME_DEBUG
413  printf("WRITE: cache_p_cpu=%d / cache_p_fdc=%d drive_selected=%d" NL, cache_p_cpu, cache_p_fdc, drive);
414 #endif
415  DEBUG("FDC: WRITE: head_track=%d need_track=%d head_side=%d need_side=%d need_sector=%d drive_selected=%d" NL,
416  DRV_HEAD_TRACK(drive), track, DRV_HEAD_SIDE(drive), side, sector, drive
417  );
418  } else {
419 #ifdef SOME_DEBUG
420  printf("Sorry, write protected stuff ..." NL);
421 #endif
422  }
423  break;
424  case 0x10: // head step out or no step
425  if (!(cmd & 4)) { // if only not TIME operation, which does not step!
426  if (DRV_HEAD_TRACK(drive))
427  DRV_HEAD_TRACK(drive)--;
428  if (!DRV_HEAD_TRACK(drive))
429  DRV_STATUS_A(drive) |= 1; // track 0 flag
430  DEBUG("FDC: head position = %d" NL, DRV_HEAD_TRACK(drive));
431  }
432  break;
433  case 0x18: // head step in
434  if (DRV_HEAD_TRACK(drive) < 128)
435  DRV_HEAD_TRACK(drive)++;
436  DEBUG("FDC: head position = %d" NL, DRV_HEAD_TRACK(drive));
437  DRV_STATUS_A(drive) &= 0xFE; // track 0 flag off
438  break;
439  case 0x20: // motor spin up
440  control |= 32;
441  DRV_STATUS_A(drive) |= 16; // according to the specification, RNF bit should be set at the end of the operation
442  break;
443  case 0x00: // cancel running command?? NOTE: also if low bit is 1: clear pointer!
444  // Note: there was a typo in my previous versions ... to have break HERE! So pointer reset never executed actually ...... :-@
445  // It seems C65 DOS uses this, without this, the pointer maintaince is bad, and soon it will freeze on EQ check somewhere!
446  // That's why I had to introduce some odd workarounds to reset pointers etc manually, which is NOT what F011 would do, I guess!
447  if (cmd & 1) {
448 #ifdef SOME_DEBUG
449  printf("BEFORE pointer reset: cache_p_cpu=%d cache_p_fdc=%d" NL, cache_p_cpu, cache_p_fdc);
450 #endif
451  cache_p_cpu = 0;
452  cache_p_fdc = 0;
453  DEBUG("FDC: WARN: resetting cache pointers" NL);
454  //status_a |= 32; // turn EQ on
455  DRV_STATUS_A(drive) &= 255 - 64; // turn DRQ off
456  DRV_STATUS_B(drive) &= 127; // turn RDREQ off
457 
458  }
459  break;
460  default:
461  DEBUG("FDC: WARN: unknown comand: $%02X" NL, cmd);
462  //status_a &= 127; // well, not a valid command, revoke busy status ...
463  break;
464  }
465  curcmd = -1;
466 #ifdef DEBUG_FOR_PAUL
467  printf("PAUL: issued FDC command $%02X pointer is %d" NL, cmd, cache_p_cpu);
468 #endif
469 }
470 
471 
473 {
474  Uint8 result;
475 #ifdef DEBUG_FOR_PAUL
476  printf("PAUL: FDC register %d will be read, buffer pointer is %d now" NL, addr, cache_p_cpu);
477 #endif
478  /* Emulate BUSY timing, with a very bad manner: ie, decrement a counter on each register read to give some time to wait.
479  FIXME: not sure if it's needed and what happen if C65 DOS gots "instant" operations done without BUSY ever set ... Not a real happening, but it can be with my primitive emulation :)
480  Won't work if DOS is IRQ driven ... */
481  if (DRV_STATUS_A(drive) & 128) { // check the BUSY flag
482  // Note: this is may not used at all, check the end of write reg func!
483  if (emulate_busy > 0)
484  emulate_busy--;
485  if (emulate_busy <= 0) {
486 #ifdef SOME_DEBUG
487  printf("Delayed command execution!!!" NL);
488 #endif
489  execute_command(); // execute the command only now for real ... (it will also turn BUSY flag - bit 7 - OFF in status_a)
490  }
491  }
492  switch (addr) {
493  case 0:
494  result = control;
495  break;
496  case 1:
497  result = cmd;
498  break;
499  case 2: // STATUS register A
500  result = DRV_STATUS_A(drive);
501  break;
502  case 3: // STATUS register B
503  result = DRV_STATUS_B(drive);
504  DRV_STATUS_B(drive) &= ~64; // turn WTREQ off, as it seems CPU noticed with reading this register, that is was the case for a while. Somewhat incorrect implementation ... :-/
505  break;
506  case 4:
507  result = track;
508  break;
509  case 5:
510  result = sector;
511  break;
512  case 6:
513  result = side;
514  break;
515  case 7:
516  // FIXME: this algorithm do not "enforce" the internals of F011, just if the software comply the rules otherwise ......
517  DRV_STATUS_A(drive) &= ~64; // clear DRQ
518  DRV_STATUS_B(drive) &= 127; // turn RDREQ off after the first access, this is somewhat incorrect :-P
519  if (DRV_STATUS_A(drive) & 32) // if EQ was already set and another byte passed ---> LOST
520  DRV_STATUS_A(drive) |= 4; // LOST!!!! but probably incorrect, since no new read is done by FDC since then just "wrapping" the read data, LOST remains till next command!
521  result = cache[cache_p_cpu ^ swap_mask];
522  cache_p_cpu = (cache_p_cpu + 1) & 511;
523  // always check EQ-situation _after_ the operation! Since initially they are same and it won't be loved by C65 DOS at all, honoured with a freeze
524  if (cache_p_cpu == cache_p_fdc)
525  DRV_STATUS_A(drive) |= 32; // turn EQ on
526  else
527  DRV_STATUS_A(drive) &= ~32; // turn EQ off
528  break;
529  case 8:
530  result = dskclock;
531  break;
532  case 9:
533  result = step;
534  break;
535  case 10:
536  result = 0; // No "protection code" ...
537  break;
538  default:
539  result = 0xFF;
540  break;
541  }
542  DEBUG("FDC: reading register %d result is $%02X" NL, addr, result);
543 #ifdef DEBUG_FOR_PAUL
544  printf("PAUL: FDC register %d has been read, result was $%02X buffer pointer is %d now" NL, addr, result, cache_p_cpu);
545 #endif
546  return result;
547 }
548 
549 
550 /* --- SNAPSHOT RELATED --- */
551 
552 
553 #ifdef XEMU_SNAPSHOT_SUPPORT
554 
555 #include <string.h>
556 
557 #define SNAPSHOT_FDC_BLOCK_VERSION 0
558 
559 #ifdef MEGA65
560 #define SNAPSHOT_FDC_BLOCK_SIZE 0x100
561 #else
562 #define CACHE_SIZE 512
563 #define SNAPSHOT_FDC_BLOCK_SIZE (0x100 + CACHE_SIZE)
564 #endif
565 
566 int fdc_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block )
567 {
568  Uint8 buffer[SNAPSHOT_FDC_BLOCK_SIZE];
569  int a;
570  if (block->block_version != SNAPSHOT_FDC_BLOCK_VERSION || block->sub_counter || block->sub_size != sizeof buffer)
571  RETURN_XSNAPERR_USER("Bad FDC-F011 block syntax");
572  a = xemusnap_read_file(buffer, sizeof buffer);
573  if (a) return a;
574  /* loading state ... */
575  curcmd = P_AS_BE32(buffer + 4);
576  emulate_busy = P_AS_BE32(buffer + 8);
577  drive = P_AS_BE32(buffer + 12);
578  DRV_HEAD_SIDE(drive) = P_AS_BE32(buffer + 0);
579  cache_p_cpu = P_AS_BE32(buffer + 16);
580  cache_p_fdc = P_AS_BE32(buffer + 20);
581  swap_mask = P_AS_BE32(buffer + 24);
582  drives[drive].have_disk = P_AS_BE32(buffer + 28);
583  drives[drive].have_write = P_AS_BE32(buffer + 32);
584  DRV_HEAD_TRACK(drive) = buffer[128];
585  track = buffer[129];
586  sector = buffer[130];
587  side = buffer[131];
588  control = buffer[132];
589  DRV_STATUS_A(drive) = buffer[133];
590  DRV_STATUS_B(drive) = buffer[134];
591  cmd = buffer[135];
592  dskclock = buffer[136];
593  step = buffer[137];
594 #ifndef MEGA65
595  memcpy(cache, buffer + 0x100, CACHE_SIZE);
596 #endif
597  return 0;
598 }
599 
600 
601 int fdc_snapshot_save_state ( const struct xemu_snapshot_definition_st *def )
602 {
603  Uint8 buffer[SNAPSHOT_FDC_BLOCK_SIZE];
604  int a = xemusnap_write_block_header(def->idstr, SNAPSHOT_FDC_BLOCK_VERSION);
605  if (a) return a;
606  memset(buffer, 0xFF, sizeof buffer);
607  /* saving state ... */
608  U32_AS_BE(buffer + 0, DRV_HEAD_SIDE(drive));
609  U32_AS_BE(buffer + 4, curcmd);
610  U32_AS_BE(buffer + 8, emulate_busy);
611  U32_AS_BE(buffer + 12, drive);
612  U32_AS_BE(buffer + 16, cache_p_cpu);
613  U32_AS_BE(buffer + 20, cache_p_fdc);
614  U32_AS_BE(buffer + 24, swap_mask);
615  U32_AS_BE(buffer + 28, drives[drive].have_disk);
616  U32_AS_BE(buffer + 32, drives[drive].have_write);
617  buffer[128] = DRV_HEAD_TRACK(drive);
618  buffer[129] = track;
619  buffer[130] = sector;
620  buffer[131] = side;
621  buffer[132] = control;
622  buffer[133] = DRV_STATUS_A(drive);
623  buffer[134] = DRV_STATUS_B(drive);
624  buffer[135] = cmd;
625  buffer[136] = dskclock;
626  buffer[137] = step;
627 #ifndef MEGA65
628  memcpy(buffer + 0x100, cache, CACHE_SIZE);
629 #endif
630  return xemusnap_write_sub_block(buffer, sizeof buffer);
631 }
632 
633 #endif
DRV_HEAD_SIDE
#define DRV_HEAD_SIDE(drv_no)
Definition: f011_core.c:95
emutools.h
fdc_read_reg
Uint8 fdc_read_reg(int addr)
Definition: f011_core.c:472
head_track_storage
Uint8 head_track_storage
Definition: f011_core.c:75
fdc_set_disk
void fdc_set_disk(int which, int in_have_disk, int in_have_write)
Definition: f011_core.c:177
fdc_get_buffer_disk_address
int fdc_get_buffer_disk_address(void)
Definition: f011_core.c:137
fdc_allow_disk_access
void fdc_allow_disk_access(int in)
Definition: f011_core.c:171
f011_core.h
fdc_cb_wr_sec
int fdc_cb_wr_sec(const int which, Uint8 *buffer, const Uint8 side, const Uint8 track, const Uint8 sector)
Definition: commodore_65.c:296
have_disk
int have_disk
Definition: f011_core.c:67
status_b_storage
Uint8 status_b_storage
Definition: f011_core.c:72
INFO_WINDOW
#define INFO_WINDOW(...)
Definition: xep128.h:114
addr
int addr
Definition: dma65.c:81
m65-memcontent-generator.data
data
Definition: m65-memcontent-generator.py:119
FDC_ALLOW_DISK_ACCESS
#define FDC_ALLOW_DISK_ACCESS
Definition: f011_core.h:23
DRV_STATUS_A
#define DRV_STATUS_A(drv_no)
Definition: f011_core.c:83
Uint8
uint8_t Uint8
Definition: fat32.c:51
head_side_storage
int head_side_storage
Definition: f011_core.c:78
block
Uint32 block
Definition: fat32.c:156
drive
char * drive
Definition: commodore_geos.c:151
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
fdc_get_buffer_cpu_address
int fdc_get_buffer_cpu_address(void)
Definition: f011_core.c:143
DRV_HEAD_TRACK
#define DRV_HEAD_TRACK(drv_no)
Definition: f011_core.c:101
fdc_get_led_state
int fdc_get_led_state(int blink_inc)
Definition: f011_core.c:161
have_write
int have_write
Definition: f011_core.c:67
NL
#define NL
Definition: fat32.c:37
fdc_write_reg
void fdc_write_reg(int addr, Uint8 data)
Definition: f011_core.c:277
fdc_cb_rd_sec
int fdc_cb_rd_sec(const int which, Uint8 *buffer, const Uint8 side, const Uint8 track, const Uint8 sector)
Definition: commodore_65.c:290
cpu65.h
fdc_init
void fdc_init(Uint8 *cache_set)
Definition: f011_core.c:110
step
int step
Definition: dma65.c:84
status_a_storage
Uint8 status_a_storage
Definition: f011_core.c:69
fdc_get_status_b
int fdc_get_status_b(const int which)
Definition: f011_core.c:155
FDC_DENY_DISK_ACCESS_ONCE
#define FDC_DENY_DISK_ACCESS_ONCE
Definition: f011_core.h:24
fdc_get_status_a
int fdc_get_status_a(const int which)
Definition: f011_core.c:149
DEBUG
#define DEBUG(...)
Definition: emutools_basicdefs.h:167
DRV_STATUS_B
#define DRV_STATUS_B(drv_no)
Definition: f011_core.c:89