Xemu [doxygen]  hyppo 0a42be3a057156924bc1b626a687bd6e27349c45 @ Sat 19 Mar 02:15:11 CET 2022
dma65.c
Go to the documentation of this file.
1 /* F018 DMA core emulation for Commodore 65.
2  Part of the Xemu project. https://github.com/lgblgblgb/xemu
3  Copyright (C)2016-2021 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 #include "xemu/emutools.h"
20 #include "dma65.h"
21 #include "xemu/cpu65.h"
22 
23 
24 /* NOTES ABOUT C65 DMAgic "F018", AND THIS EMULATION:
25 
26  * this emulation part uses externally supplied functions and it depends on the target emulator how to implement read/write ops by DMA!
27  * modulo currently not handled, and it seems there is not so much decent specification how it does work
28  * INT / interrupt is not handled
29  * MIX command is implemented, though without decent specification is more like a guessing only
30  * SWAP command is also a guessing, but a better one, as it's kinda logical what it is used for
31  * COPY/FILL should be OK
32  * DMA length of ZERO probably means $10000 [but not so much info on this ...] FIXME?
33  * Current emulation timing is incorrect
34  * Speed of DMA in a real C65 would be affected by many factors, kind of op, or even VIC-3 fetchings etc ... "Of course" it's not emulated ...
35  * DMA sees the upper memory regions (ie in BANK 4 and above), which may have "something", though REC (Ram Expansion Controller) is not implemented yet
36  * It's currently unknown that DMA registers are modified as DMA list is read, ie what happens if only reg 0 is re-written
37  * C65 specification tells about "not implemented sub-commands": I am curious what "sub commands" are or planned as, etc ...
38  * Reading status would resume interrupted DMA operation (?) it's not emulated
39  * FIXME: I am not sure what's the truth: DMA session "warps" within the 1Mbyte address range or within the 64K address set?!
40 */
41 
42 
43 //#define DO_DEBUG_DMA
44 
45 #ifdef DO_DEBUG_DMA
46 # define DEBUGDMA(...) DEBUGPRINT(__VA_ARGS__)
47 #else
48 # define DEBUGDMA(...) DEBUG(__VA_ARGS__)
49 #endif
50 
52 Uint8 dma_registers[16]; // The four DMA registers (with last values written by the CPU)
53 int dma_chip_revision; // revision of DMA chip
55 int rom_date = 0;
56 
57 
63 };
64 
65 
66 static int length; // DMA operation length
67 static int command; // DMA command (-1, no command yet) byte of DMA list reading
68 static enum dma_op_types dma_op; // two lower bits of "command"
69 static int chained; // 1 = chained (read next DMA operation "descriptor")
70 static int list_addr; // Current address of the DMA list, controller will read to "execute"
71 static Uint8 minterms[4]; // Used with MIX DMA command only
72 static int in_dma_update; // signal that DMA update do something. Currently only useful to avoid PANIC when DMA would modify its own registers
73 static int dma_self_write_warning; // Warning window policy for the event in case of the happening described in the comment of the previous line :)
74 static int list_base; // base address of the DMA list fetch, like base in the source/target struct later, see there
75 static int filler_byte; // byte used for FILL DMA command only
76 
77 #define DMA_ADDRESSING(channel) ((channel.addr & channel.mask) | channel.base)
78 
79 // source and target DMA "channels":
80 static struct {
81  int addr; // address of the current operation.
82  int base; // base address for "addr", always a "pure" number!
83  int mask; // mask value to handle warp around etc, eg 0xFFFF for 64K, 0xFFF for 4K (I/O)
84  int step; // step value, zero(HOLD)/negative/positive.
85  int is_modulo; // modulo mode, if it's not zero
86  int is_io; // channel access I/O instead of memory, if it's not zero
87 } source, target;
88 
89 static struct {
91 } modulo;
92 
93 
94 
95 
96 #define DMA_READ_SOURCE() (XEMU_UNLIKELY(source.is_io) ? DMA_SOURCE_IOREADER_FUNC(DMA_ADDRESSING(source)) : DMA_SOURCE_MEMREADER_FUNC(DMA_ADDRESSING(source)))
97 #define DMA_READ_TARGET() (XEMU_UNLIKELY(target.is_io) ? DMA_TARGET_IOREADER_FUNC(DMA_ADDRESSING(target)) : DMA_TARGET_MEMREADER_FUNC(DMA_ADDRESSING(target)))
98 #define DMA_WRITE_SOURCE(data) do { \
99  if (XEMU_UNLIKELY(source.is_io)) \
100  DMA_SOURCE_IOWRITER_FUNC(DMA_ADDRESSING(source), data); \
101  else \
102  DMA_SOURCE_MEMWRITER_FUNC(DMA_ADDRESSING(source), data); \
103  } while (0)
104 #define DMA_WRITE_TARGET(data) do { \
105  if (XEMU_UNLIKELY(target.is_io)) \
106  DMA_TARGET_IOWRITER_FUNC(DMA_ADDRESSING(target), data); \
107  else \
108  DMA_TARGET_MEMWRITER_FUNC(DMA_ADDRESSING(target), data); \
109  } while (0)
110 
111 // Unlike the functions above, DMA list read is always memory (not I/O)
112 // FIXME: I guess here, that reading DMA list also warps within a 64K area
113 #ifndef DO_DEBUG_DMA
114 #define DMA_READ_LIST_NEXT_BYTE() DMA_LIST_READER_FUNC(((list_addr++) & 0xFFFF) | list_base)
115 #else
116 static int dma_list_entry_pos = 0;
117 
118 static Uint8 DMA_READ_LIST_NEXT_BYTE ( void )
119 {
120  int addr = ((list_addr++) & 0xFFFF) | list_base;
122  DEBUGPRINT("DMA: reading DMA (rev#%d) list from $%08X ($%02X) [#%d]: $%02X" NL, dma_chip_revision, addr, (list_addr-1) & 0xFFFF, dma_list_entry_pos++, data);
123  return data;
124 }
125 #endif
126 
127 
128 static XEMU_INLINE void copy_next ( void )
129 {
131  source.addr += source.step;
132  target.addr += target.step;
133 }
134 
135 static XEMU_INLINE void fill_next ( void )
136 {
137  DMA_WRITE_TARGET(filler_byte);
138  target.addr += target.step;
139 }
140 
141 static XEMU_INLINE void swap_next ( void )
142 {
143  Uint8 sa = DMA_READ_SOURCE();
144  Uint8 da = DMA_READ_TARGET();
145  DMA_WRITE_SOURCE(da);
146  DMA_WRITE_TARGET(sa);
147  source.addr += source.step;
148  target.addr += target.step;
149 }
150 
151 static XEMU_INLINE void mix_next ( void )
152 {
153  Uint8 sa = DMA_READ_SOURCE();
154  Uint8 da = DMA_READ_TARGET();
155  // NOTE: it's not clear from the specification, what MIX
156  // does. I assume, that it does some kind of minterm
157  // with source and target and writes the result to
158  // target. I'm not even sure how the minterms are
159  // interpreted on the bits of two bytes too much. FIXME!!!
160  da =
161  (( sa) & ( da) & minterms[3]) |
162  (( sa) & (~da) & minterms[2]) |
163  ((~sa) & ( da) & minterms[1]) |
164  ((~sa) & (~da) & minterms[0]) ;
165  DMA_WRITE_TARGET(da);
166  source.addr += source.step;
167  target.addr += target.step;
168 }
169 
170 
171 
172 
173 
174 
176 {
177  // The following condition is commented out for now. FIXME: how it is handled for real?!
178  //if (vic_iomode != VIC4_IOMODE)
179  // addr &= 3;
180  if (XEMU_UNLIKELY(in_dma_update)) {
181  // this is just an emergency stuff to disallow DMA to update its own registers ... FIXME: what would be the correct policy?
182  // NOTE: without this, issuing a DMA transfer updating DMA registers would affect an on-going DMA transfer!
183  if (dma_self_write_warning) {
184  dma_self_write_warning = 0;
185  ERROR_WINDOW("DMA writes its own registers, ignoring!\nThere will be no more warning on this!");
186  }
187  DEBUG("DMA: WARNING: tries to write own register by DMA reg#%d with value of $%02X" NL, addr, data);
188  return;
189  }
190  if (addr > 3) { // in case of C65, the extended registers cannot be written
191  DEBUG("DMA: trying to write M65-specific DMA register (%d) with a C65 ..." NL, addr);
192  return;
193  }
195  if (addr)
196  return; // Only writing register 0 starts the DMA operation, otherwise just return from this function (reg write already happened)
198  FATAL("dma_write_reg(): new DMA op with dma_status != 0");
199  list_addr = dma_registers[0] | (dma_registers[1] << 8); // | ((dma_registers[2] & 0xF) << 16);
200  list_base = (dma_registers[2] & 0xF) << 16;
201  DEBUGDMA("DMA: list address is $%06X now, just written to register %d value $%02X @ PC=$%04X" NL, list_base | list_addr, addr, data, cpu65.pc);
202  dma_status = 0x80; // DMA is busy now, also to signal the emulator core to call dma_update() in its main loop
203  command = -1; // signal dma_update() that it's needed to fetch the DMA command, no command is fetched yet
204  cpu65.multi_step_stop_trigger = 1; // trigger stopping multi-op CPU emulation mode, otherwise "delayed DMAs" would overlap each other resulting "panic"
205 }
206 
207 
208 
209 /* Main emulation loop should call this function regularly, if dma_status is not zero.
210  This way we have 'real' DMA, ie works while the rest of the machine is emulated too.
211  Please note, that the "exact" timing of DMA and eg the CPU is still incorrect, but it's far
212  better than the previous version where DMA was "blocky", ie the whole machine was "halted" while DMA worked ...
213  NOTE: there was an ugly description here about the differences between F018A,F018B. it's a too big topic
214  for a comment here, so it has been deleted. See here: http://c65.lgb.hu/dma.html */
215 int dma_update ( void )
216 {
217  Uint8 subcommand;
218  int cycles = 0;
220  FATAL("dma_update() called with no dma_status set!");
221  if (XEMU_UNLIKELY(command == -1)) {
222  // command == -1 signals the situation, that the (next) DMA command should be read!
223  // This part is highly incorrect, ie fetching so many bytes in one step only of dma_update()
224  // NOTE: in case of MEGA65: DMA_READ_LIST_NEXT_BYTE() uses the "megabyte" part already (taken from reg#4, in case if that reg is written)
225 #ifdef DO_DEBUG_DMA
226  dma_list_entry_pos = 0;
227 #endif
228  command = DMA_READ_LIST_NEXT_BYTE();
229  dma_op = (enum dma_op_types)(command & 3);
230  modulo.col_limit = DMA_READ_LIST_NEXT_BYTE();
231  modulo.row_limit = DMA_READ_LIST_NEXT_BYTE();
232  length = modulo.col_limit | (modulo.row_limit << 8);
233  filler_byte = DMA_READ_LIST_NEXT_BYTE() ; // source low byte is also the filler byte in case of FILL command
234  source.addr =(DMA_READ_LIST_NEXT_BYTE() << 8) | filler_byte; // -- "" --
235  source.addr |= DMA_READ_LIST_NEXT_BYTE() << 16;
236  target.addr = DMA_READ_LIST_NEXT_BYTE() ;
237  target.addr |= DMA_READ_LIST_NEXT_BYTE() << 8;
238  target.addr |= DMA_READ_LIST_NEXT_BYTE() << 16;
239  if (dma_chip_revision) // for F018B we have an extra byte fetch here! used later in this function [making DMA list one byte longer, indeed]
240  subcommand = DMA_READ_LIST_NEXT_BYTE();
241  else
242  subcommand = 0; // just make gcc happy not to generate warning later
243  modulo.value = DMA_READ_LIST_NEXT_BYTE() ;
244  modulo.value |= DMA_READ_LIST_NEXT_BYTE() << 8;
245  if (dma_chip_revision) {
246  cycles += 12; // FIXME: correct timing?
247  // F018B ("new") behaviour
248  source.step = (subcommand & 2) ? 0 : ((command & 16) ? -1 : 1);
249  target.step = (subcommand & 8) ? 0 : ((command & 32) ? -1 : 1);
250  source.is_modulo = (subcommand & 1);
251  target.is_modulo = (subcommand & 4);
252  if (dma_op == MIX_OP) { // if it's a MIX command
253  // FIXME: what about minterms in F018B?! Maybe upper bits of subcommand, but we can't [?] know ... Try that theory for now ...
254  minterms[0] = (subcommand & 16) ? 0xFF : 0x00;
255  minterms[1] = (subcommand & 32) ? 0xFF : 0x00;
256  minterms[2] = (subcommand & 64) ? 0xFF : 0x00;
257  minterms[3] = (subcommand & 128) ? 0xFF : 0x00;
258  }
259  } else {
260  cycles += 11; // FIXME: correct timing?
261  // F018A ("old") behaviour
262  source.step = (source.addr & 0x100000) ? 0 : ((source.addr & 0x400000) ? -1 : 1);
263  target.step = (target.addr & 0x100000) ? 0 : ((target.addr & 0x400000) ? -1 : 1);
264  source.is_modulo = (source.addr & 0x200000);
265  target.is_modulo = (target.addr & 0x200000);
266  if (dma_op == MIX_OP) { // if it's a MIX command
267  minterms[0] = (command & 16) ? 0xFF : 0x00;
268  minterms[1] = (command & 32) ? 0xFF : 0x00;
269  minterms[2] = (command & 64) ? 0xFF : 0x00;
270  minterms[3] = (command & 128) ? 0xFF : 0x00;
271  }
272  }
273  // use modulo mode if:
274  // * dma_init() is used with this feature to be enabled
275  // * any of source or target has the MODulo bit set
276  // FIXME: logically, in case of FILL command, source uses modulo setting does not make sense, and we don't want to use modulo length counting at all
277  if (XEMU_UNLIKELY(source.is_modulo || target.is_modulo)) {
278  if (modulo.enabled) {
279  modulo.used = 1;
280  modulo.col_counter = 0;
281  modulo.row_counter = 0;
282  // GUESSING/FIXME/TODO: with modulo, col/row counters = 0 means $100 for real, as with non-modulo mode counter = 0 means $10000 (16 bit counter there, 8 bit wides here!)
283  if (!modulo.col_limit)
284  modulo.col_limit = 0x100;
285  if (!modulo.row_limit)
286  modulo.row_limit = 0x100;
287  } else {
288  modulo.used = 0;
289  DEBUGPRINT("DMA: warning, MODulo mode wanted to be used, but not enabled by you!" NL);
290  }
291  } else
292  modulo.used = 0;
293  // if modulo.used is zero, source.is_modulo and target.is_modulo is never used (but maybe for logging)
294  // so commenting the part below:
295 #if 0
296  if (!modulo.used) {
297  if (source.is_modulo || target.is_modulo)
298  DEBUG("DMA: denying using MODulo ..." NL);
299  source.is_modulo = 0;
300  target.is_modulo = 0;
301  }
302 #endif
303  // It *seems* I/O stuff is still in the place even with F018B. FIXME: is it true?
304  source.is_io = (source.addr & 0x800000);
305  target.is_io = (target.addr & 0x800000);
306  /* source selection */
307  if (source.is_io) {
308  source.mask = 0xFFF; // 4K I/O size (warps within 4K only)
309  source.base = 0; // in case of I/O, base is not interpreted in Xemu (uses pure numbers 0-$FFF, no $DXXX), and must be zero ...
310  source.addr = (source.addr & 0xFFF); // for C65, it is pure number, no fixed-point arith. here
311  } else {
312  source.mask = 0xFFFF; // warp around within 64K. I am still not sure, it happens with DMA on 64K or 1M. M65 VHDL does 64K, so I switched that too.
313  if (dma_chip_revision)
314  source.base = (source.addr & 0x7F0000); // base selection for C65, in case of F018B (3 more bits of addresses)
315  else
316  source.base = (source.addr & 0x0F0000); // base selection for C65, in case of F018A
317  source.addr = (source.addr & 0x00FFFF); // offset from base, for C65 this is pure number, *NO* fixed point arithmetic here!
318  }
319  /* target selection - see similar lines with comments above, for source ... */
320  if (target.is_io) {
321  target.mask = 0xFFF;
322  target.base = 0;
323  target.addr = (target.addr & 0xFFF);
324  } else {
325  target.mask = 0xFFFF;
326  if (dma_chip_revision)
327  target.base = (target.addr & 0x7F0000);
328  else
329  target.base = (target.addr & 0x0F0000);
330  target.addr = (target.addr & 0x00FFFF);
331  }
332  /* other stuff */
333  chained = (command & 4);
334  DEBUG("DMA: READ COMMAND: $%07X[%s%s %d] -> $%07X[%s%s %d] (L=$%04X) CMD=%d (%s)" NL,
335  DMA_ADDRESSING(source), source.is_io ? "I/O" : "MEM", source.is_modulo ? " MOD" : "", source.step,
336  DMA_ADDRESSING(target), target.is_io ? "I/O" : "MEM", target.is_modulo ? " MOD" : "", target.step,
337  length, dma_op, chained ? "CHAINED" : "LAST"
338  );
339  if (!length)
340  length = 0x10000; // I *think* length of zero means 64K. Probably it's not true!!
341  return cycles;
342  }
343  // We have valid command to be executed, or continue to execute
344  //DEBUG("DMA: EXECUTING: command=%d length=$%04X" NL, command & 3, length);
345  in_dma_update = 1;
346  switch (dma_op) {
347  case COPY_OP: // COPY command (0)
348  copy_next();
349  cycles = 2; // FIXME: correct timing?
350  break;
351  case MIX_OP: // MIX command (1)
352  mix_next();
353  cycles = 3; // FIXME: correct timing?
354  break;
355  case SWAP_OP: // SWAP command (2)
356  swap_next();
357  cycles = 4; // FIXME: correct timing?
358  break;
359  case FILL_OP: // FILL command (3)
360  fill_next();
361  cycles = 1; // FIXME: correct timing?
362  break;
363  }
364  // Maintain length counter or modulo-related counters (in the second case, also add modulo value if needed)
365  if (XEMU_UNLIKELY(modulo.used)) {
366  // modulo mode. we don't use the usual "length" but own counters.
367  // but we DO reset "length" to zero if modulo transfer is done, so
368  // it will be detected by the 'if' at the bottom of this huge function as the end of the DMA op
369  if (modulo.col_counter == modulo.col_limit) {
370  if (modulo.row_counter == modulo.row_limit) {
371  length = 0; // just to fullfish end-of-operation condition
372  } else {
373  // end of modulo "columns", but there are "rows" left, reset columns counter, increment row counter,
374  // also add the modulo value to source and/or target channels, if the given channel is in modulo mode
375  modulo.col_counter = 0;
376  modulo.row_counter++;
377  if (source.is_modulo)
378  source.addr += modulo.value;
379  if (target.is_modulo)
380  target.addr += modulo.value;
381  }
382  } else {
383  modulo.col_counter++;
384  }
385  } else {
386  length--; // non-MODulo mode, we simply decrement length counter ...
387  }
388  if (length <= 0) { // end of DMA operation for the current DMA list entry?
389  if (chained) { // chained?
390  DEBUGDMA("DMA: end of operation, but chained!" NL);
391  dma_status = 0x81; // still busy then, with also bit0 set (chained)
392  command = -1; // signal for next DMA command fetch
393  } else {
394  DEBUGDMA("DMA: end of operation, no chained next one." NL);
395  dma_status = 0; // end of DMA command
396  command = -1;
397  }
398  }
399  in_dma_update = 0;
400  return cycles;
401 }
402 
403 
404 
405 int dma_update_multi_steps ( int do_for_cycles )
406 {
407  int cycles = 0;
408  do {
409  cycles += dma_update();
410  } while (cycles <= do_for_cycles && dma_status);
411  return cycles;
412 }
413 
414 
416 {
417  if (p == NULL) {
418  DEBUGPRINT("ROM: version check is disabled (NULL pointer), previous version info: %d" NL, rom_date);
419  } else if (p[0] == 0x56) { // 'V'
420  rom_date = 0;
421  for (int a = 0; a < 6; a++) {
422  p++;
423  if (*p >= '0' && *p <= '9')
424  rom_date = rom_date * 10 + *p - '0';
425  else {
426  rom_date = -1;
427  DEBUGPRINT("ROM: version check failed (num-numberic character)" NL);
428  return;
429  }
430  }
431  DEBUGPRINT("ROM: version check succeeded, detected version: %d" NL, rom_date);
432  } else {
433  DEBUGPRINT("ROM: version check failed (no leading 'V')" NL);
434  rom_date = -1;
435  }
436 }
437 
438 
439 void dma_init_set_rev ( unsigned int revision, Uint8 *rom_ver_signature )
440 {
441  detect_rom_date(rom_ver_signature);
442  int rom_suggested_dma_revision = (rom_date < 900000 || rom_date > 910522);
443  DEBUGPRINT("ROM: version check suggests DMA revision %d" NL, rom_suggested_dma_revision);
444  revision &= 0xFF;
445  if (revision > 2) {
446  FATAL("Unknown DMA revision value tried to be set (%d)!", revision);
447  } else if (revision == 2) {
448  if (!rom_ver_signature)
449  FATAL("dma_ini_set_rev(): revision == 2 (auto-detect) but rom_ver_signature == NULL (cannot auto-detect)");
450  if (rom_date <= 0)
451  WARNING_WINDOW("ROM version cannot be detected, and DMA revision auto-detection was requested.\nDefaulting to revision %d.\nWarning, this may cause incorrect behaviour!", rom_suggested_dma_revision);
452  dma_chip_revision = rom_suggested_dma_revision;
453  dma_chip_initial_revision = rom_suggested_dma_revision;
454  DEBUGPRINT("DMA: setting chip revision to #%d based on the ROM auto-detection" NL, dma_chip_initial_revision);
455  } else {
456  dma_chip_revision = revision;
457  dma_chip_initial_revision = revision;
458  if (dma_chip_revision != rom_suggested_dma_revision && rom_date > 0)
459  WARNING_WINDOW("DMA revision is forced to be %d, while ROM version (%d)\nsuggested revision is %d. Using the forced revision %d.\nWarning, this may cause incorrect behaviour!", dma_chip_revision, rom_date, rom_suggested_dma_revision, dma_chip_revision);
460  DEBUGPRINT("DMA: setting chip revision to #%d based on configuration/command line request (forced). Suggested revision by ROM date: #%d" NL, dma_chip_initial_revision, rom_suggested_dma_revision);
461  }
462 }
463 
464 
465 void dma_init ( unsigned int revision )
466 {
467  modulo.enabled = (revision & DMA_FEATURE_MODULO);
468  revision &= ~DMA_FEATURE_MODULO;
469  if (revision & DMA_FEATURE_DYNMODESET)
470  FATAL("DMA feature DMA_FEATRUE_DYNMODESET is not supported on C65!");
471  if (revision == 2)
472  revision = 1; // in case of "auto-detect" we use rev1, let it be for dma_init_set_rev() called by target later to refine this
473  if (revision > 1) {
474  FATAL("Unknown DMA revision value tried to be set (%d)!", revision);
475  } else {
476  dma_chip_revision = revision;
477  dma_chip_initial_revision = revision;
478  }
479  DEBUGPRINT("DMA: initializing DMA engine for chip revision %d (initially, may be modified later!), dyn_mode=%s, modulo_support=%s." NL,
481  "NEVER(C65)",
482  modulo.enabled ? "ENABLED" : "DISABLED"
483  );
484  dma_reset();
485 }
486 
487 
488 void dma_reset ( void )
489 {
490  command = -1; // no command is fetched yet
491  dma_status = 0;
492  memset(dma_registers, 0, sizeof dma_registers);
493  source.base = 0;
494  target.base = 0;
495  list_base = 0;
496  in_dma_update = 0;
497  dma_self_write_warning = 1;
499 }
500 
501 
503 {
504  // FIXME: status on ALL registers when read?!
505  DEBUG("DMA: register reading at addr of %d" NL, addr);
506 #if 0
507  if ((addr & 3) != 3)
508  return 0xFF; // other registers are (??????) writeonly? FIXME?
509 #endif
510  return dma_status;
511 }
512 
513 
514 /* --- SNAPSHOT RELATED --- */
515 
516 #ifdef XEMU_SNAPSHOT_SUPPORT
517 
518 // Note: currently state is not saved "within" a DMA operation. It's only a problem, if a DMA
519 // operation is not handled fully here, but implemented as an iterating update method from the
520 // emulator code. FIXME.
521 
522 #include <string.h>
523 
524 #define SNAPSHOT_DMA_BLOCK_VERSION 2
525 #define SNAPSHOT_DMA_BLOCK_SIZE 0x100
526 
527 
528 int dma_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block )
529 {
530  Uint8 buffer[SNAPSHOT_DMA_BLOCK_SIZE];
531  int a;
532  if (block->block_version != SNAPSHOT_DMA_BLOCK_VERSION || block->sub_counter || block->sub_size != sizeof buffer)
533  RETURN_XSNAPERR_USER("Bad C65 block syntax");
534  a = xemusnap_read_file(buffer, sizeof buffer);
535  if (a) return a;
536  /* loading state ... */
537  memcpy(dma_registers, buffer, sizeof dma_registers);
538  dma_chip_revision = buffer[0x80];
539  dma_chip_initial_revision = buffer[0x81];
540  modulo.enabled = buffer[0x83];
541  dma_status = buffer[0x84];
542  in_dma_update = buffer[0x85];
543  return 0;
544 }
545 
546 
547 int dma_snapshot_save_state ( const struct xemu_snapshot_definition_st *def )
548 {
549  Uint8 buffer[SNAPSHOT_DMA_BLOCK_SIZE];
550  int a = xemusnap_write_block_header(def->idstr, SNAPSHOT_DMA_BLOCK_VERSION);
551  if (a) return a;
552  memset(buffer, 0xFF, sizeof buffer);
553  /* saving state ... */
554  memcpy(buffer, dma_registers, sizeof dma_registers);
555  buffer[0x80] = dma_chip_revision;
556  buffer[0x81] = dma_chip_initial_revision;
557  buffer[0x83] = modulo.enabled ? 1 : 0;
558  buffer[0x84] = dma_status; // bit useless to store (see below, actually it's a problem), but to think about the future ...
559  buffer[0x85] = in_dma_update ? 1 : 0; // -- "" --
560  if (dma_status)
561  WARNING_WINDOW("f018_core DMA snapshot save: snapshot with DMA pending! Snapshot WILL BE incorrect on loading! FIXME!"); // FIXME!
562  return xemusnap_write_sub_block(buffer, sizeof buffer);
563 }
564 
565 #endif
dma_op_types
dma_op_types
Definition: dma65.c:58
DMA_FEATURE_MODULO
#define DMA_FEATURE_MODULO
Definition: dma65.h:25
dma_reset
void dma_reset(void)
Definition: dma65.c:488
enabled
int enabled
Definition: dma65.c:90
dma_update
int dma_update(void)
Definition: dma65.c:215
DMA_READ_TARGET
#define DMA_READ_TARGET()
Definition: dma65.c:97
dma_init_set_rev
void dma_init_set_rev(unsigned int revision, Uint8 *rom_ver_signature)
Definition: dma65.c:439
DMA_ADDRESSING
#define DMA_ADDRESSING(channel)
Definition: dma65.c:77
rom_date
int rom_date
Definition: dma65.c:55
emutools.h
row_limit
int row_limit
Definition: dma65.c:90
DMA_WRITE_TARGET
#define DMA_WRITE_TARGET(data)
Definition: dma65.c:104
WARNING_WINDOW
#define WARNING_WINDOW(...)
Definition: xep128.h:115
addr
int addr
Definition: dma65.c:81
col_limit
int col_limit
Definition: dma65.c:90
dma_status
Uint8 dma_status
Definition: dma65.c:51
XEMU_INLINE
#define XEMU_INLINE
Definition: emutools_basicdefs.h:126
m65-memcontent-generator.data
data
Definition: m65-memcontent-generator.py:119
detect_rom_date
void detect_rom_date(Uint8 *p)
Definition: dma65.c:415
Uint8
uint8_t Uint8
Definition: fat32.c:51
used
int used
Definition: dma65.c:90
dma_registers
Uint8 dma_registers[16]
Definition: dma65.c:52
dma_chip_revision
int dma_chip_revision
Definition: dma65.c:53
DMA_WRITE_SOURCE
#define DMA_WRITE_SOURCE(data)
Definition: dma65.c:98
block
Uint32 block
Definition: fat32.c:156
DEBUGPRINT
#define DEBUGPRINT(...)
Definition: emutools_basicdefs.h:171
col_counter
int col_counter
Definition: dma65.c:90
SWAP_OP
@ SWAP_OP
Definition: dma65.c:61
FILL_OP
@ FILL_OP
Definition: dma65.c:62
ERROR_WINDOW
#define ERROR_WINDOW(...)
Definition: xep128.h:116
NL
#define NL
Definition: fat32.c:37
base
int base
Definition: dma65.c:82
is_modulo
int is_modulo
Definition: dma65.c:85
dma_write_reg
void dma_write_reg(int addr, Uint8 data)
Definition: dma65.c:175
DMA_FEATURE_DYNMODESET
#define DMA_FEATURE_DYNMODESET
Definition: dma65.h:24
cpu65.h
MIX_OP
@ MIX_OP
Definition: dma65.c:60
dma_update_multi_steps
int dma_update_multi_steps(int do_for_cycles)
Definition: dma65.c:405
COPY_OP
@ COPY_OP
Definition: dma65.c:59
dma_read_reg
Uint8 dma_read_reg(int addr)
Definition: dma65.c:502
DMA_LIST_READER_FUNC
#define DMA_LIST_READER_FUNC
Definition: xemu-target.h:17
step
int step
Definition: dma65.c:84
is_io
int is_io
Definition: dma65.c:86
DMA_READ_SOURCE
#define DMA_READ_SOURCE()
Definition: dma65.c:96
DEBUGDMA
#define DEBUGDMA(...)
Definition: dma65.c:48
dma_init
void dma_init(unsigned int revision)
Definition: dma65.c:465
mask
int mask
Definition: dma65.c:83
value
int value
Definition: dma65.c:90
DMA_READ_LIST_NEXT_BYTE
#define DMA_READ_LIST_NEXT_BYTE()
Definition: dma65.c:114
row_counter
int row_counter
Definition: dma65.c:90
DEBUG
#define DEBUG(...)
Definition: emutools_basicdefs.h:167
dma65.h
FATAL
#define FATAL(...)
Definition: xep128.h:117
XEMU_UNLIKELY
#define XEMU_UNLIKELY(__x__)
Definition: emutools_basicdefs.h:125
dma_chip_initial_revision
int dma_chip_initial_revision
Definition: dma65.c:54