symbian-qemu-0.9.1-12/qemu-symbian-svp/hw/gus.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * QEMU Proxy for Gravis Ultrasound GF1 emulation by Tibor "TS" Schütz
       
     3  *
       
     4  * Copyright (c) 2002-2005 Vassili Karpov (malc)
       
     5  *
       
     6  * 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
       
     8  * in the Software without restriction, including without limitation the rights
       
     9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       
    10  * copies of the Software, and to permit persons to whom the Software is
       
    11  * furnished to do so, subject to the following conditions:
       
    12  *
       
    13  * The above copyright notice and this permission notice shall be included in
       
    14  * all copies or substantial portions of the Software.
       
    15  *
       
    16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
       
    19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       
    22  * THE SOFTWARE.
       
    23  */
       
    24 #include "hw.h"
       
    25 #include "audiodev.h"
       
    26 #include "audio/audio.h"
       
    27 #include "isa.h"
       
    28 #include "gusemu.h"
       
    29 #include "gustate.h"
       
    30 
       
    31 #define dolog(...) AUD_log ("audio", __VA_ARGS__)
       
    32 #ifdef DEBUG
       
    33 #define ldebug(...) dolog (__VA_ARGS__)
       
    34 #else
       
    35 #define ldebug(...)
       
    36 #endif
       
    37 
       
    38 #ifdef WORDS_BIGENDIAN
       
    39 #define GUS_ENDIANNESS 1
       
    40 #else
       
    41 #define GUS_ENDIANNESS 0
       
    42 #endif
       
    43 
       
    44 #define IO_READ_PROTO(name) \
       
    45     static uint32_t name (void *opaque, uint32_t nport)
       
    46 #define IO_WRITE_PROTO(name) \
       
    47     static void name (void *opaque, uint32_t nport, uint32_t val)
       
    48 
       
    49 static struct {
       
    50     int port;
       
    51     int irq;
       
    52     int dma;
       
    53     int freq;
       
    54 } conf = {0x240, 7, 3, 44100};
       
    55 
       
    56 typedef struct GUSState {
       
    57     GUSEmuState emu;
       
    58     QEMUSoundCard card;
       
    59     int freq;
       
    60     int pos, left, shift, irqs;
       
    61     GUSsample *mixbuf;
       
    62     uint8_t himem[1024 * 1024 + 32 + 4096];
       
    63     int samples;
       
    64     SWVoiceOut *voice;
       
    65     int64_t last_ticks;
       
    66     qemu_irq *pic;
       
    67 } GUSState;
       
    68 
       
    69 IO_READ_PROTO (gus_readb)
       
    70 {
       
    71     GUSState *s = opaque;
       
    72 
       
    73     return gus_read (&s->emu, nport, 1);
       
    74 }
       
    75 
       
    76 IO_READ_PROTO (gus_readw)
       
    77 {
       
    78     GUSState *s = opaque;
       
    79 
       
    80     return gus_read (&s->emu, nport, 2);
       
    81 }
       
    82 
       
    83 IO_WRITE_PROTO (gus_writeb)
       
    84 {
       
    85     GUSState *s = opaque;
       
    86 
       
    87     gus_write (&s->emu, nport, 1, val);
       
    88 }
       
    89 
       
    90 IO_WRITE_PROTO (gus_writew)
       
    91 {
       
    92     GUSState *s = opaque;
       
    93 
       
    94     gus_write (&s->emu, nport, 2, val);
       
    95 }
       
    96 
       
    97 static int write_audio (GUSState *s, int samples)
       
    98 {
       
    99     int net = 0;
       
   100     int pos = s->pos;
       
   101 
       
   102     while (samples) {
       
   103         int nbytes, wbytes, wsampl;
       
   104 
       
   105         nbytes = samples << s->shift;
       
   106         wbytes = AUD_write (
       
   107             s->voice,
       
   108             s->mixbuf + (pos << (s->shift - 1)),
       
   109             nbytes
       
   110             );
       
   111 
       
   112         if (wbytes) {
       
   113             wsampl = wbytes >> s->shift;
       
   114 
       
   115             samples -= wsampl;
       
   116             pos = (pos + wsampl) % s->samples;
       
   117 
       
   118             net += wsampl;
       
   119         }
       
   120         else {
       
   121             break;
       
   122         }
       
   123     }
       
   124 
       
   125     return net;
       
   126 }
       
   127 
       
   128 static void GUS_callback (void *opaque, int free)
       
   129 {
       
   130     int samples, to_play, net = 0;
       
   131     GUSState *s = opaque;
       
   132 
       
   133     samples = free >> s->shift;
       
   134     to_play = audio_MIN (samples, s->left);
       
   135 
       
   136     while (to_play) {
       
   137         int written = write_audio (s, to_play);
       
   138 
       
   139         if (!written) {
       
   140             goto reset;
       
   141         }
       
   142 
       
   143         s->left -= written;
       
   144         to_play -= written;
       
   145         samples -= written;
       
   146         net += written;
       
   147     }
       
   148 
       
   149     samples = audio_MIN (samples, s->samples);
       
   150     if (samples) {
       
   151         gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf);
       
   152 
       
   153         while (samples) {
       
   154             int written = write_audio (s, samples);
       
   155             if (!written) {
       
   156                 break;
       
   157             }
       
   158             samples -= written;
       
   159             net += written;
       
   160         }
       
   161     }
       
   162     s->left = samples;
       
   163 
       
   164 reset:
       
   165     gus_irqgen (&s->emu, (double) (net * 1000000) / s->freq);
       
   166 }
       
   167 
       
   168 int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n)
       
   169 {
       
   170     GUSState *s = emu->opaque;
       
   171     /* qemu_irq_lower (s->pic[hwirq]); */
       
   172     qemu_irq_raise (s->pic[hwirq]);
       
   173     s->irqs += n;
       
   174     ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs);
       
   175     return n;
       
   176 }
       
   177 
       
   178 void GUS_irqclear (GUSEmuState *emu, int hwirq)
       
   179 {
       
   180     GUSState *s = emu->opaque;
       
   181     ldebug ("irqclear %d %d\n", hwirq, s->irqs);
       
   182     qemu_irq_lower (s->pic[hwirq]);
       
   183     s->irqs -= 1;
       
   184 #ifdef IRQ_STORM
       
   185     if (s->irqs > 0) {
       
   186         qemu_irq_raise (s->pic[hwirq]);
       
   187     }
       
   188 #endif
       
   189 }
       
   190 
       
   191 void GUS_dmarequest (GUSEmuState *der)
       
   192 {
       
   193     /* GUSState *s = (GUSState *) der; */
       
   194     ldebug ("dma request %d\n", der->gusdma);
       
   195     DMA_hold_DREQ (der->gusdma);
       
   196 }
       
   197 
       
   198 static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
       
   199 {
       
   200     GUSState *s = opaque;
       
   201     char tmpbuf[4096];
       
   202     int pos = dma_pos, mode, left = dma_len - dma_pos;
       
   203 
       
   204     ldebug ("read DMA %#x %d\n", dma_pos, dma_len);
       
   205     mode = DMA_get_channel_mode (s->emu.gusdma);
       
   206     while (left) {
       
   207         int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf));
       
   208         int copied;
       
   209 
       
   210         ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos);
       
   211         copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy);
       
   212         gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied);
       
   213         left -= copied;
       
   214         pos += copied;
       
   215     }
       
   216 
       
   217     if (0 == ((mode >> 4) & 1)) {
       
   218         DMA_release_DREQ (s->emu.gusdma);
       
   219     }
       
   220     return dma_len;
       
   221 }
       
   222 
       
   223 static void GUS_save (QEMUFile *f, void *opaque)
       
   224 {
       
   225     GUSState *s = opaque;
       
   226 
       
   227     qemu_put_be32 (f, s->pos);
       
   228     qemu_put_be32 (f, s->left);
       
   229     qemu_put_be32 (f, s->shift);
       
   230     qemu_put_be32 (f, s->irqs);
       
   231     qemu_put_be32 (f, s->samples);
       
   232     qemu_put_be64 (f, s->last_ticks);
       
   233     qemu_put_buffer (f, s->himem, sizeof (s->himem));
       
   234 }
       
   235 
       
   236 static int GUS_load (QEMUFile *f, void *opaque, int version_id)
       
   237 {
       
   238     GUSState *s = opaque;
       
   239 
       
   240     if (version_id != 2)
       
   241         return -EINVAL;
       
   242 
       
   243     s->pos = qemu_get_be32 (f);
       
   244     s->left = qemu_get_be32 (f);
       
   245     s->shift = qemu_get_be32 (f);
       
   246     s->irqs = qemu_get_be32 (f);
       
   247     s->samples = qemu_get_be32 (f);
       
   248     s->last_ticks = qemu_get_be64 (f);
       
   249     qemu_get_buffer (f, s->himem, sizeof (s->himem));
       
   250     return 0;
       
   251 }
       
   252 
       
   253 int GUS_init (AudioState *audio, qemu_irq *pic)
       
   254 {
       
   255     GUSState *s;
       
   256     struct audsettings as;
       
   257 
       
   258     if (!audio) {
       
   259         dolog ("No audio state\n");
       
   260         return -1;
       
   261     }
       
   262 
       
   263     s = qemu_mallocz (sizeof (*s));
       
   264     if (!s) {
       
   265         dolog ("Could not allocate memory for GUS (%zu bytes)\n",
       
   266                sizeof (*s));
       
   267         return -1;
       
   268     }
       
   269 
       
   270     AUD_register_card (audio, "gus", &s->card);
       
   271 
       
   272     as.freq = conf.freq;
       
   273     as.nchannels = 2;
       
   274     as.fmt = AUD_FMT_S16;
       
   275     as.endianness = GUS_ENDIANNESS;
       
   276 
       
   277     s->voice = AUD_open_out (
       
   278         &s->card,
       
   279         NULL,
       
   280         "gus",
       
   281         s,
       
   282         GUS_callback,
       
   283         &as
       
   284         );
       
   285 
       
   286     if (!s->voice) {
       
   287         AUD_remove_card (&s->card);
       
   288         qemu_free (s);
       
   289         return -1;
       
   290     }
       
   291 
       
   292     s->shift = 2;
       
   293     s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift;
       
   294     s->mixbuf = qemu_mallocz (s->samples << s->shift);
       
   295     if (!s->mixbuf) {
       
   296         AUD_close_out (&s->card, s->voice);
       
   297         AUD_remove_card (&s->card);
       
   298         qemu_free (s);
       
   299         return -1;
       
   300     }
       
   301 
       
   302     register_ioport_write (conf.port, 1, 1, gus_writeb, s);
       
   303     register_ioport_write (conf.port, 1, 2, gus_writew, s);
       
   304 
       
   305     register_ioport_read ((conf.port + 0x100) & 0xf00, 1, 1, gus_readb, s);
       
   306     register_ioport_read ((conf.port + 0x100) & 0xf00, 1, 2, gus_readw, s);
       
   307 
       
   308     register_ioport_write (conf.port + 6, 10, 1, gus_writeb, s);
       
   309     register_ioport_write (conf.port + 6, 10, 2, gus_writew, s);
       
   310     register_ioport_read (conf.port + 6, 10, 1, gus_readb, s);
       
   311     register_ioport_read (conf.port + 6, 10, 2, gus_readw, s);
       
   312 
       
   313 
       
   314     register_ioport_write (conf.port + 0x100, 8, 1, gus_writeb, s);
       
   315     register_ioport_write (conf.port + 0x100, 8, 2, gus_writew, s);
       
   316     register_ioport_read (conf.port + 0x100, 8, 1, gus_readb, s);
       
   317     register_ioport_read (conf.port + 0x100, 8, 2, gus_readw, s);
       
   318 
       
   319     DMA_register_channel (conf.dma, GUS_read_DMA, s);
       
   320     s->emu.gusirq = conf.irq;
       
   321     s->emu.gusdma = conf.dma;
       
   322     s->emu.himemaddr = s->himem;
       
   323     s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32;
       
   324     s->emu.opaque = s;
       
   325     s->freq = conf.freq;
       
   326     s->pic = pic;
       
   327 
       
   328     AUD_set_active_out (s->voice, 1);
       
   329 
       
   330     register_savevm ("gus", 0, 2, GUS_save, GUS_load, s);
       
   331     return 0;
       
   332 }