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 MEGA65
2  Part of the Xemu project. 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 #include "xemu/emutools.h"
20 #include "dma65.h"
21 #include "xemu/cpu65.h"
22 #include "rom.h"
23 
24 
25 /* NOTES ABOUT C65/M65 DMAgic "F018", AND THIS EMULATION:
26 
27  * this emulation part uses externally supplied functions and it depends on the target emulator how to implement read/write ops by DMA!
28  * modulo currently not handled, and it seems there is not so much decent specification how it does work
29  * INT / interrupt is not handled
30  * MIX command is implemented, though without decent specification is more like a guessing only
31  * SWAP command is also a guessing, but a better one, as it's kinda logical what it is used for
32  * COPY/FILL should be OK
33  * DMA length of ZERO probably means $10000 [but not so much info on this ...] FIXME?
34  * Current emulation timing is incorrect
35  * 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 ...
36  * 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
37  * It's currently unknown that DMA registers are modified as DMA list is read, ie what happens if only reg 0 is re-written
38  * C65 specification tells about "not implemented sub-commands": I am curious what "sub commands" are or planned as, etc ...
39  * Reading status would resume interrupted DMA operation (?) it's not emulated
40  * MEGA65 macro is defined in case of M65 target, then we handle the "megabyte slice stuff" as well.
41  * FIXME: I am not sure what's the truth: DMA session "warps" within the 1Mbyte address range or within the 64K address set?!
42  Since M65 VHDL says within 64K, I go the same here currently ...
43 */
44 
45 
46 //#define DO_DEBUG_DMA
47 
48 #ifdef DO_DEBUG_DMA
49 # define DEBUGDMA(...) DEBUGPRINT(__VA_ARGS__)
50 #else
51 # define DEBUGDMA(...) DEBUG(__VA_ARGS__)
52 #endif
53 
55 Uint8 dma_registers[16]; // The four DMA registers (with last values written by the CPU)
56 int dma_chip_revision; // revision of DMA chip
57 int dma_chip_revision_is_dynamic; // allowed to change DMA chip revision (normally yes) by MEGA65
60 // Hacky stuff:
61 // low byte: the transparent byte value
62 // bit 8: zero = transprent mode is used, 1 = no DMA transparency is in used
63 // This is done this way to have a single 'if' to check both of enabled transparency and the transparent value,
64 // since the value (being 8 bit) to be written would never match values > $FF
65 static unsigned int dma_transparency;
66 
67 
73 };
74 
75 
76 static int length; // DMA operation length
77 static int command; // DMA command (-1, no command yet) byte of DMA list reading
78 static enum dma_op_types dma_op; // two lower bits of "command"
79 static int chained; // 1 = chained (read next DMA operation "descriptor")
80 static int list_addr; // Current address of the DMA list, controller will read to "execute"
81 static Uint8 minterms[4]; // Used with MIX DMA command only
82 static int in_dma_update; // signal that DMA update do something. Currently only useful to avoid PANIC when DMA would modify its own registers
83 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 :)
84 static int list_base; // base address of the DMA list fetch, like base in the source/target struct later, see there
85 static int filler_byte; // byte used for FILL DMA command only
86 
87 
88 /* BEGIN HACK */
89 static struct {
90  int enabled;
92  //Uint8 saved_regs[16];
94 } hack;
95 /* END HACK */
96 
97 
98 // In case of MEGA65 we should support fractional steps (1 byte for fraction). We do this to have
99 // common code base with C65 emulator's DMA to have a single variable for source (source_addr) and
100 // target (target_addr) "within the megabyte" variable, but in case of C65 it's the "clean" address,
101 // while in case of MEGA65, the lower 8 bits are the fraction. This macro is used to reference the
102 // integer part only, in case of C65 it's simply as-is, for MEGA65, it's skipping the low 8 bits, ie
103 // shifting data to the right by 8. Note, that since within a megabyte DMA can operate only, 32 bits
104 // are more than enough even this way (20 bits + 8 bits = 28 bits, even signed 32 bit int type is
105 // suitable).
106 // DMA_*_SKIP_RATE macros just gives the step rate (always positive). In case of C65, it's of course
107 // fixed to be 1 ('hold' is not handled this way). For MEGA65, we use (see the source fix-point
108 // arithmetic description above!) the actual M65 DMA registers for those settings.
109 // DMA_ADDR_FRACT_PART() is used only in debug functions to log
110 #define DMA_ADDR_INTEGER_PART(p) ((p)>>8)
111 #define DMA_SOURCE_SKIP_RATE ((int)(dma_registers[0x08] | (dma_registers[0x09] << 8)))
112 #define DMA_TARGET_SKIP_RATE ((int)(dma_registers[0x0A] | (dma_registers[0x0B] << 8)))
113 #define DMA_ADDR_FRACT_PART(p) ((p) & 0xFF)
114 
115 #define DMA_ADDRESSING(channel) ((DMA_ADDR_INTEGER_PART(channel.addr) & channel.mask) + channel.base)
116 
117 // source and target DMA "channels":
118 static struct {
119  int addr; // address of the current operation. On MEGA65 it's a fixed-point math value, on C65 it's a pure number
120  int base; // base address for "addr", always a "pure" number! On M65 it also contains the "megabyte selection"
121  int mask; // mask value to handle warp around etc, eg 0xFFFF for 64K, 0xFFF for 4K (I/O)
122  int step; // step value, zero(HOLD)/negative/positive. On MEGA65, this is a fixed point arithmetic value!!
123  int is_modulo; // modulo mode, if it's not zero
124  int is_io; // channel access I/O instead of memory, if it's not zero
125 } source, target;
126 
127 static struct {
129 } modulo;
130 
131 
132 // On C65, DMA cannot cross 64K boundaries, so the right mask is 0xFFFF
133 // On MEGA65 it seems to be 1Mbyte, thus the mask should be 0xFFFFF
134 // No idea about the list mask, which is not the normal dma read/write ops,
135 // but reading the DMA listself only.
136 #define MEM_LIST_MASK 0xFFFF
137 #define MEM_ADDR_MASK 0xFFFFF
138 
139 
140 #define DMA_READ_SOURCE() (XEMU_UNLIKELY(source.is_io) ? DMA_SOURCE_IOREADER_FUNC(DMA_ADDRESSING(source)) : DMA_SOURCE_MEMREADER_FUNC(DMA_ADDRESSING(source)))
141 #define DMA_READ_TARGET() (XEMU_UNLIKELY(target.is_io) ? DMA_TARGET_IOREADER_FUNC(DMA_ADDRESSING(target)) : DMA_TARGET_MEMREADER_FUNC(DMA_ADDRESSING(target)))
142 
143 
144 #if 0
145 #define DMA_WRITE_SOURCE(data) do { \
146  if (XEMU_UNLIKELY(source.is_io)) \
147  DMA_SOURCE_IOWRITER_FUNC(DMA_ADDRESSING(source), data); \
148  else \
149  DMA_SOURCE_MEMWRITER_FUNC(DMA_ADDRESSING(source), data); \
150  } while (0)
151 #define DMA_WRITE_TARGET(data) do { \
152  if (XEMU_UNLIKELY(target.is_io)) \
153  DMA_TARGET_IOWRITER_FUNC(DMA_ADDRESSING(target), data); \
154  else \
155  DMA_TARGET_MEMWRITER_FUNC(DMA_ADDRESSING(target), data); \
156  } while (0)
157 #endif
158 
159 static XEMU_INLINE void DMA_WRITE_SOURCE ( Uint8 data )
160 {
161  if (XEMU_LIKELY((unsigned int)data != dma_transparency)) {
162  if (XEMU_UNLIKELY(source.is_io))
164  else
166  }
167 }
168 
169 static XEMU_INLINE void DMA_WRITE_TARGET ( Uint8 data )
170 {
171  if (XEMU_LIKELY((unsigned int)data != dma_transparency)) {
172  if (XEMU_UNLIKELY(target.is_io))
174  else
176  }
177 }
178 
179 // Unlike the functions above, DMA list read is always memory (not I/O)
180 // Also the "step" is always one. So it's a bit special case ... even on M65 we don't use fixed point math here, just pure number
181 // FIXME: I guess here, that reading DMA list also warps within a 64K area
182 #ifndef DO_DEBUG_DMA
183 #define DMA_READ_LIST_NEXT_BYTE() DMA_LIST_READER_FUNC(((list_addr++) & MEM_LIST_MASK) | list_base)
184 #else
185 static int dma_list_entry_pos = 0;
186 
187 static Uint8 DMA_READ_LIST_NEXT_BYTE ( void )
188 {
189  int addr = ((list_addr++) & MEM_LIST_MASK) | list_base;
191  DEBUGPRINT("DMA: reading DMA (rev#%d) list from $%08X ($%02X) [#%d]: $%02X" NL, dma_chip_revision, addr, (list_addr-1) & MEM_LIST_MASK, dma_list_entry_pos++, data);
192  return data;
193 }
194 #endif
195 
196 
197 static XEMU_INLINE void copy_next ( void )
198 {
200  source.addr += source.step;
201  target.addr += target.step;
202 }
203 
204 static XEMU_INLINE void fill_next ( void )
205 {
206  DMA_WRITE_TARGET(filler_byte);
207  target.addr += target.step;
208 }
209 
210 static XEMU_INLINE void swap_next ( void )
211 {
212  Uint8 sa = DMA_READ_SOURCE();
213  Uint8 da = DMA_READ_TARGET();
214  DMA_WRITE_SOURCE(da);
215  DMA_WRITE_TARGET(sa);
216  source.addr += source.step;
217  target.addr += target.step;
218 }
219 
220 static XEMU_INLINE void mix_next ( void )
221 {
222  Uint8 sa = DMA_READ_SOURCE();
223  Uint8 da = DMA_READ_TARGET();
224  // NOTE: it's not clear from the specification, what MIX
225  // does. I assume, that it does some kind of minterm
226  // with source and target and writes the result to
227  // target. I'm not even sure how the minterms are
228  // interpreted on the bits of two bytes too much. FIXME!!!
229  da =
230  (( sa) & ( da) & minterms[3]) |
231  (( sa) & (~da) & minterms[2]) |
232  ((~sa) & ( da) & minterms[1]) |
233  ((~sa) & (~da) & minterms[0]) ;
234  DMA_WRITE_TARGET(da);
235  source.addr += source.step;
236  target.addr += target.step;
237 }
238 
239 
240 
241 
242 
243 
245 {
246  // The following condition is commented out for now. FIXME: how it is handled for real?!
247  //if (vic_iomode != VIC4_IOMODE)
248  // addr &= 3;
249  if (XEMU_UNLIKELY(in_dma_update)) {
250  // this is just an emergency stuff to disallow DMA to update its own registers ... FIXME: what would be the correct policy?
251  // NOTE: without this, issuing a DMA transfer updating DMA registers would affect an on-going DMA transfer!
252  if (dma_self_write_warning) {
253  dma_self_write_warning = 0;
254  ERROR_WINDOW("DMA writes its own registers, ignoring!\nThere will be no more warning on this!");
255  }
256  DEBUG("DMA: WARNING: tries to write own register by DMA reg#%d with value of $%02X" NL, addr, data);
257  return;
258  }
260  hack.enhanced_dma = 0;
261  switch (addr) {
262  case 0x2: // for compatibility with C65, MEGA65 here resets the MB part of the DMA list address
263  dma_registers[4] = 0; // this is the "MB" part of the DMA list address (hopefully ...)
264  break;
265  case 0xE: // Set low order bits of DMA list address, without starting (MEGA65 feature, but without VIC4 new mode, this reg will never addressed here anyway)
266  dma_registers[0] = data;
267  break;
268  case 5:
269  if (hack.enabled) {
270  addr = 0; // fool the code that it's a DMA trigger ...
271  hack.enhanced_dma = 1; // ... and also set the enhanced mode flag though
272  dma_registers[0] = data;
273  dma_registers[5] = data;
274  }
275  break;
276  }
277  if (addr)
278  return; // Only writing register 0 starts the DMA operation, otherwise just return from this function (reg write already happened)
280  FATAL("dma_write_reg(): new DMA op with dma_status != 0");
281  list_addr = dma_registers[0] | (dma_registers[1] << 8); // | ((dma_registers[2] & 0xF) << 16);
282  if (hack.enhanced_dma) {
283  DEBUGDMA("DMA: initiation of ENCHANCED MODE DMA!!!!\n");
284  // FAKE enhanced mode DMA! By using old register-set pre-inited, and allow
285  // an enhanced option list to modify them :-@
286  dma_registers[0x08] = 0; // source skip rate, fraction part
287  dma_registers[0x09] = 1; // source skip rate, integer part
288  dma_registers[0x0A] = 0; // target skip rate, fraction part
289  dma_registers[0x0B] = 1; // target skip rate, integer part
290  dma_registers[0x05] = 0; // source MB
291  dma_registers[0x06] = 0; // target MB
292  hack.saved_revision = dma_chip_revision; // save this (will be restored at the end of DMA chain) so the "hack" can modify without loosing the original setting
293  } else
294  DEBUGDMA("DMA: initiation of normal mode DMA\n");
295  list_base = (dma_registers[4] << 20) | ((dma_registers[2] & 0xF) << 16);
296  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);
297  dma_status = 0x80; // DMA is busy now, also to signal the emulator core to call dma_update() in its main loop
298  command = -1; // signal dma_update() that it's needed to fetch the DMA command, no command is fetched yet
299  cpu65.multi_step_stop_trigger = 1; // trigger stopping multi-op CPU emulation mode, otherwise "delayed DMAs" would overlap each other resulting "panic"
300 }
301 
302 
303 int dma_is_in_use ( void )
304 {
305  return in_dma_update;
306 }
307 
308 
309 /* Main emulation loop should call this function regularly, if dma_status is not zero.
310  This way we have 'real' DMA, ie works while the rest of the machine is emulated too.
311  Please note, that the "exact" timing of DMA and eg the CPU is still incorrect, but it's far
312  better than the previous version where DMA was "blocky", ie the whole machine was "halted" while DMA worked ...
313  NOTE: there was an ugly description here about the differences between F018A,F018B. it's a too big topic
314  for a comment here, so it has been deleted. See here: http://c65.lgb.hu/dma.html */
315 int dma_update ( void )
316 {
317  Uint8 subcommand;
318  int cycles = 0;
320  FATAL("dma_update() called with no dma_status set!");
321  if (XEMU_UNLIKELY(command == -1)) {
322  /* don't do this here, it will reset these in chained list for enhanced DMA!
323  if (hack.enabled) {
324  dma_registers[0x08] = 0; // source skip rate, fraction part
325  dma_registers[0x09] = 1; // source skip rate, integer part
326  dma_registers[0x0A] = 0; // target skip rate, fraction part
327  dma_registers[0x0B] = 1; // target skip rate, integer part
328  dma_registers[0x05] = 0; // source MB
329  dma_registers[0x06] = 0; // target MB
330  } */
331  if (hack.enhanced_dma) {
332  Uint8 opt;
333  //memcpy(hack.saved_regs, dma_registers, sizeof(dma_registers));
334  hack.saved_revision = dma_chip_revision;
335  do {
336  opt = DMA_READ_LIST_NEXT_BYTE();
337  DEBUGDMA("DMA: enhanced option byte $%02X read" NL, opt);
338  cycles++;
339  switch (opt) {
340  case 0x06: // disable transparency
341  dma_transparency |= 0x100;
342  break;
343  case 0x07: // enable transparency
344  dma_transparency &= 0xFF;
345  break;
346  case 0x0A:
348  break;
349  case 0x0B:
351  break;
352  case 0x80: // set MB of source
354  cycles++;
355  break;
356  case 0x81: // set MB of target
358  cycles++;
359  break;
360  case 0x82: // set source skip rate, fraction part
362  cycles++;
363  break;
364  case 0x83: // set source skip rate, integer part
366  cycles++;
367  break;
368  case 0x84: // set target skip rate, fraction part
370  cycles++;
371  break;
372  case 0x85: // set target skip rate, integer part
374  cycles++;
375  break;
376  case 0x86: // byte value to be treated as "transparent" (ie: skip writing that data), if enabled
377  dma_transparency = (dma_transparency & 0x100) | (unsigned int)DMA_READ_LIST_NEXT_BYTE();
378  cycles++;
379  break;
380  case 0x00:
381  DEBUGDMA("DMA: end of enhanced options" NL);
382  break;
383  default:
384  // maybe later we should keep this quiet ...
385  DEBUGPRINT("DMA: *unknown* enhanced option: $%02X @ PC=$%04X" NL, opt, cpu65.pc);
386  if ((opt & 0x80)) {
387  (void)DMA_READ_LIST_NEXT_BYTE(); // skip one byte for unknown option >= $80
388  cycles++;
389  }
390  break;
391  }
392  } while (opt);
393  }
394  // command == -1 signals the situation, that the (next) DMA command should be read!
395  // This part is highly incorrect, ie fetching so many bytes in one step only of dma_update()
396  // set DMA revision based on register #3 bit 0, this is a MEGA65 feature
397  if (dma_chip_revision_override >= 0) {
398  DEBUG("DMA: changing DMA revision by enhanced list %d -> %d" NL, dma_chip_revision, dma_chip_revision_override);
400  } else if (dma_chip_revision != (dma_registers[3] & 1)) {
402  DEBUG("DMA: changing DMA revision %d -> %d" NL, dma_chip_revision, dma_registers[3] & 1);
404  } else {
405  DEBUG("DMA: WARNING: M65 software wants to change DMA revision, but you did not allow it!" NL);
406  }
407  }
408  // 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)
409 #ifdef DO_DEBUG_DMA
410  dma_list_entry_pos = 0;
411 #endif
412  command = DMA_READ_LIST_NEXT_BYTE();
413  dma_op = (enum dma_op_types)(command & 3);
414  modulo.col_limit = DMA_READ_LIST_NEXT_BYTE();
415  modulo.row_limit = DMA_READ_LIST_NEXT_BYTE();
416  length = modulo.col_limit | (modulo.row_limit << 8);
417  filler_byte = DMA_READ_LIST_NEXT_BYTE() ; // source low byte is also the filler byte in case of FILL command
418  source.addr =(DMA_READ_LIST_NEXT_BYTE() << 8) | filler_byte; // -- "" --
419  source.addr |= DMA_READ_LIST_NEXT_BYTE() << 16;
420  target.addr = DMA_READ_LIST_NEXT_BYTE() ;
421  target.addr |= DMA_READ_LIST_NEXT_BYTE() << 8;
422  target.addr |= DMA_READ_LIST_NEXT_BYTE() << 16;
423  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]
424  subcommand = DMA_READ_LIST_NEXT_BYTE();
425  else
426  subcommand = 0; // just make gcc happy not to generate warning later
427  // On MEGA65, modulo is used as a fixed point arithmetic value
428  modulo.value = DMA_READ_LIST_NEXT_BYTE() << 8;
429  modulo.value |= DMA_READ_LIST_NEXT_BYTE() << 16;
430  if (dma_chip_revision) {
431  cycles += 12; // FIXME: correct timing?
432  // F018B ("new") behaviour
433  source.step = (subcommand & 2) ? 0 : ((command & 16) ? -DMA_SOURCE_SKIP_RATE : DMA_SOURCE_SKIP_RATE);
434  target.step = (subcommand & 8) ? 0 : ((command & 32) ? -DMA_TARGET_SKIP_RATE : DMA_TARGET_SKIP_RATE);
435  source.is_modulo = (subcommand & 1);
436  target.is_modulo = (subcommand & 4);
437  if (dma_op == MIX_OP) { // if it's a MIX command
438  // FIXME: what about minterms in F018B?! Maybe upper bits of subcommand, but we can't [?] know ... Try that theory for now ...
439  minterms[0] = (subcommand & 16) ? 0xFF : 0x00;
440  minterms[1] = (subcommand & 32) ? 0xFF : 0x00;
441  minterms[2] = (subcommand & 64) ? 0xFF : 0x00;
442  minterms[3] = (subcommand & 128) ? 0xFF : 0x00;
443  }
444  } else {
445  cycles += 11; // FIXME: correct timing?
446  // F018A ("old") behaviour
447  source.step = (source.addr & 0x100000) ? 0 : ((source.addr & 0x400000) ? -DMA_SOURCE_SKIP_RATE : DMA_SOURCE_SKIP_RATE);
448  target.step = (target.addr & 0x100000) ? 0 : ((target.addr & 0x400000) ? -DMA_TARGET_SKIP_RATE : DMA_TARGET_SKIP_RATE);
449  source.is_modulo = (source.addr & 0x200000);
450  target.is_modulo = (target.addr & 0x200000);
451  if (dma_op == MIX_OP) { // if it's a MIX command
452  minterms[0] = (command & 16) ? 0xFF : 0x00;
453  minterms[1] = (command & 32) ? 0xFF : 0x00;
454  minterms[2] = (command & 64) ? 0xFF : 0x00;
455  minterms[3] = (command & 128) ? 0xFF : 0x00;
456  }
457  }
458  // use modulo mode if:
459  // * dma_init() is used with this feature to be enabled
460  // * any of source or target has the MODulo bit set
461  // 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
462  if (XEMU_UNLIKELY(source.is_modulo || target.is_modulo)) {
463  if (modulo.enabled) {
464  modulo.used = 1;
465  modulo.col_counter = 0;
466  modulo.row_counter = 0;
467  // 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!)
468  if (!modulo.col_limit)
469  modulo.col_limit = 0x100;
470  if (!modulo.row_limit)
471  modulo.row_limit = 0x100;
472  } else {
473  modulo.used = 0;
474  DEBUGPRINT("DMA: warning, MODulo mode wanted to be used, but not enabled by you!" NL);
475  }
476  } else
477  modulo.used = 0;
478  // if modulo.used is zero, source.is_modulo and target.is_modulo is never used (but maybe for logging)
479  // so commenting the part below:
480 #if 0
481  if (!modulo.used) {
482  if (source.is_modulo || target.is_modulo)
483  DEBUG("DMA: denying using MODulo ..." NL);
484  source.is_modulo = 0;
485  target.is_modulo = 0;
486  }
487 #endif
488  // It *seems* I/O stuff is still in the place even with F018B. FIXME: is it true?
489  source.is_io = (source.addr & 0x800000);
490  target.is_io = (target.addr & 0x800000);
491  /* source selection */
492  if (source.is_io) {
493  source.mask = 0xFFF; // 4K I/O size (warps within 4K only)
494  source.base = 0; // in case of I/O, base is not interpreted in Xemu (uses pure numbers 0-$FFF, no $DXXX, not even M65-spec mapping), and must be zero ...
495  source.addr = (source.addr & 0xFFF) << 8; // for M65, it is fixed-point arithmetic
496  } else {
497  source.mask = MEM_ADDR_MASK; // in case of memory (not I/O) access, we have again a mask, see at "MEM_ADDR_MASK" for more explanation
498  // base selection for M65
499  // M65 has an "mbyte part" register for source (and target too)
500  // however, with F018B there are 3 bits over 1Mbyte as well, and it seems M65 (see VHDL code) add these together then. Interesting.
501  if (dma_chip_revision)
502  source.base = (source.addr & 0x0F0000) | (((dma_registers[5] << 20) + (source.addr & 0x700000)) & 0xFF00000);
503  else
504  source.base = (source.addr & 0x0F0000) | ( dma_registers[5] << 20);
505  source.addr = (source.addr & 0x00FFFF) << 8;// offset from base, for M65 this *IS* fixed point arithmetic!
506  }
507  /* target selection - see similar lines with comments above, for source ... */
508  if (target.is_io) {
509  target.mask = 0xFFF;
510  target.base = 0;
511  target.addr = (target.addr & 0xFFF) << 8;
512  } else {
513  target.mask = MEM_ADDR_MASK;
514  if (dma_chip_revision)
515  target.base = (target.addr & 0x0F0000) | (((dma_registers[6] << 20) + (target.addr & 0x700000)) & 0xFF00000);
516  else
517  target.base = (target.addr & 0x0F0000) | ( dma_registers[6] << 20);
518  target.addr = (target.addr & 0x00FFFF) << 8;
519  }
520  /* other stuff */
521  chained = (command & 4);
522  // FIXME: this is a debug mesg, yeah, but with fractional step on M65, the step values needs to be interpreted with keep in mind the fixed point math ...
523  DEBUG("DMA: READ COMMAND: $%07X[%s%s %d:%d] -> $%07X[%s%s %d:%d] (L=$%04X) CMD=%d (%s)" NL,
524  DMA_ADDRESSING(source), source.is_io ? "I/O" : "MEM", source.is_modulo ? " MOD" : "", DMA_ADDR_INTEGER_PART(source.step), DMA_ADDR_FRACT_PART(source.step),
525  DMA_ADDRESSING(target), target.is_io ? "I/O" : "MEM", target.is_modulo ? " MOD" : "", DMA_ADDR_INTEGER_PART(target.step), DMA_ADDR_FRACT_PART(target.step),
526  length, dma_op, chained ? "CHAINED" : "LAST"
527  );
528  if (!length)
529  length = 0x10000; // I *think* length of zero means 64K. Probably it's not true!!
530  return cycles;
531  }
532  // We have valid command to be executed, or continue to execute
533  //DEBUG("DMA: EXECUTING: command=%d length=$%04X" NL, command & 3, length);
534  in_dma_update = 1;
535  switch (dma_op) {
536  case COPY_OP: // COPY command (0)
537  copy_next();
538  cycles = 2; // FIXME: correct timing?
539  break;
540  case MIX_OP: // MIX command (1)
541  mix_next();
542  cycles = 3; // FIXME: correct timing?
543  break;
544  case SWAP_OP: // SWAP command (2)
545  swap_next();
546  cycles = 4; // FIXME: correct timing?
547  break;
548  case FILL_OP: // FILL command (3)
549  fill_next();
550  cycles = 1; // FIXME: correct timing?
551  break;
552  }
553  // Maintain length counter or modulo-related counters (in the second case, also add modulo value if needed)
554  if (XEMU_UNLIKELY(modulo.used)) {
555  // modulo mode. we don't use the usual "length" but own counters.
556  // but we DO reset "length" to zero if modulo transfer is done, so
557  // it will be detected by the 'if' at the bottom of this huge function as the end of the DMA op
558  if (modulo.col_counter == modulo.col_limit) {
559  if (modulo.row_counter == modulo.row_limit) {
560  length = 0; // just to fullfish end-of-operation condition
561  } else {
562  // end of modulo "columns", but there are "rows" left, reset columns counter, increment row counter,
563  // also add the modulo value to source and/or target channels, if the given channel is in modulo mode
564  modulo.col_counter = 0;
565  modulo.row_counter++;
566  if (source.is_modulo)
567  source.addr += modulo.value;
568  if (target.is_modulo)
569  target.addr += modulo.value;
570  }
571  } else {
572  modulo.col_counter++;
573  }
574  } else {
575  length--; // non-MODulo mode, we simply decrement length counter ...
576  }
577  if (length <= 0) { // end of DMA operation for the current DMA list entry?
578  if (chained) { // chained?
579  DEBUGDMA("DMA: end of operation, but chained!" NL);
580  dma_status = 0x81; // still busy then, with also bit0 set (chained)
581  command = -1; // signal for next DMA command fetch
582  } else {
583  DEBUGDMA("DMA: end of operation, no chained next one." NL);
584  dma_status = 0; // end of DMA command
585  command = -1;
586  if (hack.enhanced_dma) {
587  DEBUGDMA("DMA: enhanced-end-of-op, restoring context" NL);
588  //memcpy(dma_registers, hack.saved_regs, sizeof(dma_registers));
589  dma_chip_revision = hack.saved_revision;
590  hack.enhanced_dma = 0;
591  }
593  // MEGA65: reset fractional step registers to the default at the end! (it seems M65 does this, by reading its VHDL source)
594  // Note, this is the old DMA behaviour not connected to the "hack" ...
595  dma_registers[0x08] = 0; // source skip rate, fraction part
596  dma_registers[0x09] = 1; // source skip rate, integer part
597  dma_registers[0x0A] = 0; // target skip rate, fraction part
598  dma_registers[0x0B] = 1; // target skip rate, integer part
599  dma_registers[5] = 0; // set back to megabyte selection zero for source
600  dma_registers[6] = 0; // set back to megabyte selection zero for target
601  dma_transparency = 0x100; // no DMA transparency by default
602  }
603  }
604  in_dma_update = 0;
605  return cycles;
606 }
607 
608 
609 
610 int dma_update_multi_steps ( int do_for_cycles )
611 {
612  int cycles = 0;
613  do {
614  cycles += dma_update();
615  } while (cycles <= do_for_cycles && dma_status);
616  return cycles;
617 }
618 
619 
620 void dma_init_set_rev ( unsigned int revision, const Uint8 *rom )
621 {
623  const int rom_suggested_dma_revision = (rom_date < 900000 || rom_date > 910522 || rom_is_openroms);
624  DEBUGPRINT("DMA: ROM version (%d %s) check suggests DMA revision %d" NL, rom_date, rom_name, rom_suggested_dma_revision);
625  revision &= 0xFF;
626  if (revision > 2) {
627  FATAL("Unknown DMA revision value tried to be set (%d)!", revision);
628  } else if (revision == 2) {
629  if (!rom)
630  FATAL("dma_ini_set_rev(): revision == 2 (auto-detect) but rom == NULL (cannot auto-detect)");
631  if (rom_date <= 0)
632  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);
633  dma_chip_revision = rom_suggested_dma_revision;
634  dma_chip_initial_revision = rom_suggested_dma_revision;
635  DEBUGPRINT("DMA: setting chip revision to #%d based on the ROM auto-detection" NL, dma_chip_initial_revision);
636  } else {
637  dma_chip_revision = revision;
638  dma_chip_initial_revision = revision;
639  if (dma_chip_revision != rom_suggested_dma_revision && rom_date > 0)
640  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);
641  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);
642  }
644 }
645 
646 
647 void dma_init ( unsigned int revision )
648 {
649  modulo.enabled = (revision & DMA_FEATURE_MODULO);
650  revision &= ~DMA_FEATURE_MODULO;
651  hack.enhanced_dma = 0;
652  dma_chip_revision_is_dynamic = (revision & DMA_FEATURE_DYNMODESET); // this bit flag means that normal behaviour (M65 can change DMA revision) works
653  revision &= ~DMA_FEATURE_DYNMODESET;
654  hack.enabled = (revision & DMA_FEATURE_HACK);
655  revision &= ~DMA_FEATURE_HACK;
656  DEBUGPRINT("DMA: 'hack' (preliminary!! support for new-style M65 DMA) status: %s" NL, hack.enabled ? "**ENABLED**" : "disabled");
657  if (revision == 2)
658  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
659  if (revision > 1) {
660  FATAL("Unknown DMA revision value tried to be set (%d)!", revision);
661  } else {
662  dma_chip_revision = revision;
663  dma_chip_initial_revision = revision;
664  }
665  DEBUGPRINT("DMA: initializing DMA engine for chip revision %d (initially, may be modified later!), dyn_mode=%s, modulo_support=%s." NL,
667  dma_chip_revision_is_dynamic ? "YES(M65-aware)" : "NO(not-M65-aware)",
668  modulo.enabled ? "ENABLED" : "DISABLED"
669  );
670  dma_reset();
671 }
672 
673 
674 void dma_reset ( void )
675 {
676  command = -1; // no command is fetched yet
677  dma_status = 0;
678  memset(dma_registers, 0, sizeof dma_registers);
679  source.base = 0;
680  target.base = 0;
681  list_base = 0;
682  in_dma_update = 0;
683  dma_self_write_warning = 1;
685  dma_registers[0x09] = 1; // fixpoint math source step integer part (1), fractional (reg#8) is already zero by memset() above
686  dma_registers[0x0B] = 1; // fixpoint math target step integer part (1), fractional (reg#A) is already zero by memset() above
688  dma_transparency = 0x100; // disable transparency by default
690 }
691 
692 
694 {
695  // FIXME: status on ALL registers when read?!
696  DEBUG("DMA: register reading at addr of %d" NL, addr);
697 #if 0
698  if ((addr & 3) != 3)
699  return 0xFF; // other registers are (??????) writeonly? FIXME?
700 #endif
701  return dma_status;
702 }
703 
704 
705 /* --- SNAPSHOT RELATED --- */
706 
707 #ifdef XEMU_SNAPSHOT_SUPPORT
708 
709 // Note: currently state is not saved "within" a DMA operation. It's only a problem, if a DMA
710 // operation is not handled fully here, but implemented as an iterating update method from the
711 // emulator code. FIXME.
712 
713 #include <string.h>
714 
715 #define SNAPSHOT_DMA_BLOCK_VERSION 2
716 #define SNAPSHOT_DMA_BLOCK_SIZE 0x100
717 
718 
719 int dma_snapshot_load_state ( const struct xemu_snapshot_definition_st *def, struct xemu_snapshot_block_st *block )
720 {
721  Uint8 buffer[SNAPSHOT_DMA_BLOCK_SIZE];
722  int a;
723  if (block->block_version != SNAPSHOT_DMA_BLOCK_VERSION || block->sub_counter || block->sub_size != sizeof buffer)
724  RETURN_XSNAPERR_USER("Bad C65 block syntax");
725  a = xemusnap_read_file(buffer, sizeof buffer);
726  if (a) return a;
727  /* loading state ... */
728  memcpy(dma_registers, buffer, sizeof dma_registers);
729  dma_chip_revision = buffer[0x80];
730  dma_chip_initial_revision = buffer[0x81];
731  dma_chip_revision_is_dynamic = buffer[0x82];
732  modulo.enabled = buffer[0x83];
733  dma_status = buffer[0x84];
734  in_dma_update = buffer[0x85];
735  return 0;
736 }
737 
738 
739 int dma_snapshot_save_state ( const struct xemu_snapshot_definition_st *def )
740 {
741  Uint8 buffer[SNAPSHOT_DMA_BLOCK_SIZE];
742  int a = xemusnap_write_block_header(def->idstr, SNAPSHOT_DMA_BLOCK_VERSION);
743  if (a) return a;
744  memset(buffer, 0xFF, sizeof buffer);
745  /* saving state ... */
746  memcpy(buffer, dma_registers, sizeof dma_registers);
747  buffer[0x80] = dma_chip_revision;
748  buffer[0x81] = dma_chip_initial_revision;
749  buffer[0x82] = dma_chip_revision_is_dynamic ? 1 : 0;
750  buffer[0x83] = modulo.enabled ? 1 : 0;
751  buffer[0x84] = dma_status; // bit useless to store (see below, actually it's a problem), but to think about the future ...
752  buffer[0x85] = in_dma_update ? 1 : 0; // -- "" --
753  if (dma_status)
754  WARNING_WINDOW("f018_core DMA snapshot save: snapshot with DMA pending! Snapshot WILL BE incorrect on loading! FIXME!"); // FIXME!
755  return xemusnap_write_sub_block(buffer, sizeof buffer);
756 }
757 
758 #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
rom_is_openroms
int rom_is_openroms
Definition: rom.c:28
MEM_LIST_MASK
#define MEM_LIST_MASK
Definition: dma65.c:136
enabled
int enabled
Definition: dma65.c:90
dma_update
int dma_update(void)
Definition: dma65.c:215
dma_init_set_rev
void dma_init_set_rev(unsigned int revision, Uint8 *rom_ver_signature)
Definition: dma65.c:439
DMA_SOURCE_SKIP_RATE
#define DMA_SOURCE_SKIP_RATE
Definition: dma65.c:111
rom_date
int rom_date
Definition: dma65.c:55
emutools.h
row_limit
int row_limit
Definition: dma65.c:90
rom_detect_date
void rom_detect_date(const Uint8 *rom)
Definition: rom.c:82
dma_chip_revision_override
int dma_chip_revision_override
Definition: dma65.c:58
dma_chip_revision_is_dynamic
int dma_chip_revision_is_dynamic
Definition: dma65.c:57
MEM_ADDR_MASK
#define MEM_ADDR_MASK
Definition: dma65.c:137
DMA_WRITE_TARGET
#define DMA_WRITE_TARGET(data)
Definition: dma65.c:104
WARNING_WINDOW
#define WARNING_WINDOW(...)
Definition: xep128.h:115
DMA_READ_SOURCE
#define DMA_READ_SOURCE()
Definition: dma65.c:140
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
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
XEMU_LIKELY
#define XEMU_LIKELY(__x__)
Definition: emutools_basicdefs.h:124
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
rom.h
cpu65.h
MIX_OP
@ MIX_OP
Definition: dma65.c:60
DMA_ADDR_INTEGER_PART
#define DMA_ADDR_INTEGER_PART(p)
Definition: dma65.c:110
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_ADDRESSING
#define DMA_ADDRESSING(channel)
Definition: dma65.c:115
DMA_TARGET_SKIP_RATE
#define DMA_TARGET_SKIP_RATE
Definition: dma65.c:112
DMA_SOURCE_MEMWRITER_FUNC
#define DMA_SOURCE_MEMWRITER_FUNC
Definition: xemu-target.h:14
dma_init
void dma_init(unsigned int revision)
Definition: dma65.c:465
DMA_SOURCE_IOWRITER_FUNC
#define DMA_SOURCE_IOWRITER_FUNC
Definition: xemu-target.h:13
DMA_ADDR_FRACT_PART
#define DMA_ADDR_FRACT_PART(p)
Definition: dma65.c:113
mask
int mask
Definition: dma65.c:83
value
int value
Definition: dma65.c:90
rom_name
const char * rom_name
Definition: rom.c:45
DMA_FEATURE_HACK
#define DMA_FEATURE_HACK
Definition: dma65.h:26
dma65.h
saved_revision
int saved_revision
Definition: dma65.c:93
DEBUGDMA
#define DEBUGDMA(...)
Definition: dma65.c:51
row_counter
int row_counter
Definition: dma65.c:90
DEBUG
#define DEBUG(...)
Definition: emutools_basicdefs.h:167
DMA_READ_TARGET
#define DMA_READ_TARGET()
Definition: dma65.c:141
enhanced_dma
int enhanced_dma
Definition: dma65.c:91
DMA_READ_LIST_NEXT_BYTE
#define DMA_READ_LIST_NEXT_BYTE()
Definition: dma65.c:183
FATAL
#define FATAL(...)
Definition: xep128.h:117
dma_is_in_use
int dma_is_in_use(void)
Definition: dma65.c:303
XEMU_UNLIKELY
#define XEMU_UNLIKELY(__x__)
Definition: emutools_basicdefs.h:125
rom
char * rom
Definition: rc2014.c:38
dma_chip_initial_revision
int dma_chip_initial_revision
Definition: dma65.c:54