1 |
/* |
/* |
2 |
* QEMU DMA emulation |
* QEMU DMA emulation |
3 |
* |
* |
4 |
* Copyright (c) 2003 Vassili Karpov (malc) |
* Copyright (c) 2003-2004 Vassili Karpov (malc) |
5 |
* |
* |
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
7 |
* of this software and associated documentation files (the "Software"), to deal |
* of this software and associated documentation files (the "Software"), to deal |
8 |
* in the Software without restriction, including without limitation the rights |
* in the Software without restriction, including without limitation the rights |
23 |
*/ |
*/ |
24 |
#include "vl.h" |
#include "vl.h" |
25 |
|
|
26 |
//#define DEBUG_DMA |
/* #define DEBUG_DMA */ |
27 |
|
|
28 |
#define log(...) fprintf (stderr, "dma: " __VA_ARGS__) |
#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__) |
29 |
#ifdef DEBUG_DMA |
#ifdef DEBUG_DMA |
30 |
#define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__) |
#define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__) |
31 |
#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__) |
#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__) |
86 |
|
|
87 |
ichan = channels[nport & 7]; |
ichan = channels[nport & 7]; |
88 |
if (-1 == ichan) { |
if (-1 == ichan) { |
89 |
log ("invalid channel %#x %#x\n", nport, data); |
dolog ("invalid channel %#x %#x\n", nport, data); |
90 |
return; |
return; |
91 |
} |
} |
92 |
d->regs[ichan].page = data; |
d->regs[ichan].page = data; |
99 |
|
|
100 |
ichan = channels[nport & 7]; |
ichan = channels[nport & 7]; |
101 |
if (-1 == ichan) { |
if (-1 == ichan) { |
102 |
log ("invalid channel %#x %#x\n", nport, data); |
dolog ("invalid channel %#x %#x\n", nport, data); |
103 |
return; |
return; |
104 |
} |
} |
105 |
d->regs[ichan].pageh = data; |
d->regs[ichan].pageh = data; |
112 |
|
|
113 |
ichan = channels[nport & 7]; |
ichan = channels[nport & 7]; |
114 |
if (-1 == ichan) { |
if (-1 == ichan) { |
115 |
log ("invalid channel read %#x\n", nport); |
dolog ("invalid channel read %#x\n", nport); |
116 |
return 0; |
return 0; |
117 |
} |
} |
118 |
return d->regs[ichan].page; |
return d->regs[ichan].page; |
125 |
|
|
126 |
ichan = channels[nport & 7]; |
ichan = channels[nport & 7]; |
127 |
if (-1 == ichan) { |
if (-1 == ichan) { |
128 |
log ("invalid channel read %#x\n", nport); |
dolog ("invalid channel read %#x\n", nport); |
129 |
return 0; |
return 0; |
130 |
} |
} |
131 |
return d->regs[ichan].pageh; |
return d->regs[ichan].pageh; |
136 |
struct dma_regs *r; |
struct dma_regs *r; |
137 |
|
|
138 |
r = d->regs + ichan; |
r = d->regs + ichan; |
139 |
r->now[ADDR] = r->base[0] << d->dshift; |
r->now[ADDR] = r->base[ADDR] << d->dshift; |
140 |
r->now[COUNT] = 0; |
r->now[COUNT] = 0; |
141 |
} |
} |
142 |
|
|
152 |
static uint32_t read_chan (void *opaque, uint32_t nport) |
static uint32_t read_chan (void *opaque, uint32_t nport) |
153 |
{ |
{ |
154 |
struct dma_cont *d = opaque; |
struct dma_cont *d = opaque; |
155 |
int ichan, nreg, iport, ff, val; |
int ichan, nreg, iport, ff, val, dir; |
156 |
struct dma_regs *r; |
struct dma_regs *r; |
157 |
|
|
158 |
iport = (nport >> d->dshift) & 0x0f; |
iport = (nport >> d->dshift) & 0x0f; |
160 |
nreg = iport & 1; |
nreg = iport & 1; |
161 |
r = d->regs + ichan; |
r = d->regs + ichan; |
162 |
|
|
163 |
|
dir = ((r->mode >> 5) & 1) ? -1 : 1; |
164 |
ff = getff (d); |
ff = getff (d); |
165 |
if (nreg) |
if (nreg) |
166 |
val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; |
val = (r->base[COUNT] << d->dshift) - r->now[COUNT]; |
167 |
else |
else |
168 |
val = r->now[ADDR] + r->now[COUNT]; |
val = r->now[ADDR] + r->now[COUNT] * dir; |
169 |
|
|
170 |
|
ldebug ("read_chan %#x -> %d\n", iport, val); |
171 |
return (val >> (d->dshift + (ff << 3))) & 0xff; |
return (val >> (d->dshift + (ff << 3))) & 0xff; |
172 |
} |
} |
173 |
|
|
192 |
static void write_cont (void *opaque, uint32_t nport, uint32_t data) |
static void write_cont (void *opaque, uint32_t nport, uint32_t data) |
193 |
{ |
{ |
194 |
struct dma_cont *d = opaque; |
struct dma_cont *d = opaque; |
195 |
int iport, ichan; |
int iport, ichan = 0; |
196 |
|
|
197 |
iport = (nport >> d->dshift) & 0x0f; |
iport = (nport >> d->dshift) & 0x0f; |
198 |
switch (iport) { |
switch (iport) { |
199 |
case 8: /* command */ |
case 0x08: /* command */ |
200 |
if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { |
if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { |
201 |
log ("command %#x not supported\n", data); |
dolog ("command %#x not supported\n", data); |
202 |
return; |
return; |
203 |
} |
} |
204 |
d->command = data; |
d->command = data; |
205 |
break; |
break; |
206 |
|
|
207 |
case 9: |
case 0x09: |
208 |
ichan = data & 3; |
ichan = data & 3; |
209 |
if (data & 4) { |
if (data & 4) { |
210 |
d->status |= 1 << (ichan + 4); |
d->status |= 1 << (ichan + 4); |
215 |
d->status &= ~(1 << ichan); |
d->status &= ~(1 << ichan); |
216 |
break; |
break; |
217 |
|
|
218 |
case 0xa: /* single mask */ |
case 0x0a: /* single mask */ |
219 |
if (data & 4) |
if (data & 4) |
220 |
d->mask |= 1 << (data & 3); |
d->mask |= 1 << (data & 3); |
221 |
else |
else |
222 |
d->mask &= ~(1 << (data & 3)); |
d->mask &= ~(1 << (data & 3)); |
223 |
break; |
break; |
224 |
|
|
225 |
case 0xb: /* mode */ |
case 0x0b: /* mode */ |
226 |
{ |
{ |
227 |
ichan = data & 3; |
ichan = data & 3; |
228 |
#ifdef DEBUG_DMA |
#ifdef DEBUG_DMA |
229 |
int op; |
{ |
230 |
int ai; |
int op, ai, dir, opmode; |
|
int dir; |
|
|
int opmode; |
|
|
|
|
231 |
op = (data >> 2) & 3; |
op = (data >> 2) & 3; |
232 |
ai = (data >> 4) & 1; |
ai = (data >> 4) & 1; |
233 |
dir = (data >> 5) & 1; |
dir = (data >> 5) & 1; |
235 |
|
|
236 |
linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n", |
linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n", |
237 |
ichan, op, ai, dir, opmode); |
ichan, op, ai, dir, opmode); |
238 |
|
} |
239 |
#endif |
#endif |
|
|
|
240 |
d->regs[ichan].mode = data; |
d->regs[ichan].mode = data; |
241 |
break; |
break; |
242 |
} |
} |
243 |
|
|
244 |
case 0xc: /* clear flip flop */ |
case 0x0c: /* clear flip flop */ |
245 |
d->flip_flop = 0; |
d->flip_flop = 0; |
246 |
break; |
break; |
247 |
|
|
248 |
case 0xd: /* reset */ |
case 0x0d: /* reset */ |
249 |
d->flip_flop = 0; |
d->flip_flop = 0; |
250 |
d->mask = ~0; |
d->mask = ~0; |
251 |
d->status = 0; |
d->status = 0; |
252 |
d->command = 0; |
d->command = 0; |
253 |
break; |
break; |
254 |
|
|
255 |
case 0xe: /* clear mask for all channels */ |
case 0x0e: /* clear mask for all channels */ |
256 |
d->mask = 0; |
d->mask = 0; |
257 |
break; |
break; |
258 |
|
|
259 |
case 0xf: /* write mask for all channels */ |
case 0x0f: /* write mask for all channels */ |
260 |
d->mask = data; |
d->mask = data; |
261 |
break; |
break; |
262 |
|
|
263 |
default: |
default: |
264 |
log ("dma: unknown iport %#x\n", iport); |
dolog ("unknown iport %#x\n", iport); |
265 |
break; |
break; |
266 |
} |
} |
267 |
|
|
268 |
#ifdef DEBUG_DMA |
#ifdef DEBUG_DMA |
269 |
if (0xc != iport) { |
if (0xc != iport) { |
270 |
linfo ("nport %#06x, ichan % 2d, val %#06x\n", |
linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n", |
271 |
nport, ichan, data); |
nport, ichan, data); |
272 |
} |
} |
273 |
#endif |
#endif |
277 |
{ |
{ |
278 |
struct dma_cont *d = opaque; |
struct dma_cont *d = opaque; |
279 |
int iport, val; |
int iport, val; |
280 |
|
|
281 |
iport = (nport >> d->dshift) & 0x0f; |
iport = (nport >> d->dshift) & 0x0f; |
282 |
switch (iport) { |
switch (iport) { |
283 |
case 0x08: /* status */ |
case 0x08: /* status */ |
284 |
val = d->status; |
val = d->status; |
285 |
d->status &= 0xf0; |
d->status &= 0xf0; |
286 |
break; |
break; |
287 |
case 0x0f: /* mask */ |
case 0x0f: /* mask */ |
288 |
val = d->mask; |
val = d->mask; |
289 |
break; |
break; |
290 |
default: |
default: |
291 |
val = 0; |
val = 0; |
292 |
break; |
break; |
293 |
} |
} |
294 |
|
|
295 |
|
ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val); |
296 |
return val; |
return val; |
297 |
} |
} |
298 |
|
|
323 |
|
|
324 |
static void channel_run (int ncont, int ichan) |
static void channel_run (int ncont, int ichan) |
325 |
{ |
{ |
|
struct dma_regs *r; |
|
326 |
int n; |
int n; |
327 |
target_ulong addr; |
struct dma_regs *r = &dma_controllers[ncont].regs[ichan]; |
328 |
/* int ai, dir; */ |
#ifdef DEBUG_DMA |
329 |
|
int dir, opmode; |
330 |
|
|
331 |
r = dma_controllers[ncont].regs + ichan; |
dir = (r->mode >> 5) & 1; |
332 |
/* ai = r->mode & 16; */ |
opmode = (r->mode >> 6) & 3; |
|
/* dir = r->mode & 32 ? -1 : 1; */ |
|
333 |
|
|
334 |
/* NOTE: pageh is only used by PPC PREP */ |
if (dir) { |
335 |
addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; |
dolog ("DMA in address decrement mode\n"); |
336 |
n = r->transfer_handler (r->opaque, addr, |
} |
337 |
(r->base[COUNT] << ncont) + (1 << ncont)); |
if (opmode != 1) { |
338 |
r->now[COUNT] = n; |
dolog ("DMA not in single mode select %#x\n", opmode); |
339 |
|
} |
340 |
|
#endif |
341 |
|
|
342 |
ldebug ("dma_pos %d size %d\n", |
r = dma_controllers[ncont].regs + ichan; |
343 |
n, (r->base[1] << ncont) + (1 << ncont)); |
n = r->transfer_handler (r->opaque, ichan + (ncont << 2), |
344 |
|
r->now[COUNT], (r->base[COUNT] + 1) << ncont); |
345 |
|
r->now[COUNT] = n; |
346 |
|
ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont); |
347 |
} |
} |
348 |
|
|
349 |
void DMA_run (void) |
void DMA_run (void) |
366 |
} |
} |
367 |
|
|
368 |
void DMA_register_channel (int nchan, |
void DMA_register_channel (int nchan, |
369 |
DMA_transfer_handler transfer_handler, |
DMA_transfer_handler transfer_handler, |
370 |
void *opaque) |
void *opaque) |
371 |
{ |
{ |
372 |
struct dma_regs *r; |
struct dma_regs *r; |
380 |
r->opaque = opaque; |
r->opaque = opaque; |
381 |
} |
} |
382 |
|
|
383 |
|
int DMA_read_memory (int nchan, void *buf, int pos, int len) |
384 |
|
{ |
385 |
|
struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; |
386 |
|
target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; |
387 |
|
|
388 |
|
if (r->mode & 0x20) { |
389 |
|
int i; |
390 |
|
uint8_t *p = buf; |
391 |
|
|
392 |
|
cpu_physical_memory_read (addr - pos - len, buf, len); |
393 |
|
/* What about 16bit transfers? */ |
394 |
|
for (i = 0; i < len >> 1; i++) { |
395 |
|
uint8_t b = p[len - i - 1]; |
396 |
|
p[i] = b; |
397 |
|
} |
398 |
|
} |
399 |
|
else |
400 |
|
cpu_physical_memory_read (addr + pos, buf, len); |
401 |
|
|
402 |
|
return len; |
403 |
|
} |
404 |
|
|
405 |
|
int DMA_write_memory (int nchan, void *buf, int pos, int len) |
406 |
|
{ |
407 |
|
struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3]; |
408 |
|
target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR]; |
409 |
|
|
410 |
|
if (r->mode & 0x20) { |
411 |
|
int i; |
412 |
|
uint8_t *p = buf; |
413 |
|
|
414 |
|
cpu_physical_memory_write (addr - pos - len, buf, len); |
415 |
|
/* What about 16bit transfers? */ |
416 |
|
for (i = 0; i < len; i++) { |
417 |
|
uint8_t b = p[len - i - 1]; |
418 |
|
p[i] = b; |
419 |
|
} |
420 |
|
} |
421 |
|
else |
422 |
|
cpu_physical_memory_write (addr + pos, buf, len); |
423 |
|
|
424 |
|
return len; |
425 |
|
} |
426 |
|
|
427 |
/* request the emulator to transfer a new DMA memory block ASAP */ |
/* request the emulator to transfer a new DMA memory block ASAP */ |
428 |
void DMA_schedule(int nchan) |
void DMA_schedule(int nchan) |
429 |
{ |
{ |
437 |
} |
} |
438 |
|
|
439 |
/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */ |
/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */ |
440 |
static void dma_init2(struct dma_cont *d, int base, int dshift, |
static void dma_init2(struct dma_cont *d, int base, int dshift, |
441 |
int page_base, int pageh_base) |
int page_base, int pageh_base) |
442 |
{ |
{ |
443 |
const static int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 }; |
const static int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 }; |
449 |
register_ioport_read (base + (i << dshift), 1, 1, read_chan, d); |
register_ioport_read (base + (i << dshift), 1, 1, read_chan, d); |
450 |
} |
} |
451 |
for (i = 0; i < LENOFA (page_port_list); i++) { |
for (i = 0; i < LENOFA (page_port_list); i++) { |
452 |
register_ioport_write (page_base + page_port_list[i], 1, 1, |
register_ioport_write (page_base + page_port_list[i], 1, 1, |
453 |
write_page, d); |
write_page, d); |
454 |
register_ioport_read (page_base + page_port_list[i], 1, 1, |
register_ioport_read (page_base + page_port_list[i], 1, 1, |
455 |
read_page, d); |
read_page, d); |
456 |
if (pageh_base >= 0) { |
if (pageh_base >= 0) { |
457 |
register_ioport_write (pageh_base + page_port_list[i], 1, 1, |
register_ioport_write (pageh_base + page_port_list[i], 1, 1, |
458 |
write_pageh, d); |
write_pageh, d); |
459 |
register_ioport_read (pageh_base + page_port_list[i], 1, 1, |
register_ioport_read (pageh_base + page_port_list[i], 1, 1, |
460 |
read_pageh, d); |
read_pageh, d); |
461 |
} |
} |
462 |
} |
} |
463 |
for (i = 0; i < 8; i++) { |
for (i = 0; i < 8; i++) { |
464 |
register_ioport_write (base + ((i + 8) << dshift), 1, 1, |
register_ioport_write (base + ((i + 8) << dshift), 1, 1, |
465 |
write_cont, d); |
write_cont, d); |
466 |
register_ioport_read (base + ((i + 8) << dshift), 1, 1, |
register_ioport_read (base + ((i + 8) << dshift), 1, 1, |
467 |
read_cont, d); |
read_cont, d); |
468 |
} |
} |
469 |
qemu_register_reset(dma_reset, d); |
qemu_register_reset(dma_reset, d); |
470 |
dma_reset(d); |
dma_reset(d); |
471 |
} |
} |
472 |
|
|
473 |
|
static void dma_save (QEMUFile *f, void *opaque) |
474 |
|
{ |
475 |
|
struct dma_cont *d = opaque; |
476 |
|
int i; |
477 |
|
|
478 |
|
/* qemu_put_8s (f, &d->status); */ |
479 |
|
qemu_put_8s (f, &d->command); |
480 |
|
qemu_put_8s (f, &d->mask); |
481 |
|
qemu_put_8s (f, &d->flip_flop); |
482 |
|
qemu_put_be32s (f, &d->dshift); |
483 |
|
|
484 |
|
for (i = 0; i < 4; ++i) { |
485 |
|
struct dma_regs *r = &d->regs[i]; |
486 |
|
qemu_put_be32s (f, &r->now[0]); |
487 |
|
qemu_put_be32s (f, &r->now[1]); |
488 |
|
qemu_put_be16s (f, &r->base[0]); |
489 |
|
qemu_put_be16s (f, &r->base[1]); |
490 |
|
qemu_put_8s (f, &r->mode); |
491 |
|
qemu_put_8s (f, &r->page); |
492 |
|
qemu_put_8s (f, &r->pageh); |
493 |
|
qemu_put_8s (f, &r->dack); |
494 |
|
qemu_put_8s (f, &r->eop); |
495 |
|
} |
496 |
|
} |
497 |
|
|
498 |
|
static int dma_load (QEMUFile *f, void *opaque, int version_id) |
499 |
|
{ |
500 |
|
struct dma_cont *d = opaque; |
501 |
|
int i; |
502 |
|
|
503 |
|
if (version_id != 1) |
504 |
|
return -EINVAL; |
505 |
|
|
506 |
|
/* qemu_get_8s (f, &d->status); */ |
507 |
|
qemu_get_8s (f, &d->command); |
508 |
|
qemu_get_8s (f, &d->mask); |
509 |
|
qemu_get_8s (f, &d->flip_flop); |
510 |
|
qemu_get_be32s (f, &d->dshift); |
511 |
|
|
512 |
|
for (i = 0; i < 4; ++i) { |
513 |
|
struct dma_regs *r = &d->regs[i]; |
514 |
|
qemu_get_be32s (f, &r->now[0]); |
515 |
|
qemu_get_be32s (f, &r->now[1]); |
516 |
|
qemu_get_be16s (f, &r->base[0]); |
517 |
|
qemu_get_be16s (f, &r->base[1]); |
518 |
|
qemu_get_8s (f, &r->mode); |
519 |
|
qemu_get_8s (f, &r->page); |
520 |
|
qemu_get_8s (f, &r->pageh); |
521 |
|
qemu_get_8s (f, &r->dack); |
522 |
|
qemu_get_8s (f, &r->eop); |
523 |
|
} |
524 |
|
return 0; |
525 |
|
} |
526 |
|
|
527 |
void DMA_init (int high_page_enable) |
void DMA_init (int high_page_enable) |
528 |
{ |
{ |
529 |
dma_init2(&dma_controllers[0], 0x00, 0, 0x80, |
dma_init2(&dma_controllers[0], 0x00, 0, 0x80, |
530 |
high_page_enable ? 0x480 : -1); |
high_page_enable ? 0x480 : -1); |
531 |
dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, |
dma_init2(&dma_controllers[1], 0xc0, 1, 0x88, |
532 |
high_page_enable ? 0x488 : -1); |
high_page_enable ? 0x488 : -1); |
533 |
|
register_savevm ("dma", 0, 1, dma_save, dma_load, &dma_controllers[0]); |
534 |
|
register_savevm ("dma", 1, 1, dma_save, dma_load, &dma_controllers[1]); |
535 |
} |
} |