mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-08 10:13:56 -06:00
audio merge (malc)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1125 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
8f46820d92
commit
85571bc741
24 changed files with 6096 additions and 1732 deletions
309
hw/adlib.c
Normal file
309
hw/adlib.c
Normal file
|
@ -0,0 +1,309 @@
|
|||
/*
|
||||
* QEMU Adlib emulation
|
||||
*
|
||||
* Copyright (c) 2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#include "vl.h"
|
||||
|
||||
#define AUDIO_CAP "adlib"
|
||||
#include "audio/audio.h"
|
||||
|
||||
#ifdef USE_YMF262
|
||||
#define HAS_YMF262 1
|
||||
#include "ymf262.h"
|
||||
void YMF262UpdateOneQEMU(int which, INT16 *dst, int length);
|
||||
#define SHIFT 2
|
||||
#else
|
||||
#include "fmopl.h"
|
||||
#define SHIFT 1
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#define small_delay() Sleep (1)
|
||||
#else
|
||||
#define small_delay() usleep (1)
|
||||
#endif
|
||||
|
||||
#define IO_READ_PROTO(name) \
|
||||
uint32_t name (void *opaque, uint32_t nport)
|
||||
#define IO_WRITE_PROTO(name) \
|
||||
void name (void *opaque, uint32_t nport, uint32_t val)
|
||||
|
||||
static struct {
|
||||
int port;
|
||||
int freq;
|
||||
} conf = {0x220, 44100};
|
||||
|
||||
typedef struct {
|
||||
int enabled;
|
||||
int active;
|
||||
int cparam;
|
||||
int64_t ticks;
|
||||
int bufpos;
|
||||
int16_t *mixbuf;
|
||||
double interval;
|
||||
QEMUTimer *ts, *opl_ts;
|
||||
SWVoice *voice;
|
||||
int left, pos, samples, bytes_per_second, old_free;
|
||||
int refcount;
|
||||
#ifndef USE_YMF262
|
||||
FM_OPL *opl;
|
||||
#endif
|
||||
} AdlibState;
|
||||
|
||||
static AdlibState adlib;
|
||||
|
||||
static IO_WRITE_PROTO(adlib_write)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
int a = nport & 3;
|
||||
int status;
|
||||
|
||||
s->ticks = qemu_get_clock (vm_clock);
|
||||
s->active = 1;
|
||||
AUD_enable (s->voice, 1);
|
||||
|
||||
#ifdef USE_YMF262
|
||||
status = YMF262Write (0, a, val);
|
||||
#else
|
||||
status = OPLWrite (s->opl, a, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
static IO_READ_PROTO(adlib_read)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
uint8_t data;
|
||||
int a = nport & 3;
|
||||
|
||||
#ifdef USE_YMF262
|
||||
(void) s;
|
||||
data = YMF262Read (0, a);
|
||||
#else
|
||||
data = OPLRead (s->opl, a);
|
||||
#endif
|
||||
return data;
|
||||
}
|
||||
|
||||
static void OPL_timer (void *opaque)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
#ifdef USE_YMF262
|
||||
YMF262TimerOver (s->cparam >> 1, s->cparam & 1);
|
||||
#else
|
||||
OPLTimerOver (s->opl, s->cparam);
|
||||
#endif
|
||||
qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
|
||||
}
|
||||
|
||||
static void YMF262TimerHandler (int c, double interval_Sec)
|
||||
{
|
||||
AdlibState *s = &adlib;
|
||||
if (interval_Sec == 0.0) {
|
||||
qemu_del_timer (s->opl_ts);
|
||||
return;
|
||||
}
|
||||
s->cparam = c;
|
||||
s->interval = ticks_per_sec * interval_Sec;
|
||||
qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
|
||||
small_delay ();
|
||||
}
|
||||
|
||||
static int write_audio (AdlibState *s, int samples)
|
||||
{
|
||||
int net = 0;
|
||||
int ss = samples;
|
||||
while (samples) {
|
||||
int nbytes = samples << SHIFT;
|
||||
int wbytes = AUD_write (s->voice,
|
||||
s->mixbuf + (s->pos << (SHIFT - 1)),
|
||||
nbytes);
|
||||
int wsampl = wbytes >> SHIFT;
|
||||
samples -= wsampl;
|
||||
s->pos = (s->pos + wsampl) % s->samples;
|
||||
net += wsampl;
|
||||
if (!wbytes)
|
||||
break;
|
||||
}
|
||||
if (net > ss) {
|
||||
dolog ("WARNING: net > ss\n");
|
||||
}
|
||||
return net;
|
||||
}
|
||||
|
||||
static void timer (void *opaque)
|
||||
{
|
||||
AdlibState *s = opaque;
|
||||
int elapsed, samples, net = 0;
|
||||
|
||||
if (s->refcount)
|
||||
dolog ("refcount=%d\n", s->refcount);
|
||||
|
||||
s->refcount += 1;
|
||||
if (!(s->active && s->enabled))
|
||||
goto reset;
|
||||
|
||||
AUD_run ();
|
||||
|
||||
while (s->left) {
|
||||
int written = write_audio (s, s->left);
|
||||
net += written;
|
||||
if (!written)
|
||||
goto reset2;
|
||||
s->left -= written;
|
||||
}
|
||||
s->pos = 0;
|
||||
|
||||
elapsed = AUD_calc_elapsed (s->voice);
|
||||
if (!elapsed)
|
||||
goto reset2;
|
||||
|
||||
/* elapsed = AUD_get_free (s->voice); */
|
||||
samples = elapsed >> SHIFT;
|
||||
if (!samples)
|
||||
goto reset2;
|
||||
|
||||
samples = audio_MIN (samples, s->samples - s->pos);
|
||||
if (s->left)
|
||||
dolog ("left=%d samples=%d elapsed=%d free=%d\n",
|
||||
s->left, samples, elapsed, AUD_get_free (s->voice));
|
||||
|
||||
if (!samples)
|
||||
goto reset2;
|
||||
|
||||
#ifdef USE_YMF262
|
||||
YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
|
||||
#else
|
||||
YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
|
||||
#endif
|
||||
|
||||
while (samples) {
|
||||
int written = write_audio (s, samples);
|
||||
net += written;
|
||||
if (!written)
|
||||
break;
|
||||
samples -= written;
|
||||
}
|
||||
if (!samples)
|
||||
s->pos = 0;
|
||||
s->left = samples;
|
||||
|
||||
reset2:
|
||||
AUD_adjust (s->voice, net << SHIFT);
|
||||
reset:
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + ticks_per_sec / 1024);
|
||||
s->refcount -= 1;
|
||||
}
|
||||
|
||||
static void Adlib_fini (AdlibState *s)
|
||||
{
|
||||
#ifdef USE_YMF262
|
||||
YMF262Shutdown ();
|
||||
#else
|
||||
if (s->opl) {
|
||||
OPLDestroy (s->opl);
|
||||
s->opl = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (s->opl_ts)
|
||||
qemu_free_timer (s->opl_ts);
|
||||
|
||||
if (s->ts)
|
||||
qemu_free_timer (s->ts);
|
||||
|
||||
#define maybe_free(p) if (p) qemu_free (p)
|
||||
maybe_free (s->mixbuf);
|
||||
#undef maybe_free
|
||||
|
||||
s->active = 0;
|
||||
s->enabled = 0;
|
||||
}
|
||||
|
||||
void Adlib_init (void)
|
||||
{
|
||||
AdlibState *s = &adlib;
|
||||
|
||||
memset (s, 0, sizeof (*s));
|
||||
|
||||
#ifdef USE_YMF262
|
||||
if (YMF262Init (1, 14318180, conf.freq)) {
|
||||
dolog ("YMF262Init %d failed\n", conf.freq);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
YMF262SetTimerHandler (0, YMF262TimerHandler, 0);
|
||||
s->enabled = 1;
|
||||
}
|
||||
#else
|
||||
s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
|
||||
if (!s->opl) {
|
||||
dolog ("OPLCreate %d failed\n", conf.freq);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
OPLSetTimerHandler (s->opl, YMF262TimerHandler, 0);
|
||||
s->enabled = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
s->opl_ts = qemu_new_timer (vm_clock, OPL_timer, s);
|
||||
if (!s->opl_ts) {
|
||||
dolog ("Can not get timer for adlib emulation\n");
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
}
|
||||
|
||||
s->ts = qemu_new_timer (vm_clock, timer, s);
|
||||
if (!s->opl_ts) {
|
||||
dolog ("Can not get timer for adlib emulation\n");
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
}
|
||||
|
||||
s->voice = AUD_open (s->voice, "adlib", conf.freq, SHIFT, AUD_FMT_S16);
|
||||
if (!s->voice) {
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
}
|
||||
|
||||
s->bytes_per_second = conf.freq << SHIFT;
|
||||
s->samples = AUD_get_buffer_size (s->voice) >> SHIFT;
|
||||
s->mixbuf = qemu_mallocz (s->samples << SHIFT);
|
||||
|
||||
if (!s->mixbuf) {
|
||||
dolog ("not enough memory for adlib mixing buffer (%d)\n",
|
||||
s->samples << SHIFT);
|
||||
Adlib_fini (s);
|
||||
return;
|
||||
}
|
||||
register_ioport_read (0x388, 4, 1, adlib_read, s);
|
||||
register_ioport_write (0x388, 4, 1, adlib_write, s);
|
||||
|
||||
register_ioport_read (conf.port, 4, 1, adlib_read, s);
|
||||
register_ioport_write (conf.port, 4, 1, adlib_write, s);
|
||||
|
||||
register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
|
||||
register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
|
||||
|
||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
|
||||
}
|
215
hw/dma.c
215
hw/dma.c
|
@ -1,8 +1,8 @@
|
|||
/*
|
||||
* QEMU DMA emulation
|
||||
*
|
||||
* Copyright (c) 2003 Vassili Karpov (malc)
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2003-2004 Vassili Karpov (malc)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
|
@ -23,9 +23,9 @@
|
|||
*/
|
||||
#include "vl.h"
|
||||
|
||||
//#define DEBUG_DMA
|
||||
/* #define DEBUG_DMA */
|
||||
|
||||
#define log(...) fprintf (stderr, "dma: " __VA_ARGS__)
|
||||
#define dolog(...) fprintf (stderr, "dma: " __VA_ARGS__)
|
||||
#ifdef DEBUG_DMA
|
||||
#define lwarn(...) fprintf (stderr, "dma: " __VA_ARGS__)
|
||||
#define linfo(...) fprintf (stderr, "dma: " __VA_ARGS__)
|
||||
|
@ -86,7 +86,7 @@ static void write_page (void *opaque, uint32_t nport, uint32_t data)
|
|||
|
||||
ichan = channels[nport & 7];
|
||||
if (-1 == ichan) {
|
||||
log ("invalid channel %#x %#x\n", nport, data);
|
||||
dolog ("invalid channel %#x %#x\n", nport, data);
|
||||
return;
|
||||
}
|
||||
d->regs[ichan].page = data;
|
||||
|
@ -99,7 +99,7 @@ static void write_pageh (void *opaque, uint32_t nport, uint32_t data)
|
|||
|
||||
ichan = channels[nport & 7];
|
||||
if (-1 == ichan) {
|
||||
log ("invalid channel %#x %#x\n", nport, data);
|
||||
dolog ("invalid channel %#x %#x\n", nport, data);
|
||||
return;
|
||||
}
|
||||
d->regs[ichan].pageh = data;
|
||||
|
@ -112,7 +112,7 @@ static uint32_t read_page (void *opaque, uint32_t nport)
|
|||
|
||||
ichan = channels[nport & 7];
|
||||
if (-1 == ichan) {
|
||||
log ("invalid channel read %#x\n", nport);
|
||||
dolog ("invalid channel read %#x\n", nport);
|
||||
return 0;
|
||||
}
|
||||
return d->regs[ichan].page;
|
||||
|
@ -125,7 +125,7 @@ static uint32_t read_pageh (void *opaque, uint32_t nport)
|
|||
|
||||
ichan = channels[nport & 7];
|
||||
if (-1 == ichan) {
|
||||
log ("invalid channel read %#x\n", nport);
|
||||
dolog ("invalid channel read %#x\n", nport);
|
||||
return 0;
|
||||
}
|
||||
return d->regs[ichan].pageh;
|
||||
|
@ -136,7 +136,7 @@ static inline void init_chan (struct dma_cont *d, int ichan)
|
|||
struct dma_regs *r;
|
||||
|
||||
r = d->regs + ichan;
|
||||
r->now[ADDR] = r->base[0] << d->dshift;
|
||||
r->now[ADDR] = r->base[ADDR] << d->dshift;
|
||||
r->now[COUNT] = 0;
|
||||
}
|
||||
|
||||
|
@ -152,7 +152,7 @@ static inline int getff (struct dma_cont *d)
|
|||
static uint32_t read_chan (void *opaque, uint32_t nport)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int ichan, nreg, iport, ff, val;
|
||||
int ichan, nreg, iport, ff, val, dir;
|
||||
struct dma_regs *r;
|
||||
|
||||
iport = (nport >> d->dshift) & 0x0f;
|
||||
|
@ -160,12 +160,14 @@ static uint32_t read_chan (void *opaque, uint32_t nport)
|
|||
nreg = iport & 1;
|
||||
r = d->regs + ichan;
|
||||
|
||||
dir = ((r->mode >> 5) & 1) ? -1 : 1;
|
||||
ff = getff (d);
|
||||
if (nreg)
|
||||
val = (r->base[COUNT] << d->dshift) - r->now[COUNT];
|
||||
else
|
||||
val = r->now[ADDR] + r->now[COUNT];
|
||||
val = r->now[ADDR] + r->now[COUNT] * dir;
|
||||
|
||||
ldebug ("read_chan %#x -> %d\n", iport, val);
|
||||
return (val >> (d->dshift + (ff << 3))) & 0xff;
|
||||
}
|
||||
|
||||
|
@ -190,19 +192,19 @@ static void write_chan (void *opaque, uint32_t nport, uint32_t data)
|
|||
static void write_cont (void *opaque, uint32_t nport, uint32_t data)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int iport, ichan;
|
||||
int iport, ichan = 0;
|
||||
|
||||
iport = (nport >> d->dshift) & 0x0f;
|
||||
switch (iport) {
|
||||
case 8: /* command */
|
||||
case 0x08: /* command */
|
||||
if ((data != 0) && (data & CMD_NOT_SUPPORTED)) {
|
||||
log ("command %#x not supported\n", data);
|
||||
dolog ("command %#x not supported\n", data);
|
||||
return;
|
||||
}
|
||||
d->command = data;
|
||||
break;
|
||||
|
||||
case 9:
|
||||
case 0x09:
|
||||
ichan = data & 3;
|
||||
if (data & 4) {
|
||||
d->status |= 1 << (ichan + 4);
|
||||
|
@ -213,22 +215,19 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data)
|
|||
d->status &= ~(1 << ichan);
|
||||
break;
|
||||
|
||||
case 0xa: /* single mask */
|
||||
case 0x0a: /* single mask */
|
||||
if (data & 4)
|
||||
d->mask |= 1 << (data & 3);
|
||||
else
|
||||
d->mask &= ~(1 << (data & 3));
|
||||
break;
|
||||
|
||||
case 0xb: /* mode */
|
||||
case 0x0b: /* mode */
|
||||
{
|
||||
ichan = data & 3;
|
||||
#ifdef DEBUG_DMA
|
||||
int op;
|
||||
int ai;
|
||||
int dir;
|
||||
int opmode;
|
||||
|
||||
{
|
||||
int op, ai, dir, opmode;
|
||||
op = (data >> 2) & 3;
|
||||
ai = (data >> 4) & 1;
|
||||
dir = (data >> 5) & 1;
|
||||
|
@ -236,39 +235,39 @@ static void write_cont (void *opaque, uint32_t nport, uint32_t data)
|
|||
|
||||
linfo ("ichan %d, op %d, ai %d, dir %d, opmode %d\n",
|
||||
ichan, op, ai, dir, opmode);
|
||||
}
|
||||
#endif
|
||||
|
||||
d->regs[ichan].mode = data;
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xc: /* clear flip flop */
|
||||
case 0x0c: /* clear flip flop */
|
||||
d->flip_flop = 0;
|
||||
break;
|
||||
|
||||
case 0xd: /* reset */
|
||||
case 0x0d: /* reset */
|
||||
d->flip_flop = 0;
|
||||
d->mask = ~0;
|
||||
d->status = 0;
|
||||
d->command = 0;
|
||||
break;
|
||||
|
||||
case 0xe: /* clear mask for all channels */
|
||||
case 0x0e: /* clear mask for all channels */
|
||||
d->mask = 0;
|
||||
break;
|
||||
|
||||
case 0xf: /* write mask for all channels */
|
||||
case 0x0f: /* write mask for all channels */
|
||||
d->mask = data;
|
||||
break;
|
||||
|
||||
default:
|
||||
log ("dma: unknown iport %#x\n", iport);
|
||||
dolog ("unknown iport %#x\n", iport);
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_DMA
|
||||
if (0xc != iport) {
|
||||
linfo ("nport %#06x, ichan % 2d, val %#06x\n",
|
||||
linfo ("write_cont: nport %#06x, ichan % 2d, val %#06x\n",
|
||||
nport, ichan, data);
|
||||
}
|
||||
#endif
|
||||
|
@ -278,20 +277,22 @@ static uint32_t read_cont (void *opaque, uint32_t nport)
|
|||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int iport, val;
|
||||
|
||||
|
||||
iport = (nport >> d->dshift) & 0x0f;
|
||||
switch (iport) {
|
||||
case 0x08: /* status */
|
||||
case 0x08: /* status */
|
||||
val = d->status;
|
||||
d->status &= 0xf0;
|
||||
break;
|
||||
case 0x0f: /* mask */
|
||||
case 0x0f: /* mask */
|
||||
val = d->mask;
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
ldebug ("read_cont: nport %#06x, iport %#04x val %#x\n", nport, iport, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
@ -322,23 +323,27 @@ void DMA_release_DREQ (int nchan)
|
|||
|
||||
static void channel_run (int ncont, int ichan)
|
||||
{
|
||||
struct dma_regs *r;
|
||||
int n;
|
||||
target_ulong addr;
|
||||
/* int ai, dir; */
|
||||
struct dma_regs *r = &dma_controllers[ncont].regs[ichan];
|
||||
#ifdef DEBUG_DMA
|
||||
int dir, opmode;
|
||||
|
||||
dir = (r->mode >> 5) & 1;
|
||||
opmode = (r->mode >> 6) & 3;
|
||||
|
||||
if (dir) {
|
||||
dolog ("DMA in address decrement mode\n");
|
||||
}
|
||||
if (opmode != 1) {
|
||||
dolog ("DMA not in single mode select %#x\n", opmode);
|
||||
}
|
||||
#endif
|
||||
|
||||
r = dma_controllers[ncont].regs + ichan;
|
||||
/* ai = r->mode & 16; */
|
||||
/* dir = r->mode & 32 ? -1 : 1; */
|
||||
|
||||
/* NOTE: pageh is only used by PPC PREP */
|
||||
addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
|
||||
n = r->transfer_handler (r->opaque, addr,
|
||||
(r->base[COUNT] << ncont) + (1 << ncont));
|
||||
n = r->transfer_handler (r->opaque, ichan + (ncont << 2),
|
||||
r->now[COUNT], (r->base[COUNT] + 1) << ncont);
|
||||
r->now[COUNT] = n;
|
||||
|
||||
ldebug ("dma_pos %d size %d\n",
|
||||
n, (r->base[1] << ncont) + (1 << ncont));
|
||||
ldebug ("dma_pos %d size %d\n", n, (r->base[COUNT] + 1) << ncont);
|
||||
}
|
||||
|
||||
void DMA_run (void)
|
||||
|
@ -361,7 +366,7 @@ void DMA_run (void)
|
|||
}
|
||||
|
||||
void DMA_register_channel (int nchan,
|
||||
DMA_transfer_handler transfer_handler,
|
||||
DMA_transfer_handler transfer_handler,
|
||||
void *opaque)
|
||||
{
|
||||
struct dma_regs *r;
|
||||
|
@ -375,6 +380,50 @@ void DMA_register_channel (int nchan,
|
|||
r->opaque = opaque;
|
||||
}
|
||||
|
||||
int DMA_read_memory (int nchan, void *buf, int pos, int len)
|
||||
{
|
||||
struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
|
||||
target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
|
||||
|
||||
if (r->mode & 0x20) {
|
||||
int i;
|
||||
uint8_t *p = buf;
|
||||
|
||||
cpu_physical_memory_read (addr - pos - len, buf, len);
|
||||
/* What about 16bit transfers? */
|
||||
for (i = 0; i < len >> 1; i++) {
|
||||
uint8_t b = p[len - i - 1];
|
||||
p[i] = b;
|
||||
}
|
||||
}
|
||||
else
|
||||
cpu_physical_memory_read (addr + pos, buf, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int DMA_write_memory (int nchan, void *buf, int pos, int len)
|
||||
{
|
||||
struct dma_regs *r = &dma_controllers[nchan > 3].regs[nchan & 3];
|
||||
target_ulong addr = ((r->pageh & 0x7f) << 24) | (r->page << 16) | r->now[ADDR];
|
||||
|
||||
if (r->mode & 0x20) {
|
||||
int i;
|
||||
uint8_t *p = buf;
|
||||
|
||||
cpu_physical_memory_write (addr - pos - len, buf, len);
|
||||
/* What about 16bit transfers? */
|
||||
for (i = 0; i < len; i++) {
|
||||
uint8_t b = p[len - i - 1];
|
||||
p[i] = b;
|
||||
}
|
||||
}
|
||||
else
|
||||
cpu_physical_memory_write (addr + pos, buf, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* request the emulator to transfer a new DMA memory block ASAP */
|
||||
void DMA_schedule(int nchan)
|
||||
{
|
||||
|
@ -388,7 +437,7 @@ static void dma_reset(void *opaque)
|
|||
}
|
||||
|
||||
/* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
|
||||
static void dma_init2(struct dma_cont *d, int base, int dshift,
|
||||
static void dma_init2(struct dma_cont *d, int base, int dshift,
|
||||
int page_base, int pageh_base)
|
||||
{
|
||||
const static int page_port_list[] = { 0x1, 0x2, 0x3, 0x7 };
|
||||
|
@ -400,31 +449,87 @@ static void dma_init2(struct dma_cont *d, int base, int dshift,
|
|||
register_ioport_read (base + (i << dshift), 1, 1, read_chan, d);
|
||||
}
|
||||
for (i = 0; i < LENOFA (page_port_list); i++) {
|
||||
register_ioport_write (page_base + page_port_list[i], 1, 1,
|
||||
register_ioport_write (page_base + page_port_list[i], 1, 1,
|
||||
write_page, d);
|
||||
register_ioport_read (page_base + page_port_list[i], 1, 1,
|
||||
register_ioport_read (page_base + page_port_list[i], 1, 1,
|
||||
read_page, d);
|
||||
if (pageh_base >= 0) {
|
||||
register_ioport_write (pageh_base + page_port_list[i], 1, 1,
|
||||
register_ioport_write (pageh_base + page_port_list[i], 1, 1,
|
||||
write_pageh, d);
|
||||
register_ioport_read (pageh_base + page_port_list[i], 1, 1,
|
||||
register_ioport_read (pageh_base + page_port_list[i], 1, 1,
|
||||
read_pageh, d);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < 8; i++) {
|
||||
register_ioport_write (base + ((i + 8) << dshift), 1, 1,
|
||||
register_ioport_write (base + ((i + 8) << dshift), 1, 1,
|
||||
write_cont, d);
|
||||
register_ioport_read (base + ((i + 8) << dshift), 1, 1,
|
||||
register_ioport_read (base + ((i + 8) << dshift), 1, 1,
|
||||
read_cont, d);
|
||||
}
|
||||
qemu_register_reset(dma_reset, d);
|
||||
dma_reset(d);
|
||||
}
|
||||
|
||||
static void dma_save (QEMUFile *f, void *opaque)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int i;
|
||||
|
||||
/* qemu_put_8s (f, &d->status); */
|
||||
qemu_put_8s (f, &d->command);
|
||||
qemu_put_8s (f, &d->mask);
|
||||
qemu_put_8s (f, &d->flip_flop);
|
||||
qemu_put_be32s (f, &d->dshift);
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
struct dma_regs *r = &d->regs[i];
|
||||
qemu_put_be32s (f, &r->now[0]);
|
||||
qemu_put_be32s (f, &r->now[1]);
|
||||
qemu_put_be16s (f, &r->base[0]);
|
||||
qemu_put_be16s (f, &r->base[1]);
|
||||
qemu_put_8s (f, &r->mode);
|
||||
qemu_put_8s (f, &r->page);
|
||||
qemu_put_8s (f, &r->pageh);
|
||||
qemu_put_8s (f, &r->dack);
|
||||
qemu_put_8s (f, &r->eop);
|
||||
}
|
||||
}
|
||||
|
||||
static int dma_load (QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
struct dma_cont *d = opaque;
|
||||
int i;
|
||||
|
||||
if (version_id != 1)
|
||||
return -EINVAL;
|
||||
|
||||
/* qemu_get_8s (f, &d->status); */
|
||||
qemu_get_8s (f, &d->command);
|
||||
qemu_get_8s (f, &d->mask);
|
||||
qemu_get_8s (f, &d->flip_flop);
|
||||
qemu_get_be32s (f, &d->dshift);
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
struct dma_regs *r = &d->regs[i];
|
||||
qemu_get_be32s (f, &r->now[0]);
|
||||
qemu_get_be32s (f, &r->now[1]);
|
||||
qemu_get_be16s (f, &r->base[0]);
|
||||
qemu_get_be16s (f, &r->base[1]);
|
||||
qemu_get_8s (f, &r->mode);
|
||||
qemu_get_8s (f, &r->page);
|
||||
qemu_get_8s (f, &r->pageh);
|
||||
qemu_get_8s (f, &r->dack);
|
||||
qemu_get_8s (f, &r->eop);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DMA_init (int high_page_enable)
|
||||
{
|
||||
dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
|
||||
dma_init2(&dma_controllers[0], 0x00, 0, 0x80,
|
||||
high_page_enable ? 0x480 : -1);
|
||||
dma_init2(&dma_controllers[1], 0xc0, 1, 0x88,
|
||||
high_page_enable ? 0x488 : -1);
|
||||
register_savevm ("dma", 0, 1, dma_save, dma_load, &dma_controllers[0]);
|
||||
register_savevm ("dma", 1, 1, dma_save, dma_load, &dma_controllers[1]);
|
||||
}
|
||||
|
|
31
hw/fdc.c
31
hw/fdc.c
|
@ -313,7 +313,8 @@ static void fd_reset (fdrive_t *drv)
|
|||
|
||||
static void fdctrl_reset (fdctrl_t *fdctrl, int do_irq);
|
||||
static void fdctrl_reset_fifo (fdctrl_t *fdctrl);
|
||||
static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size);
|
||||
static int fdctrl_transfer_handler (void *opaque, int nchan,
|
||||
int dma_pos, int dma_len);
|
||||
static void fdctrl_raise_irq (fdctrl_t *fdctrl, uint8_t status);
|
||||
static void fdctrl_result_timer(void *opaque);
|
||||
|
||||
|
@ -908,7 +909,8 @@ static void fdctrl_start_transfer_del (fdctrl_t *fdctrl, int direction)
|
|||
}
|
||||
|
||||
/* handlers for DMA transfers */
|
||||
static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size)
|
||||
static int fdctrl_transfer_handler (void *opaque, int nchan,
|
||||
int dma_pos, int dma_len)
|
||||
{
|
||||
fdctrl_t *fdctrl;
|
||||
fdrive_t *cur_drv;
|
||||
|
@ -924,8 +926,8 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size)
|
|||
if (fdctrl->data_dir == FD_DIR_SCANE || fdctrl->data_dir == FD_DIR_SCANL ||
|
||||
fdctrl->data_dir == FD_DIR_SCANH)
|
||||
status2 = 0x04;
|
||||
if (size > fdctrl->data_len)
|
||||
size = fdctrl->data_len;
|
||||
if (dma_len > fdctrl->data_len)
|
||||
dma_len = fdctrl->data_len;
|
||||
if (cur_drv->bs == NULL) {
|
||||
if (fdctrl->data_dir == FD_DIR_WRITE)
|
||||
fdctrl_stop_transfer(fdctrl, 0x60, 0x00, 0x00);
|
||||
|
@ -935,8 +937,8 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size)
|
|||
goto transfer_error;
|
||||
}
|
||||
rel_pos = fdctrl->data_pos % FD_SECTOR_LEN;
|
||||
for (start_pos = fdctrl->data_pos; fdctrl->data_pos < size;) {
|
||||
len = size - fdctrl->data_pos;
|
||||
for (start_pos = fdctrl->data_pos; fdctrl->data_pos < dma_len;) {
|
||||
len = dma_len - fdctrl->data_pos;
|
||||
if (len + rel_pos > FD_SECTOR_LEN)
|
||||
len = FD_SECTOR_LEN - rel_pos;
|
||||
FLOPPY_DPRINTF("copy %d bytes (%d %d %d) %d pos %d %02x %02x "
|
||||
|
@ -958,13 +960,17 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size)
|
|||
switch (fdctrl->data_dir) {
|
||||
case FD_DIR_READ:
|
||||
/* READ commands */
|
||||
cpu_physical_memory_write(addr + fdctrl->data_pos,
|
||||
fdctrl->fifo + rel_pos, len);
|
||||
DMA_write_memory (nchan, fdctrl->fifo + rel_pos,
|
||||
fdctrl->data_pos, len);
|
||||
/* cpu_physical_memory_write(addr + fdctrl->data_pos, */
|
||||
/* fdctrl->fifo + rel_pos, len); */
|
||||
break;
|
||||
case FD_DIR_WRITE:
|
||||
/* WRITE commands */
|
||||
cpu_physical_memory_read(addr + fdctrl->data_pos,
|
||||
fdctrl->fifo + rel_pos, len);
|
||||
DMA_read_memory (nchan, fdctrl->fifo + rel_pos,
|
||||
fdctrl->data_pos, len);
|
||||
/* cpu_physical_memory_read(addr + fdctrl->data_pos, */
|
||||
/* fdctrl->fifo + rel_pos, len); */
|
||||
if (bdrv_write(cur_drv->bs, fd_sector(cur_drv),
|
||||
fdctrl->fifo, 1) < 0) {
|
||||
FLOPPY_ERROR("writting sector %d\n", fd_sector(cur_drv));
|
||||
|
@ -977,8 +983,9 @@ static int fdctrl_transfer_handler (void *opaque, target_ulong addr, int size)
|
|||
{
|
||||
uint8_t tmpbuf[FD_SECTOR_LEN];
|
||||
int ret;
|
||||
cpu_physical_memory_read(addr + fdctrl->data_pos,
|
||||
tmpbuf, len);
|
||||
DMA_read_memory (nchan, tmpbuf, fdctrl->data_pos, len);
|
||||
/* cpu_physical_memory_read(addr + fdctrl->data_pos, */
|
||||
/* tmpbuf, len); */
|
||||
ret = memcmp(tmpbuf, fdctrl->fifo + rel_pos, len);
|
||||
if (ret == 0) {
|
||||
status2 = 0x08;
|
||||
|
|
1390
hw/fmopl.c
Normal file
1390
hw/fmopl.c
Normal file
File diff suppressed because it is too large
Load diff
174
hw/fmopl.h
Normal file
174
hw/fmopl.h
Normal file
|
@ -0,0 +1,174 @@
|
|||
#ifndef __FMOPL_H_
|
||||
#define __FMOPL_H_
|
||||
|
||||
/* --- select emulation chips --- */
|
||||
#define BUILD_YM3812 (HAS_YM3812)
|
||||
//#define BUILD_YM3526 (HAS_YM3526)
|
||||
//#define BUILD_Y8950 (HAS_Y8950)
|
||||
|
||||
/* --- system optimize --- */
|
||||
/* select bit size of output : 8 or 16 */
|
||||
#define OPL_OUTPUT_BIT 16
|
||||
|
||||
/* compiler dependence */
|
||||
#ifndef OSD_CPU_H
|
||||
#define OSD_CPU_H
|
||||
typedef unsigned char UINT8; /* unsigned 8bit */
|
||||
typedef unsigned short UINT16; /* unsigned 16bit */
|
||||
typedef unsigned int UINT32; /* unsigned 32bit */
|
||||
typedef signed char INT8; /* signed 8bit */
|
||||
typedef signed short INT16; /* signed 16bit */
|
||||
typedef signed int INT32; /* signed 32bit */
|
||||
#endif
|
||||
|
||||
#if (OPL_OUTPUT_BIT==16)
|
||||
typedef INT16 OPLSAMPLE;
|
||||
#endif
|
||||
#if (OPL_OUTPUT_BIT==8)
|
||||
typedef unsigned char OPLSAMPLE;
|
||||
#endif
|
||||
|
||||
|
||||
#if BUILD_Y8950
|
||||
#include "ymdeltat.h"
|
||||
#endif
|
||||
|
||||
typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
|
||||
typedef void (*OPL_IRQHANDLER)(int param,int irq);
|
||||
typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
|
||||
typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
|
||||
typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
|
||||
|
||||
/* !!!!! here is private section , do not access there member direct !!!!! */
|
||||
|
||||
#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
|
||||
#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
|
||||
#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
|
||||
#define OPL_TYPE_IO 0x08 /* I/O port */
|
||||
|
||||
/* Saving is necessary for member of the 'R' mark for suspend/resume */
|
||||
/* ---------- OPL one of slot ---------- */
|
||||
typedef struct fm_opl_slot {
|
||||
INT32 TL; /* total level :TL << 8 */
|
||||
INT32 TLL; /* adjusted now TL */
|
||||
UINT8 KSR; /* key scale rate :(shift down bit) */
|
||||
INT32 *AR; /* attack rate :&AR_TABLE[AR<<2] */
|
||||
INT32 *DR; /* decay rate :&DR_TALBE[DR<<2] */
|
||||
INT32 SL; /* sustin level :SL_TALBE[SL] */
|
||||
INT32 *RR; /* release rate :&DR_TABLE[RR<<2] */
|
||||
UINT8 ksl; /* keyscale level :(shift down bits) */
|
||||
UINT8 ksr; /* key scale rate :kcode>>KSR */
|
||||
UINT32 mul; /* multiple :ML_TABLE[ML] */
|
||||
UINT32 Cnt; /* frequency count : */
|
||||
UINT32 Incr; /* frequency step : */
|
||||
/* envelope generator state */
|
||||
UINT8 eg_typ; /* envelope type flag */
|
||||
UINT8 evm; /* envelope phase */
|
||||
INT32 evc; /* envelope counter */
|
||||
INT32 eve; /* envelope counter end point */
|
||||
INT32 evs; /* envelope counter step */
|
||||
INT32 evsa; /* envelope step for AR :AR[ksr] */
|
||||
INT32 evsd; /* envelope step for DR :DR[ksr] */
|
||||
INT32 evsr; /* envelope step for RR :RR[ksr] */
|
||||
/* LFO */
|
||||
UINT8 ams; /* ams flag */
|
||||
UINT8 vib; /* vibrate flag */
|
||||
/* wave selector */
|
||||
INT32 **wavetable;
|
||||
}OPL_SLOT;
|
||||
|
||||
/* ---------- OPL one of channel ---------- */
|
||||
typedef struct fm_opl_channel {
|
||||
OPL_SLOT SLOT[2];
|
||||
UINT8 CON; /* connection type */
|
||||
UINT8 FB; /* feed back :(shift down bit) */
|
||||
INT32 *connect1; /* slot1 output pointer */
|
||||
INT32 *connect2; /* slot2 output pointer */
|
||||
INT32 op1_out[2]; /* slot1 output for selfeedback */
|
||||
/* phase generator state */
|
||||
UINT32 block_fnum; /* block+fnum : */
|
||||
UINT8 kcode; /* key code : KeyScaleCode */
|
||||
UINT32 fc; /* Freq. Increment base */
|
||||
UINT32 ksl_base; /* KeyScaleLevel Base step */
|
||||
UINT8 keyon; /* key on/off flag */
|
||||
} OPL_CH;
|
||||
|
||||
/* OPL state */
|
||||
typedef struct fm_opl_f {
|
||||
UINT8 type; /* chip type */
|
||||
int clock; /* master clock (Hz) */
|
||||
int rate; /* sampling rate (Hz) */
|
||||
double freqbase; /* frequency base */
|
||||
double TimerBase; /* Timer base time (==sampling time) */
|
||||
UINT8 address; /* address register */
|
||||
UINT8 status; /* status flag */
|
||||
UINT8 statusmask; /* status mask */
|
||||
UINT32 mode; /* Reg.08 : CSM , notesel,etc. */
|
||||
/* Timer */
|
||||
int T[2]; /* timer counter */
|
||||
UINT8 st[2]; /* timer enable */
|
||||
/* FM channel slots */
|
||||
OPL_CH *P_CH; /* pointer of CH */
|
||||
int max_ch; /* maximum channel */
|
||||
/* Rythm sention */
|
||||
UINT8 rythm; /* Rythm mode , key flag */
|
||||
#if BUILD_Y8950
|
||||
/* Delta-T ADPCM unit (Y8950) */
|
||||
YM_DELTAT *deltat; /* DELTA-T ADPCM */
|
||||
#endif
|
||||
/* Keyboard / I/O interface unit (Y8950) */
|
||||
UINT8 portDirection;
|
||||
UINT8 portLatch;
|
||||
OPL_PORTHANDLER_R porthandler_r;
|
||||
OPL_PORTHANDLER_W porthandler_w;
|
||||
int port_param;
|
||||
OPL_PORTHANDLER_R keyboardhandler_r;
|
||||
OPL_PORTHANDLER_W keyboardhandler_w;
|
||||
int keyboard_param;
|
||||
/* time tables */
|
||||
INT32 AR_TABLE[75]; /* atttack rate tables */
|
||||
INT32 DR_TABLE[75]; /* decay rate tables */
|
||||
UINT32 FN_TABLE[1024]; /* fnumber -> increment counter */
|
||||
/* LFO */
|
||||
INT32 *ams_table;
|
||||
INT32 *vib_table;
|
||||
INT32 amsCnt;
|
||||
INT32 amsIncr;
|
||||
INT32 vibCnt;
|
||||
INT32 vibIncr;
|
||||
/* wave selector enable flag */
|
||||
UINT8 wavesel;
|
||||
/* external event callback handler */
|
||||
OPL_TIMERHANDLER TimerHandler; /* TIMER handler */
|
||||
int TimerParam; /* TIMER parameter */
|
||||
OPL_IRQHANDLER IRQHandler; /* IRQ handler */
|
||||
int IRQParam; /* IRQ parameter */
|
||||
OPL_UPDATEHANDLER UpdateHandler; /* stream update handler */
|
||||
int UpdateParam; /* stream update parameter */
|
||||
} FM_OPL;
|
||||
|
||||
/* ---------- Generic interface section ---------- */
|
||||
#define OPL_TYPE_YM3526 (0)
|
||||
#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
|
||||
#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
|
||||
|
||||
FM_OPL *OPLCreate(int type, int clock, int rate);
|
||||
void OPLDestroy(FM_OPL *OPL);
|
||||
void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER TimerHandler,int channelOffset);
|
||||
void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,int param);
|
||||
void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,int param);
|
||||
/* Y8950 port handlers */
|
||||
void OPLSetPortHandler(FM_OPL *OPL,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,int param);
|
||||
void OPLSetKeyboardHandler(FM_OPL *OPL,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,int param);
|
||||
|
||||
void OPLResetChip(FM_OPL *OPL);
|
||||
int OPLWrite(FM_OPL *OPL,int a,int v);
|
||||
unsigned char OPLRead(FM_OPL *OPL,int a);
|
||||
int OPLTimerOver(FM_OPL *OPL,int c);
|
||||
|
||||
/* YM3626/YM3812 local section */
|
||||
void YM3812UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
|
||||
|
||||
void Y8950UpdateOne(FM_OPL *OPL, INT16 *buffer, int length);
|
||||
|
||||
#endif
|
3
hw/pc.c
3
hw/pc.c
|
@ -554,13 +554,10 @@ void pc_init(int ram_size, int vga_ram_size, int boot_device,
|
|||
kbd_init();
|
||||
DMA_init(0);
|
||||
|
||||
#ifndef _WIN32
|
||||
if (audio_enabled) {
|
||||
/* no audio supported yet for win32 */
|
||||
AUD_init();
|
||||
SB16_init();
|
||||
}
|
||||
#endif
|
||||
|
||||
floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue