1 /* 2 * QEMU Apple Sound Chip emulation 3 * 4 * Apple Sound Chip (ASC) 344S0063 5 * Enhanced Apple Sound Chip (EASC) 343S1063 6 * 7 * Copyright (c) 2012-2018 Laurent Vivier <laurent@vivier.eu> 8 * Copyright (c) 2022 Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> 9 * 10 * SPDX-License-Identifier: GPL-2.0-or-later 11 */ 12 13 #include "qemu/osdep.h" 14 #include "qemu/timer.h" 15 #include "hw/sysbus.h" 16 #include "hw/irq.h" 17 #include "audio/audio.h" 18 #include "hw/audio/asc.h" 19 #include "hw/qdev-properties.h" 20 #include "migration/vmstate.h" 21 #include "trace.h" 22 23 /* 24 * Linux doesn't provide information about ASC, see arch/m68k/mac/macboing.c 25 * and arch/m68k/include/asm/mac_asc.h 26 * 27 * best information is coming from MAME: 28 * https://github.com/mamedev/mame/blob/master/src/devices/sound/asc.h 29 * https://github.com/mamedev/mame/blob/master/src/devices/sound/asc.cpp 30 * Emulation by R. Belmont 31 * or MESS: 32 * http://mess.redump.net/mess/driver_info/easc 33 * 34 * 0x800: VERSION 35 * 0x801: MODE 36 * 1=FIFO mode, 37 * 2=wavetable mode 38 * 0x802: CONTROL 39 * bit 0=analog or PWM output, 40 * 1=stereo/mono, 41 * 7=processing time exceeded 42 * 0x803: FIFO MODE 43 * bit 7=clear FIFO, 44 * bit 1="non-ROM companding", 45 * bit 0="ROM companding") 46 * 0x804: FIFO IRQ STATUS 47 * bit 0=ch A 1/2 full, 48 * 1=ch A full, 49 * 2=ch B 1/2 full, 50 * 3=ch B full) 51 * 0x805: WAVETABLE CONTROL 52 * bits 0-3 wavetables 0-3 start 53 * 0x806: VOLUME 54 * bits 2-4 = 3 bit internal ASC volume, 55 * bits 5-7 = volume control sent to Sony sound chip 56 * 0x807: CLOCK RATE 57 * 0 = Mac 22257 Hz, 58 * 1 = undefined, 59 * 2 = 22050 Hz, 60 * 3 = 44100 Hz 61 * 0x80a: PLAY REC A 62 * 0x80f: TEST 63 * bits 6-7 = digital test, 64 * bits 4-5 = analog test 65 * 0x810: WAVETABLE 0 PHASE 66 * big-endian 9.15 fixed-point, only 24 bits valid 67 * 0x814: WAVETABLE 0 INCREMENT 68 * big-endian 9.15 fixed-point, only 24 bits valid 69 * 0x818: WAVETABLE 1 PHASE 70 * 0x81C: WAVETABLE 1 INCREMENT 71 * 0x820: WAVETABLE 2 PHASE 72 * 0x824: WAVETABLE 2 INCREMENT 73 * 0x828: WAVETABLE 3 PHASE 74 * 0x82C: WAVETABLE 3 INCREMENT 75 * 0x830: UNKNOWN START 76 * NetBSD writes Wavetable data here (are there more 77 * wavetables/channels than we know about?) 78 * 0x857: UNKNOWN END 79 */ 80 81 #define ASC_SIZE 0x2000 82 83 enum { 84 ASC_VERSION = 0x00, 85 ASC_MODE = 0x01, 86 ASC_CONTROL = 0x02, 87 ASC_FIFOMODE = 0x03, 88 ASC_FIFOIRQ = 0x04, 89 ASC_WAVECTRL = 0x05, 90 ASC_VOLUME = 0x06, 91 ASC_CLOCK = 0x07, 92 ASC_PLAYRECA = 0x0a, 93 ASC_TEST = 0x0f, 94 ASC_WAVETABLE = 0x10 95 }; 96 97 #define ASC_FIFO_STATUS_HALF_FULL 1 98 #define ASC_FIFO_STATUS_FULL_EMPTY 2 99 100 #define ASC_EXTREGS_FIFOCTRL 0x8 101 #define ASC_EXTREGS_INTCTRL 0x9 102 #define ASC_EXTREGS_CDXA_DECOMP_FILT 0x10 103 104 #define ASC_FIFO_CYCLE_TIME ((NANOSECONDS_PER_SECOND / ASC_FREQ) * \ 105 0x400) 106 107 static void asc_raise_irq(ASCState *s) 108 { 109 qemu_set_irq(s->irq, 1); 110 } 111 112 static void asc_lower_irq(ASCState *s) 113 { 114 qemu_set_irq(s->irq, 0); 115 } 116 117 static uint8_t asc_fifo_get(ASCFIFOState *fs) 118 { 119 ASCState *s = container_of(fs, ASCState, fifos[fs->index]); 120 bool fifo_half_irq_enabled = fs->extregs[ASC_EXTREGS_INTCTRL] & 1; 121 uint8_t val; 122 123 assert(fs->cnt); 124 125 val = fs->fifo[fs->rptr]; 126 trace_asc_fifo_get('A' + fs->index, fs->rptr, fs->cnt, val); 127 128 fs->rptr++; 129 fs->rptr &= 0x3ff; 130 fs->cnt--; 131 132 if (fs->cnt <= 0x1ff) { 133 /* FIFO less than half full */ 134 fs->int_status |= ASC_FIFO_STATUS_HALF_FULL; 135 } else { 136 /* FIFO more than half full */ 137 fs->int_status &= ~ASC_FIFO_STATUS_HALF_FULL; 138 } 139 140 if (fs->cnt == 0x1ff && fifo_half_irq_enabled) { 141 /* Raise FIFO half full IRQ */ 142 asc_raise_irq(s); 143 } 144 145 if (fs->cnt == 0) { 146 /* Raise FIFO empty IRQ */ 147 fs->int_status |= ASC_FIFO_STATUS_FULL_EMPTY; 148 asc_raise_irq(s); 149 } 150 151 return val; 152 } 153 154 static int generate_fifo(ASCState *s, int maxsamples) 155 { 156 int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 157 uint8_t *buf = s->mixbuf; 158 int i, wcount = 0; 159 160 while (wcount < maxsamples) { 161 uint8_t val; 162 int16_t d, f0, f1; 163 int32_t t; 164 int shift, filter; 165 bool hasdata = false; 166 167 for (i = 0; i < 2; i++) { 168 ASCFIFOState *fs = &s->fifos[i]; 169 170 switch (fs->extregs[ASC_EXTREGS_FIFOCTRL] & 0x83) { 171 case 0x82: 172 /* 173 * CD-XA BRR mode: decompress 15 bytes into 28 16-bit 174 * samples 175 */ 176 if (!fs->cnt) { 177 val = 0x80; 178 break; 179 } 180 181 if (fs->xa_cnt == -1) { 182 /* Start of packet, get flags */ 183 fs->xa_flags = asc_fifo_get(fs); 184 fs->xa_cnt = 0; 185 } 186 187 shift = fs->xa_flags & 0xf; 188 filter = fs->xa_flags >> 4; 189 f0 = (int8_t)fs->extregs[ASC_EXTREGS_CDXA_DECOMP_FILT + 190 (filter << 1) + 1]; 191 f1 = (int8_t)fs->extregs[ASC_EXTREGS_CDXA_DECOMP_FILT + 192 (filter << 1)]; 193 194 if ((fs->xa_cnt & 1) == 0) { 195 if (!fs->cnt) { 196 val = 0x80; 197 break; 198 } 199 200 fs->xa_val = asc_fifo_get(fs); 201 d = (fs->xa_val & 0xf) << 12; 202 } else { 203 d = (fs->xa_val & 0xf0) << 8; 204 } 205 t = (d >> shift) + (((fs->xa_last[0] * f0) + 206 (fs->xa_last[1] * f1) + 32) >> 6); 207 if (t < -32768) { 208 t = -32768; 209 } else if (t > 32767) { 210 t = 32767; 211 } 212 213 /* 214 * CD-XA BRR generates 16-bit signed output, so convert to 215 * 8-bit before writing to buffer. Does real hardware do the 216 * same? 217 */ 218 val = (uint8_t)(t / 256) ^ 0x80; 219 hasdata = true; 220 fs->xa_cnt++; 221 222 fs->xa_last[1] = fs->xa_last[0]; 223 fs->xa_last[0] = (int16_t)t; 224 225 if (fs->xa_cnt == 28) { 226 /* End of packet */ 227 fs->xa_cnt = -1; 228 } 229 break; 230 231 default: 232 /* fallthrough */ 233 case 0x80: 234 /* Raw mode */ 235 if (fs->cnt) { 236 val = asc_fifo_get(fs); 237 hasdata = true; 238 } else { 239 val = 0x80; 240 } 241 break; 242 } 243 244 buf[wcount * 2 + i] = val; 245 } 246 247 if (!hasdata) { 248 break; 249 } 250 251 wcount++; 252 } 253 254 /* 255 * MacOS (un)helpfully leaves the FIFO engine running even when it has 256 * finished writing out samples, but still expects the FIFO empty 257 * interrupts to be generated for each FIFO cycle (without these interrupts 258 * MacOS will freeze) 259 */ 260 if (s->fifos[0].cnt == 0 && s->fifos[1].cnt == 0) { 261 if (!s->fifo_empty_ns) { 262 /* FIFO has completed first empty cycle */ 263 s->fifo_empty_ns = now; 264 } else if (now > (s->fifo_empty_ns + ASC_FIFO_CYCLE_TIME)) { 265 /* FIFO has completed entire cycle with no data */ 266 s->fifos[0].int_status |= ASC_FIFO_STATUS_HALF_FULL | 267 ASC_FIFO_STATUS_FULL_EMPTY; 268 s->fifos[1].int_status |= ASC_FIFO_STATUS_HALF_FULL | 269 ASC_FIFO_STATUS_FULL_EMPTY; 270 s->fifo_empty_ns = now; 271 asc_raise_irq(s); 272 } 273 } else { 274 /* FIFO contains data, reset empty time */ 275 s->fifo_empty_ns = 0; 276 } 277 278 return wcount; 279 } 280 281 static int generate_wavetable(ASCState *s, int maxsamples) 282 { 283 uint8_t *buf = s->mixbuf; 284 int channel, count = 0; 285 286 while (count < maxsamples) { 287 uint32_t left = 0, right = 0; 288 uint8_t sample; 289 290 for (channel = 0; channel < 4; channel++) { 291 ASCFIFOState *fs = &s->fifos[channel >> 1]; 292 int chanreg = ASC_WAVETABLE + (channel << 3); 293 uint32_t phase, incr, offset; 294 295 phase = ldl_be_p(&s->regs[chanreg]); 296 incr = ldl_be_p(&s->regs[chanreg + sizeof(uint32_t)]); 297 298 phase += incr; 299 offset = (phase >> 15) & 0x1ff; 300 sample = fs->fifo[0x200 * (channel >> 1) + offset]; 301 302 stl_be_p(&s->regs[chanreg], phase); 303 304 left += sample; 305 right += sample; 306 } 307 308 buf[count * 2] = left >> 2; 309 buf[count * 2 + 1] = right >> 2; 310 311 count++; 312 } 313 314 return count; 315 } 316 317 static void asc_out_cb(void *opaque, int free_b) 318 { 319 ASCState *s = opaque; 320 int samples, generated; 321 322 if (free_b == 0) { 323 return; 324 } 325 326 samples = MIN(s->samples, free_b >> s->shift); 327 328 switch (s->regs[ASC_MODE] & 3) { 329 default: 330 /* Off */ 331 generated = 0; 332 break; 333 case 1: 334 /* FIFO mode */ 335 generated = generate_fifo(s, samples); 336 break; 337 case 2: 338 /* Wave table mode */ 339 generated = generate_wavetable(s, samples); 340 break; 341 } 342 343 if (!generated) { 344 /* Workaround for audio underflow bug on Windows dsound backend */ 345 int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 346 int silent_samples = muldiv64(now - s->fifo_empty_ns, 347 NANOSECONDS_PER_SECOND, ASC_FREQ); 348 349 if (silent_samples > ASC_FIFO_CYCLE_TIME / 2) { 350 /* 351 * No new FIFO data within half a cycle time (~23ms) so fill the 352 * entire available buffer with silence. This prevents an issue 353 * with the Windows dsound backend whereby the sound appears to 354 * loop because the FIFO has run out of data, and the driver 355 * reuses the stale content in its circular audio buffer. 356 */ 357 AUD_write(s->voice, s->silentbuf, samples << s->shift); 358 } 359 return; 360 } 361 362 AUD_write(s->voice, s->mixbuf, generated << s->shift); 363 } 364 365 static uint64_t asc_fifo_read(void *opaque, hwaddr addr, 366 unsigned size) 367 { 368 ASCFIFOState *fs = opaque; 369 370 trace_asc_read_fifo('A' + fs->index, addr, size, fs->fifo[addr]); 371 return fs->fifo[addr]; 372 } 373 374 static void asc_fifo_write(void *opaque, hwaddr addr, uint64_t value, 375 unsigned size) 376 { 377 ASCFIFOState *fs = opaque; 378 ASCState *s = container_of(fs, ASCState, fifos[fs->index]); 379 bool fifo_half_irq_enabled = fs->extregs[ASC_EXTREGS_INTCTRL] & 1; 380 381 trace_asc_write_fifo('A' + fs->index, addr, size, fs->wptr, fs->cnt, value); 382 383 if (s->regs[ASC_MODE] == 1) { 384 fs->fifo[fs->wptr++] = value; 385 fs->wptr &= 0x3ff; 386 fs->cnt++; 387 388 if (fs->cnt <= 0x1ff) { 389 /* FIFO less than half full */ 390 fs->int_status |= ASC_FIFO_STATUS_HALF_FULL; 391 } else { 392 /* FIFO at least half full */ 393 fs->int_status &= ~ASC_FIFO_STATUS_HALF_FULL; 394 } 395 396 if (fs->cnt == 0x200 && fifo_half_irq_enabled) { 397 /* Raise FIFO half full interrupt */ 398 asc_raise_irq(s); 399 } 400 401 if (fs->cnt == 0x3ff) { 402 /* Raise FIFO full interrupt */ 403 fs->int_status |= ASC_FIFO_STATUS_FULL_EMPTY; 404 asc_raise_irq(s); 405 } 406 } else { 407 fs->fifo[addr] = value; 408 } 409 return; 410 } 411 412 static const MemoryRegionOps asc_fifo_ops = { 413 .read = asc_fifo_read, 414 .write = asc_fifo_write, 415 .impl = { 416 .min_access_size = 1, 417 .max_access_size = 1, 418 }, 419 .endianness = DEVICE_BIG_ENDIAN, 420 }; 421 422 static void asc_fifo_reset(ASCFIFOState *fs); 423 424 static uint64_t asc_read(void *opaque, hwaddr addr, 425 unsigned size) 426 { 427 ASCState *s = opaque; 428 uint64_t prev, value; 429 430 switch (addr) { 431 case ASC_VERSION: 432 switch (s->type) { 433 default: 434 case ASC_TYPE_ASC: 435 value = 0; 436 break; 437 case ASC_TYPE_EASC: 438 value = 0xb0; 439 break; 440 } 441 break; 442 case ASC_FIFOIRQ: 443 prev = (s->fifos[0].int_status & 0x3) | 444 (s->fifos[1].int_status & 0x3) << 2; 445 446 s->fifos[0].int_status = 0; 447 s->fifos[1].int_status = 0; 448 asc_lower_irq(s); 449 value = prev; 450 break; 451 default: 452 value = s->regs[addr]; 453 break; 454 } 455 456 trace_asc_read_reg(addr, size, value); 457 return value; 458 } 459 460 static void asc_write(void *opaque, hwaddr addr, uint64_t value, 461 unsigned size) 462 { 463 ASCState *s = opaque; 464 465 switch (addr) { 466 case ASC_MODE: 467 value &= 3; 468 if (value != s->regs[ASC_MODE]) { 469 asc_fifo_reset(&s->fifos[0]); 470 asc_fifo_reset(&s->fifos[1]); 471 asc_lower_irq(s); 472 if (value != 0) { 473 AUD_set_active_out(s->voice, 1); 474 } else { 475 AUD_set_active_out(s->voice, 0); 476 } 477 } 478 break; 479 case ASC_FIFOMODE: 480 if (value & 0x80) { 481 asc_fifo_reset(&s->fifos[0]); 482 asc_fifo_reset(&s->fifos[1]); 483 asc_lower_irq(s); 484 } 485 break; 486 case ASC_WAVECTRL: 487 break; 488 case ASC_VOLUME: 489 { 490 int vol = (value & 0xe0); 491 492 AUD_set_volume_out(s->voice, 0, vol, vol); 493 break; 494 } 495 } 496 497 trace_asc_write_reg(addr, size, value); 498 s->regs[addr] = value; 499 } 500 501 static const MemoryRegionOps asc_regs_ops = { 502 .read = asc_read, 503 .write = asc_write, 504 .endianness = DEVICE_BIG_ENDIAN, 505 .impl = { 506 .min_access_size = 1, 507 .max_access_size = 1, 508 } 509 }; 510 511 static uint64_t asc_ext_read(void *opaque, hwaddr addr, 512 unsigned size) 513 { 514 ASCFIFOState *fs = opaque; 515 uint64_t value; 516 517 value = fs->extregs[addr]; 518 519 trace_asc_read_extreg('A' + fs->index, addr, size, value); 520 return value; 521 } 522 523 static void asc_ext_write(void *opaque, hwaddr addr, uint64_t value, 524 unsigned size) 525 { 526 ASCFIFOState *fs = opaque; 527 528 trace_asc_write_extreg('A' + fs->index, addr, size, value); 529 530 fs->extregs[addr] = value; 531 } 532 533 static const MemoryRegionOps asc_extregs_ops = { 534 .read = asc_ext_read, 535 .write = asc_ext_write, 536 .impl = { 537 .min_access_size = 1, 538 .max_access_size = 1, 539 }, 540 .endianness = DEVICE_BIG_ENDIAN, 541 }; 542 543 static int asc_post_load(void *opaque, int version) 544 { 545 ASCState *s = ASC(opaque); 546 547 if (s->regs[ASC_MODE] != 0) { 548 AUD_set_active_out(s->voice, 1); 549 } 550 551 return 0; 552 } 553 554 static const VMStateDescription vmstate_asc_fifo = { 555 .name = "apple-sound-chip.fifo", 556 .version_id = 0, 557 .minimum_version_id = 0, 558 .fields = (VMStateField[]) { 559 VMSTATE_UINT8_ARRAY(fifo, ASCFIFOState, ASC_FIFO_SIZE), 560 VMSTATE_UINT8(int_status, ASCFIFOState), 561 VMSTATE_INT32(cnt, ASCFIFOState), 562 VMSTATE_INT32(wptr, ASCFIFOState), 563 VMSTATE_INT32(rptr, ASCFIFOState), 564 VMSTATE_UINT8_ARRAY(extregs, ASCFIFOState, ASC_EXTREG_SIZE), 565 VMSTATE_INT32(xa_cnt, ASCFIFOState), 566 VMSTATE_UINT8(xa_val, ASCFIFOState), 567 VMSTATE_UINT8(xa_flags, ASCFIFOState), 568 VMSTATE_INT16_ARRAY(xa_last, ASCFIFOState, 2), 569 VMSTATE_END_OF_LIST() 570 } 571 }; 572 573 static const VMStateDescription vmstate_asc = { 574 .name = "apple-sound-chip", 575 .version_id = 0, 576 .minimum_version_id = 0, 577 .post_load = asc_post_load, 578 .fields = (VMStateField[]) { 579 VMSTATE_STRUCT_ARRAY(fifos, ASCState, 2, 0, vmstate_asc_fifo, 580 ASCFIFOState), 581 VMSTATE_UINT8_ARRAY(regs, ASCState, ASC_REG_SIZE), 582 VMSTATE_INT64(fifo_empty_ns, ASCState), 583 VMSTATE_END_OF_LIST() 584 } 585 }; 586 587 static void asc_fifo_reset(ASCFIFOState *fs) 588 { 589 fs->wptr = 0; 590 fs->rptr = 0; 591 fs->cnt = 0; 592 fs->xa_cnt = -1; 593 fs->int_status = 0; 594 } 595 596 static void asc_fifo_init(ASCFIFOState *fs, int index) 597 { 598 ASCState *s = container_of(fs, ASCState, fifos[index]); 599 char *name; 600 601 fs->index = index; 602 name = g_strdup_printf("asc.fifo%c", 'A' + index); 603 memory_region_init_io(&fs->mem_fifo, OBJECT(s), &asc_fifo_ops, fs, 604 name, ASC_FIFO_SIZE); 605 g_free(name); 606 607 name = g_strdup_printf("asc.extregs%c", 'A' + index); 608 memory_region_init_io(&fs->mem_extregs, OBJECT(s), &asc_extregs_ops, 609 fs, name, ASC_EXTREG_SIZE); 610 g_free(name); 611 } 612 613 static void asc_reset_hold(Object *obj) 614 { 615 ASCState *s = ASC(obj); 616 617 AUD_set_active_out(s->voice, 0); 618 619 memset(s->regs, 0, sizeof(s->regs)); 620 asc_fifo_reset(&s->fifos[0]); 621 asc_fifo_reset(&s->fifos[1]); 622 s->fifo_empty_ns = 0; 623 624 if (s->type == ASC_TYPE_ASC) { 625 /* FIFO half full IRQs enabled by default */ 626 s->fifos[0].extregs[ASC_EXTREGS_INTCTRL] = 1; 627 s->fifos[1].extregs[ASC_EXTREGS_INTCTRL] = 1; 628 } 629 } 630 631 static void asc_unrealize(DeviceState *dev) 632 { 633 ASCState *s = ASC(dev); 634 635 g_free(s->mixbuf); 636 g_free(s->silentbuf); 637 638 AUD_remove_card(&s->card); 639 } 640 641 static void asc_realize(DeviceState *dev, Error **errp) 642 { 643 ASCState *s = ASC(dev); 644 struct audsettings as; 645 646 if (!AUD_register_card("Apple Sound Chip", &s->card, errp)) { 647 return; 648 } 649 650 as.freq = ASC_FREQ; 651 as.nchannels = 2; 652 as.fmt = AUDIO_FORMAT_U8; 653 as.endianness = AUDIO_HOST_ENDIANNESS; 654 655 s->voice = AUD_open_out(&s->card, s->voice, "asc.out", s, asc_out_cb, 656 &as); 657 s->shift = 1; 658 s->samples = AUD_get_buffer_size_out(s->voice) >> s->shift; 659 s->mixbuf = g_malloc0(s->samples << s->shift); 660 661 s->silentbuf = g_malloc0(s->samples << s->shift); 662 memset(s->silentbuf, 0x80, s->samples << s->shift); 663 664 /* Add easc registers if required */ 665 if (s->type == ASC_TYPE_EASC) { 666 memory_region_add_subregion(&s->asc, ASC_EXTREG_OFFSET, 667 &s->fifos[0].mem_extregs); 668 memory_region_add_subregion(&s->asc, 669 ASC_EXTREG_OFFSET + ASC_EXTREG_SIZE, 670 &s->fifos[1].mem_extregs); 671 } 672 } 673 674 static void asc_init(Object *obj) 675 { 676 ASCState *s = ASC(obj); 677 SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 678 679 memory_region_init(&s->asc, OBJECT(obj), "asc", ASC_SIZE); 680 681 asc_fifo_init(&s->fifos[0], 0); 682 asc_fifo_init(&s->fifos[1], 1); 683 684 memory_region_add_subregion(&s->asc, ASC_FIFO_OFFSET, 685 &s->fifos[0].mem_fifo); 686 memory_region_add_subregion(&s->asc, 687 ASC_FIFO_OFFSET + ASC_FIFO_SIZE, 688 &s->fifos[1].mem_fifo); 689 690 memory_region_init_io(&s->mem_regs, OBJECT(obj), &asc_regs_ops, s, 691 "asc.regs", ASC_REG_SIZE); 692 memory_region_add_subregion(&s->asc, ASC_REG_OFFSET, &s->mem_regs); 693 694 sysbus_init_irq(sbd, &s->irq); 695 sysbus_init_mmio(sbd, &s->asc); 696 } 697 698 static Property asc_properties[] = { 699 DEFINE_AUDIO_PROPERTIES(ASCState, card), 700 DEFINE_PROP_UINT8("asctype", ASCState, type, ASC_TYPE_ASC), 701 DEFINE_PROP_END_OF_LIST(), 702 }; 703 704 static void asc_class_init(ObjectClass *oc, void *data) 705 { 706 DeviceClass *dc = DEVICE_CLASS(oc); 707 ResettableClass *rc = RESETTABLE_CLASS(oc); 708 709 dc->realize = asc_realize; 710 dc->unrealize = asc_unrealize; 711 set_bit(DEVICE_CATEGORY_SOUND, dc->categories); 712 dc->vmsd = &vmstate_asc; 713 device_class_set_props(dc, asc_properties); 714 rc->phases.hold = asc_reset_hold; 715 } 716 717 static const TypeInfo asc_info_types[] = { 718 { 719 .name = TYPE_ASC, 720 .parent = TYPE_SYS_BUS_DEVICE, 721 .instance_size = sizeof(ASCState), 722 .instance_init = asc_init, 723 .class_init = asc_class_init, 724 }, 725 }; 726 727 DEFINE_TYPES(asc_info_types) 728