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 */
240b8fa32fSMarkus Armbruster
256086a565SPeter Maydell #include "qemu/osdep.h"
268a824e4dSEduardo Habkost #include "hw/audio/soundhw.h"
2749ab747fSPaolo Bonzini #include "audio/audio.h"
2864552b6bSMarkus Armbruster #include "hw/irq.h"
2949ab747fSPaolo Bonzini #include "hw/isa/isa.h"
30a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
31d6454270SMarkus Armbruster #include "migration/vmstate.h"
3249ab747fSPaolo Bonzini #include "qemu/timer.h"
3349ab747fSPaolo Bonzini #include "qemu/host-utils.h"
348ec660b8SJohn Arbuckle #include "qemu/log.h"
350b8fa32fSMarkus Armbruster #include "qemu/module.h"
368ec660b8SJohn Arbuckle #include "qapi/error.h"
37db1015e9SEduardo Habkost #include "qom/object.h"
3849ab747fSPaolo Bonzini
3949ab747fSPaolo Bonzini #define dolog(...) AUD_log ("sb16", __VA_ARGS__)
4049ab747fSPaolo Bonzini
4149ab747fSPaolo Bonzini /* #define DEBUG */
4249ab747fSPaolo Bonzini /* #define DEBUG_SB16_MOST */
4349ab747fSPaolo Bonzini
4449ab747fSPaolo Bonzini #ifdef DEBUG
4549ab747fSPaolo Bonzini #define ldebug(...) dolog (__VA_ARGS__)
4649ab747fSPaolo Bonzini #else
4749ab747fSPaolo Bonzini #define ldebug(...)
4849ab747fSPaolo Bonzini #endif
4949ab747fSPaolo Bonzini
5049ab747fSPaolo Bonzini static const char e3[] = "COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.";
5149ab747fSPaolo Bonzini
52399f05a6SAndreas Färber #define TYPE_SB16 "sb16"
538063396bSEduardo Habkost OBJECT_DECLARE_SIMPLE_TYPE(SB16State, SB16)
54399f05a6SAndreas Färber
55db1015e9SEduardo Habkost struct SB16State {
56399f05a6SAndreas Färber ISADevice parent_obj;
57399f05a6SAndreas Färber
5849ab747fSPaolo Bonzini QEMUSoundCard card;
5949ab747fSPaolo Bonzini qemu_irq pic;
6049ab747fSPaolo Bonzini uint32_t irq;
6149ab747fSPaolo Bonzini uint32_t dma;
6249ab747fSPaolo Bonzini uint32_t hdma;
6349ab747fSPaolo Bonzini uint32_t port;
6449ab747fSPaolo Bonzini uint32_t ver;
65f203c16eSHervé Poussineau IsaDma *isa_dma;
66f203c16eSHervé Poussineau IsaDma *isa_hdma;
6749ab747fSPaolo Bonzini
6849ab747fSPaolo Bonzini int in_index;
6949ab747fSPaolo Bonzini int out_data_len;
7049ab747fSPaolo Bonzini int fmt_stereo;
7149ab747fSPaolo Bonzini int fmt_signed;
7249ab747fSPaolo Bonzini int fmt_bits;
7385bc5852SKővágó, Zoltán AudioFormat fmt;
7449ab747fSPaolo Bonzini int dma_auto;
7549ab747fSPaolo Bonzini int block_size;
7649ab747fSPaolo Bonzini int fifo;
7749ab747fSPaolo Bonzini int freq;
7849ab747fSPaolo Bonzini int time_const;
7949ab747fSPaolo Bonzini int speaker;
8049ab747fSPaolo Bonzini int needed_bytes;
8149ab747fSPaolo Bonzini int cmd;
8249ab747fSPaolo Bonzini int use_hdma;
8349ab747fSPaolo Bonzini int highspeed;
8449ab747fSPaolo Bonzini int can_write;
8549ab747fSPaolo Bonzini
8649ab747fSPaolo Bonzini int v2x6;
8749ab747fSPaolo Bonzini
8849ab747fSPaolo Bonzini uint8_t csp_param;
8949ab747fSPaolo Bonzini uint8_t csp_value;
9049ab747fSPaolo Bonzini uint8_t csp_mode;
9149ab747fSPaolo Bonzini uint8_t csp_regs[256];
9249ab747fSPaolo Bonzini uint8_t csp_index;
9349ab747fSPaolo Bonzini uint8_t csp_reg83[4];
9449ab747fSPaolo Bonzini int csp_reg83r;
9549ab747fSPaolo Bonzini int csp_reg83w;
9649ab747fSPaolo Bonzini
9749ab747fSPaolo Bonzini uint8_t in2_data[10];
9849ab747fSPaolo Bonzini uint8_t out_data[50];
9949ab747fSPaolo Bonzini uint8_t test_reg;
10049ab747fSPaolo Bonzini uint8_t last_read_byte;
10149ab747fSPaolo Bonzini int nzero;
10249ab747fSPaolo Bonzini
10349ab747fSPaolo Bonzini int left_till_irq;
10449ab747fSPaolo Bonzini
10549ab747fSPaolo Bonzini int dma_running;
10649ab747fSPaolo Bonzini int bytes_per_second;
10749ab747fSPaolo Bonzini int align;
10849ab747fSPaolo Bonzini int audio_free;
10949ab747fSPaolo Bonzini SWVoiceOut *voice;
11049ab747fSPaolo Bonzini
11149ab747fSPaolo Bonzini QEMUTimer *aux_ts;
11249ab747fSPaolo Bonzini /* mixer state */
11349ab747fSPaolo Bonzini int mixer_nreg;
11449ab747fSPaolo Bonzini uint8_t mixer_regs[256];
115e305a165SMarc-André Lureau PortioList portio_list;
116db1015e9SEduardo Habkost };
11749ab747fSPaolo Bonzini
118a2cd86a9SPhilippe Mathieu-Daudé #define SAMPLE_RATE_MIN 5000
119a2cd86a9SPhilippe Mathieu-Daudé #define SAMPLE_RATE_MAX 45000
120a2cd86a9SPhilippe Mathieu-Daudé
12149ab747fSPaolo Bonzini static void SB_audio_callback (void *opaque, int free);
12249ab747fSPaolo Bonzini
magic_of_irq(int irq)12349ab747fSPaolo Bonzini static int magic_of_irq (int irq)
12449ab747fSPaolo Bonzini {
12549ab747fSPaolo Bonzini switch (irq) {
12649ab747fSPaolo Bonzini case 5:
12749ab747fSPaolo Bonzini return 2;
12849ab747fSPaolo Bonzini case 7:
12949ab747fSPaolo Bonzini return 4;
13049ab747fSPaolo Bonzini case 9:
13149ab747fSPaolo Bonzini return 1;
13249ab747fSPaolo Bonzini case 10:
13349ab747fSPaolo Bonzini return 8;
13449ab747fSPaolo Bonzini default:
1358ec660b8SJohn Arbuckle qemu_log_mask(LOG_GUEST_ERROR, "bad irq %d\n", irq);
13649ab747fSPaolo Bonzini return 2;
13749ab747fSPaolo Bonzini }
13849ab747fSPaolo Bonzini }
13949ab747fSPaolo Bonzini
irq_of_magic(int magic)14049ab747fSPaolo Bonzini static int irq_of_magic (int magic)
14149ab747fSPaolo Bonzini {
14249ab747fSPaolo Bonzini switch (magic) {
14349ab747fSPaolo Bonzini case 1:
14449ab747fSPaolo Bonzini return 9;
14549ab747fSPaolo Bonzini case 2:
14649ab747fSPaolo Bonzini return 5;
14749ab747fSPaolo Bonzini case 4:
14849ab747fSPaolo Bonzini return 7;
14949ab747fSPaolo Bonzini case 8:
15049ab747fSPaolo Bonzini return 10;
15149ab747fSPaolo Bonzini default:
1528ec660b8SJohn Arbuckle qemu_log_mask(LOG_GUEST_ERROR, "bad irq magic %d\n", magic);
15349ab747fSPaolo Bonzini return -1;
15449ab747fSPaolo Bonzini }
15549ab747fSPaolo Bonzini }
15649ab747fSPaolo Bonzini
15749ab747fSPaolo Bonzini #if 0
15849ab747fSPaolo Bonzini static void log_dsp (SB16State *dsp)
15949ab747fSPaolo Bonzini {
16049ab747fSPaolo Bonzini ldebug ("%s:%s:%d:%s:dmasize=%d:freq=%d:const=%d:speaker=%d\n",
16149ab747fSPaolo Bonzini dsp->fmt_stereo ? "Stereo" : "Mono",
16249ab747fSPaolo Bonzini dsp->fmt_signed ? "Signed" : "Unsigned",
16349ab747fSPaolo Bonzini dsp->fmt_bits,
16449ab747fSPaolo Bonzini dsp->dma_auto ? "Auto" : "Single",
16549ab747fSPaolo Bonzini dsp->block_size,
16649ab747fSPaolo Bonzini dsp->freq,
16749ab747fSPaolo Bonzini dsp->time_const,
16849ab747fSPaolo Bonzini dsp->speaker);
16949ab747fSPaolo Bonzini }
17049ab747fSPaolo Bonzini #endif
17149ab747fSPaolo Bonzini
speaker(SB16State * s,int on)17249ab747fSPaolo Bonzini static void speaker (SB16State *s, int on)
17349ab747fSPaolo Bonzini {
17449ab747fSPaolo Bonzini s->speaker = on;
17549ab747fSPaolo Bonzini /* AUD_enable (s->voice, on); */
17649ab747fSPaolo Bonzini }
17749ab747fSPaolo Bonzini
control(SB16State * s,int hold)17849ab747fSPaolo Bonzini static void control (SB16State *s, int hold)
17949ab747fSPaolo Bonzini {
18049ab747fSPaolo Bonzini int dma = s->use_hdma ? s->hdma : s->dma;
181f203c16eSHervé Poussineau IsaDma *isa_dma = s->use_hdma ? s->isa_hdma : s->isa_dma;
182f203c16eSHervé Poussineau IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma);
18349ab747fSPaolo Bonzini s->dma_running = hold;
18449ab747fSPaolo Bonzini
18549ab747fSPaolo Bonzini ldebug ("hold %d high %d dma %d\n", hold, s->use_hdma, dma);
18649ab747fSPaolo Bonzini
18749ab747fSPaolo Bonzini if (hold) {
188f203c16eSHervé Poussineau k->hold_DREQ(isa_dma, dma);
18949ab747fSPaolo Bonzini AUD_set_active_out (s->voice, 1);
19049ab747fSPaolo Bonzini }
19149ab747fSPaolo Bonzini else {
192f203c16eSHervé Poussineau k->release_DREQ(isa_dma, dma);
19349ab747fSPaolo Bonzini AUD_set_active_out (s->voice, 0);
19449ab747fSPaolo Bonzini }
19549ab747fSPaolo Bonzini }
19649ab747fSPaolo Bonzini
aux_timer(void * opaque)19749ab747fSPaolo Bonzini static void aux_timer (void *opaque)
19849ab747fSPaolo Bonzini {
19949ab747fSPaolo Bonzini SB16State *s = opaque;
20049ab747fSPaolo Bonzini s->can_write = 1;
20149ab747fSPaolo Bonzini qemu_irq_raise (s->pic);
20249ab747fSPaolo Bonzini }
20349ab747fSPaolo Bonzini
20449ab747fSPaolo Bonzini #define DMA8_AUTO 1
20549ab747fSPaolo Bonzini #define DMA8_HIGH 2
20649ab747fSPaolo Bonzini
continue_dma8(SB16State * s)20749ab747fSPaolo Bonzini static void continue_dma8 (SB16State *s)
20849ab747fSPaolo Bonzini {
20949ab747fSPaolo Bonzini if (s->freq > 0) {
21049ab747fSPaolo Bonzini struct audsettings as;
21149ab747fSPaolo Bonzini
21249ab747fSPaolo Bonzini s->audio_free = 0;
21349ab747fSPaolo Bonzini
21449ab747fSPaolo Bonzini as.freq = s->freq;
21549ab747fSPaolo Bonzini as.nchannels = 1 << s->fmt_stereo;
21649ab747fSPaolo Bonzini as.fmt = s->fmt;
21749ab747fSPaolo Bonzini as.endianness = 0;
21849ab747fSPaolo Bonzini
21949ab747fSPaolo Bonzini s->voice = AUD_open_out (
22049ab747fSPaolo Bonzini &s->card,
22149ab747fSPaolo Bonzini s->voice,
22249ab747fSPaolo Bonzini "sb16",
22349ab747fSPaolo Bonzini s,
22449ab747fSPaolo Bonzini SB_audio_callback,
22549ab747fSPaolo Bonzini &as
22649ab747fSPaolo Bonzini );
22749ab747fSPaolo Bonzini }
22849ab747fSPaolo Bonzini
22949ab747fSPaolo Bonzini control (s, 1);
23049ab747fSPaolo Bonzini }
23149ab747fSPaolo Bonzini
restrict_sampling_rate(int freq)23260e543f5SQiang Liu static inline int restrict_sampling_rate(int freq)
23360e543f5SQiang Liu {
23460e543f5SQiang Liu if (freq < SAMPLE_RATE_MIN) {
23560e543f5SQiang Liu qemu_log_mask(LOG_GUEST_ERROR,
23660e543f5SQiang Liu "sampling range too low: %d, increasing to %u\n",
23760e543f5SQiang Liu freq, SAMPLE_RATE_MIN);
23860e543f5SQiang Liu return SAMPLE_RATE_MIN;
23960e543f5SQiang Liu } else if (freq > SAMPLE_RATE_MAX) {
24060e543f5SQiang Liu qemu_log_mask(LOG_GUEST_ERROR,
24160e543f5SQiang Liu "sampling range too high: %d, decreasing to %u\n",
24260e543f5SQiang Liu freq, SAMPLE_RATE_MAX);
24360e543f5SQiang Liu return SAMPLE_RATE_MAX;
24460e543f5SQiang Liu } else {
24560e543f5SQiang Liu return freq;
24660e543f5SQiang Liu }
24760e543f5SQiang Liu }
24860e543f5SQiang Liu
dma_cmd8(SB16State * s,int mask,int dma_len)24949ab747fSPaolo Bonzini static void dma_cmd8 (SB16State *s, int mask, int dma_len)
25049ab747fSPaolo Bonzini {
25185bc5852SKővágó, Zoltán s->fmt = AUDIO_FORMAT_U8;
25249ab747fSPaolo Bonzini s->use_hdma = 0;
25349ab747fSPaolo Bonzini s->fmt_bits = 8;
25449ab747fSPaolo Bonzini s->fmt_signed = 0;
25549ab747fSPaolo Bonzini s->fmt_stereo = (s->mixer_regs[0x0e] & 2) != 0;
25649ab747fSPaolo Bonzini if (-1 == s->time_const) {
25749ab747fSPaolo Bonzini if (s->freq <= 0)
25849ab747fSPaolo Bonzini s->freq = 11025;
25949ab747fSPaolo Bonzini }
26049ab747fSPaolo Bonzini else {
26149ab747fSPaolo Bonzini int tmp = (256 - s->time_const);
26249ab747fSPaolo Bonzini s->freq = (1000000 + (tmp / 2)) / tmp;
26349ab747fSPaolo Bonzini }
26460e543f5SQiang Liu s->freq = restrict_sampling_rate(s->freq);
26549ab747fSPaolo Bonzini
26649ab747fSPaolo Bonzini if (dma_len != -1) {
26749ab747fSPaolo Bonzini s->block_size = dma_len << s->fmt_stereo;
26849ab747fSPaolo Bonzini }
26949ab747fSPaolo Bonzini else {
27049ab747fSPaolo Bonzini /* This is apparently the only way to make both Act1/PL
27149ab747fSPaolo Bonzini and SecondReality/FC work
27249ab747fSPaolo Bonzini
27349ab747fSPaolo Bonzini Act1 sets block size via command 0x48 and it's an odd number
27449ab747fSPaolo Bonzini SR does the same with even number
27549ab747fSPaolo Bonzini Both use stereo, and Creatives own documentation states that
27649ab747fSPaolo Bonzini 0x48 sets block size in bytes less one.. go figure */
27749ab747fSPaolo Bonzini s->block_size &= ~s->fmt_stereo;
27849ab747fSPaolo Bonzini }
27949ab747fSPaolo Bonzini
28049ab747fSPaolo Bonzini s->freq >>= s->fmt_stereo;
28149ab747fSPaolo Bonzini s->left_till_irq = s->block_size;
28249ab747fSPaolo Bonzini s->bytes_per_second = (s->freq << s->fmt_stereo);
28349ab747fSPaolo Bonzini /* s->highspeed = (mask & DMA8_HIGH) != 0; */
28449ab747fSPaolo Bonzini s->dma_auto = (mask & DMA8_AUTO) != 0;
28549ab747fSPaolo Bonzini s->align = (1 << s->fmt_stereo) - 1;
28649ab747fSPaolo Bonzini
28749ab747fSPaolo Bonzini if (s->block_size & s->align) {
2888ec660b8SJohn Arbuckle qemu_log_mask(LOG_GUEST_ERROR, "warning: misaligned block size %d,"
2898ec660b8SJohn Arbuckle " alignment %d\n", s->block_size, s->align + 1);
29049ab747fSPaolo Bonzini }
29149ab747fSPaolo Bonzini
29249ab747fSPaolo Bonzini ldebug ("freq %d, stereo %d, sign %d, bits %d, "
29349ab747fSPaolo Bonzini "dma %d, auto %d, fifo %d, high %d\n",
29449ab747fSPaolo Bonzini s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
29549ab747fSPaolo Bonzini s->block_size, s->dma_auto, s->fifo, s->highspeed);
29649ab747fSPaolo Bonzini
29749ab747fSPaolo Bonzini continue_dma8 (s);
29849ab747fSPaolo Bonzini speaker (s, 1);
29949ab747fSPaolo Bonzini }
30049ab747fSPaolo Bonzini
dma_cmd(SB16State * s,uint8_t cmd,uint8_t d0,int dma_len)30149ab747fSPaolo Bonzini static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len)
30249ab747fSPaolo Bonzini {
30349ab747fSPaolo Bonzini s->use_hdma = cmd < 0xc0;
30449ab747fSPaolo Bonzini s->fifo = (cmd >> 1) & 1;
30549ab747fSPaolo Bonzini s->dma_auto = (cmd >> 2) & 1;
30649ab747fSPaolo Bonzini s->fmt_signed = (d0 >> 4) & 1;
30749ab747fSPaolo Bonzini s->fmt_stereo = (d0 >> 5) & 1;
30849ab747fSPaolo Bonzini
30949ab747fSPaolo Bonzini switch (cmd >> 4) {
31049ab747fSPaolo Bonzini case 11:
31149ab747fSPaolo Bonzini s->fmt_bits = 16;
31249ab747fSPaolo Bonzini break;
31349ab747fSPaolo Bonzini
31449ab747fSPaolo Bonzini case 12:
31549ab747fSPaolo Bonzini s->fmt_bits = 8;
31649ab747fSPaolo Bonzini break;
31749ab747fSPaolo Bonzini }
31849ab747fSPaolo Bonzini
31949ab747fSPaolo Bonzini if (-1 != s->time_const) {
32049ab747fSPaolo Bonzini #if 1
32149ab747fSPaolo Bonzini int tmp = 256 - s->time_const;
32249ab747fSPaolo Bonzini s->freq = (1000000 + (tmp / 2)) / tmp;
32349ab747fSPaolo Bonzini #else
32449ab747fSPaolo Bonzini /* s->freq = 1000000 / ((255 - s->time_const) << s->fmt_stereo); */
32549ab747fSPaolo Bonzini s->freq = 1000000 / ((255 - s->time_const));
32649ab747fSPaolo Bonzini #endif
32749ab747fSPaolo Bonzini s->time_const = -1;
32849ab747fSPaolo Bonzini }
32949ab747fSPaolo Bonzini
33049ab747fSPaolo Bonzini s->block_size = dma_len + 1;
33149ab747fSPaolo Bonzini s->block_size <<= (s->fmt_bits == 16);
33249ab747fSPaolo Bonzini if (!s->dma_auto) {
33349ab747fSPaolo Bonzini /* It is clear that for DOOM and auto-init this value
33449ab747fSPaolo Bonzini shouldn't take stereo into account, while Miles Sound Systems
33549ab747fSPaolo Bonzini setsound.exe with single transfer mode wouldn't work without it
33649ab747fSPaolo Bonzini wonders of SB16 yet again */
33749ab747fSPaolo Bonzini s->block_size <<= s->fmt_stereo;
33849ab747fSPaolo Bonzini }
33949ab747fSPaolo Bonzini
34049ab747fSPaolo Bonzini ldebug ("freq %d, stereo %d, sign %d, bits %d, "
34149ab747fSPaolo Bonzini "dma %d, auto %d, fifo %d, high %d\n",
34249ab747fSPaolo Bonzini s->freq, s->fmt_stereo, s->fmt_signed, s->fmt_bits,
34349ab747fSPaolo Bonzini s->block_size, s->dma_auto, s->fifo, s->highspeed);
34449ab747fSPaolo Bonzini
34549ab747fSPaolo Bonzini if (16 == s->fmt_bits) {
34649ab747fSPaolo Bonzini if (s->fmt_signed) {
34785bc5852SKővágó, Zoltán s->fmt = AUDIO_FORMAT_S16;
34849ab747fSPaolo Bonzini }
34949ab747fSPaolo Bonzini else {
35085bc5852SKővágó, Zoltán s->fmt = AUDIO_FORMAT_U16;
35149ab747fSPaolo Bonzini }
35249ab747fSPaolo Bonzini }
35349ab747fSPaolo Bonzini else {
35449ab747fSPaolo Bonzini if (s->fmt_signed) {
35585bc5852SKővágó, Zoltán s->fmt = AUDIO_FORMAT_S8;
35649ab747fSPaolo Bonzini }
35749ab747fSPaolo Bonzini else {
35885bc5852SKővágó, Zoltán s->fmt = AUDIO_FORMAT_U8;
35949ab747fSPaolo Bonzini }
36049ab747fSPaolo Bonzini }
36149ab747fSPaolo Bonzini
36249ab747fSPaolo Bonzini s->left_till_irq = s->block_size;
36349ab747fSPaolo Bonzini
36449ab747fSPaolo Bonzini s->bytes_per_second = (s->freq << s->fmt_stereo) << (s->fmt_bits == 16);
36549ab747fSPaolo Bonzini s->highspeed = 0;
36649ab747fSPaolo Bonzini s->align = (1 << (s->fmt_stereo + (s->fmt_bits == 16))) - 1;
36749ab747fSPaolo Bonzini if (s->block_size & s->align) {
3688ec660b8SJohn Arbuckle qemu_log_mask(LOG_GUEST_ERROR, "warning: misaligned block size %d,"
3698ec660b8SJohn Arbuckle " alignment %d\n", s->block_size, s->align + 1);
37049ab747fSPaolo Bonzini }
37149ab747fSPaolo Bonzini
37249ab747fSPaolo Bonzini if (s->freq) {
37349ab747fSPaolo Bonzini struct audsettings as;
37449ab747fSPaolo Bonzini
37549ab747fSPaolo Bonzini s->audio_free = 0;
37649ab747fSPaolo Bonzini
37749ab747fSPaolo Bonzini as.freq = s->freq;
37849ab747fSPaolo Bonzini as.nchannels = 1 << s->fmt_stereo;
37949ab747fSPaolo Bonzini as.fmt = s->fmt;
38049ab747fSPaolo Bonzini as.endianness = 0;
38149ab747fSPaolo Bonzini
38249ab747fSPaolo Bonzini s->voice = AUD_open_out (
38349ab747fSPaolo Bonzini &s->card,
38449ab747fSPaolo Bonzini s->voice,
38549ab747fSPaolo Bonzini "sb16",
38649ab747fSPaolo Bonzini s,
38749ab747fSPaolo Bonzini SB_audio_callback,
38849ab747fSPaolo Bonzini &as
38949ab747fSPaolo Bonzini );
39049ab747fSPaolo Bonzini }
39149ab747fSPaolo Bonzini
39249ab747fSPaolo Bonzini control (s, 1);
39349ab747fSPaolo Bonzini speaker (s, 1);
39449ab747fSPaolo Bonzini }
39549ab747fSPaolo Bonzini
dsp_out_data(SB16State * s,uint8_t val)39649ab747fSPaolo Bonzini static inline void dsp_out_data (SB16State *s, uint8_t val)
39749ab747fSPaolo Bonzini {
39849ab747fSPaolo Bonzini ldebug ("outdata %#x\n", val);
39949ab747fSPaolo Bonzini if ((size_t) s->out_data_len < sizeof (s->out_data)) {
40049ab747fSPaolo Bonzini s->out_data[s->out_data_len++] = val;
40149ab747fSPaolo Bonzini }
40249ab747fSPaolo Bonzini }
40349ab747fSPaolo Bonzini
dsp_get_data(SB16State * s)40449ab747fSPaolo Bonzini static inline uint8_t dsp_get_data (SB16State *s)
40549ab747fSPaolo Bonzini {
40649ab747fSPaolo Bonzini if (s->in_index) {
40749ab747fSPaolo Bonzini return s->in2_data[--s->in_index];
40849ab747fSPaolo Bonzini }
40949ab747fSPaolo Bonzini else {
41049ab747fSPaolo Bonzini dolog ("buffer underflow\n");
41149ab747fSPaolo Bonzini return 0;
41249ab747fSPaolo Bonzini }
41349ab747fSPaolo Bonzini }
41449ab747fSPaolo Bonzini
command(SB16State * s,uint8_t cmd)41549ab747fSPaolo Bonzini static void command (SB16State *s, uint8_t cmd)
41649ab747fSPaolo Bonzini {
41749ab747fSPaolo Bonzini ldebug ("command %#x\n", cmd);
41849ab747fSPaolo Bonzini
41949ab747fSPaolo Bonzini if (cmd > 0xaf && cmd < 0xd0) {
42049ab747fSPaolo Bonzini if (cmd & 8) {
4218ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "ADC not yet supported (command %#x)\n",
4228ec660b8SJohn Arbuckle cmd);
42349ab747fSPaolo Bonzini }
42449ab747fSPaolo Bonzini
42549ab747fSPaolo Bonzini switch (cmd >> 4) {
42649ab747fSPaolo Bonzini case 11:
42749ab747fSPaolo Bonzini case 12:
42849ab747fSPaolo Bonzini break;
42949ab747fSPaolo Bonzini default:
4308ec660b8SJohn Arbuckle qemu_log_mask(LOG_GUEST_ERROR, "%#x wrong bits\n", cmd);
43149ab747fSPaolo Bonzini }
43249ab747fSPaolo Bonzini s->needed_bytes = 3;
43349ab747fSPaolo Bonzini }
43449ab747fSPaolo Bonzini else {
43549ab747fSPaolo Bonzini s->needed_bytes = 0;
43649ab747fSPaolo Bonzini
43749ab747fSPaolo Bonzini switch (cmd) {
43849ab747fSPaolo Bonzini case 0x03:
43949ab747fSPaolo Bonzini dsp_out_data (s, 0x10); /* s->csp_param); */
44049ab747fSPaolo Bonzini goto warn;
44149ab747fSPaolo Bonzini
44249ab747fSPaolo Bonzini case 0x04:
44349ab747fSPaolo Bonzini s->needed_bytes = 1;
44449ab747fSPaolo Bonzini goto warn;
44549ab747fSPaolo Bonzini
44649ab747fSPaolo Bonzini case 0x05:
44749ab747fSPaolo Bonzini s->needed_bytes = 2;
44849ab747fSPaolo Bonzini goto warn;
44949ab747fSPaolo Bonzini
45049ab747fSPaolo Bonzini case 0x08:
45149ab747fSPaolo Bonzini /* __asm__ ("int3"); */
45249ab747fSPaolo Bonzini goto warn;
45349ab747fSPaolo Bonzini
45449ab747fSPaolo Bonzini case 0x0e:
45549ab747fSPaolo Bonzini s->needed_bytes = 2;
45649ab747fSPaolo Bonzini goto warn;
45749ab747fSPaolo Bonzini
45849ab747fSPaolo Bonzini case 0x09:
45949ab747fSPaolo Bonzini dsp_out_data (s, 0xf8);
46049ab747fSPaolo Bonzini goto warn;
46149ab747fSPaolo Bonzini
46249ab747fSPaolo Bonzini case 0x0f:
46349ab747fSPaolo Bonzini s->needed_bytes = 1;
46449ab747fSPaolo Bonzini goto warn;
46549ab747fSPaolo Bonzini
46649ab747fSPaolo Bonzini case 0x10:
46749ab747fSPaolo Bonzini s->needed_bytes = 1;
46849ab747fSPaolo Bonzini goto warn;
46949ab747fSPaolo Bonzini
47049ab747fSPaolo Bonzini case 0x14:
47149ab747fSPaolo Bonzini s->needed_bytes = 2;
47249ab747fSPaolo Bonzini s->block_size = 0;
47349ab747fSPaolo Bonzini break;
47449ab747fSPaolo Bonzini
47549ab747fSPaolo Bonzini case 0x1c: /* Auto-Initialize DMA DAC, 8-bit */
47649ab747fSPaolo Bonzini dma_cmd8 (s, DMA8_AUTO, -1);
47749ab747fSPaolo Bonzini break;
47849ab747fSPaolo Bonzini
47949ab747fSPaolo Bonzini case 0x20: /* Direct ADC, Juice/PL */
48049ab747fSPaolo Bonzini dsp_out_data (s, 0xff);
48149ab747fSPaolo Bonzini goto warn;
48249ab747fSPaolo Bonzini
48349ab747fSPaolo Bonzini case 0x35:
4848ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "0x35 - MIDI command not implemented\n");
48549ab747fSPaolo Bonzini break;
48649ab747fSPaolo Bonzini
48749ab747fSPaolo Bonzini case 0x40:
48849ab747fSPaolo Bonzini s->freq = -1;
48949ab747fSPaolo Bonzini s->time_const = -1;
49049ab747fSPaolo Bonzini s->needed_bytes = 1;
49149ab747fSPaolo Bonzini break;
49249ab747fSPaolo Bonzini
49349ab747fSPaolo Bonzini case 0x41:
49449ab747fSPaolo Bonzini s->freq = -1;
49549ab747fSPaolo Bonzini s->time_const = -1;
49649ab747fSPaolo Bonzini s->needed_bytes = 2;
49749ab747fSPaolo Bonzini break;
49849ab747fSPaolo Bonzini
49949ab747fSPaolo Bonzini case 0x42:
50049ab747fSPaolo Bonzini s->freq = -1;
50149ab747fSPaolo Bonzini s->time_const = -1;
50249ab747fSPaolo Bonzini s->needed_bytes = 2;
50349ab747fSPaolo Bonzini goto warn;
50449ab747fSPaolo Bonzini
50549ab747fSPaolo Bonzini case 0x45:
50649ab747fSPaolo Bonzini dsp_out_data (s, 0xaa);
50749ab747fSPaolo Bonzini goto warn;
50849ab747fSPaolo Bonzini
50949ab747fSPaolo Bonzini case 0x47: /* Continue Auto-Initialize DMA 16bit */
51049ab747fSPaolo Bonzini break;
51149ab747fSPaolo Bonzini
51249ab747fSPaolo Bonzini case 0x48:
51349ab747fSPaolo Bonzini s->needed_bytes = 2;
51449ab747fSPaolo Bonzini break;
51549ab747fSPaolo Bonzini
51649ab747fSPaolo Bonzini case 0x74:
51749ab747fSPaolo Bonzini s->needed_bytes = 2; /* DMA DAC, 4-bit ADPCM */
5188ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "0x75 - DMA DAC, 4-bit ADPCM not"
5198ec660b8SJohn Arbuckle " implemented\n");
52049ab747fSPaolo Bonzini break;
52149ab747fSPaolo Bonzini
52249ab747fSPaolo Bonzini case 0x75: /* DMA DAC, 4-bit ADPCM Reference */
52349ab747fSPaolo Bonzini s->needed_bytes = 2;
5248ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "0x74 - DMA DAC, 4-bit ADPCM Reference not"
5258ec660b8SJohn Arbuckle " implemented\n");
52649ab747fSPaolo Bonzini break;
52749ab747fSPaolo Bonzini
52849ab747fSPaolo Bonzini case 0x76: /* DMA DAC, 2.6-bit ADPCM */
52949ab747fSPaolo Bonzini s->needed_bytes = 2;
5308ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "0x74 - DMA DAC, 2.6-bit ADPCM not"
5318ec660b8SJohn Arbuckle " implemented\n");
53249ab747fSPaolo Bonzini break;
53349ab747fSPaolo Bonzini
53449ab747fSPaolo Bonzini case 0x77: /* DMA DAC, 2.6-bit ADPCM Reference */
53549ab747fSPaolo Bonzini s->needed_bytes = 2;
5368ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "0x74 - DMA DAC, 2.6-bit ADPCM Reference"
5378ec660b8SJohn Arbuckle " not implemented\n");
53849ab747fSPaolo Bonzini break;
53949ab747fSPaolo Bonzini
54049ab747fSPaolo Bonzini case 0x7d:
5418ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "0x7d - Autio-Initialize DMA DAC, 4-bit"
5428ec660b8SJohn Arbuckle " ADPCM Reference\n");
5438ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "not implemented\n");
54449ab747fSPaolo Bonzini break;
54549ab747fSPaolo Bonzini
54649ab747fSPaolo Bonzini case 0x7f:
5478ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "0x7d - Autio-Initialize DMA DAC, 2.6-bit"
5488ec660b8SJohn Arbuckle " ADPCM Reference\n");
5498ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "not implemented\n");
55049ab747fSPaolo Bonzini break;
55149ab747fSPaolo Bonzini
55249ab747fSPaolo Bonzini case 0x80:
55349ab747fSPaolo Bonzini s->needed_bytes = 2;
55449ab747fSPaolo Bonzini break;
55549ab747fSPaolo Bonzini
55649ab747fSPaolo Bonzini case 0x90:
55749ab747fSPaolo Bonzini case 0x91:
55849ab747fSPaolo Bonzini dma_cmd8 (s, ((cmd & 1) == 0) | DMA8_HIGH, -1);
55949ab747fSPaolo Bonzini break;
56049ab747fSPaolo Bonzini
56149ab747fSPaolo Bonzini case 0xd0: /* halt DMA operation. 8bit */
56249ab747fSPaolo Bonzini control (s, 0);
56349ab747fSPaolo Bonzini break;
56449ab747fSPaolo Bonzini
56549ab747fSPaolo Bonzini case 0xd1: /* speaker on */
56649ab747fSPaolo Bonzini speaker (s, 1);
56749ab747fSPaolo Bonzini break;
56849ab747fSPaolo Bonzini
56949ab747fSPaolo Bonzini case 0xd3: /* speaker off */
57049ab747fSPaolo Bonzini speaker (s, 0);
57149ab747fSPaolo Bonzini break;
57249ab747fSPaolo Bonzini
57349ab747fSPaolo Bonzini case 0xd4: /* continue DMA operation. 8bit */
57449ab747fSPaolo Bonzini /* KQ6 (or maybe Sierras audblst.drv in general) resets
57549ab747fSPaolo Bonzini the frequency between halt/continue */
57649ab747fSPaolo Bonzini continue_dma8 (s);
57749ab747fSPaolo Bonzini break;
57849ab747fSPaolo Bonzini
57949ab747fSPaolo Bonzini case 0xd5: /* halt DMA operation. 16bit */
58049ab747fSPaolo Bonzini control (s, 0);
58149ab747fSPaolo Bonzini break;
58249ab747fSPaolo Bonzini
58349ab747fSPaolo Bonzini case 0xd6: /* continue DMA operation. 16bit */
58449ab747fSPaolo Bonzini control (s, 1);
58549ab747fSPaolo Bonzini break;
58649ab747fSPaolo Bonzini
58749ab747fSPaolo Bonzini case 0xd9: /* exit auto-init DMA after this block. 16bit */
58849ab747fSPaolo Bonzini s->dma_auto = 0;
58949ab747fSPaolo Bonzini break;
59049ab747fSPaolo Bonzini
59149ab747fSPaolo Bonzini case 0xda: /* exit auto-init DMA after this block. 8bit */
59249ab747fSPaolo Bonzini s->dma_auto = 0;
59349ab747fSPaolo Bonzini break;
59449ab747fSPaolo Bonzini
59549ab747fSPaolo Bonzini case 0xe0: /* DSP identification */
59649ab747fSPaolo Bonzini s->needed_bytes = 1;
59749ab747fSPaolo Bonzini break;
59849ab747fSPaolo Bonzini
59949ab747fSPaolo Bonzini case 0xe1:
60049ab747fSPaolo Bonzini dsp_out_data (s, s->ver & 0xff);
60149ab747fSPaolo Bonzini dsp_out_data (s, s->ver >> 8);
60249ab747fSPaolo Bonzini break;
60349ab747fSPaolo Bonzini
60449ab747fSPaolo Bonzini case 0xe2:
60549ab747fSPaolo Bonzini s->needed_bytes = 1;
60649ab747fSPaolo Bonzini goto warn;
60749ab747fSPaolo Bonzini
60849ab747fSPaolo Bonzini case 0xe3:
60949ab747fSPaolo Bonzini {
61049ab747fSPaolo Bonzini int i;
61149ab747fSPaolo Bonzini for (i = sizeof (e3) - 1; i >= 0; --i)
61249ab747fSPaolo Bonzini dsp_out_data (s, e3[i]);
61349ab747fSPaolo Bonzini }
61449ab747fSPaolo Bonzini break;
61549ab747fSPaolo Bonzini
61649ab747fSPaolo Bonzini case 0xe4: /* write test reg */
61749ab747fSPaolo Bonzini s->needed_bytes = 1;
61849ab747fSPaolo Bonzini break;
61949ab747fSPaolo Bonzini
62049ab747fSPaolo Bonzini case 0xe7:
6218ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "Attempt to probe for ESS (0xe7)?\n");
62249ab747fSPaolo Bonzini break;
62349ab747fSPaolo Bonzini
62449ab747fSPaolo Bonzini case 0xe8: /* read test reg */
62549ab747fSPaolo Bonzini dsp_out_data (s, s->test_reg);
62649ab747fSPaolo Bonzini break;
62749ab747fSPaolo Bonzini
62849ab747fSPaolo Bonzini case 0xf2:
62949ab747fSPaolo Bonzini case 0xf3:
63049ab747fSPaolo Bonzini dsp_out_data (s, 0xaa);
63149ab747fSPaolo Bonzini s->mixer_regs[0x82] |= (cmd == 0xf2) ? 1 : 2;
63249ab747fSPaolo Bonzini qemu_irq_raise (s->pic);
63349ab747fSPaolo Bonzini break;
63449ab747fSPaolo Bonzini
63549ab747fSPaolo Bonzini case 0xf9:
63649ab747fSPaolo Bonzini s->needed_bytes = 1;
63749ab747fSPaolo Bonzini goto warn;
63849ab747fSPaolo Bonzini
63949ab747fSPaolo Bonzini case 0xfa:
64049ab747fSPaolo Bonzini dsp_out_data (s, 0);
64149ab747fSPaolo Bonzini goto warn;
64249ab747fSPaolo Bonzini
64349ab747fSPaolo Bonzini case 0xfc: /* FIXME */
64449ab747fSPaolo Bonzini dsp_out_data (s, 0);
64549ab747fSPaolo Bonzini goto warn;
64649ab747fSPaolo Bonzini
64749ab747fSPaolo Bonzini default:
6488ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "Unrecognized command %#x\n", cmd);
64949ab747fSPaolo Bonzini break;
65049ab747fSPaolo Bonzini }
65149ab747fSPaolo Bonzini }
65249ab747fSPaolo Bonzini
65349ab747fSPaolo Bonzini if (!s->needed_bytes) {
65449ab747fSPaolo Bonzini ldebug ("\n");
65549ab747fSPaolo Bonzini }
65649ab747fSPaolo Bonzini
65749ab747fSPaolo Bonzini exit:
65849ab747fSPaolo Bonzini if (!s->needed_bytes) {
65949ab747fSPaolo Bonzini s->cmd = -1;
66049ab747fSPaolo Bonzini }
66149ab747fSPaolo Bonzini else {
66249ab747fSPaolo Bonzini s->cmd = cmd;
66349ab747fSPaolo Bonzini }
66449ab747fSPaolo Bonzini return;
66549ab747fSPaolo Bonzini
66649ab747fSPaolo Bonzini warn:
6678ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "warning: command %#x,%d is not truly understood"
6688ec660b8SJohn Arbuckle " yet\n", cmd, s->needed_bytes);
66949ab747fSPaolo Bonzini goto exit;
67049ab747fSPaolo Bonzini
67149ab747fSPaolo Bonzini }
67249ab747fSPaolo Bonzini
dsp_get_lohi(SB16State * s)67349ab747fSPaolo Bonzini static uint16_t dsp_get_lohi (SB16State *s)
67449ab747fSPaolo Bonzini {
67549ab747fSPaolo Bonzini uint8_t hi = dsp_get_data (s);
67649ab747fSPaolo Bonzini uint8_t lo = dsp_get_data (s);
67749ab747fSPaolo Bonzini return (hi << 8) | lo;
67849ab747fSPaolo Bonzini }
67949ab747fSPaolo Bonzini
dsp_get_hilo(SB16State * s)68049ab747fSPaolo Bonzini static uint16_t dsp_get_hilo (SB16State *s)
68149ab747fSPaolo Bonzini {
68249ab747fSPaolo Bonzini uint8_t lo = dsp_get_data (s);
68349ab747fSPaolo Bonzini uint8_t hi = dsp_get_data (s);
68449ab747fSPaolo Bonzini return (hi << 8) | lo;
68549ab747fSPaolo Bonzini }
68649ab747fSPaolo Bonzini
complete(SB16State * s)68749ab747fSPaolo Bonzini static void complete (SB16State *s)
68849ab747fSPaolo Bonzini {
68949ab747fSPaolo Bonzini int d0, d1, d2;
69049ab747fSPaolo Bonzini ldebug ("complete command %#x, in_index %d, needed_bytes %d\n",
69149ab747fSPaolo Bonzini s->cmd, s->in_index, s->needed_bytes);
69249ab747fSPaolo Bonzini
69349ab747fSPaolo Bonzini if (s->cmd > 0xaf && s->cmd < 0xd0) {
69449ab747fSPaolo Bonzini d2 = dsp_get_data (s);
69549ab747fSPaolo Bonzini d1 = dsp_get_data (s);
69649ab747fSPaolo Bonzini d0 = dsp_get_data (s);
69749ab747fSPaolo Bonzini
69849ab747fSPaolo Bonzini if (s->cmd & 8) {
69949ab747fSPaolo Bonzini dolog ("ADC params cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
70049ab747fSPaolo Bonzini s->cmd, d0, d1, d2);
70149ab747fSPaolo Bonzini }
70249ab747fSPaolo Bonzini else {
70349ab747fSPaolo Bonzini ldebug ("cmd = %#x d0 = %d, d1 = %d, d2 = %d\n",
70449ab747fSPaolo Bonzini s->cmd, d0, d1, d2);
70549ab747fSPaolo Bonzini dma_cmd (s, s->cmd, d0, d1 + (d2 << 8));
70649ab747fSPaolo Bonzini }
70749ab747fSPaolo Bonzini }
70849ab747fSPaolo Bonzini else {
70949ab747fSPaolo Bonzini switch (s->cmd) {
71049ab747fSPaolo Bonzini case 0x04:
71149ab747fSPaolo Bonzini s->csp_mode = dsp_get_data (s);
71249ab747fSPaolo Bonzini s->csp_reg83r = 0;
71349ab747fSPaolo Bonzini s->csp_reg83w = 0;
71449ab747fSPaolo Bonzini ldebug ("CSP command 0x04: mode=%#x\n", s->csp_mode);
71549ab747fSPaolo Bonzini break;
71649ab747fSPaolo Bonzini
71749ab747fSPaolo Bonzini case 0x05:
71849ab747fSPaolo Bonzini s->csp_param = dsp_get_data (s);
71949ab747fSPaolo Bonzini s->csp_value = dsp_get_data (s);
72049ab747fSPaolo Bonzini ldebug ("CSP command 0x05: param=%#x value=%#x\n",
72149ab747fSPaolo Bonzini s->csp_param,
72249ab747fSPaolo Bonzini s->csp_value);
72349ab747fSPaolo Bonzini break;
72449ab747fSPaolo Bonzini
72549ab747fSPaolo Bonzini case 0x0e:
72649ab747fSPaolo Bonzini d0 = dsp_get_data (s);
72749ab747fSPaolo Bonzini d1 = dsp_get_data (s);
72849ab747fSPaolo Bonzini ldebug ("write CSP register %d <- %#x\n", d1, d0);
72949ab747fSPaolo Bonzini if (d1 == 0x83) {
73049ab747fSPaolo Bonzini ldebug ("0x83[%d] <- %#x\n", s->csp_reg83r, d0);
73149ab747fSPaolo Bonzini s->csp_reg83[s->csp_reg83r % 4] = d0;
73249ab747fSPaolo Bonzini s->csp_reg83r += 1;
73349ab747fSPaolo Bonzini }
73449ab747fSPaolo Bonzini else {
73549ab747fSPaolo Bonzini s->csp_regs[d1] = d0;
73649ab747fSPaolo Bonzini }
73749ab747fSPaolo Bonzini break;
73849ab747fSPaolo Bonzini
73949ab747fSPaolo Bonzini case 0x0f:
74049ab747fSPaolo Bonzini d0 = dsp_get_data (s);
74149ab747fSPaolo Bonzini ldebug ("read CSP register %#x -> %#x, mode=%#x\n",
74249ab747fSPaolo Bonzini d0, s->csp_regs[d0], s->csp_mode);
74349ab747fSPaolo Bonzini if (d0 == 0x83) {
74449ab747fSPaolo Bonzini ldebug ("0x83[%d] -> %#x\n",
74549ab747fSPaolo Bonzini s->csp_reg83w,
74649ab747fSPaolo Bonzini s->csp_reg83[s->csp_reg83w % 4]);
74749ab747fSPaolo Bonzini dsp_out_data (s, s->csp_reg83[s->csp_reg83w % 4]);
74849ab747fSPaolo Bonzini s->csp_reg83w += 1;
74949ab747fSPaolo Bonzini }
75049ab747fSPaolo Bonzini else {
75149ab747fSPaolo Bonzini dsp_out_data (s, s->csp_regs[d0]);
75249ab747fSPaolo Bonzini }
75349ab747fSPaolo Bonzini break;
75449ab747fSPaolo Bonzini
75549ab747fSPaolo Bonzini case 0x10:
75649ab747fSPaolo Bonzini d0 = dsp_get_data (s);
75749ab747fSPaolo Bonzini dolog ("cmd 0x10 d0=%#x\n", d0);
75849ab747fSPaolo Bonzini break;
75949ab747fSPaolo Bonzini
76049ab747fSPaolo Bonzini case 0x14:
76149ab747fSPaolo Bonzini dma_cmd8 (s, 0, dsp_get_lohi (s) + 1);
76249ab747fSPaolo Bonzini break;
76349ab747fSPaolo Bonzini
76449ab747fSPaolo Bonzini case 0x40:
76549ab747fSPaolo Bonzini s->time_const = dsp_get_data (s);
76649ab747fSPaolo Bonzini ldebug ("set time const %d\n", s->time_const);
76749ab747fSPaolo Bonzini break;
76849ab747fSPaolo Bonzini
76949ab747fSPaolo Bonzini case 0x41:
770edd7541bSPaolo Bonzini case 0x42:
771edd7541bSPaolo Bonzini /*
772edd7541bSPaolo Bonzini * 0x41 is documented as setting the output sample rate,
773edd7541bSPaolo Bonzini * and 0x42 the input sample rate, but in fact SB16 hardware
774edd7541bSPaolo Bonzini * seems to have only a single sample rate under the hood,
775edd7541bSPaolo Bonzini * and FT2 sets output freq with this (go figure). Compare:
776edd7541bSPaolo Bonzini * http://homepages.cae.wisc.edu/~brodskye/sb16doc/sb16doc.html#SamplingRate
777edd7541bSPaolo Bonzini */
77860e543f5SQiang Liu s->freq = restrict_sampling_rate(dsp_get_hilo(s));
77949ab747fSPaolo Bonzini ldebug ("set freq %d\n", s->freq);
78049ab747fSPaolo Bonzini break;
78149ab747fSPaolo Bonzini
78249ab747fSPaolo Bonzini case 0x48:
78349ab747fSPaolo Bonzini s->block_size = dsp_get_lohi (s) + 1;
78449ab747fSPaolo Bonzini ldebug ("set dma block len %d\n", s->block_size);
78549ab747fSPaolo Bonzini break;
78649ab747fSPaolo Bonzini
78749ab747fSPaolo Bonzini case 0x74:
78849ab747fSPaolo Bonzini case 0x75:
78949ab747fSPaolo Bonzini case 0x76:
79049ab747fSPaolo Bonzini case 0x77:
79149ab747fSPaolo Bonzini /* ADPCM stuff, ignore */
79249ab747fSPaolo Bonzini break;
79349ab747fSPaolo Bonzini
79449ab747fSPaolo Bonzini case 0x80:
79549ab747fSPaolo Bonzini {
79649ab747fSPaolo Bonzini int freq, samples, bytes;
79749ab747fSPaolo Bonzini int64_t ticks;
79849ab747fSPaolo Bonzini
79949ab747fSPaolo Bonzini freq = s->freq > 0 ? s->freq : 11025;
80049ab747fSPaolo Bonzini samples = dsp_get_lohi (s) + 1;
80149ab747fSPaolo Bonzini bytes = samples << s->fmt_stereo << (s->fmt_bits == 16);
80273bcb24dSRutuja Shah ticks = muldiv64(bytes, NANOSECONDS_PER_SECOND, freq);
80373bcb24dSRutuja Shah if (ticks < NANOSECONDS_PER_SECOND / 1024) {
80449ab747fSPaolo Bonzini qemu_irq_raise (s->pic);
80549ab747fSPaolo Bonzini }
80649ab747fSPaolo Bonzini else {
80749ab747fSPaolo Bonzini if (s->aux_ts) {
808bc72ad67SAlex Bligh timer_mod (
80949ab747fSPaolo Bonzini s->aux_ts,
810bc72ad67SAlex Bligh qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + ticks
81149ab747fSPaolo Bonzini );
81249ab747fSPaolo Bonzini }
81349ab747fSPaolo Bonzini }
81449ab747fSPaolo Bonzini ldebug ("mix silence %d %d %" PRId64 "\n", samples, bytes, ticks);
81549ab747fSPaolo Bonzini }
81649ab747fSPaolo Bonzini break;
81749ab747fSPaolo Bonzini
81849ab747fSPaolo Bonzini case 0xe0:
81949ab747fSPaolo Bonzini d0 = dsp_get_data (s);
82049ab747fSPaolo Bonzini s->out_data_len = 0;
82149ab747fSPaolo Bonzini ldebug ("E0 data = %#x\n", d0);
82249ab747fSPaolo Bonzini dsp_out_data (s, ~d0);
82349ab747fSPaolo Bonzini break;
82449ab747fSPaolo Bonzini
82549ab747fSPaolo Bonzini case 0xe2:
82649ab747fSPaolo Bonzini #ifdef DEBUG
82749ab747fSPaolo Bonzini d0 = dsp_get_data (s);
82849ab747fSPaolo Bonzini dolog ("E2 = %#x\n", d0);
82949ab747fSPaolo Bonzini #endif
83049ab747fSPaolo Bonzini break;
83149ab747fSPaolo Bonzini
83249ab747fSPaolo Bonzini case 0xe4:
83349ab747fSPaolo Bonzini s->test_reg = dsp_get_data (s);
83449ab747fSPaolo Bonzini break;
83549ab747fSPaolo Bonzini
83649ab747fSPaolo Bonzini case 0xf9:
83749ab747fSPaolo Bonzini d0 = dsp_get_data (s);
83849ab747fSPaolo Bonzini ldebug ("command 0xf9 with %#x\n", d0);
83949ab747fSPaolo Bonzini switch (d0) {
84049ab747fSPaolo Bonzini case 0x0e:
84149ab747fSPaolo Bonzini dsp_out_data (s, 0xff);
84249ab747fSPaolo Bonzini break;
84349ab747fSPaolo Bonzini
84449ab747fSPaolo Bonzini case 0x0f:
84549ab747fSPaolo Bonzini dsp_out_data (s, 0x07);
84649ab747fSPaolo Bonzini break;
84749ab747fSPaolo Bonzini
84849ab747fSPaolo Bonzini case 0x37:
84949ab747fSPaolo Bonzini dsp_out_data (s, 0x38);
85049ab747fSPaolo Bonzini break;
85149ab747fSPaolo Bonzini
85249ab747fSPaolo Bonzini default:
85349ab747fSPaolo Bonzini dsp_out_data (s, 0x00);
85449ab747fSPaolo Bonzini break;
85549ab747fSPaolo Bonzini }
85649ab747fSPaolo Bonzini break;
85749ab747fSPaolo Bonzini
85849ab747fSPaolo Bonzini default:
8598ec660b8SJohn Arbuckle qemu_log_mask(LOG_UNIMP, "complete: unrecognized command %#x\n",
8608ec660b8SJohn Arbuckle s->cmd);
86149ab747fSPaolo Bonzini return;
86249ab747fSPaolo Bonzini }
86349ab747fSPaolo Bonzini }
86449ab747fSPaolo Bonzini
86549ab747fSPaolo Bonzini ldebug ("\n");
86649ab747fSPaolo Bonzini s->cmd = -1;
86749ab747fSPaolo Bonzini }
86849ab747fSPaolo Bonzini
legacy_reset(SB16State * s)86949ab747fSPaolo Bonzini static void legacy_reset (SB16State *s)
87049ab747fSPaolo Bonzini {
87149ab747fSPaolo Bonzini struct audsettings as;
87249ab747fSPaolo Bonzini
87349ab747fSPaolo Bonzini s->freq = 11025;
87449ab747fSPaolo Bonzini s->fmt_signed = 0;
87549ab747fSPaolo Bonzini s->fmt_bits = 8;
87649ab747fSPaolo Bonzini s->fmt_stereo = 0;
87749ab747fSPaolo Bonzini
87849ab747fSPaolo Bonzini as.freq = s->freq;
87949ab747fSPaolo Bonzini as.nchannels = 1;
88085bc5852SKővágó, Zoltán as.fmt = AUDIO_FORMAT_U8;
88149ab747fSPaolo Bonzini as.endianness = 0;
88249ab747fSPaolo Bonzini
88349ab747fSPaolo Bonzini s->voice = AUD_open_out (
88449ab747fSPaolo Bonzini &s->card,
88549ab747fSPaolo Bonzini s->voice,
88649ab747fSPaolo Bonzini "sb16",
88749ab747fSPaolo Bonzini s,
88849ab747fSPaolo Bonzini SB_audio_callback,
88949ab747fSPaolo Bonzini &as
89049ab747fSPaolo Bonzini );
89149ab747fSPaolo Bonzini
89249ab747fSPaolo Bonzini /* Not sure about that... */
89349ab747fSPaolo Bonzini /* AUD_set_active_out (s->voice, 1); */
89449ab747fSPaolo Bonzini }
89549ab747fSPaolo Bonzini
reset(SB16State * s)89649ab747fSPaolo Bonzini static void reset (SB16State *s)
89749ab747fSPaolo Bonzini {
89849ab747fSPaolo Bonzini qemu_irq_lower (s->pic);
89949ab747fSPaolo Bonzini if (s->dma_auto) {
90049ab747fSPaolo Bonzini qemu_irq_raise (s->pic);
90149ab747fSPaolo Bonzini qemu_irq_lower (s->pic);
90249ab747fSPaolo Bonzini }
90349ab747fSPaolo Bonzini
90449ab747fSPaolo Bonzini s->mixer_regs[0x82] = 0;
90549ab747fSPaolo Bonzini s->dma_auto = 0;
90649ab747fSPaolo Bonzini s->in_index = 0;
90749ab747fSPaolo Bonzini s->out_data_len = 0;
90849ab747fSPaolo Bonzini s->left_till_irq = 0;
90949ab747fSPaolo Bonzini s->needed_bytes = 0;
91049ab747fSPaolo Bonzini s->block_size = -1;
91149ab747fSPaolo Bonzini s->nzero = 0;
91249ab747fSPaolo Bonzini s->highspeed = 0;
91349ab747fSPaolo Bonzini s->v2x6 = 0;
91449ab747fSPaolo Bonzini s->cmd = -1;
91549ab747fSPaolo Bonzini
91649ab747fSPaolo Bonzini dsp_out_data (s, 0xaa);
91749ab747fSPaolo Bonzini speaker (s, 0);
91849ab747fSPaolo Bonzini control (s, 0);
91949ab747fSPaolo Bonzini legacy_reset (s);
92049ab747fSPaolo Bonzini }
92149ab747fSPaolo Bonzini
dsp_write(void * opaque,uint32_t nport,uint32_t val)9228307c294SNutan Shinde static void dsp_write(void *opaque, uint32_t nport, uint32_t val)
92349ab747fSPaolo Bonzini {
92449ab747fSPaolo Bonzini SB16State *s = opaque;
92549ab747fSPaolo Bonzini int iport;
92649ab747fSPaolo Bonzini
92749ab747fSPaolo Bonzini iport = nport - s->port;
92849ab747fSPaolo Bonzini
92949ab747fSPaolo Bonzini ldebug ("write %#x <- %#x\n", nport, val);
93049ab747fSPaolo Bonzini switch (iport) {
93149ab747fSPaolo Bonzini case 0x06:
93249ab747fSPaolo Bonzini switch (val) {
93349ab747fSPaolo Bonzini case 0x00:
93449ab747fSPaolo Bonzini if (s->v2x6 == 1) {
93549ab747fSPaolo Bonzini reset (s);
93649ab747fSPaolo Bonzini }
93749ab747fSPaolo Bonzini s->v2x6 = 0;
93849ab747fSPaolo Bonzini break;
93949ab747fSPaolo Bonzini
94049ab747fSPaolo Bonzini case 0x01:
94149ab747fSPaolo Bonzini case 0x03: /* FreeBSD kludge */
94249ab747fSPaolo Bonzini s->v2x6 = 1;
94349ab747fSPaolo Bonzini break;
94449ab747fSPaolo Bonzini
94549ab747fSPaolo Bonzini case 0xc6:
94649ab747fSPaolo Bonzini s->v2x6 = 0; /* Prince of Persia, csp.sys, diagnose.exe */
94749ab747fSPaolo Bonzini break;
94849ab747fSPaolo Bonzini
94949ab747fSPaolo Bonzini case 0xb8: /* Panic */
95049ab747fSPaolo Bonzini reset (s);
95149ab747fSPaolo Bonzini break;
95249ab747fSPaolo Bonzini
95349ab747fSPaolo Bonzini case 0x39:
95449ab747fSPaolo Bonzini dsp_out_data (s, 0x38);
95549ab747fSPaolo Bonzini reset (s);
95649ab747fSPaolo Bonzini s->v2x6 = 0x39;
95749ab747fSPaolo Bonzini break;
95849ab747fSPaolo Bonzini
95949ab747fSPaolo Bonzini default:
96049ab747fSPaolo Bonzini s->v2x6 = val;
96149ab747fSPaolo Bonzini break;
96249ab747fSPaolo Bonzini }
96349ab747fSPaolo Bonzini break;
96449ab747fSPaolo Bonzini
96549ab747fSPaolo Bonzini case 0x0c: /* write data or command | write status */
96649ab747fSPaolo Bonzini /* if (s->highspeed) */
96749ab747fSPaolo Bonzini /* break; */
96849ab747fSPaolo Bonzini
9692ab5bf67SGonglei if (s->needed_bytes == 0) {
97049ab747fSPaolo Bonzini command (s, val);
97149ab747fSPaolo Bonzini #if 0
97249ab747fSPaolo Bonzini if (0 == s->needed_bytes) {
97349ab747fSPaolo Bonzini log_dsp (s);
97449ab747fSPaolo Bonzini }
97549ab747fSPaolo Bonzini #endif
97649ab747fSPaolo Bonzini }
97749ab747fSPaolo Bonzini else {
97849ab747fSPaolo Bonzini if (s->in_index == sizeof (s->in2_data)) {
97949ab747fSPaolo Bonzini dolog ("in data overrun\n");
98049ab747fSPaolo Bonzini }
98149ab747fSPaolo Bonzini else {
98249ab747fSPaolo Bonzini s->in2_data[s->in_index++] = val;
98349ab747fSPaolo Bonzini if (s->in_index == s->needed_bytes) {
98449ab747fSPaolo Bonzini s->needed_bytes = 0;
98549ab747fSPaolo Bonzini complete (s);
98649ab747fSPaolo Bonzini #if 0
98749ab747fSPaolo Bonzini log_dsp (s);
98849ab747fSPaolo Bonzini #endif
98949ab747fSPaolo Bonzini }
99049ab747fSPaolo Bonzini }
99149ab747fSPaolo Bonzini }
99249ab747fSPaolo Bonzini break;
99349ab747fSPaolo Bonzini
99449ab747fSPaolo Bonzini default:
99549ab747fSPaolo Bonzini ldebug ("(nport=%#x, val=%#x)\n", nport, val);
99649ab747fSPaolo Bonzini break;
99749ab747fSPaolo Bonzini }
99849ab747fSPaolo Bonzini }
99949ab747fSPaolo Bonzini
dsp_read(void * opaque,uint32_t nport)10008307c294SNutan Shinde static uint32_t dsp_read(void *opaque, uint32_t nport)
100149ab747fSPaolo Bonzini {
100249ab747fSPaolo Bonzini SB16State *s = opaque;
100349ab747fSPaolo Bonzini int iport, retval, ack = 0;
100449ab747fSPaolo Bonzini
100549ab747fSPaolo Bonzini iport = nport - s->port;
100649ab747fSPaolo Bonzini
100749ab747fSPaolo Bonzini switch (iport) {
100849ab747fSPaolo Bonzini case 0x06: /* reset */
100949ab747fSPaolo Bonzini retval = 0xff;
101049ab747fSPaolo Bonzini break;
101149ab747fSPaolo Bonzini
101249ab747fSPaolo Bonzini case 0x0a: /* read data */
101349ab747fSPaolo Bonzini if (s->out_data_len) {
101449ab747fSPaolo Bonzini retval = s->out_data[--s->out_data_len];
101549ab747fSPaolo Bonzini s->last_read_byte = retval;
101649ab747fSPaolo Bonzini }
101749ab747fSPaolo Bonzini else {
101849ab747fSPaolo Bonzini if (s->cmd != -1) {
101949ab747fSPaolo Bonzini dolog ("empty output buffer for command %#x\n",
102049ab747fSPaolo Bonzini s->cmd);
102149ab747fSPaolo Bonzini }
102249ab747fSPaolo Bonzini retval = s->last_read_byte;
102349ab747fSPaolo Bonzini /* goto error; */
102449ab747fSPaolo Bonzini }
102549ab747fSPaolo Bonzini break;
102649ab747fSPaolo Bonzini
102749ab747fSPaolo Bonzini case 0x0c: /* 0 can write */
102849ab747fSPaolo Bonzini retval = s->can_write ? 0 : 0x80;
102949ab747fSPaolo Bonzini break;
103049ab747fSPaolo Bonzini
103149ab747fSPaolo Bonzini case 0x0d: /* timer interrupt clear */
103249ab747fSPaolo Bonzini /* dolog ("timer interrupt clear\n"); */
103349ab747fSPaolo Bonzini retval = 0;
103449ab747fSPaolo Bonzini break;
103549ab747fSPaolo Bonzini
103649ab747fSPaolo Bonzini case 0x0e: /* data available status | irq 8 ack */
103749ab747fSPaolo Bonzini retval = (!s->out_data_len || s->highspeed) ? 0 : 0x80;
103849ab747fSPaolo Bonzini if (s->mixer_regs[0x82] & 1) {
103949ab747fSPaolo Bonzini ack = 1;
10409939375cSPaolo Bonzini s->mixer_regs[0x82] &= ~1;
104149ab747fSPaolo Bonzini qemu_irq_lower (s->pic);
104249ab747fSPaolo Bonzini }
104349ab747fSPaolo Bonzini break;
104449ab747fSPaolo Bonzini
104549ab747fSPaolo Bonzini case 0x0f: /* irq 16 ack */
104649ab747fSPaolo Bonzini retval = 0xff;
104749ab747fSPaolo Bonzini if (s->mixer_regs[0x82] & 2) {
104849ab747fSPaolo Bonzini ack = 1;
10499939375cSPaolo Bonzini s->mixer_regs[0x82] &= ~2;
105049ab747fSPaolo Bonzini qemu_irq_lower (s->pic);
105149ab747fSPaolo Bonzini }
105249ab747fSPaolo Bonzini break;
105349ab747fSPaolo Bonzini
105449ab747fSPaolo Bonzini default:
105549ab747fSPaolo Bonzini goto error;
105649ab747fSPaolo Bonzini }
105749ab747fSPaolo Bonzini
105849ab747fSPaolo Bonzini if (!ack) {
105949ab747fSPaolo Bonzini ldebug ("read %#x -> %#x\n", nport, retval);
106049ab747fSPaolo Bonzini }
106149ab747fSPaolo Bonzini
106249ab747fSPaolo Bonzini return retval;
106349ab747fSPaolo Bonzini
106449ab747fSPaolo Bonzini error:
106549ab747fSPaolo Bonzini dolog ("warning: dsp_read %#x error\n", nport);
106649ab747fSPaolo Bonzini return 0xff;
106749ab747fSPaolo Bonzini }
106849ab747fSPaolo Bonzini
reset_mixer(SB16State * s)106949ab747fSPaolo Bonzini static void reset_mixer (SB16State *s)
107049ab747fSPaolo Bonzini {
107149ab747fSPaolo Bonzini int i;
107249ab747fSPaolo Bonzini
107349ab747fSPaolo Bonzini memset (s->mixer_regs, 0xff, 0x7f);
107449ab747fSPaolo Bonzini memset (s->mixer_regs + 0x83, 0xff, sizeof (s->mixer_regs) - 0x83);
107549ab747fSPaolo Bonzini
107649ab747fSPaolo Bonzini s->mixer_regs[0x02] = 4; /* master volume 3bits */
107749ab747fSPaolo Bonzini s->mixer_regs[0x06] = 4; /* MIDI volume 3bits */
107849ab747fSPaolo Bonzini s->mixer_regs[0x08] = 0; /* CD volume 3bits */
107949ab747fSPaolo Bonzini s->mixer_regs[0x0a] = 0; /* voice volume 2bits */
108049ab747fSPaolo Bonzini
108149ab747fSPaolo Bonzini /* d5=input filt, d3=lowpass filt, d1,d2=input source */
108249ab747fSPaolo Bonzini s->mixer_regs[0x0c] = 0;
108349ab747fSPaolo Bonzini
108449ab747fSPaolo Bonzini /* d5=output filt, d1=stereo switch */
108549ab747fSPaolo Bonzini s->mixer_regs[0x0e] = 0;
108649ab747fSPaolo Bonzini
108749ab747fSPaolo Bonzini /* voice volume L d5,d7, R d1,d3 */
108849ab747fSPaolo Bonzini s->mixer_regs[0x04] = (4 << 5) | (4 << 1);
108949ab747fSPaolo Bonzini /* master ... */
109049ab747fSPaolo Bonzini s->mixer_regs[0x22] = (4 << 5) | (4 << 1);
109149ab747fSPaolo Bonzini /* MIDI ... */
109249ab747fSPaolo Bonzini s->mixer_regs[0x26] = (4 << 5) | (4 << 1);
109349ab747fSPaolo Bonzini
109449ab747fSPaolo Bonzini for (i = 0x30; i < 0x48; i++) {
109549ab747fSPaolo Bonzini s->mixer_regs[i] = 0x20;
109649ab747fSPaolo Bonzini }
109749ab747fSPaolo Bonzini }
109849ab747fSPaolo Bonzini
mixer_write_indexb(void * opaque,uint32_t nport,uint32_t val)10998307c294SNutan Shinde static void mixer_write_indexb(void *opaque, uint32_t nport, uint32_t val)
110049ab747fSPaolo Bonzini {
110149ab747fSPaolo Bonzini SB16State *s = opaque;
110249ab747fSPaolo Bonzini (void) nport;
110349ab747fSPaolo Bonzini s->mixer_nreg = val;
110449ab747fSPaolo Bonzini }
110549ab747fSPaolo Bonzini
mixer_write_datab(void * opaque,uint32_t nport,uint32_t val)11068307c294SNutan Shinde static void mixer_write_datab(void *opaque, uint32_t nport, uint32_t val)
110749ab747fSPaolo Bonzini {
110849ab747fSPaolo Bonzini SB16State *s = opaque;
110949ab747fSPaolo Bonzini
111049ab747fSPaolo Bonzini (void) nport;
111149ab747fSPaolo Bonzini ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val);
111249ab747fSPaolo Bonzini
111349ab747fSPaolo Bonzini switch (s->mixer_nreg) {
111449ab747fSPaolo Bonzini case 0x00:
111549ab747fSPaolo Bonzini reset_mixer (s);
111649ab747fSPaolo Bonzini break;
111749ab747fSPaolo Bonzini
111849ab747fSPaolo Bonzini case 0x80:
111949ab747fSPaolo Bonzini {
112049ab747fSPaolo Bonzini int irq = irq_of_magic (val);
112149ab747fSPaolo Bonzini ldebug ("setting irq to %d (val=%#x)\n", irq, val);
112249ab747fSPaolo Bonzini if (irq > 0) {
112349ab747fSPaolo Bonzini s->irq = irq;
112449ab747fSPaolo Bonzini }
112549ab747fSPaolo Bonzini }
112649ab747fSPaolo Bonzini break;
112749ab747fSPaolo Bonzini
112849ab747fSPaolo Bonzini case 0x81:
112949ab747fSPaolo Bonzini {
113049ab747fSPaolo Bonzini int dma, hdma;
113149ab747fSPaolo Bonzini
113249ab747fSPaolo Bonzini dma = ctz32 (val & 0xf);
113349ab747fSPaolo Bonzini hdma = ctz32 (val & 0xf0);
113449ab747fSPaolo Bonzini if (dma != s->dma || hdma != s->hdma) {
11358ec660b8SJohn Arbuckle qemu_log_mask(LOG_GUEST_ERROR, "attempt to change DMA 8bit"
11368ec660b8SJohn Arbuckle " %d(%d), 16bit %d(%d) (val=%#x)\n", dma, s->dma,
11378ec660b8SJohn Arbuckle hdma, s->hdma, val);
113849ab747fSPaolo Bonzini }
113949ab747fSPaolo Bonzini #if 0
114049ab747fSPaolo Bonzini s->dma = dma;
114149ab747fSPaolo Bonzini s->hdma = hdma;
114249ab747fSPaolo Bonzini #endif
114349ab747fSPaolo Bonzini }
114449ab747fSPaolo Bonzini break;
114549ab747fSPaolo Bonzini
114649ab747fSPaolo Bonzini case 0x82:
11478ec660b8SJohn Arbuckle qemu_log_mask(LOG_GUEST_ERROR, "attempt to write into IRQ status"
11488ec660b8SJohn Arbuckle " register (val=%#x)\n", val);
114949ab747fSPaolo Bonzini return;
115049ab747fSPaolo Bonzini
115149ab747fSPaolo Bonzini default:
115249ab747fSPaolo Bonzini if (s->mixer_nreg >= 0x80) {
115349ab747fSPaolo Bonzini ldebug ("attempt to write mixer[%#x] <- %#x\n", s->mixer_nreg, val);
115449ab747fSPaolo Bonzini }
115549ab747fSPaolo Bonzini break;
115649ab747fSPaolo Bonzini }
115749ab747fSPaolo Bonzini
115849ab747fSPaolo Bonzini s->mixer_regs[s->mixer_nreg] = val;
115949ab747fSPaolo Bonzini }
116049ab747fSPaolo Bonzini
mixer_read(void * opaque,uint32_t nport)11618307c294SNutan Shinde static uint32_t mixer_read(void *opaque, uint32_t nport)
116249ab747fSPaolo Bonzini {
116349ab747fSPaolo Bonzini SB16State *s = opaque;
116449ab747fSPaolo Bonzini
116549ab747fSPaolo Bonzini (void) nport;
116649ab747fSPaolo Bonzini #ifndef DEBUG_SB16_MOST
116749ab747fSPaolo Bonzini if (s->mixer_nreg != 0x82) {
116849ab747fSPaolo Bonzini ldebug ("mixer_read[%#x] -> %#x\n",
116949ab747fSPaolo Bonzini s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
117049ab747fSPaolo Bonzini }
117149ab747fSPaolo Bonzini #else
117249ab747fSPaolo Bonzini ldebug ("mixer_read[%#x] -> %#x\n",
117349ab747fSPaolo Bonzini s->mixer_nreg, s->mixer_regs[s->mixer_nreg]);
117449ab747fSPaolo Bonzini #endif
117549ab747fSPaolo Bonzini return s->mixer_regs[s->mixer_nreg];
117649ab747fSPaolo Bonzini }
117749ab747fSPaolo Bonzini
write_audio(SB16State * s,int nchan,int dma_pos,int dma_len,int len)117849ab747fSPaolo Bonzini static int write_audio (SB16State *s, int nchan, int dma_pos,
117949ab747fSPaolo Bonzini int dma_len, int len)
118049ab747fSPaolo Bonzini {
1181f203c16eSHervé Poussineau IsaDma *isa_dma = nchan == s->dma ? s->isa_dma : s->isa_hdma;
1182f203c16eSHervé Poussineau IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma);
118349ab747fSPaolo Bonzini int temp, net;
118449ab747fSPaolo Bonzini uint8_t tmpbuf[4096];
118549ab747fSPaolo Bonzini
118649ab747fSPaolo Bonzini temp = len;
118749ab747fSPaolo Bonzini net = 0;
118849ab747fSPaolo Bonzini
118949ab747fSPaolo Bonzini while (temp) {
119049ab747fSPaolo Bonzini int left = dma_len - dma_pos;
119149ab747fSPaolo Bonzini int copied;
119249ab747fSPaolo Bonzini size_t to_copy;
119349ab747fSPaolo Bonzini
119458935915SKővágó, Zoltán to_copy = MIN (temp, left);
119549ab747fSPaolo Bonzini if (to_copy > sizeof (tmpbuf)) {
119649ab747fSPaolo Bonzini to_copy = sizeof (tmpbuf);
119749ab747fSPaolo Bonzini }
119849ab747fSPaolo Bonzini
1199f203c16eSHervé Poussineau copied = k->read_memory(isa_dma, nchan, tmpbuf, dma_pos, to_copy);
120049ab747fSPaolo Bonzini copied = AUD_write (s->voice, tmpbuf, copied);
120149ab747fSPaolo Bonzini
120249ab747fSPaolo Bonzini temp -= copied;
120349ab747fSPaolo Bonzini dma_pos = (dma_pos + copied) % dma_len;
120449ab747fSPaolo Bonzini net += copied;
120549ab747fSPaolo Bonzini
120649ab747fSPaolo Bonzini if (!copied) {
120749ab747fSPaolo Bonzini break;
120849ab747fSPaolo Bonzini }
120949ab747fSPaolo Bonzini }
121049ab747fSPaolo Bonzini
121149ab747fSPaolo Bonzini return net;
121249ab747fSPaolo Bonzini }
121349ab747fSPaolo Bonzini
SB_read_DMA(void * opaque,int nchan,int dma_pos,int dma_len)121449ab747fSPaolo Bonzini static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
121549ab747fSPaolo Bonzini {
121649ab747fSPaolo Bonzini SB16State *s = opaque;
121749ab747fSPaolo Bonzini int till, copy, written, free;
121849ab747fSPaolo Bonzini
121949ab747fSPaolo Bonzini if (s->block_size <= 0) {
12208ec660b8SJohn Arbuckle qemu_log_mask(LOG_GUEST_ERROR, "invalid block size=%d nchan=%d"
12218ec660b8SJohn Arbuckle " dma_pos=%d dma_len=%d\n", s->block_size, nchan,
12228ec660b8SJohn Arbuckle dma_pos, dma_len);
122349ab747fSPaolo Bonzini return dma_pos;
122449ab747fSPaolo Bonzini }
122549ab747fSPaolo Bonzini
122649ab747fSPaolo Bonzini if (s->left_till_irq < 0) {
122749ab747fSPaolo Bonzini s->left_till_irq = s->block_size;
122849ab747fSPaolo Bonzini }
122949ab747fSPaolo Bonzini
123049ab747fSPaolo Bonzini if (s->voice) {
123149ab747fSPaolo Bonzini free = s->audio_free & ~s->align;
123249ab747fSPaolo Bonzini if ((free <= 0) || !dma_len) {
123349ab747fSPaolo Bonzini return dma_pos;
123449ab747fSPaolo Bonzini }
123549ab747fSPaolo Bonzini }
123649ab747fSPaolo Bonzini else {
123749ab747fSPaolo Bonzini free = dma_len;
123849ab747fSPaolo Bonzini }
123949ab747fSPaolo Bonzini
124049ab747fSPaolo Bonzini copy = free;
124149ab747fSPaolo Bonzini till = s->left_till_irq;
124249ab747fSPaolo Bonzini
124349ab747fSPaolo Bonzini #ifdef DEBUG_SB16_MOST
124449ab747fSPaolo Bonzini dolog ("pos:%06d %d till:%d len:%d\n",
124549ab747fSPaolo Bonzini dma_pos, free, till, dma_len);
124649ab747fSPaolo Bonzini #endif
124749ab747fSPaolo Bonzini
124849ab747fSPaolo Bonzini if (till <= copy) {
12492ab5bf67SGonglei if (s->dma_auto == 0) {
125049ab747fSPaolo Bonzini copy = till;
125149ab747fSPaolo Bonzini }
125249ab747fSPaolo Bonzini }
125349ab747fSPaolo Bonzini
125449ab747fSPaolo Bonzini written = write_audio (s, nchan, dma_pos, dma_len, copy);
125549ab747fSPaolo Bonzini dma_pos = (dma_pos + written) % dma_len;
125649ab747fSPaolo Bonzini s->left_till_irq -= written;
125749ab747fSPaolo Bonzini
125849ab747fSPaolo Bonzini if (s->left_till_irq <= 0) {
125949ab747fSPaolo Bonzini s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1;
126049ab747fSPaolo Bonzini qemu_irq_raise (s->pic);
12612ab5bf67SGonglei if (s->dma_auto == 0) {
126249ab747fSPaolo Bonzini control (s, 0);
126349ab747fSPaolo Bonzini speaker (s, 0);
126449ab747fSPaolo Bonzini }
126549ab747fSPaolo Bonzini }
126649ab747fSPaolo Bonzini
126749ab747fSPaolo Bonzini #ifdef DEBUG_SB16_MOST
126849ab747fSPaolo Bonzini ldebug ("pos %5d free %5d size %5d till % 5d copy %5d written %5d size %5d\n",
126949ab747fSPaolo Bonzini dma_pos, free, dma_len, s->left_till_irq, copy, written,
127049ab747fSPaolo Bonzini s->block_size);
127149ab747fSPaolo Bonzini #endif
127249ab747fSPaolo Bonzini
127349ab747fSPaolo Bonzini while (s->left_till_irq <= 0) {
127449ab747fSPaolo Bonzini s->left_till_irq = s->block_size + s->left_till_irq;
127549ab747fSPaolo Bonzini }
127649ab747fSPaolo Bonzini
127749ab747fSPaolo Bonzini return dma_pos;
127849ab747fSPaolo Bonzini }
127949ab747fSPaolo Bonzini
SB_audio_callback(void * opaque,int free)128049ab747fSPaolo Bonzini static void SB_audio_callback (void *opaque, int free)
128149ab747fSPaolo Bonzini {
128249ab747fSPaolo Bonzini SB16State *s = opaque;
128349ab747fSPaolo Bonzini s->audio_free = free;
128449ab747fSPaolo Bonzini }
128549ab747fSPaolo Bonzini
sb16_post_load(void * opaque,int version_id)128649ab747fSPaolo Bonzini static int sb16_post_load (void *opaque, int version_id)
128749ab747fSPaolo Bonzini {
128849ab747fSPaolo Bonzini SB16State *s = opaque;
128949ab747fSPaolo Bonzini
129049ab747fSPaolo Bonzini if (s->voice) {
129149ab747fSPaolo Bonzini AUD_close_out (&s->card, s->voice);
129249ab747fSPaolo Bonzini s->voice = NULL;
129349ab747fSPaolo Bonzini }
129449ab747fSPaolo Bonzini
129549ab747fSPaolo Bonzini if (s->dma_running) {
129649ab747fSPaolo Bonzini if (s->freq) {
129749ab747fSPaolo Bonzini struct audsettings as;
129849ab747fSPaolo Bonzini
129949ab747fSPaolo Bonzini s->audio_free = 0;
130049ab747fSPaolo Bonzini
130149ab747fSPaolo Bonzini as.freq = s->freq;
130249ab747fSPaolo Bonzini as.nchannels = 1 << s->fmt_stereo;
130349ab747fSPaolo Bonzini as.fmt = s->fmt;
130449ab747fSPaolo Bonzini as.endianness = 0;
130549ab747fSPaolo Bonzini
130649ab747fSPaolo Bonzini s->voice = AUD_open_out (
130749ab747fSPaolo Bonzini &s->card,
130849ab747fSPaolo Bonzini s->voice,
130949ab747fSPaolo Bonzini "sb16",
131049ab747fSPaolo Bonzini s,
131149ab747fSPaolo Bonzini SB_audio_callback,
131249ab747fSPaolo Bonzini &as
131349ab747fSPaolo Bonzini );
131449ab747fSPaolo Bonzini }
131549ab747fSPaolo Bonzini
131649ab747fSPaolo Bonzini control (s, 1);
131749ab747fSPaolo Bonzini speaker (s, s->speaker);
131849ab747fSPaolo Bonzini }
131949ab747fSPaolo Bonzini return 0;
132049ab747fSPaolo Bonzini }
132149ab747fSPaolo Bonzini
132249ab747fSPaolo Bonzini static const VMStateDescription vmstate_sb16 = {
132349ab747fSPaolo Bonzini .name = "sb16",
132449ab747fSPaolo Bonzini .version_id = 1,
132549ab747fSPaolo Bonzini .minimum_version_id = 1,
132649ab747fSPaolo Bonzini .post_load = sb16_post_load,
1327856a6fe4SRichard Henderson .fields = (const VMStateField[]) {
1328*fa293f81SPhilippe Mathieu-Daudé VMSTATE_UNUSED( 4 /* irq */
1329*fa293f81SPhilippe Mathieu-Daudé + 4 /* dma */
1330*fa293f81SPhilippe Mathieu-Daudé + 4 /* hdma */
1331*fa293f81SPhilippe Mathieu-Daudé + 4 /* port */
1332*fa293f81SPhilippe Mathieu-Daudé + 4 /* ver */),
133349ab747fSPaolo Bonzini VMSTATE_INT32 (in_index, SB16State),
133449ab747fSPaolo Bonzini VMSTATE_INT32 (out_data_len, SB16State),
133549ab747fSPaolo Bonzini VMSTATE_INT32 (fmt_stereo, SB16State),
133649ab747fSPaolo Bonzini VMSTATE_INT32 (fmt_signed, SB16State),
133749ab747fSPaolo Bonzini VMSTATE_INT32 (fmt_bits, SB16State),
133849ab747fSPaolo Bonzini VMSTATE_UINT32 (fmt, SB16State),
133949ab747fSPaolo Bonzini VMSTATE_INT32 (dma_auto, SB16State),
134049ab747fSPaolo Bonzini VMSTATE_INT32 (block_size, SB16State),
134149ab747fSPaolo Bonzini VMSTATE_INT32 (fifo, SB16State),
134249ab747fSPaolo Bonzini VMSTATE_INT32 (freq, SB16State),
134349ab747fSPaolo Bonzini VMSTATE_INT32 (time_const, SB16State),
134449ab747fSPaolo Bonzini VMSTATE_INT32 (speaker, SB16State),
134549ab747fSPaolo Bonzini VMSTATE_INT32 (needed_bytes, SB16State),
134649ab747fSPaolo Bonzini VMSTATE_INT32 (cmd, SB16State),
134749ab747fSPaolo Bonzini VMSTATE_INT32 (use_hdma, SB16State),
134849ab747fSPaolo Bonzini VMSTATE_INT32 (highspeed, SB16State),
134949ab747fSPaolo Bonzini VMSTATE_INT32 (can_write, SB16State),
135049ab747fSPaolo Bonzini VMSTATE_INT32 (v2x6, SB16State),
135149ab747fSPaolo Bonzini
135249ab747fSPaolo Bonzini VMSTATE_UINT8 (csp_param, SB16State),
135349ab747fSPaolo Bonzini VMSTATE_UINT8 (csp_value, SB16State),
135449ab747fSPaolo Bonzini VMSTATE_UINT8 (csp_mode, SB16State),
135549ab747fSPaolo Bonzini VMSTATE_UINT8 (csp_param, SB16State),
135649ab747fSPaolo Bonzini VMSTATE_BUFFER (csp_regs, SB16State),
135749ab747fSPaolo Bonzini VMSTATE_UINT8 (csp_index, SB16State),
135849ab747fSPaolo Bonzini VMSTATE_BUFFER (csp_reg83, SB16State),
135949ab747fSPaolo Bonzini VMSTATE_INT32 (csp_reg83r, SB16State),
136049ab747fSPaolo Bonzini VMSTATE_INT32 (csp_reg83w, SB16State),
136149ab747fSPaolo Bonzini
136249ab747fSPaolo Bonzini VMSTATE_BUFFER (in2_data, SB16State),
136349ab747fSPaolo Bonzini VMSTATE_BUFFER (out_data, SB16State),
136449ab747fSPaolo Bonzini VMSTATE_UINT8 (test_reg, SB16State),
136549ab747fSPaolo Bonzini VMSTATE_UINT8 (last_read_byte, SB16State),
136649ab747fSPaolo Bonzini
136749ab747fSPaolo Bonzini VMSTATE_INT32 (nzero, SB16State),
136849ab747fSPaolo Bonzini VMSTATE_INT32 (left_till_irq, SB16State),
136949ab747fSPaolo Bonzini VMSTATE_INT32 (dma_running, SB16State),
137049ab747fSPaolo Bonzini VMSTATE_INT32 (bytes_per_second, SB16State),
137149ab747fSPaolo Bonzini VMSTATE_INT32 (align, SB16State),
137249ab747fSPaolo Bonzini
137349ab747fSPaolo Bonzini VMSTATE_INT32 (mixer_nreg, SB16State),
137449ab747fSPaolo Bonzini VMSTATE_BUFFER (mixer_regs, SB16State),
137549ab747fSPaolo Bonzini
137649ab747fSPaolo Bonzini VMSTATE_END_OF_LIST ()
137749ab747fSPaolo Bonzini }
137849ab747fSPaolo Bonzini };
137949ab747fSPaolo Bonzini
138049ab747fSPaolo Bonzini static const MemoryRegionPortio sb16_ioport_list[] = {
138149ab747fSPaolo Bonzini { 4, 1, 1, .write = mixer_write_indexb },
138249ab747fSPaolo Bonzini { 5, 1, 1, .read = mixer_read, .write = mixer_write_datab },
138349ab747fSPaolo Bonzini { 6, 1, 1, .read = dsp_read, .write = dsp_write },
138449ab747fSPaolo Bonzini { 10, 1, 1, .read = dsp_read },
138549ab747fSPaolo Bonzini { 12, 1, 1, .write = dsp_write },
138649ab747fSPaolo Bonzini { 12, 4, 1, .read = dsp_read },
138749ab747fSPaolo Bonzini PORTIO_END_OF_LIST (),
138849ab747fSPaolo Bonzini };
138949ab747fSPaolo Bonzini
139049ab747fSPaolo Bonzini
sb16_initfn(Object * obj)1391db895a1eSAndreas Färber static void sb16_initfn (Object *obj)
139249ab747fSPaolo Bonzini {
1393db895a1eSAndreas Färber SB16State *s = SB16 (obj);
139449ab747fSPaolo Bonzini
139549ab747fSPaolo Bonzini s->cmd = -1;
1396db895a1eSAndreas Färber }
1397db895a1eSAndreas Färber
sb16_realizefn(DeviceState * dev,Error ** errp)1398db895a1eSAndreas Färber static void sb16_realizefn (DeviceState *dev, Error **errp)
1399db895a1eSAndreas Färber {
1400db895a1eSAndreas Färber ISADevice *isadev = ISA_DEVICE (dev);
14018e7db8abSPhilippe Mathieu-Daudé ISABus *bus = isa_bus_from_device(isadev);
1402db895a1eSAndreas Färber SB16State *s = SB16 (dev);
1403f203c16eSHervé Poussineau IsaDmaClass *k;
1404db895a1eSAndreas Färber
1405cb94ff5fSMartin Kletzander if (!AUD_register_card ("sb16", &s->card, errp)) {
1406cb94ff5fSMartin Kletzander return;
1407cb94ff5fSMartin Kletzander }
1408cb94ff5fSMartin Kletzander
14098e7db8abSPhilippe Mathieu-Daudé s->isa_hdma = isa_bus_get_dma(bus, s->hdma);
14108e7db8abSPhilippe Mathieu-Daudé s->isa_dma = isa_bus_get_dma(bus, s->dma);
1411c9073238SThomas Huth if (!s->isa_dma || !s->isa_hdma) {
1412c9073238SThomas Huth error_setg(errp, "ISA controller does not support DMA");
1413c9073238SThomas Huth return;
1414c9073238SThomas Huth }
1415c9073238SThomas Huth
14168e7db8abSPhilippe Mathieu-Daudé s->pic = isa_bus_get_irq(bus, s->irq);
141749ab747fSPaolo Bonzini
141849ab747fSPaolo Bonzini s->mixer_regs[0x80] = magic_of_irq (s->irq);
141949ab747fSPaolo Bonzini s->mixer_regs[0x81] = (1 << s->dma) | (1 << s->hdma);
142049ab747fSPaolo Bonzini s->mixer_regs[0x82] = 2 << 5;
142149ab747fSPaolo Bonzini
142249ab747fSPaolo Bonzini s->csp_regs[5] = 1;
142349ab747fSPaolo Bonzini s->csp_regs[9] = 0xf8;
142449ab747fSPaolo Bonzini
142549ab747fSPaolo Bonzini reset_mixer (s);
1426bc72ad67SAlex Bligh s->aux_ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, aux_timer, s);
142749ab747fSPaolo Bonzini if (!s->aux_ts) {
14288ec660b8SJohn Arbuckle error_setg(errp, "warning: Could not create auxiliary timer");
142949ab747fSPaolo Bonzini }
143049ab747fSPaolo Bonzini
1431e305a165SMarc-André Lureau isa_register_portio_list(isadev, &s->portio_list, s->port,
1432e305a165SMarc-André Lureau sb16_ioport_list, s, "sb16");
143349ab747fSPaolo Bonzini
1434f203c16eSHervé Poussineau k = ISADMA_GET_CLASS(s->isa_hdma);
1435f203c16eSHervé Poussineau k->register_channel(s->isa_hdma, s->hdma, SB_read_DMA, s);
1436f203c16eSHervé Poussineau
1437f203c16eSHervé Poussineau k = ISADMA_GET_CLASS(s->isa_dma);
1438f203c16eSHervé Poussineau k->register_channel(s->isa_dma, s->dma, SB_read_DMA, s);
1439f203c16eSHervé Poussineau
144049ab747fSPaolo Bonzini s->can_write = 1;
144149ab747fSPaolo Bonzini }
144249ab747fSPaolo Bonzini
144349ab747fSPaolo Bonzini static Property sb16_properties[] = {
144488e47b9aSKővágó, Zoltán DEFINE_AUDIO_PROPERTIES(SB16State, card),
1445c7bcc85dSPaolo Bonzini DEFINE_PROP_UINT32 ("version", SB16State, ver, 0x0405), /* 4.5 */
1446c7bcc85dSPaolo Bonzini DEFINE_PROP_UINT32 ("iobase", SB16State, port, 0x220),
144749ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("irq", SB16State, irq, 5),
144849ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("dma", SB16State, dma, 1),
144949ab747fSPaolo Bonzini DEFINE_PROP_UINT32 ("dma16", SB16State, hdma, 5),
145049ab747fSPaolo Bonzini DEFINE_PROP_END_OF_LIST (),
145149ab747fSPaolo Bonzini };
145249ab747fSPaolo Bonzini
sb16_class_initfn(ObjectClass * klass,void * data)145349ab747fSPaolo Bonzini static void sb16_class_initfn (ObjectClass *klass, void *data)
145449ab747fSPaolo Bonzini {
145549ab747fSPaolo Bonzini DeviceClass *dc = DEVICE_CLASS (klass);
1456db895a1eSAndreas Färber
1457db895a1eSAndreas Färber dc->realize = sb16_realizefn;
1458125ee0edSMarcel Apfelbaum set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
145949ab747fSPaolo Bonzini dc->desc = "Creative Sound Blaster 16";
146049ab747fSPaolo Bonzini dc->vmsd = &vmstate_sb16;
14614f67d30bSMarc-André Lureau device_class_set_props(dc, sb16_properties);
146249ab747fSPaolo Bonzini }
146349ab747fSPaolo Bonzini
146449ab747fSPaolo Bonzini static const TypeInfo sb16_info = {
1465399f05a6SAndreas Färber .name = TYPE_SB16,
146649ab747fSPaolo Bonzini .parent = TYPE_ISA_DEVICE,
146749ab747fSPaolo Bonzini .instance_size = sizeof (SB16State),
1468db895a1eSAndreas Färber .instance_init = sb16_initfn,
146949ab747fSPaolo Bonzini .class_init = sb16_class_initfn,
147049ab747fSPaolo Bonzini };
147149ab747fSPaolo Bonzini
sb16_register_types(void)147249ab747fSPaolo Bonzini static void sb16_register_types (void)
147349ab747fSPaolo Bonzini {
147449ab747fSPaolo Bonzini type_register_static (&sb16_info);
14754b96159eSGerd Hoffmann deprecated_register_soundhw("sb16", "Creative Sound Blaster 16",
14764b96159eSGerd Hoffmann 1, TYPE_SB16);
147749ab747fSPaolo Bonzini }
147849ab747fSPaolo Bonzini
147949ab747fSPaolo Bonzini type_init (sb16_register_types)
1480