xref: /openbmc/qemu/hw/audio/sb16.c (revision bc72ad67543f5c5d39c005ff0ca72da37642a1fb)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU Soundblaster 16 emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2003-2005 Vassili Karpov (malc)
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
749ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
849ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
949ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1049ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1149ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1249ab747fSPaolo Bonzini  *
1349ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1449ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1949ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2149ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2249ab747fSPaolo Bonzini  * THE SOFTWARE.
2349ab747fSPaolo Bonzini  */
2449ab747fSPaolo Bonzini #include "hw/hw.h"
2549ab747fSPaolo Bonzini #include "hw/audio/audio.h"
2649ab747fSPaolo Bonzini #include "audio/audio.h"
2749ab747fSPaolo Bonzini #include "hw/isa/isa.h"
2849ab747fSPaolo Bonzini #include "hw/qdev.h"
2949ab747fSPaolo Bonzini #include "qemu/timer.h"
3049ab747fSPaolo Bonzini #include "qemu/host-utils.h"
3149ab747fSPaolo Bonzini 
3249ab747fSPaolo Bonzini #define dolog(...) AUD_log ("sb16", __VA_ARGS__)
3349ab747fSPaolo Bonzini 
3449ab747fSPaolo Bonzini /* #define DEBUG */
3549ab747fSPaolo Bonzini /* #define DEBUG_SB16_MOST */
3649ab747fSPaolo Bonzini 
3749ab747fSPaolo Bonzini #ifdef DEBUG
3849ab747fSPaolo Bonzini #define ldebug(...) dolog (__VA_ARGS__)
3949ab747fSPaolo Bonzini #else
4049ab747fSPaolo Bonzini #define ldebug(...)
4149ab747fSPaolo Bonzini #endif
4249ab747fSPaolo Bonzini 
4349ab747fSPaolo Bonzini #define IO_READ_PROTO(name)                             \
4449ab747fSPaolo Bonzini     uint32_t name (void *opaque, uint32_t nport)
4549ab747fSPaolo Bonzini #define IO_WRITE_PROTO(name)                                    \
4649ab747fSPaolo Bonzini     void name (void *opaque, uint32_t nport, uint32_t val)
4749ab747fSPaolo Bonzini 
4849ab747fSPaolo Bonzini static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
4949ab747fSPaolo Bonzini 
50399f05a6SAndreas Färber #define TYPE_SB16 "sb16"
51399f05a6SAndreas Färber #define SB16(obj) OBJECT_CHECK (SB16State, (obj), TYPE_SB16)
52399f05a6SAndreas Färber 
5349ab747fSPaolo Bonzini typedef struct SB16State {
54399f05a6SAndreas Färber     ISADevice parent_obj;
55399f05a6SAndreas Färber 
5649ab747fSPaolo Bonzini     QEMUSoundCard card;
5749ab747fSPaolo Bonzini     qemu_irq pic;
5849ab747fSPaolo Bonzini     uint32_t irq;
5949ab747fSPaolo Bonzini     uint32_t dma;
6049ab747fSPaolo Bonzini     uint32_t hdma;
6149ab747fSPaolo Bonzini     uint32_t port;
6249ab747fSPaolo Bonzini     uint32_t ver;
6349ab747fSPaolo Bonzini 
6449ab747fSPaolo Bonzini     int in_index;
6549ab747fSPaolo Bonzini     int out_data_len;
6649ab747fSPaolo Bonzini     int fmt_stereo;
6749ab747fSPaolo Bonzini     int fmt_signed;
6849ab747fSPaolo Bonzini     int fmt_bits;
6949ab747fSPaolo Bonzini     audfmt_e fmt;
7049ab747fSPaolo Bonzini     int dma_auto;
7149ab747fSPaolo Bonzini     int block_size;
7249ab747fSPaolo Bonzini     int fifo;
7349ab747fSPaolo Bonzini     int freq;
7449ab747fSPaolo Bonzini     int time_const;
7549ab747fSPaolo Bonzini     int speaker;
7649ab747fSPaolo Bonzini     int needed_bytes;
7749ab747fSPaolo Bonzini     int cmd;
7849ab747fSPaolo Bonzini     int use_hdma;
7949ab747fSPaolo Bonzini     int highspeed;
8049ab747fSPaolo Bonzini     int can_write;
8149ab747fSPaolo Bonzini 
8249ab747fSPaolo Bonzini     int v2x6;
8349ab747fSPaolo Bonzini 
8449ab747fSPaolo Bonzini     uint8_t csp_param;
8549ab747fSPaolo Bonzini     uint8_t csp_value;
8649ab747fSPaolo Bonzini     uint8_t csp_mode;
8749ab747fSPaolo Bonzini     uint8_t csp_regs[256];
8849ab747fSPaolo Bonzini     uint8_t csp_index;
8949ab747fSPaolo Bonzini     uint8_t csp_reg83[4];
9049ab747fSPaolo Bonzini     int csp_reg83r;
9149ab747fSPaolo Bonzini     int csp_reg83w;
9249ab747fSPaolo Bonzini 
9349ab747fSPaolo Bonzini     uint8_t in2_data[10];
9449ab747fSPaolo Bonzini     uint8_t out_data[50];
9549ab747fSPaolo Bonzini     uint8_t test_reg;
9649ab747fSPaolo Bonzini     uint8_t last_read_byte;
9749ab747fSPaolo Bonzini     int nzero;
9849ab747fSPaolo Bonzini 
9949ab747fSPaolo Bonzini     int left_till_irq;
10049ab747fSPaolo Bonzini 
10149ab747fSPaolo Bonzini     int dma_running;
10249ab747fSPaolo Bonzini     int bytes_per_second;
10349ab747fSPaolo Bonzini     int align;
10449ab747fSPaolo Bonzini     int audio_free;
10549ab747fSPaolo Bonzini     SWVoiceOut *voice;
10649ab747fSPaolo Bonzini 
10749ab747fSPaolo Bonzini     QEMUTimer *aux_ts;
10849ab747fSPaolo Bonzini     /* mixer state */
10949ab747fSPaolo Bonzini     int mixer_nreg;
11049ab747fSPaolo Bonzini     uint8_t mixer_regs[256];
11149ab747fSPaolo Bonzini } SB16State;
11249ab747fSPaolo Bonzini 
11349ab747fSPaolo Bonzini static void SB_audio_callback (void *opaque, int free);
11449ab747fSPaolo Bonzini 
11549ab747fSPaolo Bonzini static int magic_of_irq (int irq)
11649ab747fSPaolo Bonzini {
11749ab747fSPaolo Bonzini     switch (irq) {
11849ab747fSPaolo Bonzini     case 5:
11949ab747fSPaolo Bonzini         return 2;
12049ab747fSPaolo Bonzini     case 7:
12149ab747fSPaolo Bonzini         return 4;
12249ab747fSPaolo Bonzini     case 9:
12349ab747fSPaolo Bonzini         return 1;
12449ab747fSPaolo Bonzini     case 10:
12549ab747fSPaolo Bonzini         return 8;
12649ab747fSPaolo Bonzini     default:
12749ab747fSPaolo Bonzini         dolog ("bad irq %d\n", irq);
12849ab747fSPaolo Bonzini         return 2;
12949ab747fSPaolo Bonzini     }
13049ab747fSPaolo Bonzini }
13149ab747fSPaolo Bonzini 
13249ab747fSPaolo Bonzini static int irq_of_magic (int magic)
13349ab747fSPaolo Bonzini {
13449ab747fSPaolo Bonzini     switch (magic) {
13549ab747fSPaolo Bonzini     case 1:
13649ab747fSPaolo Bonzini         return 9;
13749ab747fSPaolo Bonzini     case 2:
13849ab747fSPaolo Bonzini         return 5;
13949ab747fSPaolo Bonzini     case 4:
14049ab747fSPaolo Bonzini         return 7;
14149ab747fSPaolo Bonzini     case 8:
14249ab747fSPaolo Bonzini         return 10;
14349ab747fSPaolo Bonzini     default:
14449ab747fSPaolo Bonzini         dolog ("bad irq magic %d\n", magic);
14549ab747fSPaolo Bonzini         return -1;
14649ab747fSPaolo Bonzini     }
14749ab747fSPaolo Bonzini }
14849ab747fSPaolo Bonzini 
14949ab747fSPaolo Bonzini #if 0
15049ab747fSPaolo Bonzini static void log_dsp (SB16State *dsp)
15149ab747fSPaolo Bonzini {
15249ab747fSPaolo Bonzini     ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n",
15349ab747fSPaolo Bonzini             dsp->fmt_stereo ? "Stereo" : "Mono",
15449ab747fSPaolo Bonzini             dsp->fmt_signed ? "Signed" : "Unsigned",
15549ab747fSPaolo Bonzini             dsp->fmt_bits,
15649ab747fSPaolo Bonzini             dsp->dma_auto ? "Auto" : "Single",
15749ab747fSPaolo Bonzini             dsp->block_size,
15849ab747fSPaolo Bonzini             dsp->freq,
15949ab747fSPaolo Bonzini             dsp->time_const,
16049ab747fSPaolo Bonzini             dsp->speaker);
16149ab747fSPaolo Bonzini }
16249ab747fSPaolo Bonzini #endif
16349ab747fSPaolo Bonzini 
16449ab747fSPaolo Bonzini static void speaker (SB16State *s, int on)
16549ab747fSPaolo Bonzini {
16649ab747fSPaolo Bonzini     s->speaker = on;
16749ab747fSPaolo Bonzini     /* AUD_enable (s->voice, on); */
16849ab747fSPaolo Bonzini }
16949ab747fSPaolo Bonzini 
17049ab747fSPaolo Bonzini static void control (SB16State *s, int hold)
17149ab747fSPaolo Bonzini {
17249ab747fSPaolo Bonzini     int dma = s->use_hdma ? s->hdma : s->dma;
17349ab747fSPaolo Bonzini     s->dma_running = hold;
17449ab747fSPaolo Bonzini 
17549ab747fSPaolo Bonzini     ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma);
17649ab747fSPaolo Bonzini 
17749ab747fSPaolo Bonzini     if (hold) {
17849ab747fSPaolo Bonzini         DMA_hold_DREQ (dma);
17949ab747fSPaolo Bonzini         AUD_set_active_out (s->voice, 1);
18049ab747fSPaolo Bonzini     }
18149ab747fSPaolo Bonzini     else {
18249ab747fSPaolo Bonzini         DMA_release_DREQ (dma);
18349ab747fSPaolo Bonzini         AUD_set_active_out (s->voice, 0);
18449ab747fSPaolo Bonzini     }
18549ab747fSPaolo Bonzini }
18649ab747fSPaolo Bonzini 
18749ab747fSPaolo Bonzini static void aux_timer (void *opaque)
18849ab747fSPaolo Bonzini {
18949ab747fSPaolo Bonzini     SB16State *s = opaque;
19049ab747fSPaolo Bonzini     s->can_write = 1;
19149ab747fSPaolo Bonzini     qemu_irq_raise (s->pic);
19249ab747fSPaolo Bonzini }
19349ab747fSPaolo Bonzini 
19449ab747fSPaolo Bonzini #define DMA8_AUTO 1
19549ab747fSPaolo Bonzini #define DMA8_HIGH 2
19649ab747fSPaolo Bonzini 
19749ab747fSPaolo Bonzini static void continue_dma8 (SB16State *s)
19849ab747fSPaolo Bonzini {
19949ab747fSPaolo Bonzini     if (s->freq > 0) {
20049ab747fSPaolo Bonzini         struct audsettings as;
20149ab747fSPaolo Bonzini 
20249ab747fSPaolo Bonzini         s->audio_free = 0;
20349ab747fSPaolo Bonzini 
20449ab747fSPaolo Bonzini         as.freq = s->freq;
20549ab747fSPaolo Bonzini         as.nchannels = 1 << s->fmt_stereo;
20649ab747fSPaolo Bonzini         as.fmt = s->fmt;
20749ab747fSPaolo Bonzini         as.endianness = 0;
20849ab747fSPaolo Bonzini 
20949ab747fSPaolo Bonzini         s->voice = AUD_open_out (
21049ab747fSPaolo Bonzini             &s->card,
21149ab747fSPaolo Bonzini             s->voice,
21249ab747fSPaolo Bonzini             "sb16",
21349ab747fSPaolo Bonzini             s,
21449ab747fSPaolo Bonzini             SB_audio_callback,
21549ab747fSPaolo Bonzini             &as
21649ab747fSPaolo Bonzini             );
21749ab747fSPaolo Bonzini     }
21849ab747fSPaolo Bonzini 
21949ab747fSPaolo Bonzini     control (s, 1);
22049ab747fSPaolo Bonzini }
22149ab747fSPaolo Bonzini 
22249ab747fSPaolo Bonzini static void dma_cmd8 (SB16State *s, int mask, int dma_len)
22349ab747fSPaolo Bonzini {
22449ab747fSPaolo Bonzini     s->fmt = AUD_FMT_U8;
22549ab747fSPaolo Bonzini     s->use_hdma = 0;
22649ab747fSPaolo Bonzini     s->fmt_bits = 8;
22749ab747fSPaolo Bonzini     s->fmt_signed = 0;
22849ab747fSPaolo Bonzini     s->fmt_stereo = (s->mixer_regs[0x0e] & 2) != 0;
22949ab747fSPaolo Bonzini     if (-1 == s->time_const) {
23049ab747fSPaolo Bonzini         if (s->freq <= 0)
23149ab747fSPaolo Bonzini             s->freq = 11025;
23249ab747fSPaolo Bonzini     }
23349ab747fSPaolo Bonzini     else {
23449ab747fSPaolo Bonzini         int tmp = (256 - s->time_const);
23549ab747fSPaolo Bonzini         s->freq = (1000000 + (tmp / 2)) / tmp;
23649ab747fSPaolo Bonzini     }
23749ab747fSPaolo Bonzini 
23849ab747fSPaolo Bonzini     if (dma_len != -1) {
23949ab747fSPaolo Bonzini         s->block_size = dma_len << s->fmt_stereo;
24049ab747fSPaolo Bonzini     }
24149ab747fSPaolo Bonzini     else {
24249ab747fSPaolo Bonzini         /* This is apparently the only way to make both Act1/PL
24349ab747fSPaolo Bonzini            and SecondReality/FC work
24449ab747fSPaolo Bonzini 
24549ab747fSPaolo Bonzini            Act1 sets block size via command 0x48 and it's an odd number
24649ab747fSPaolo Bonzini            SR does the same with even number
24749ab747fSPaolo Bonzini            Both use stereo, and Creatives own documentation states that
24849ab747fSPaolo Bonzini            0x48 sets block size in bytes less one.. go figure */
24949ab747fSPaolo Bonzini         s->block_size &= ~s->fmt_stereo;
25049ab747fSPaolo Bonzini     }
25149ab747fSPaolo Bonzini 
25249ab747fSPaolo Bonzini     s->freq >>= s->fmt_stereo;
25349ab747fSPaolo Bonzini     s->left_till_irq = s->block_size;
25449ab747fSPaolo Bonzini     s->bytes_per_second = (s->freq << s->fmt_stereo);
25549ab747fSPaolo Bonzini     /* s->highspeed = (mask & DMA8_HIGH) != 0; */
25649ab747fSPaolo Bonzini     s->dma_auto = (mask & DMA8_AUTO) != 0;
25749ab747fSPaolo Bonzini     s->align = (1 << s->fmt_stereo) - 1;
25849ab747fSPaolo Bonzini 
25949ab747fSPaolo Bonzini     if (s->block_size & s->align) {
26049ab747fSPaolo Bonzini         dolog ("warning: misaligned block size %d, alignment %d\n",
26149ab747fSPaolo Bonzini                s->block_size, s->align + 1);
26249ab747fSPaolo Bonzini     }
26349ab747fSPaolo Bonzini 
26449ab747fSPaolo Bonzini     ldebug ("freq %d, stereo %d, sign %d, bits %d, "
26549ab747fSPaolo Bonzini             "dma %d, auto %d, fifo %d, high %d\n",
26649ab747fSPaolo Bonzini             s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
26749ab747fSPaolo Bonzini             s->block_size, s->dma_auto, s->fifo, s->highspeed);
26849ab747fSPaolo Bonzini 
26949ab747fSPaolo Bonzini     continue_dma8 (s);
27049ab747fSPaolo Bonzini     speaker (s, 1);
27149ab747fSPaolo Bonzini }
27249ab747fSPaolo Bonzini 
27349ab747fSPaolo Bonzini static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
27449ab747fSPaolo Bonzini {
27549ab747fSPaolo Bonzini     s->use_hdma = cmd < 0xc0;
27649ab747fSPaolo Bonzini     s->fifo = (cmd >> 1) & 1;
27749ab747fSPaolo Bonzini     s->dma_auto = (cmd >> 2) & 1;
27849ab747fSPaolo Bonzini     s->fmt_signed = (d0 >> 4) & 1;
27949ab747fSPaolo Bonzini     s->fmt_stereo = (d0 >> 5) & 1;
28049ab747fSPaolo Bonzini 
28149ab747fSPaolo Bonzini     switch (cmd >> 4) {
28249ab747fSPaolo Bonzini     case 11:
28349ab747fSPaolo Bonzini         s->fmt_bits = 16;
28449ab747fSPaolo Bonzini         break;
28549ab747fSPaolo Bonzini 
28649ab747fSPaolo Bonzini     case 12:
28749ab747fSPaolo Bonzini         s->fmt_bits = 8;
28849ab747fSPaolo Bonzini         break;
28949ab747fSPaolo Bonzini     }
29049ab747fSPaolo Bonzini 
29149ab747fSPaolo Bonzini     if (-1 != s->time_const) {
29249ab747fSPaolo Bonzini #if 1
29349ab747fSPaolo Bonzini         int tmp = 256 - s->time_const;
29449ab747fSPaolo Bonzini         s->freq = (1000000 + (tmp / 2)) / tmp;
29549ab747fSPaolo Bonzini #else
29649ab747fSPaolo Bonzini         /* s->freq = 1000000 / ((255 - s->time_const) << s->fmt_stereo); */
29749ab747fSPaolo Bonzini         s->freq = 1000000 / ((255 - s->time_const));
29849ab747fSPaolo Bonzini #endif
29949ab747fSPaolo Bonzini         s->time_const = -1;
30049ab747fSPaolo Bonzini     }
30149ab747fSPaolo Bonzini 
30249ab747fSPaolo Bonzini     s->block_size = dma_len + 1;
30349ab747fSPaolo Bonzini     s->block_size <<= (s->fmt_bits == 16);
30449ab747fSPaolo Bonzini     if (!s->dma_auto) {
30549ab747fSPaolo Bonzini         /* It is clear that for DOOM and auto-init this value
30649ab747fSPaolo Bonzini            shouldn't take stereo into account, while Miles Sound Systems
30749ab747fSPaolo Bonzini            setsound.exe with single transfer mode wouldn't work without it
30849ab747fSPaolo Bonzini            wonders of SB16 yet again */
30949ab747fSPaolo Bonzini         s->block_size <<= s->fmt_stereo;
31049ab747fSPaolo Bonzini     }
31149ab747fSPaolo Bonzini 
31249ab747fSPaolo Bonzini     ldebug ("freq %d, stereo %d, sign %d, bits %d, "
31349ab747fSPaolo Bonzini             "dma %d, auto %d, fifo %d, high %d\n",
31449ab747fSPaolo Bonzini             s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
31549ab747fSPaolo Bonzini             s->block_size, s->dma_auto, s->fifo, s->highspeed);
31649ab747fSPaolo Bonzini 
31749ab747fSPaolo Bonzini     if (16 == s->fmt_bits) {
31849ab747fSPaolo Bonzini         if (s->fmt_signed) {
31949ab747fSPaolo Bonzini             s->fmt = AUD_FMT_S16;
32049ab747fSPaolo Bonzini         }
32149ab747fSPaolo Bonzini         else {
32249ab747fSPaolo Bonzini             s->fmt = AUD_FMT_U16;
32349ab747fSPaolo Bonzini         }
32449ab747fSPaolo Bonzini     }
32549ab747fSPaolo Bonzini     else {
32649ab747fSPaolo Bonzini         if (s->fmt_signed) {
32749ab747fSPaolo Bonzini             s->fmt = AUD_FMT_S8;
32849ab747fSPaolo Bonzini         }
32949ab747fSPaolo Bonzini         else {
33049ab747fSPaolo Bonzini             s->fmt = AUD_FMT_U8;
33149ab747fSPaolo Bonzini         }
33249ab747fSPaolo Bonzini     }
33349ab747fSPaolo Bonzini 
33449ab747fSPaolo Bonzini     s->left_till_irq = s->block_size;
33549ab747fSPaolo Bonzini 
33649ab747fSPaolo Bonzini     s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16);
33749ab747fSPaolo Bonzini     s->highspeed = 0;
33849ab747fSPaolo Bonzini     s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1;
33949ab747fSPaolo Bonzini     if (s->block_size & s->align) {
34049ab747fSPaolo Bonzini         dolog ("warning: misaligned block size %d, alignment %d\n",
34149ab747fSPaolo Bonzini                s->block_size, s->align + 1);
34249ab747fSPaolo Bonzini     }
34349ab747fSPaolo Bonzini 
34449ab747fSPaolo Bonzini     if (s->freq) {
34549ab747fSPaolo Bonzini         struct audsettings as;
34649ab747fSPaolo Bonzini 
34749ab747fSPaolo Bonzini         s->audio_free = 0;
34849ab747fSPaolo Bonzini 
34949ab747fSPaolo Bonzini         as.freq = s->freq;
35049ab747fSPaolo Bonzini         as.nchannels = 1 << s->fmt_stereo;
35149ab747fSPaolo Bonzini         as.fmt = s->fmt;
35249ab747fSPaolo Bonzini         as.endianness = 0;
35349ab747fSPaolo Bonzini 
35449ab747fSPaolo Bonzini         s->voice = AUD_open_out (
35549ab747fSPaolo Bonzini             &s->card,
35649ab747fSPaolo Bonzini             s->voice,
35749ab747fSPaolo Bonzini             "sb16",
35849ab747fSPaolo Bonzini             s,
35949ab747fSPaolo Bonzini             SB_audio_callback,
36049ab747fSPaolo Bonzini             &as
36149ab747fSPaolo Bonzini             );
36249ab747fSPaolo Bonzini     }
36349ab747fSPaolo Bonzini 
36449ab747fSPaolo Bonzini     control (s, 1);
36549ab747fSPaolo Bonzini     speaker (s, 1);
36649ab747fSPaolo Bonzini }
36749ab747fSPaolo Bonzini 
36849ab747fSPaolo Bonzini static inline void dsp_out_data (SB16State *s, uint8_t val)
36949ab747fSPaolo Bonzini {
37049ab747fSPaolo Bonzini     ldebug ("outdata %#x\n", val);
37149ab747fSPaolo Bonzini     if ((size_t) s->out_data_len < sizeof (s->out_data)) {
37249ab747fSPaolo Bonzini         s->out_data[s->out_data_len++] = val;
37349ab747fSPaolo Bonzini     }
37449ab747fSPaolo Bonzini }
37549ab747fSPaolo Bonzini 
37649ab747fSPaolo Bonzini static inline uint8_t dsp_get_data (SB16State *s)
37749ab747fSPaolo Bonzini {
37849ab747fSPaolo Bonzini     if (s->in_index) {
37949ab747fSPaolo Bonzini         return s->in2_data[--s->in_index];
38049ab747fSPaolo Bonzini     }
38149ab747fSPaolo Bonzini     else {
38249ab747fSPaolo Bonzini         dolog ("buffer underflow\n");
38349ab747fSPaolo Bonzini         return 0;
38449ab747fSPaolo Bonzini     }
38549ab747fSPaolo Bonzini }
38649ab747fSPaolo Bonzini 
38749ab747fSPaolo Bonzini static void command (SB16State *s, uint8_t cmd)
38849ab747fSPaolo Bonzini {
38949ab747fSPaolo Bonzini     ldebug ("command %#x\n", cmd);
39049ab747fSPaolo Bonzini 
39149ab747fSPaolo Bonzini     if (cmd > 0xaf && cmd < 0xd0) {
39249ab747fSPaolo Bonzini         if (cmd & 8) {
39349ab747fSPaolo Bonzini             dolog ("ADC not yet supported (command %#x)\n", cmd);
39449ab747fSPaolo Bonzini         }
39549ab747fSPaolo Bonzini 
39649ab747fSPaolo Bonzini         switch (cmd >> 4) {
39749ab747fSPaolo Bonzini         case 11:
39849ab747fSPaolo Bonzini         case 12:
39949ab747fSPaolo Bonzini             break;
40049ab747fSPaolo Bonzini         default:
40149ab747fSPaolo Bonzini             dolog ("%#x wrong bits\n", cmd);
40249ab747fSPaolo Bonzini         }
40349ab747fSPaolo Bonzini         s->needed_bytes = 3;
40449ab747fSPaolo Bonzini     }
40549ab747fSPaolo Bonzini     else {
40649ab747fSPaolo Bonzini         s->needed_bytes = 0;
40749ab747fSPaolo Bonzini 
40849ab747fSPaolo Bonzini         switch (cmd) {
40949ab747fSPaolo Bonzini         case 0x03:
41049ab747fSPaolo Bonzini             dsp_out_data (s, 0x10); /* s->csp_param); */
41149ab747fSPaolo Bonzini             goto warn;
41249ab747fSPaolo Bonzini 
41349ab747fSPaolo Bonzini         case 0x04:
41449ab747fSPaolo Bonzini             s->needed_bytes = 1;
41549ab747fSPaolo Bonzini             goto warn;
41649ab747fSPaolo Bonzini 
41749ab747fSPaolo Bonzini         case 0x05:
41849ab747fSPaolo Bonzini             s->needed_bytes = 2;
41949ab747fSPaolo Bonzini             goto warn;
42049ab747fSPaolo Bonzini 
42149ab747fSPaolo Bonzini         case 0x08:
42249ab747fSPaolo Bonzini             /* __asm__ ("int3"); */
42349ab747fSPaolo Bonzini             goto warn;
42449ab747fSPaolo Bonzini 
42549ab747fSPaolo Bonzini         case 0x0e:
42649ab747fSPaolo Bonzini             s->needed_bytes = 2;
42749ab747fSPaolo Bonzini             goto warn;
42849ab747fSPaolo Bonzini 
42949ab747fSPaolo Bonzini         case 0x09:
43049ab747fSPaolo Bonzini             dsp_out_data (s, 0xf8);
43149ab747fSPaolo Bonzini             goto warn;
43249ab747fSPaolo Bonzini 
43349ab747fSPaolo Bonzini         case 0x0f:
43449ab747fSPaolo Bonzini             s->needed_bytes = 1;
43549ab747fSPaolo Bonzini             goto warn;
43649ab747fSPaolo Bonzini 
43749ab747fSPaolo Bonzini         case 0x10:
43849ab747fSPaolo Bonzini             s->needed_bytes = 1;
43949ab747fSPaolo Bonzini             goto warn;
44049ab747fSPaolo Bonzini 
44149ab747fSPaolo Bonzini         case 0x14:
44249ab747fSPaolo Bonzini             s->needed_bytes = 2;
44349ab747fSPaolo Bonzini             s->block_size = 0;
44449ab747fSPaolo Bonzini             break;
44549ab747fSPaolo Bonzini 
44649ab747fSPaolo Bonzini         case 0x1c:              /* Auto-Initialize DMA DAC, 8-bit */
44749ab747fSPaolo Bonzini             dma_cmd8 (s, DMA8_AUTO, -1);
44849ab747fSPaolo Bonzini             break;
44949ab747fSPaolo Bonzini 
45049ab747fSPaolo Bonzini         case 0x20:              /* Direct ADC, Juice/PL */
45149ab747fSPaolo Bonzini             dsp_out_data (s, 0xff);
45249ab747fSPaolo Bonzini             goto warn;
45349ab747fSPaolo Bonzini 
45449ab747fSPaolo Bonzini         case 0x35:
45549ab747fSPaolo Bonzini             dolog ("0x35 - MIDI command not implemented\n");
45649ab747fSPaolo Bonzini             break;
45749ab747fSPaolo Bonzini 
45849ab747fSPaolo Bonzini         case 0x40:
45949ab747fSPaolo Bonzini             s->freq = -1;
46049ab747fSPaolo Bonzini             s->time_const = -1;
46149ab747fSPaolo Bonzini             s->needed_bytes = 1;
46249ab747fSPaolo Bonzini             break;
46349ab747fSPaolo Bonzini 
46449ab747fSPaolo Bonzini         case 0x41:
46549ab747fSPaolo Bonzini             s->freq = -1;
46649ab747fSPaolo Bonzini             s->time_const = -1;
46749ab747fSPaolo Bonzini             s->needed_bytes = 2;
46849ab747fSPaolo Bonzini             break;
46949ab747fSPaolo Bonzini 
47049ab747fSPaolo Bonzini         case 0x42:
47149ab747fSPaolo Bonzini             s->freq = -1;
47249ab747fSPaolo Bonzini             s->time_const = -1;
47349ab747fSPaolo Bonzini             s->needed_bytes = 2;
47449ab747fSPaolo Bonzini             goto warn;
47549ab747fSPaolo Bonzini 
47649ab747fSPaolo Bonzini         case 0x45:
47749ab747fSPaolo Bonzini             dsp_out_data (s, 0xaa);
47849ab747fSPaolo Bonzini             goto warn;
47949ab747fSPaolo Bonzini 
48049ab747fSPaolo Bonzini         case 0x47:                /* Continue Auto-Initialize DMA 16bit */
48149ab747fSPaolo Bonzini             break;
48249ab747fSPaolo Bonzini 
48349ab747fSPaolo Bonzini         case 0x48:
48449ab747fSPaolo Bonzini             s->needed_bytes = 2;
48549ab747fSPaolo Bonzini             break;
48649ab747fSPaolo Bonzini 
48749ab747fSPaolo Bonzini         case 0x74:
48849ab747fSPaolo Bonzini             s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
48949ab747fSPaolo Bonzini             dolog ("0x75 - DMA DAC, 4-bit ADPCM not implemented\n");
49049ab747fSPaolo Bonzini             break;
49149ab747fSPaolo Bonzini 
49249ab747fSPaolo Bonzini         case 0x75:              /* DMA DAC, 4-bit ADPCM Reference */
49349ab747fSPaolo Bonzini             s->needed_bytes = 2;
49449ab747fSPaolo Bonzini             dolog ("0x74 - DMA DAC, 4-bit ADPCM Reference not implemented\n");
49549ab747fSPaolo Bonzini             break;
49649ab747fSPaolo Bonzini 
49749ab747fSPaolo Bonzini         case 0x76:              /* DMA DAC, 2.6-bit ADPCM */
49849ab747fSPaolo Bonzini             s->needed_bytes = 2;
49949ab747fSPaolo Bonzini             dolog ("0x74 - DMA DAC, 2.6-bit ADPCM not implemented\n");
50049ab747fSPaolo Bonzini             break;
50149ab747fSPaolo Bonzini 
50249ab747fSPaolo Bonzini         case 0x77:              /* DMA DAC, 2.6-bit ADPCM Reference */
50349ab747fSPaolo Bonzini             s->needed_bytes = 2;
50449ab747fSPaolo Bonzini             dolog ("0x74 - DMA DAC, 2.6-bit ADPCM Reference not implemented\n");
50549ab747fSPaolo Bonzini             break;
50649ab747fSPaolo Bonzini 
50749ab747fSPaolo Bonzini         case 0x7d:
50849ab747fSPaolo Bonzini             dolog ("0x7d - Autio-Initialize DMA DAC, 4-bit ADPCM Reference\n");
50949ab747fSPaolo Bonzini             dolog ("not implemented\n");
51049ab747fSPaolo Bonzini             break;
51149ab747fSPaolo Bonzini 
51249ab747fSPaolo Bonzini         case 0x7f:
51349ab747fSPaolo Bonzini             dolog (
51449ab747fSPaolo Bonzini                 "0x7d - Autio-Initialize DMA DAC, 2.6-bit ADPCM Reference\n"
51549ab747fSPaolo Bonzini                 );
51649ab747fSPaolo Bonzini             dolog ("not implemented\n");
51749ab747fSPaolo Bonzini             break;
51849ab747fSPaolo Bonzini 
51949ab747fSPaolo Bonzini         case 0x80:
52049ab747fSPaolo Bonzini             s->needed_bytes = 2;
52149ab747fSPaolo Bonzini             break;
52249ab747fSPaolo Bonzini 
52349ab747fSPaolo Bonzini         case 0x90:
52449ab747fSPaolo Bonzini         case 0x91:
52549ab747fSPaolo Bonzini             dma_cmd8 (s, ((cmd & 1) == 0) | DMA8_HIGH, -1);
52649ab747fSPaolo Bonzini             break;
52749ab747fSPaolo Bonzini 
52849ab747fSPaolo Bonzini         case 0xd0:              /* halt DMA operation. 8bit */
52949ab747fSPaolo Bonzini             control (s, 0);
53049ab747fSPaolo Bonzini             break;
53149ab747fSPaolo Bonzini 
53249ab747fSPaolo Bonzini         case 0xd1:              /* speaker on */
53349ab747fSPaolo Bonzini             speaker (s, 1);
53449ab747fSPaolo Bonzini             break;
53549ab747fSPaolo Bonzini 
53649ab747fSPaolo Bonzini         case 0xd3:              /* speaker off */
53749ab747fSPaolo Bonzini             speaker (s, 0);
53849ab747fSPaolo Bonzini             break;
53949ab747fSPaolo Bonzini 
54049ab747fSPaolo Bonzini         case 0xd4:              /* continue DMA operation. 8bit */
54149ab747fSPaolo Bonzini             /* KQ6 (or maybe Sierras audblst.drv in general) resets
54249ab747fSPaolo Bonzini                the frequency between halt/continue */
54349ab747fSPaolo Bonzini             continue_dma8 (s);
54449ab747fSPaolo Bonzini             break;
54549ab747fSPaolo Bonzini 
54649ab747fSPaolo Bonzini         case 0xd5:              /* halt DMA operation. 16bit */
54749ab747fSPaolo Bonzini             control (s, 0);
54849ab747fSPaolo Bonzini             break;
54949ab747fSPaolo Bonzini 
55049ab747fSPaolo Bonzini         case 0xd6:              /* continue DMA operation. 16bit */
55149ab747fSPaolo Bonzini             control (s, 1);
55249ab747fSPaolo Bonzini             break;
55349ab747fSPaolo Bonzini 
55449ab747fSPaolo Bonzini         case 0xd9:              /* exit auto-init DMA after this block. 16bit */
55549ab747fSPaolo Bonzini             s->dma_auto = 0;
55649ab747fSPaolo Bonzini             break;
55749ab747fSPaolo Bonzini 
55849ab747fSPaolo Bonzini         case 0xda:              /* exit auto-init DMA after this block. 8bit */
55949ab747fSPaolo Bonzini             s->dma_auto = 0;
56049ab747fSPaolo Bonzini             break;
56149ab747fSPaolo Bonzini 
56249ab747fSPaolo Bonzini         case 0xe0:              /* DSP identification */
56349ab747fSPaolo Bonzini             s->needed_bytes = 1;
56449ab747fSPaolo Bonzini             break;
56549ab747fSPaolo Bonzini 
56649ab747fSPaolo Bonzini         case 0xe1:
56749ab747fSPaolo Bonzini             dsp_out_data (s, s->ver & 0xff);
56849ab747fSPaolo Bonzini             dsp_out_data (s, s->ver >> 8);
56949ab747fSPaolo Bonzini             break;
57049ab747fSPaolo Bonzini 
57149ab747fSPaolo Bonzini         case 0xe2:
57249ab747fSPaolo Bonzini             s->needed_bytes = 1;
57349ab747fSPaolo Bonzini             goto warn;
57449ab747fSPaolo Bonzini 
57549ab747fSPaolo Bonzini         case 0xe3:
57649ab747fSPaolo Bonzini             {
57749ab747fSPaolo Bonzini                 int i;
57849ab747fSPaolo Bonzini                 for (i = sizeof (e3) - 1; i >= 0; --i)
57949ab747fSPaolo Bonzini                     dsp_out_data (s, e3[i]);
58049ab747fSPaolo Bonzini             }
58149ab747fSPaolo Bonzini             break;
58249ab747fSPaolo Bonzini 
58349ab747fSPaolo Bonzini         case 0xe4:              /* write test reg */
58449ab747fSPaolo Bonzini             s->needed_bytes = 1;
58549ab747fSPaolo Bonzini             break;
58649ab747fSPaolo Bonzini 
58749ab747fSPaolo Bonzini         case 0xe7:
58849ab747fSPaolo Bonzini             dolog ("Attempt to probe for ESS (0xe7)?\n");
58949ab747fSPaolo Bonzini             break;
59049ab747fSPaolo Bonzini 
59149ab747fSPaolo Bonzini         case 0xe8:              /* read test reg */
59249ab747fSPaolo Bonzini             dsp_out_data (s, s->test_reg);
59349ab747fSPaolo Bonzini             break;
59449ab747fSPaolo Bonzini 
59549ab747fSPaolo Bonzini         case 0xf2:
59649ab747fSPaolo Bonzini         case 0xf3:
59749ab747fSPaolo Bonzini             dsp_out_data (s, 0xaa);
59849ab747fSPaolo Bonzini             s->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
59949ab747fSPaolo Bonzini             qemu_irq_raise (s->pic);
60049ab747fSPaolo Bonzini             break;
60149ab747fSPaolo Bonzini 
60249ab747fSPaolo Bonzini         case 0xf9:
60349ab747fSPaolo Bonzini             s->needed_bytes = 1;
60449ab747fSPaolo Bonzini             goto warn;
60549ab747fSPaolo Bonzini 
60649ab747fSPaolo Bonzini         case 0xfa:
60749ab747fSPaolo Bonzini             dsp_out_data (s, 0);
60849ab747fSPaolo Bonzini             goto warn;
60949ab747fSPaolo Bonzini 
61049ab747fSPaolo Bonzini         case 0xfc:              /* FIXME */
61149ab747fSPaolo Bonzini             dsp_out_data (s, 0);
61249ab747fSPaolo Bonzini             goto warn;
61349ab747fSPaolo Bonzini 
61449ab747fSPaolo Bonzini         default:
61549ab747fSPaolo Bonzini             dolog ("Unrecognized command %#x\n", cmd);
61649ab747fSPaolo Bonzini             break;
61749ab747fSPaolo Bonzini         }
61849ab747fSPaolo Bonzini     }
61949ab747fSPaolo Bonzini 
62049ab747fSPaolo Bonzini     if (!s->needed_bytes) {
62149ab747fSPaolo Bonzini         ldebug ("\n");
62249ab747fSPaolo Bonzini     }
62349ab747fSPaolo Bonzini 
62449ab747fSPaolo Bonzini  exit:
62549ab747fSPaolo Bonzini     if (!s->needed_bytes) {
62649ab747fSPaolo Bonzini         s->cmd = -1;
62749ab747fSPaolo Bonzini     }
62849ab747fSPaolo Bonzini     else {
62949ab747fSPaolo Bonzini         s->cmd = cmd;
63049ab747fSPaolo Bonzini     }
63149ab747fSPaolo Bonzini     return;
63249ab747fSPaolo Bonzini 
63349ab747fSPaolo Bonzini  warn:
63449ab747fSPaolo Bonzini     dolog ("warning: command %#x,%d is not truly understood yet\n",
63549ab747fSPaolo Bonzini            cmd, s->needed_bytes);
63649ab747fSPaolo Bonzini     goto exit;
63749ab747fSPaolo Bonzini 
63849ab747fSPaolo Bonzini }
63949ab747fSPaolo Bonzini 
64049ab747fSPaolo Bonzini static uint16_t dsp_get_lohi (SB16State *s)
64149ab747fSPaolo Bonzini {
64249ab747fSPaolo Bonzini     uint8_t hi = dsp_get_data (s);
64349ab747fSPaolo Bonzini     uint8_t lo = dsp_get_data (s);
64449ab747fSPaolo Bonzini     return (hi << 8) | lo;
64549ab747fSPaolo Bonzini }
64649ab747fSPaolo Bonzini 
64749ab747fSPaolo Bonzini static uint16_t dsp_get_hilo (SB16State *s)
64849ab747fSPaolo Bonzini {
64949ab747fSPaolo Bonzini     uint8_t lo = dsp_get_data (s);
65049ab747fSPaolo Bonzini     uint8_t hi = dsp_get_data (s);
65149ab747fSPaolo Bonzini     return (hi << 8) | lo;
65249ab747fSPaolo Bonzini }
65349ab747fSPaolo Bonzini 
65449ab747fSPaolo Bonzini static void complete (SB16State *s)
65549ab747fSPaolo Bonzini {
65649ab747fSPaolo Bonzini     int d0, d1, d2;
65749ab747fSPaolo Bonzini     ldebug ("complete command %#x, in_index %d, needed_bytes %d\n",
65849ab747fSPaolo Bonzini             s->cmd, s->in_index, s->needed_bytes);
65949ab747fSPaolo Bonzini 
66049ab747fSPaolo Bonzini     if (s->cmd > 0xaf && s->cmd < 0xd0) {
66149ab747fSPaolo Bonzini         d2 = dsp_get_data (s);
66249ab747fSPaolo Bonzini         d1 = dsp_get_data (s);
66349ab747fSPaolo Bonzini         d0 = dsp_get_data (s);
66449ab747fSPaolo Bonzini 
66549ab747fSPaolo Bonzini         if (s->cmd & 8) {
66649ab747fSPaolo Bonzini             dolog ("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
66749ab747fSPaolo Bonzini                    s->cmd, d0, d1, d2);
66849ab747fSPaolo Bonzini         }
66949ab747fSPaolo Bonzini         else {
67049ab747fSPaolo Bonzini             ldebug ("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
67149ab747fSPaolo Bonzini                     s->cmd, d0, d1, d2);
67249ab747fSPaolo Bonzini             dma_cmd (s, s->cmd, d0, d1 + (d2 << 8));
67349ab747fSPaolo Bonzini         }
67449ab747fSPaolo Bonzini     }
67549ab747fSPaolo Bonzini     else {
67649ab747fSPaolo Bonzini         switch (s->cmd) {
67749ab747fSPaolo Bonzini         case 0x04:
67849ab747fSPaolo Bonzini             s->csp_mode = dsp_get_data (s);
67949ab747fSPaolo Bonzini             s->csp_reg83r = 0;
68049ab747fSPaolo Bonzini             s->csp_reg83w = 0;
68149ab747fSPaolo Bonzini             ldebug ("CSP command 0x04: mode=%#x\n", s->csp_mode);
68249ab747fSPaolo Bonzini             break;
68349ab747fSPaolo Bonzini 
68449ab747fSPaolo Bonzini         case 0x05:
68549ab747fSPaolo Bonzini             s->csp_param = dsp_get_data (s);
68649ab747fSPaolo Bonzini             s->csp_value = dsp_get_data (s);
68749ab747fSPaolo Bonzini             ldebug ("CSP command 0x05: param=%#x value=%#x\n",
68849ab747fSPaolo Bonzini                     s->csp_param,
68949ab747fSPaolo Bonzini                     s->csp_value);
69049ab747fSPaolo Bonzini             break;
69149ab747fSPaolo Bonzini 
69249ab747fSPaolo Bonzini         case 0x0e:
69349ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
69449ab747fSPaolo Bonzini             d1 = dsp_get_data (s);
69549ab747fSPaolo Bonzini             ldebug ("write CSP register %d <- %#x\n", d1, d0);
69649ab747fSPaolo Bonzini             if (d1 == 0x83) {
69749ab747fSPaolo Bonzini                 ldebug ("0x83[%d] <- %#x\n", s->csp_reg83r, d0);
69849ab747fSPaolo Bonzini                 s->csp_reg83[s->csp_reg83r % 4] = d0;
69949ab747fSPaolo Bonzini                 s->csp_reg83r += 1;
70049ab747fSPaolo Bonzini             }
70149ab747fSPaolo Bonzini             else {
70249ab747fSPaolo Bonzini                 s->csp_regs[d1] = d0;
70349ab747fSPaolo Bonzini             }
70449ab747fSPaolo Bonzini             break;
70549ab747fSPaolo Bonzini 
70649ab747fSPaolo Bonzini         case 0x0f:
70749ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
70849ab747fSPaolo Bonzini             ldebug ("read CSP register %#x -> %#x, mode=%#x\n",
70949ab747fSPaolo Bonzini                     d0, s->csp_regs[d0], s->csp_mode);
71049ab747fSPaolo Bonzini             if (d0 == 0x83) {
71149ab747fSPaolo Bonzini                 ldebug ("0x83[%d] -> %#x\n",
71249ab747fSPaolo Bonzini                         s->csp_reg83w,
71349ab747fSPaolo Bonzini                         s->csp_reg83[s->csp_reg83w % 4]);
71449ab747fSPaolo Bonzini                 dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]);
71549ab747fSPaolo Bonzini                 s->csp_reg83w += 1;
71649ab747fSPaolo Bonzini             }
71749ab747fSPaolo Bonzini             else {
71849ab747fSPaolo Bonzini                 dsp_out_data (s, s->csp_regs[d0]);
71949ab747fSPaolo Bonzini             }
72049ab747fSPaolo Bonzini             break;
72149ab747fSPaolo Bonzini 
72249ab747fSPaolo Bonzini         case 0x10:
72349ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
72449ab747fSPaolo Bonzini             dolog ("cmd 0x10 d0=%#x\n", d0);
72549ab747fSPaolo Bonzini             break;
72649ab747fSPaolo Bonzini 
72749ab747fSPaolo Bonzini         case 0x14:
72849ab747fSPaolo Bonzini             dma_cmd8 (s, 0, dsp_get_lohi (s) + 1);
72949ab747fSPaolo Bonzini             break;
73049ab747fSPaolo Bonzini 
73149ab747fSPaolo Bonzini         case 0x40:
73249ab747fSPaolo Bonzini             s->time_const = dsp_get_data (s);
73349ab747fSPaolo Bonzini             ldebug ("set time const %d\n", s->time_const);
73449ab747fSPaolo Bonzini             break;
73549ab747fSPaolo Bonzini 
73649ab747fSPaolo Bonzini         case 0x42:              /* FT2 sets output freq with this, go figure */
73749ab747fSPaolo Bonzini #if 0
73849ab747fSPaolo Bonzini             dolog ("cmd 0x42 might not do what it think it should\n");
73949ab747fSPaolo Bonzini #endif
74049ab747fSPaolo Bonzini         case 0x41:
74149ab747fSPaolo Bonzini             s->freq = dsp_get_hilo (s);
74249ab747fSPaolo Bonzini             ldebug ("set freq %d\n", s->freq);
74349ab747fSPaolo Bonzini             break;
74449ab747fSPaolo Bonzini 
74549ab747fSPaolo Bonzini         case 0x48:
74649ab747fSPaolo Bonzini             s->block_size = dsp_get_lohi (s) + 1;
74749ab747fSPaolo Bonzini             ldebug ("set dma block len %d\n", s->block_size);
74849ab747fSPaolo Bonzini             break;
74949ab747fSPaolo Bonzini 
75049ab747fSPaolo Bonzini         case 0x74:
75149ab747fSPaolo Bonzini         case 0x75:
75249ab747fSPaolo Bonzini         case 0x76:
75349ab747fSPaolo Bonzini         case 0x77:
75449ab747fSPaolo Bonzini             /* ADPCM stuff, ignore */
75549ab747fSPaolo Bonzini             break;
75649ab747fSPaolo Bonzini 
75749ab747fSPaolo Bonzini         case 0x80:
75849ab747fSPaolo Bonzini             {
75949ab747fSPaolo Bonzini                 int freq, samples, bytes;
76049ab747fSPaolo Bonzini                 int64_t ticks;
76149ab747fSPaolo Bonzini 
76249ab747fSPaolo Bonzini                 freq = s->freq > 0 ? s->freq : 11025;
76349ab747fSPaolo Bonzini                 samples = dsp_get_lohi (s) + 1;
76449ab747fSPaolo Bonzini                 bytes = samples << s->fmt_stereo << (s->fmt_bits == 16);
76549ab747fSPaolo Bonzini                 ticks = muldiv64 (bytes, get_ticks_per_sec (), freq);
76649ab747fSPaolo Bonzini                 if (ticks < get_ticks_per_sec () / 1024) {
76749ab747fSPaolo Bonzini                     qemu_irq_raise (s->pic);
76849ab747fSPaolo Bonzini                 }
76949ab747fSPaolo Bonzini                 else {
77049ab747fSPaolo Bonzini                     if (s->aux_ts) {
771*bc72ad67SAlex Bligh                         timer_mod (
77249ab747fSPaolo Bonzini                             s->aux_ts,
773*bc72ad67SAlex Bligh                             qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ticks
77449ab747fSPaolo Bonzini                             );
77549ab747fSPaolo Bonzini                     }
77649ab747fSPaolo Bonzini                 }
77749ab747fSPaolo Bonzini                 ldebug ("mix silence %d %d %" PRId64 "\n", samples, bytes, ticks);
77849ab747fSPaolo Bonzini             }
77949ab747fSPaolo Bonzini             break;
78049ab747fSPaolo Bonzini 
78149ab747fSPaolo Bonzini         case 0xe0:
78249ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
78349ab747fSPaolo Bonzini             s->out_data_len = 0;
78449ab747fSPaolo Bonzini             ldebug ("E0 data = %#x\n", d0);
78549ab747fSPaolo Bonzini             dsp_out_data (s, ~d0);
78649ab747fSPaolo Bonzini             break;
78749ab747fSPaolo Bonzini 
78849ab747fSPaolo Bonzini         case 0xe2:
78949ab747fSPaolo Bonzini #ifdef DEBUG
79049ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
79149ab747fSPaolo Bonzini             dolog ("E2 = %#x\n", d0);
79249ab747fSPaolo Bonzini #endif
79349ab747fSPaolo Bonzini             break;
79449ab747fSPaolo Bonzini 
79549ab747fSPaolo Bonzini         case 0xe4:
79649ab747fSPaolo Bonzini             s->test_reg = dsp_get_data (s);
79749ab747fSPaolo Bonzini             break;
79849ab747fSPaolo Bonzini 
79949ab747fSPaolo Bonzini         case 0xf9:
80049ab747fSPaolo Bonzini             d0 = dsp_get_data (s);
80149ab747fSPaolo Bonzini             ldebug ("command 0xf9 with %#x\n", d0);
80249ab747fSPaolo Bonzini             switch (d0) {
80349ab747fSPaolo Bonzini             case 0x0e:
80449ab747fSPaolo Bonzini                 dsp_out_data (s, 0xff);
80549ab747fSPaolo Bonzini                 break;
80649ab747fSPaolo Bonzini 
80749ab747fSPaolo Bonzini             case 0x0f:
80849ab747fSPaolo Bonzini                 dsp_out_data (s, 0x07);
80949ab747fSPaolo Bonzini                 break;
81049ab747fSPaolo Bonzini 
81149ab747fSPaolo Bonzini             case 0x37:
81249ab747fSPaolo Bonzini                 dsp_out_data (s, 0x38);
81349ab747fSPaolo Bonzini                 break;
81449ab747fSPaolo Bonzini 
81549ab747fSPaolo Bonzini             default:
81649ab747fSPaolo Bonzini                 dsp_out_data (s, 0x00);
81749ab747fSPaolo Bonzini                 break;
81849ab747fSPaolo Bonzini             }
81949ab747fSPaolo Bonzini             break;
82049ab747fSPaolo Bonzini 
82149ab747fSPaolo Bonzini         default:
82249ab747fSPaolo Bonzini             dolog ("complete: unrecognized command %#x\n", s->cmd);
82349ab747fSPaolo Bonzini             return;
82449ab747fSPaolo Bonzini         }
82549ab747fSPaolo Bonzini     }
82649ab747fSPaolo Bonzini 
82749ab747fSPaolo Bonzini     ldebug ("\n");
82849ab747fSPaolo Bonzini     s->cmd = -1;
82949ab747fSPaolo Bonzini }
83049ab747fSPaolo Bonzini 
83149ab747fSPaolo Bonzini static void legacy_reset (SB16State *s)
83249ab747fSPaolo Bonzini {
83349ab747fSPaolo Bonzini     struct audsettings as;
83449ab747fSPaolo Bonzini 
83549ab747fSPaolo Bonzini     s->freq = 11025;
83649ab747fSPaolo Bonzini     s->fmt_signed = 0;
83749ab747fSPaolo Bonzini     s->fmt_bits = 8;
83849ab747fSPaolo Bonzini     s->fmt_stereo = 0;
83949ab747fSPaolo Bonzini 
84049ab747fSPaolo Bonzini     as.freq = s->freq;
84149ab747fSPaolo Bonzini     as.nchannels = 1;
84249ab747fSPaolo Bonzini     as.fmt = AUD_FMT_U8;
84349ab747fSPaolo Bonzini     as.endianness = 0;
84449ab747fSPaolo Bonzini 
84549ab747fSPaolo Bonzini     s->voice = AUD_open_out (
84649ab747fSPaolo Bonzini         &s->card,
84749ab747fSPaolo Bonzini         s->voice,
84849ab747fSPaolo Bonzini         "sb16",
84949ab747fSPaolo Bonzini         s,
85049ab747fSPaolo Bonzini         SB_audio_callback,
85149ab747fSPaolo Bonzini         &as
85249ab747fSPaolo Bonzini         );
85349ab747fSPaolo Bonzini 
85449ab747fSPaolo Bonzini     /* Not sure about that... */
85549ab747fSPaolo Bonzini     /* AUD_set_active_out (s->voice, 1); */
85649ab747fSPaolo Bonzini }
85749ab747fSPaolo Bonzini 
85849ab747fSPaolo Bonzini static void reset (SB16State *s)
85949ab747fSPaolo Bonzini {
86049ab747fSPaolo Bonzini     qemu_irq_lower (s->pic);
86149ab747fSPaolo Bonzini     if (s->dma_auto) {
86249ab747fSPaolo Bonzini         qemu_irq_raise (s->pic);
86349ab747fSPaolo Bonzini         qemu_irq_lower (s->pic);
86449ab747fSPaolo Bonzini     }
86549ab747fSPaolo Bonzini 
86649ab747fSPaolo Bonzini     s->mixer_regs[0x82] = 0;
86749ab747fSPaolo Bonzini     s->dma_auto = 0;
86849ab747fSPaolo Bonzini     s->in_index = 0;
86949ab747fSPaolo Bonzini     s->out_data_len = 0;
87049ab747fSPaolo Bonzini     s->left_till_irq = 0;
87149ab747fSPaolo Bonzini     s->needed_bytes = 0;
87249ab747fSPaolo Bonzini     s->block_size = -1;
87349ab747fSPaolo Bonzini     s->nzero = 0;
87449ab747fSPaolo Bonzini     s->highspeed = 0;
87549ab747fSPaolo Bonzini     s->v2x6 = 0;
87649ab747fSPaolo Bonzini     s->cmd = -1;
87749ab747fSPaolo Bonzini 
87849ab747fSPaolo Bonzini     dsp_out_data (s, 0xaa);
87949ab747fSPaolo Bonzini     speaker (s, 0);
88049ab747fSPaolo Bonzini     control (s, 0);
88149ab747fSPaolo Bonzini     legacy_reset (s);
88249ab747fSPaolo Bonzini }
88349ab747fSPaolo Bonzini 
88449ab747fSPaolo Bonzini static IO_WRITE_PROTO (dsp_write)
88549ab747fSPaolo Bonzini {
88649ab747fSPaolo Bonzini     SB16State *s = opaque;
88749ab747fSPaolo Bonzini     int iport;
88849ab747fSPaolo Bonzini 
88949ab747fSPaolo Bonzini     iport = nport - s->port;
89049ab747fSPaolo Bonzini 
89149ab747fSPaolo Bonzini     ldebug ("write %#x <- %#x\n", nport, val);
89249ab747fSPaolo Bonzini     switch (iport) {
89349ab747fSPaolo Bonzini     case 0x06:
89449ab747fSPaolo Bonzini         switch (val) {
89549ab747fSPaolo Bonzini         case 0x00:
89649ab747fSPaolo Bonzini             if (s->v2x6 == 1) {
89749ab747fSPaolo Bonzini                 reset (s);
89849ab747fSPaolo Bonzini             }
89949ab747fSPaolo Bonzini             s->v2x6 = 0;
90049ab747fSPaolo Bonzini             break;
90149ab747fSPaolo Bonzini 
90249ab747fSPaolo Bonzini         case 0x01:
90349ab747fSPaolo Bonzini         case 0x03:              /* FreeBSD kludge */
90449ab747fSPaolo Bonzini             s->v2x6 = 1;
90549ab747fSPaolo Bonzini             break;
90649ab747fSPaolo Bonzini 
90749ab747fSPaolo Bonzini         case 0xc6:
90849ab747fSPaolo Bonzini             s->v2x6 = 0;        /* Prince of Persia, csp.sys, diagnose.exe */
90949ab747fSPaolo Bonzini             break;
91049ab747fSPaolo Bonzini 
91149ab747fSPaolo Bonzini         case 0xb8:              /* Panic */
91249ab747fSPaolo Bonzini             reset (s);
91349ab747fSPaolo Bonzini             break;
91449ab747fSPaolo Bonzini 
91549ab747fSPaolo Bonzini         case 0x39:
91649ab747fSPaolo Bonzini             dsp_out_data (s, 0x38);
91749ab747fSPaolo Bonzini             reset (s);
91849ab747fSPaolo Bonzini             s->v2x6 = 0x39;
91949ab747fSPaolo Bonzini             break;
92049ab747fSPaolo Bonzini 
92149ab747fSPaolo Bonzini         default:
92249ab747fSPaolo Bonzini             s->v2x6 = val;
92349ab747fSPaolo Bonzini             break;
92449ab747fSPaolo Bonzini         }
92549ab747fSPaolo Bonzini         break;
92649ab747fSPaolo Bonzini 
92749ab747fSPaolo Bonzini     case 0x0c:                  /* write data or command | write status */
92849ab747fSPaolo Bonzini /*         if (s->highspeed) */
92949ab747fSPaolo Bonzini /*             break; */
93049ab747fSPaolo Bonzini 
93149ab747fSPaolo Bonzini         if (0 == s->needed_bytes) {
93249ab747fSPaolo Bonzini             command (s, val);
93349ab747fSPaolo Bonzini #if 0
93449ab747fSPaolo Bonzini             if (0 == s->needed_bytes) {
93549ab747fSPaolo Bonzini                 log_dsp (s);
93649ab747fSPaolo Bonzini             }
93749ab747fSPaolo Bonzini #endif
93849ab747fSPaolo Bonzini         }
93949ab747fSPaolo Bonzini         else {
94049ab747fSPaolo Bonzini             if (s->in_index == sizeof (s->in2_data)) {
94149ab747fSPaolo Bonzini                 dolog ("in data overrun\n");
94249ab747fSPaolo Bonzini             }
94349ab747fSPaolo Bonzini             else {
94449ab747fSPaolo Bonzini                 s->in2_data[s->in_index++] = val;
94549ab747fSPaolo Bonzini                 if (s->in_index == s->needed_bytes) {
94649ab747fSPaolo Bonzini                     s->needed_bytes = 0;
94749ab747fSPaolo Bonzini                     complete (s);
94849ab747fSPaolo Bonzini #if 0
94949ab747fSPaolo Bonzini                     log_dsp (s);
95049ab747fSPaolo Bonzini #endif
95149ab747fSPaolo Bonzini                 }
95249ab747fSPaolo Bonzini             }
95349ab747fSPaolo Bonzini         }
95449ab747fSPaolo Bonzini         break;
95549ab747fSPaolo Bonzini 
95649ab747fSPaolo Bonzini     default:
95749ab747fSPaolo Bonzini         ldebug ("(nport=%#x, val=%#x)\n", nport, val);
95849ab747fSPaolo Bonzini         break;
95949ab747fSPaolo Bonzini     }
96049ab747fSPaolo Bonzini }
96149ab747fSPaolo Bonzini 
96249ab747fSPaolo Bonzini static IO_READ_PROTO (dsp_read)
96349ab747fSPaolo Bonzini {
96449ab747fSPaolo Bonzini     SB16State *s = opaque;
96549ab747fSPaolo Bonzini     int iport, retval, ack = 0;
96649ab747fSPaolo Bonzini 
96749ab747fSPaolo Bonzini     iport = nport - s->port;
96849ab747fSPaolo Bonzini 
96949ab747fSPaolo Bonzini     switch (iport) {
97049ab747fSPaolo Bonzini     case 0x06:                  /* reset */
97149ab747fSPaolo Bonzini         retval = 0xff;
97249ab747fSPaolo Bonzini         break;
97349ab747fSPaolo Bonzini 
97449ab747fSPaolo Bonzini     case 0x0a:                  /* read data */
97549ab747fSPaolo Bonzini         if (s->out_data_len) {
97649ab747fSPaolo Bonzini             retval = s->out_data[--s->out_data_len];
97749ab747fSPaolo Bonzini             s->last_read_byte = retval;
97849ab747fSPaolo Bonzini         }
97949ab747fSPaolo Bonzini         else {
98049ab747fSPaolo Bonzini             if (s->cmd != -1) {
98149ab747fSPaolo Bonzini                 dolog ("empty output buffer for command %#x\n",
98249ab747fSPaolo Bonzini                        s->cmd);
98349ab747fSPaolo Bonzini             }
98449ab747fSPaolo Bonzini             retval = s->last_read_byte;
98549ab747fSPaolo Bonzini             /* goto error; */
98649ab747fSPaolo Bonzini         }
98749ab747fSPaolo Bonzini         break;
98849ab747fSPaolo Bonzini 
98949ab747fSPaolo Bonzini     case 0x0c:                  /* 0 can write */
99049ab747fSPaolo Bonzini         retval = s->can_write ? 0 : 0x80;
99149ab747fSPaolo Bonzini         break;
99249ab747fSPaolo Bonzini 
99349ab747fSPaolo Bonzini     case 0x0d:                  /* timer interrupt clear */
99449ab747fSPaolo Bonzini         /* dolog ("timer interrupt clear\n"); */
99549ab747fSPaolo Bonzini         retval = 0;
99649ab747fSPaolo Bonzini         break;
99749ab747fSPaolo Bonzini 
99849ab747fSPaolo Bonzini     case 0x0e:                  /* data available status | irq 8 ack */
99949ab747fSPaolo Bonzini         retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80;
100049ab747fSPaolo Bonzini         if (s->mixer_regs[0x82] & 1) {
100149ab747fSPaolo Bonzini             ack = 1;
100249ab747fSPaolo Bonzini             s->mixer_regs[0x82] &= 1;
100349ab747fSPaolo Bonzini             qemu_irq_lower (s->pic);
100449ab747fSPaolo Bonzini         }
100549ab747fSPaolo Bonzini         break;
100649ab747fSPaolo Bonzini 
100749ab747fSPaolo Bonzini     case 0x0f:                  /* irq 16 ack */
100849ab747fSPaolo Bonzini         retval = 0xff;
100949ab747fSPaolo Bonzini         if (s->mixer_regs[0x82] & 2) {
101049ab747fSPaolo Bonzini             ack = 1;
101149ab747fSPaolo Bonzini             s->mixer_regs[0x82] &= 2;
101249ab747fSPaolo Bonzini             qemu_irq_lower (s->pic);
101349ab747fSPaolo Bonzini         }
101449ab747fSPaolo Bonzini         break;
101549ab747fSPaolo Bonzini 
101649ab747fSPaolo Bonzini     default:
101749ab747fSPaolo Bonzini         goto error;
101849ab747fSPaolo Bonzini     }
101949ab747fSPaolo Bonzini 
102049ab747fSPaolo Bonzini     if (!ack) {
102149ab747fSPaolo Bonzini         ldebug ("read %#x -> %#x\n", nport, retval);
102249ab747fSPaolo Bonzini     }
102349ab747fSPaolo Bonzini 
102449ab747fSPaolo Bonzini     return retval;
102549ab747fSPaolo Bonzini 
102649ab747fSPaolo Bonzini  error:
102749ab747fSPaolo Bonzini     dolog ("warning: dsp_read %#x error\n", nport);
102849ab747fSPaolo Bonzini     return 0xff;
102949ab747fSPaolo Bonzini }
103049ab747fSPaolo Bonzini 
103149ab747fSPaolo Bonzini static void reset_mixer (SB16State *s)
103249ab747fSPaolo Bonzini {
103349ab747fSPaolo Bonzini     int i;
103449ab747fSPaolo Bonzini 
103549ab747fSPaolo Bonzini     memset (s->mixer_regs, 0xff, 0x7f);
103649ab747fSPaolo Bonzini     memset (s->mixer_regs + 0x83, 0xff, sizeof (s->mixer_regs) - 0x83);
103749ab747fSPaolo Bonzini 
103849ab747fSPaolo Bonzini     s->mixer_regs[0x02] = 4;    /* master volume 3bits */
103949ab747fSPaolo Bonzini     s->mixer_regs[0x06] = 4;    /* MIDI volume 3bits */
104049ab747fSPaolo Bonzini     s->mixer_regs[0x08] = 0;    /* CD volume 3bits */
104149ab747fSPaolo Bonzini     s->mixer_regs[0x0a] = 0;    /* voice volume 2bits */
104249ab747fSPaolo Bonzini 
104349ab747fSPaolo Bonzini     /* d5=input filt, d3=lowpass filt, d1,d2=input source */
104449ab747fSPaolo Bonzini     s->mixer_regs[0x0c] = 0;
104549ab747fSPaolo Bonzini 
104649ab747fSPaolo Bonzini     /* d5=output filt, d1=stereo switch */
104749ab747fSPaolo Bonzini     s->mixer_regs[0x0e] = 0;
104849ab747fSPaolo Bonzini 
104949ab747fSPaolo Bonzini     /* voice volume L d5,d7, R d1,d3 */
105049ab747fSPaolo Bonzini     s->mixer_regs[0x04] = (4 << 5) | (4 << 1);
105149ab747fSPaolo Bonzini     /* master ... */
105249ab747fSPaolo Bonzini     s->mixer_regs[0x22] = (4 << 5) | (4 << 1);
105349ab747fSPaolo Bonzini     /* MIDI ... */
105449ab747fSPaolo Bonzini     s->mixer_regs[0x26] = (4 << 5) | (4 << 1);
105549ab747fSPaolo Bonzini 
105649ab747fSPaolo Bonzini     for (i = 0x30; i < 0x48; i++) {
105749ab747fSPaolo Bonzini         s->mixer_regs[i] = 0x20;
105849ab747fSPaolo Bonzini     }
105949ab747fSPaolo Bonzini }
106049ab747fSPaolo Bonzini 
106149ab747fSPaolo Bonzini static IO_WRITE_PROTO (mixer_write_indexb)
106249ab747fSPaolo Bonzini {
106349ab747fSPaolo Bonzini     SB16State *s = opaque;
106449ab747fSPaolo Bonzini     (void) nport;
106549ab747fSPaolo Bonzini     s->mixer_nreg = val;
106649ab747fSPaolo Bonzini }
106749ab747fSPaolo Bonzini 
106849ab747fSPaolo Bonzini static IO_WRITE_PROTO (mixer_write_datab)
106949ab747fSPaolo Bonzini {
107049ab747fSPaolo Bonzini     SB16State *s = opaque;
107149ab747fSPaolo Bonzini 
107249ab747fSPaolo Bonzini     (void) nport;
107349ab747fSPaolo Bonzini     ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val);
107449ab747fSPaolo Bonzini 
107549ab747fSPaolo Bonzini     switch (s->mixer_nreg) {
107649ab747fSPaolo Bonzini     case 0x00:
107749ab747fSPaolo Bonzini         reset_mixer (s);
107849ab747fSPaolo Bonzini         break;
107949ab747fSPaolo Bonzini 
108049ab747fSPaolo Bonzini     case 0x80:
108149ab747fSPaolo Bonzini         {
108249ab747fSPaolo Bonzini             int irq = irq_of_magic (val);
108349ab747fSPaolo Bonzini             ldebug ("setting irq to %d (val=%#x)\n", irq, val);
108449ab747fSPaolo Bonzini             if (irq > 0) {
108549ab747fSPaolo Bonzini                 s->irq = irq;
108649ab747fSPaolo Bonzini             }
108749ab747fSPaolo Bonzini         }
108849ab747fSPaolo Bonzini         break;
108949ab747fSPaolo Bonzini 
109049ab747fSPaolo Bonzini     case 0x81:
109149ab747fSPaolo Bonzini         {
109249ab747fSPaolo Bonzini             int dma, hdma;
109349ab747fSPaolo Bonzini 
109449ab747fSPaolo Bonzini             dma = ctz32 (val & 0xf);
109549ab747fSPaolo Bonzini             hdma = ctz32 (val & 0xf0);
109649ab747fSPaolo Bonzini             if (dma != s->dma || hdma != s->hdma) {
109749ab747fSPaolo Bonzini                 dolog (
109849ab747fSPaolo Bonzini                     "attempt to change DMA "
109949ab747fSPaolo Bonzini                     "8bit %d(%d), 16bit %d(%d) (val=%#x)\n",
110049ab747fSPaolo Bonzini                     dma, s->dma, hdma, s->hdma, val);
110149ab747fSPaolo Bonzini             }
110249ab747fSPaolo Bonzini #if 0
110349ab747fSPaolo Bonzini             s->dma = dma;
110449ab747fSPaolo Bonzini             s->hdma = hdma;
110549ab747fSPaolo Bonzini #endif
110649ab747fSPaolo Bonzini         }
110749ab747fSPaolo Bonzini         break;
110849ab747fSPaolo Bonzini 
110949ab747fSPaolo Bonzini     case 0x82:
111049ab747fSPaolo Bonzini         dolog ("attempt to write into IRQ status register (val=%#x)\n",
111149ab747fSPaolo Bonzini                val);
111249ab747fSPaolo Bonzini         return;
111349ab747fSPaolo Bonzini 
111449ab747fSPaolo Bonzini     default:
111549ab747fSPaolo Bonzini         if (s->mixer_nreg >= 0x80) {
111649ab747fSPaolo Bonzini             ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val);
111749ab747fSPaolo Bonzini         }
111849ab747fSPaolo Bonzini         break;
111949ab747fSPaolo Bonzini     }
112049ab747fSPaolo Bonzini 
112149ab747fSPaolo Bonzini     s->mixer_regs[s->mixer_nreg] = val;
112249ab747fSPaolo Bonzini }
112349ab747fSPaolo Bonzini 
112449ab747fSPaolo Bonzini static IO_WRITE_PROTO (mixer_write_indexw)
112549ab747fSPaolo Bonzini {
112649ab747fSPaolo Bonzini     mixer_write_indexb (opaque, nport, val & 0xff);
112749ab747fSPaolo Bonzini     mixer_write_datab (opaque, nport, (val >> 8) & 0xff);
112849ab747fSPaolo Bonzini }
112949ab747fSPaolo Bonzini 
113049ab747fSPaolo Bonzini static IO_READ_PROTO (mixer_read)
113149ab747fSPaolo Bonzini {
113249ab747fSPaolo Bonzini     SB16State *s = opaque;
113349ab747fSPaolo Bonzini 
113449ab747fSPaolo Bonzini     (void) nport;
113549ab747fSPaolo Bonzini #ifndef DEBUG_SB16_MOST
113649ab747fSPaolo Bonzini     if (s->mixer_nreg != 0x82) {
113749ab747fSPaolo Bonzini         ldebug ("mixer_read[%#x] -> %#x\n",
113849ab747fSPaolo Bonzini                 s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
113949ab747fSPaolo Bonzini     }
114049ab747fSPaolo Bonzini #else
114149ab747fSPaolo Bonzini     ldebug ("mixer_read[%#x] -> %#x\n",
114249ab747fSPaolo Bonzini             s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
114349ab747fSPaolo Bonzini #endif
114449ab747fSPaolo Bonzini     return s->mixer_regs[s->mixer_nreg];
114549ab747fSPaolo Bonzini }
114649ab747fSPaolo Bonzini 
114749ab747fSPaolo Bonzini static int write_audio (SB16State *s, int nchan, int dma_pos,
114849ab747fSPaolo Bonzini                         int dma_len, int len)
114949ab747fSPaolo Bonzini {
115049ab747fSPaolo Bonzini     int temp, net;
115149ab747fSPaolo Bonzini     uint8_t tmpbuf[4096];
115249ab747fSPaolo Bonzini 
115349ab747fSPaolo Bonzini     temp = len;
115449ab747fSPaolo Bonzini     net = 0;
115549ab747fSPaolo Bonzini 
115649ab747fSPaolo Bonzini     while (temp) {
115749ab747fSPaolo Bonzini         int left = dma_len - dma_pos;
115849ab747fSPaolo Bonzini         int copied;
115949ab747fSPaolo Bonzini         size_t to_copy;
116049ab747fSPaolo Bonzini 
116149ab747fSPaolo Bonzini         to_copy = audio_MIN (temp, left);
116249ab747fSPaolo Bonzini         if (to_copy > sizeof (tmpbuf)) {
116349ab747fSPaolo Bonzini             to_copy = sizeof (tmpbuf);
116449ab747fSPaolo Bonzini         }
116549ab747fSPaolo Bonzini 
116649ab747fSPaolo Bonzini         copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
116749ab747fSPaolo Bonzini         copied = AUD_write (s->voice, tmpbuf, copied);
116849ab747fSPaolo Bonzini 
116949ab747fSPaolo Bonzini         temp -= copied;
117049ab747fSPaolo Bonzini         dma_pos = (dma_pos + copied) % dma_len;
117149ab747fSPaolo Bonzini         net += copied;
117249ab747fSPaolo Bonzini 
117349ab747fSPaolo Bonzini         if (!copied) {
117449ab747fSPaolo Bonzini             break;
117549ab747fSPaolo Bonzini         }
117649ab747fSPaolo Bonzini     }
117749ab747fSPaolo Bonzini 
117849ab747fSPaolo Bonzini     return net;
117949ab747fSPaolo Bonzini }
118049ab747fSPaolo Bonzini 
118149ab747fSPaolo Bonzini static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
118249ab747fSPaolo Bonzini {
118349ab747fSPaolo Bonzini     SB16State *s = opaque;
118449ab747fSPaolo Bonzini     int till, copy, written, free;
118549ab747fSPaolo Bonzini 
118649ab747fSPaolo Bonzini     if (s->block_size <= 0) {
118749ab747fSPaolo Bonzini         dolog ("invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n",
118849ab747fSPaolo Bonzini                s->block_size, nchan, dma_pos, dma_len);
118949ab747fSPaolo Bonzini         return dma_pos;
119049ab747fSPaolo Bonzini     }
119149ab747fSPaolo Bonzini 
119249ab747fSPaolo Bonzini     if (s->left_till_irq < 0) {
119349ab747fSPaolo Bonzini         s->left_till_irq = s->block_size;
119449ab747fSPaolo Bonzini     }
119549ab747fSPaolo Bonzini 
119649ab747fSPaolo Bonzini     if (s->voice) {
119749ab747fSPaolo Bonzini         free = s->audio_free & ~s->align;
119849ab747fSPaolo Bonzini         if ((free <= 0) || !dma_len) {
119949ab747fSPaolo Bonzini             return dma_pos;
120049ab747fSPaolo Bonzini         }
120149ab747fSPaolo Bonzini     }
120249ab747fSPaolo Bonzini     else {
120349ab747fSPaolo Bonzini         free = dma_len;
120449ab747fSPaolo Bonzini     }
120549ab747fSPaolo Bonzini 
120649ab747fSPaolo Bonzini     copy = free;
120749ab747fSPaolo Bonzini     till = s->left_till_irq;
120849ab747fSPaolo Bonzini 
120949ab747fSPaolo Bonzini #ifdef DEBUG_SB16_MOST
121049ab747fSPaolo Bonzini     dolog ("pos:%06d %d till:%d len:%d\n",
121149ab747fSPaolo Bonzini            dma_pos, free, till, dma_len);
121249ab747fSPaolo Bonzini #endif
121349ab747fSPaolo Bonzini 
121449ab747fSPaolo Bonzini     if (till <= copy) {
121549ab747fSPaolo Bonzini         if (0 == s->dma_auto) {
121649ab747fSPaolo Bonzini             copy = till;
121749ab747fSPaolo Bonzini         }
121849ab747fSPaolo Bonzini     }
121949ab747fSPaolo Bonzini 
122049ab747fSPaolo Bonzini     written = write_audio (s, nchan, dma_pos, dma_len, copy);
122149ab747fSPaolo Bonzini     dma_pos = (dma_pos + written) % dma_len;
122249ab747fSPaolo Bonzini     s->left_till_irq -= written;
122349ab747fSPaolo Bonzini 
122449ab747fSPaolo Bonzini     if (s->left_till_irq <= 0) {
122549ab747fSPaolo Bonzini         s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1;
122649ab747fSPaolo Bonzini         qemu_irq_raise (s->pic);
122749ab747fSPaolo Bonzini         if (0 == s->dma_auto) {
122849ab747fSPaolo Bonzini             control (s, 0);
122949ab747fSPaolo Bonzini             speaker (s, 0);
123049ab747fSPaolo Bonzini         }
123149ab747fSPaolo Bonzini     }
123249ab747fSPaolo Bonzini 
123349ab747fSPaolo Bonzini #ifdef DEBUG_SB16_MOST
123449ab747fSPaolo Bonzini     ldebug ("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n",
123549ab747fSPaolo Bonzini             dma_pos, free, dma_len, s->left_till_irq, copy, written,
123649ab747fSPaolo Bonzini             s->block_size);
123749ab747fSPaolo Bonzini #endif
123849ab747fSPaolo Bonzini 
123949ab747fSPaolo Bonzini     while (s->left_till_irq <= 0) {
124049ab747fSPaolo Bonzini         s->left_till_irq = s->block_size + s->left_till_irq;
124149ab747fSPaolo Bonzini     }
124249ab747fSPaolo Bonzini 
124349ab747fSPaolo Bonzini     return dma_pos;
124449ab747fSPaolo Bonzini }
124549ab747fSPaolo Bonzini 
124649ab747fSPaolo Bonzini static void SB_audio_callback (void *opaque, int free)
124749ab747fSPaolo Bonzini {
124849ab747fSPaolo Bonzini     SB16State *s = opaque;
124949ab747fSPaolo Bonzini     s->audio_free = free;
125049ab747fSPaolo Bonzini }
125149ab747fSPaolo Bonzini 
125249ab747fSPaolo Bonzini static int sb16_post_load (void *opaque, int version_id)
125349ab747fSPaolo Bonzini {
125449ab747fSPaolo Bonzini     SB16State *s = opaque;
125549ab747fSPaolo Bonzini 
125649ab747fSPaolo Bonzini     if (s->voice) {
125749ab747fSPaolo Bonzini         AUD_close_out (&s->card, s->voice);
125849ab747fSPaolo Bonzini         s->voice = NULL;
125949ab747fSPaolo Bonzini     }
126049ab747fSPaolo Bonzini 
126149ab747fSPaolo Bonzini     if (s->dma_running) {
126249ab747fSPaolo Bonzini         if (s->freq) {
126349ab747fSPaolo Bonzini             struct audsettings as;
126449ab747fSPaolo Bonzini 
126549ab747fSPaolo Bonzini             s->audio_free = 0;
126649ab747fSPaolo Bonzini 
126749ab747fSPaolo Bonzini             as.freq = s->freq;
126849ab747fSPaolo Bonzini             as.nchannels = 1 << s->fmt_stereo;
126949ab747fSPaolo Bonzini             as.fmt = s->fmt;
127049ab747fSPaolo Bonzini             as.endianness = 0;
127149ab747fSPaolo Bonzini 
127249ab747fSPaolo Bonzini             s->voice = AUD_open_out (
127349ab747fSPaolo Bonzini                 &s->card,
127449ab747fSPaolo Bonzini                 s->voice,
127549ab747fSPaolo Bonzini                 "sb16",
127649ab747fSPaolo Bonzini                 s,
127749ab747fSPaolo Bonzini                 SB_audio_callback,
127849ab747fSPaolo Bonzini                 &as
127949ab747fSPaolo Bonzini                 );
128049ab747fSPaolo Bonzini         }
128149ab747fSPaolo Bonzini 
128249ab747fSPaolo Bonzini         control (s, 1);
128349ab747fSPaolo Bonzini         speaker (s, s->speaker);
128449ab747fSPaolo Bonzini     }
128549ab747fSPaolo Bonzini     return 0;
128649ab747fSPaolo Bonzini }
128749ab747fSPaolo Bonzini 
128849ab747fSPaolo Bonzini static const VMStateDescription vmstate_sb16 = {
128949ab747fSPaolo Bonzini     .name = "sb16",
129049ab747fSPaolo Bonzini     .version_id = 1,
129149ab747fSPaolo Bonzini     .minimum_version_id = 1,
129249ab747fSPaolo Bonzini     .minimum_version_id_old = 1,
129349ab747fSPaolo Bonzini     .post_load = sb16_post_load,
129449ab747fSPaolo Bonzini     .fields      = (VMStateField []) {
129549ab747fSPaolo Bonzini         VMSTATE_UINT32 (irq, SB16State),
129649ab747fSPaolo Bonzini         VMSTATE_UINT32 (dma, SB16State),
129749ab747fSPaolo Bonzini         VMSTATE_UINT32 (hdma, SB16State),
129849ab747fSPaolo Bonzini         VMSTATE_UINT32 (port, SB16State),
129949ab747fSPaolo Bonzini         VMSTATE_UINT32 (ver, SB16State),
130049ab747fSPaolo Bonzini         VMSTATE_INT32 (in_index, SB16State),
130149ab747fSPaolo Bonzini         VMSTATE_INT32 (out_data_len, SB16State),
130249ab747fSPaolo Bonzini         VMSTATE_INT32 (fmt_stereo, SB16State),
130349ab747fSPaolo Bonzini         VMSTATE_INT32 (fmt_signed, SB16State),
130449ab747fSPaolo Bonzini         VMSTATE_INT32 (fmt_bits, SB16State),
130549ab747fSPaolo Bonzini         VMSTATE_UINT32 (fmt, SB16State),
130649ab747fSPaolo Bonzini         VMSTATE_INT32 (dma_auto, SB16State),
130749ab747fSPaolo Bonzini         VMSTATE_INT32 (block_size, SB16State),
130849ab747fSPaolo Bonzini         VMSTATE_INT32 (fifo, SB16State),
130949ab747fSPaolo Bonzini         VMSTATE_INT32 (freq, SB16State),
131049ab747fSPaolo Bonzini         VMSTATE_INT32 (time_const, SB16State),
131149ab747fSPaolo Bonzini         VMSTATE_INT32 (speaker, SB16State),
131249ab747fSPaolo Bonzini         VMSTATE_INT32 (needed_bytes, SB16State),
131349ab747fSPaolo Bonzini         VMSTATE_INT32 (cmd, SB16State),
131449ab747fSPaolo Bonzini         VMSTATE_INT32 (use_hdma, SB16State),
131549ab747fSPaolo Bonzini         VMSTATE_INT32 (highspeed, SB16State),
131649ab747fSPaolo Bonzini         VMSTATE_INT32 (can_write, SB16State),
131749ab747fSPaolo Bonzini         VMSTATE_INT32 (v2x6, SB16State),
131849ab747fSPaolo Bonzini 
131949ab747fSPaolo Bonzini         VMSTATE_UINT8 (csp_param, SB16State),
132049ab747fSPaolo Bonzini         VMSTATE_UINT8 (csp_value, SB16State),
132149ab747fSPaolo Bonzini         VMSTATE_UINT8 (csp_mode, SB16State),
132249ab747fSPaolo Bonzini         VMSTATE_UINT8 (csp_param, SB16State),
132349ab747fSPaolo Bonzini         VMSTATE_BUFFER (csp_regs, SB16State),
132449ab747fSPaolo Bonzini         VMSTATE_UINT8 (csp_index, SB16State),
132549ab747fSPaolo Bonzini         VMSTATE_BUFFER (csp_reg83, SB16State),
132649ab747fSPaolo Bonzini         VMSTATE_INT32 (csp_reg83r, SB16State),
132749ab747fSPaolo Bonzini         VMSTATE_INT32 (csp_reg83w, SB16State),
132849ab747fSPaolo Bonzini 
132949ab747fSPaolo Bonzini         VMSTATE_BUFFER (in2_data, SB16State),
133049ab747fSPaolo Bonzini         VMSTATE_BUFFER (out_data, SB16State),
133149ab747fSPaolo Bonzini         VMSTATE_UINT8 (test_reg, SB16State),
133249ab747fSPaolo Bonzini         VMSTATE_UINT8 (last_read_byte, SB16State),
133349ab747fSPaolo Bonzini 
133449ab747fSPaolo Bonzini         VMSTATE_INT32 (nzero, SB16State),
133549ab747fSPaolo Bonzini         VMSTATE_INT32 (left_till_irq, SB16State),
133649ab747fSPaolo Bonzini         VMSTATE_INT32 (dma_running, SB16State),
133749ab747fSPaolo Bonzini         VMSTATE_INT32 (bytes_per_second, SB16State),
133849ab747fSPaolo Bonzini         VMSTATE_INT32 (align, SB16State),
133949ab747fSPaolo Bonzini 
134049ab747fSPaolo Bonzini         VMSTATE_INT32 (mixer_nreg, SB16State),
134149ab747fSPaolo Bonzini         VMSTATE_BUFFER (mixer_regs, SB16State),
134249ab747fSPaolo Bonzini 
134349ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST ()
134449ab747fSPaolo Bonzini     }
134549ab747fSPaolo Bonzini };
134649ab747fSPaolo Bonzini 
134749ab747fSPaolo Bonzini static const MemoryRegionPortio sb16_ioport_list[] = {
134849ab747fSPaolo Bonzini     {  4, 1, 1, .write = mixer_write_indexb },
134949ab747fSPaolo Bonzini     {  4, 1, 2, .write = mixer_write_indexw },
135049ab747fSPaolo Bonzini     {  5, 1, 1, .read = mixer_read, .write = mixer_write_datab },
135149ab747fSPaolo Bonzini     {  6, 1, 1, .read = dsp_read, .write = dsp_write },
135249ab747fSPaolo Bonzini     { 10, 1, 1, .read = dsp_read },
135349ab747fSPaolo Bonzini     { 12, 1, 1, .write = dsp_write },
135449ab747fSPaolo Bonzini     { 12, 4, 1, .read = dsp_read },
135549ab747fSPaolo Bonzini     PORTIO_END_OF_LIST (),
135649ab747fSPaolo Bonzini };
135749ab747fSPaolo Bonzini 
135849ab747fSPaolo Bonzini 
1359db895a1eSAndreas Färber static void sb16_initfn (Object *obj)
136049ab747fSPaolo Bonzini {
1361db895a1eSAndreas Färber     SB16State *s = SB16 (obj);
136249ab747fSPaolo Bonzini 
136349ab747fSPaolo Bonzini     s->cmd = -1;
1364db895a1eSAndreas Färber }
1365db895a1eSAndreas Färber 
1366db895a1eSAndreas Färber static void sb16_realizefn (DeviceState *dev, Error **errp)
1367db895a1eSAndreas Färber {
1368db895a1eSAndreas Färber     ISADevice *isadev = ISA_DEVICE (dev);
1369db895a1eSAndreas Färber     SB16State *s = SB16 (dev);
1370db895a1eSAndreas Färber 
1371db895a1eSAndreas Färber     isa_init_irq (isadev, &s->pic, s->irq);
137249ab747fSPaolo Bonzini 
137349ab747fSPaolo Bonzini     s->mixer_regs[0x80] = magic_of_irq (s->irq);
137449ab747fSPaolo Bonzini     s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma);
137549ab747fSPaolo Bonzini     s->mixer_regs[0x82] = 2 << 5;
137649ab747fSPaolo Bonzini 
137749ab747fSPaolo Bonzini     s->csp_regs[5] = 1;
137849ab747fSPaolo Bonzini     s->csp_regs[9] = 0xf8;
137949ab747fSPaolo Bonzini 
138049ab747fSPaolo Bonzini     reset_mixer (s);
1381*bc72ad67SAlex Bligh     s->aux_ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, aux_timer, s);
138249ab747fSPaolo Bonzini     if (!s->aux_ts) {
138349ab747fSPaolo Bonzini         dolog ("warning: Could not create auxiliary timer\n");
138449ab747fSPaolo Bonzini     }
138549ab747fSPaolo Bonzini 
1386db895a1eSAndreas Färber     isa_register_portio_list (isadev, s->port, sb16_ioport_list, s, "sb16");
138749ab747fSPaolo Bonzini 
138849ab747fSPaolo Bonzini     DMA_register_channel (s->hdma, SB_read_DMA, s);
138949ab747fSPaolo Bonzini     DMA_register_channel (s->dma, SB_read_DMA, s);
139049ab747fSPaolo Bonzini     s->can_write = 1;
139149ab747fSPaolo Bonzini 
139249ab747fSPaolo Bonzini     AUD_register_card ("sb16", &s->card);
139349ab747fSPaolo Bonzini }
139449ab747fSPaolo Bonzini 
139536cd6f6fSPaolo Bonzini static int SB16_init (ISABus *bus)
139649ab747fSPaolo Bonzini {
1397399f05a6SAndreas Färber     isa_create_simple (bus, TYPE_SB16);
139849ab747fSPaolo Bonzini     return 0;
139949ab747fSPaolo Bonzini }
140049ab747fSPaolo Bonzini 
140149ab747fSPaolo Bonzini static Property sb16_properties[] = {
140249ab747fSPaolo Bonzini     DEFINE_PROP_HEX32  ("version", SB16State, ver,  0x0405), /* 4.5 */
140349ab747fSPaolo Bonzini     DEFINE_PROP_HEX32  ("iobase",  SB16State, port, 0x220),
140449ab747fSPaolo Bonzini     DEFINE_PROP_UINT32 ("irq",     SB16State, irq,  5),
140549ab747fSPaolo Bonzini     DEFINE_PROP_UINT32 ("dma",     SB16State, dma,  1),
140649ab747fSPaolo Bonzini     DEFINE_PROP_UINT32 ("dma16",   SB16State, hdma, 5),
140749ab747fSPaolo Bonzini     DEFINE_PROP_END_OF_LIST (),
140849ab747fSPaolo Bonzini };
140949ab747fSPaolo Bonzini 
141049ab747fSPaolo Bonzini static void sb16_class_initfn (ObjectClass *klass, void *data)
141149ab747fSPaolo Bonzini {
141249ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS (klass);
1413db895a1eSAndreas Färber 
1414db895a1eSAndreas Färber     dc->realize = sb16_realizefn;
1415125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
141649ab747fSPaolo Bonzini     dc->desc = "Creative Sound Blaster 16";
141749ab747fSPaolo Bonzini     dc->vmsd = &vmstate_sb16;
141849ab747fSPaolo Bonzini     dc->props = sb16_properties;
141949ab747fSPaolo Bonzini }
142049ab747fSPaolo Bonzini 
142149ab747fSPaolo Bonzini static const TypeInfo sb16_info = {
1422399f05a6SAndreas Färber     .name          = TYPE_SB16,
142349ab747fSPaolo Bonzini     .parent        = TYPE_ISA_DEVICE,
142449ab747fSPaolo Bonzini     .instance_size = sizeof (SB16State),
1425db895a1eSAndreas Färber     .instance_init = sb16_initfn,
142649ab747fSPaolo Bonzini     .class_init    = sb16_class_initfn,
142749ab747fSPaolo Bonzini };
142849ab747fSPaolo Bonzini 
142949ab747fSPaolo Bonzini static void sb16_register_types (void)
143049ab747fSPaolo Bonzini {
143149ab747fSPaolo Bonzini     type_register_static (&sb16_info);
143236cd6f6fSPaolo Bonzini     isa_register_soundhw("sb16", "Creative Sound Blaster 16", SB16_init);
143349ab747fSPaolo Bonzini }
143449ab747fSPaolo Bonzini 
143549ab747fSPaolo Bonzini type_init (sb16_register_types)
1436