diff mbox series

[v4] Implement the Screamer sound chip for the mac99 machine type

Message ID 20200218012228.7336-1-programmingkidx@gmail.com
State New
Headers show
Series [v4] Implement the Screamer sound chip for the mac99 machine type | expand

Commit Message

Programmingkid Feb. 18, 2020, 1:22 a.m. UTC
This patch enables the playback of audio on a Mac OS 9 or Mac OS X guest.

Signed-off-by: John Arbuckle <programmingkidx@gmail.com>
---
v4 changes:
- Switched to using HWADDR_PRIx in several debug statements.

v3 changes:
- Updated the location of patched code in hw/ppc/kconfig.
- Removed setting the props variable in screamer.c.
- Removed the screamer_properties variable in screamer.c.

v2 changes:
- Fixed a bug that prevented the sampling rate from being changed.

 hw/audio/Kconfig              |   3 +
 hw/audio/Makefile.objs        |   2 +
 hw/audio/screamer.c           | 985 ++++++++++++++++++++++++++++++++++++++++++
 hw/misc/macio/macio.c         |  35 +-
 hw/ppc/Kconfig                |   1 +
 hw/ppc/mac.h                  |   5 +
 include/hw/audio/screamer.h   |  42 ++
 include/hw/misc/macio/macio.h |   2 +
 8 files changed, 1074 insertions(+), 1 deletion(-)
 create mode 100644 hw/audio/screamer.c
 create mode 100644 include/hw/audio/screamer.h

Comments

Howard Spoelstra Feb. 21, 2020, 9:13 a.m. UTC | #1
Hi,

It might be worth mentioning that any testing of your screamer
implementation with MacOS/OSX guests on the mac99 machine needs a
custom-built openbios.

Where possible I'll compare your screamer with the current screamer
implementation built from:
git clone -b screamer https://github.com/mcayland/qemu

All tests on OSX Sierra host with system sounds and MP3 playback through
latest QuickTime and iTunes available for the guest. Host is Intel i7-4770K
at 3.50Ghz. 32Gb memory. Audio device is an USB headset.
Overall very subjective impression is that sound problems seem to arise
quicker with strong changes in volume in the stream. Silence is produced
perfectly...
I should note that I also tested earlier with a windows build and that I
had to re-install Mac OS on three occasions to get sound going with your
screamer. Whether that was caused by a faulty installation or your screamer
is unclear to me.

There we go:

Mac OS 9.0.4: mac99,via=cuda
Apple audio extension often fails to load. (Not restricted to your
screamer. This is a longstanding issue.) See at bottom for OSX crash report.
Your screamer: shows only CD in Sound CP Input panel. Play sound through
output device is selected.
Current screamer: shows CD + External Mic. Play sound through output device
is selected.

Mac OS 9.1: mac99,via=cuda
Your screamer: No Input selection in the Sound CP.
Current screamer: Has External Mic (but no CD) in Sound CP. Play sound
through output device is not selected.

Mac OS 9.2: mac99,via=pmu
Your screamer: mp3 through iTunes and QuickTime OK. System sounds OK.
Current screamer: Has considerably more problems playing two streams
simultaneously. (mp3 through both QuickTime and iTunes.)

Mac OS X 10.0: mac99,via=cuda
Your screamer: setting the sound balance from middle position to the left
seems to control volume.
Current screamer: Serious number of drop-outs when playing MP3 through
QuickTime. Not when using iTunes. Has issues when moving the sound balance.

Mac OS X 10.1: mac99,via=cuda
Off-topic: Interestingly, when booting with via=pmu, the same error occurs
as reported above.
Your screamer: QuickTime: drop-outs. iTunes OK, even with playing system
sounds through the stream. Balance has same problem as above.
Current screamer: Serious drop-outs through both QuickTime and iTunes when
playing MP3. Balance sync gets completely lost after moving slider. More
lag in response to clicking system sounds.

Mac OSX 10.2: no test due to longstanding video issue with opening folders.

Mac OSX 10.3: mac99,via=pmu
Your screamer: drop-outs with QuickTime and iTunes. But not the clicks
heard as mentioned below. Opening the Sound preferences when playing MP3 is
OK. System sounds playing through the stream produce crackling sound.
systems sounds stop playing after several clicks on different ones. I hear
parts of earlier clicked sound when new one clicked.
Current screamer: intermittent clicks (0.5 seconds) when playing MP3 with
QuickTime and iTunes. But QuickTime much better compared to 10.1. Currently
playing mp3 gets completely distorted (doubled?) when opening Sound
preferences.

Mac OSX 10.4: mac99,via=pmu
Off-topic: From 10.4 onward, Internet radio works in iTunes. Channel update
is very slow in 10.4...
Your screamer: drop-outs with QuickTime. Sounds comparable to current
screamer. Opening Sound preferences is OK, but can make stream spiral out
of control with an echo. Seems to happen quicker when playing sound with
strong stereo effects. But always quickly recovers, unlike current
screamer. iTunes also produces drop-outs. Also with internet stream, but is
almost listenable.
Current screamer: drop-outs with QuickTime. Sounds like stream is not
always in correct order. Sound crackles. iTunes almost OK. I can hear one
or two clicks after stopping audio. Opening Sound preferences makes stream
spiral out of control with an echo.

Mac OSX 10.5: mac99,via=pmu
Your screamer: Drop-outs with QuickTime. A bit less-so with iTunes. Opening
Sound preferences provides same experience as with 10.4. Internet stream
almost listenable.
Current screamer: QuickTime produces drop-outs. Sound control panel spirals
out of control. Small audio parts still played when stopping QuickTime.
iTunes almost OK with MP3 playback, only small drop-outs. Same with
Internet radio.

For good measure I also tested 10.5 with your screamer and the recent
hardfloat patches which improve fpu performance from 9% to 11% of a real G4
1Ghz ;-)
I did not experience a considerable improvement in sound quality.

Best,
Howard

OSX host Crash report when audio extension fails:

Crashed Thread:        2

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x0000000000000008
Exception Note:        EXC_CORPSE_NOTIFY

Termination Signal:    Segmentation fault: 11
Termination Reason:    Namespace SIGNAL, Code 0xb
Terminating Process:   exc handler [0]

VM Regions Near 0x8:
-->
    __TEXT                 00000001087b0000-0000000108f7f000 [ 7996K]
r-x/rwx SM=COW  /Users/USER/*

Thread 2 Crashed:
0   qemu-system-ppc-screamer       0x0000000108c5b9ed timer_del + 13
1   qemu-system-ppc-screamer       0x000000010898bc1f audio_reset_timer +
319
2   qemu-system-ppc-screamer       0x00000001089807b6 vm_state_notify + 262
3   qemu-system-ppc-screamer       0x0000000108800b5f do_vm_stop + 47
4   qemu-system-ppc-screamer       0x0000000108985277 qemu_main + 16775
5   qemu-system-ppc-screamer       0x0000000108b46e86 call_qemu_main + 38
6   qemu-system-ppc-screamer       0x0000000108c6071e qemu_thread_start +
126
7   libsystem_pthread.dylib       0x00007fffb627093b _pthread_body + 180
8   libsystem_pthread.dylib       0x00007fffb6270887 _pthread_start + 286
9   libsystem_pthread.dylib       0x00007fffb627008d thread_start + 13
Programmingkid Feb. 21, 2020, 11:30 a.m. UTC | #2
> On Feb 21, 2020, at 4:13 AM, Howard Spoelstra <hsp.cat7@gmail.com> wrote:
> 
> Hi,
> 
> It might be worth mentioning that any testing of your screamer implementation with MacOS/OSX guests on the mac99 machine needs a custom-built openbios.
> 
> Where possible I'll compare your screamer with the current screamer implementation built from:
> git clone -b screamer https://github.com/mcayland/qemu 
> 
> All tests on OSX Sierra host with system sounds and MP3 playback through latest QuickTime and iTunes available for the guest. Host is Intel i7-4770K at 3.50Ghz. 32Gb memory. Audio device is an USB headset.
> Overall very subjective impression is that sound problems seem to arise quicker with strong changes in volume in the stream. Silence is produced perfectly...
> I should note that I also tested earlier with a windows build and that I had to re-install Mac OS on three occasions to get sound going with your screamer. Whether that was caused by a faulty installation or your screamer is unclear to me.
> 
> There we go:
> 
> Mac OS 9.0.4: mac99,via=cuda
> Apple audio extension often fails to load. (Not restricted to your screamer. This is a longstanding issue.) See at bottom for OSX crash report.
> Your screamer: shows only CD in Sound CP Input panel. Play sound through output device is selected.
> Current screamer: shows CD + External Mic. Play sound through output device is selected.
> 
> Mac OS 9.1: mac99,via=cuda
> Your screamer: No Input selection in the Sound CP. 
> Current screamer: Has External Mic (but no CD) in Sound CP. Play sound through output device is not selected.
> 
> Mac OS 9.2: mac99,via=pmu
> Your screamer: mp3 through iTunes and QuickTime OK. System sounds OK.
> Current screamer: Has considerably more problems playing two streams simultaneously. (mp3 through both QuickTime and iTunes.)
> 
> Mac OS X 10.0: mac99,via=cuda
> Your screamer: setting the sound balance from middle position to the left seems to control volume.
> Current screamer: Serious number of drop-outs when playing MP3 through QuickTime. Not when using iTunes. Has issues when moving the sound balance. 
> 
> Mac OS X 10.1: mac99,via=cuda
> Off-topic: Interestingly, when booting with via=pmu, the same error occurs as reported above.
> Your screamer: QuickTime: drop-outs. iTunes OK, even with playing system sounds through the stream. Balance has same problem as above.
> Current screamer: Serious drop-outs through both QuickTime and iTunes when playing MP3. Balance sync gets completely lost after moving slider. More lag in response to clicking system sounds.
> 
> Mac OSX 10.2: no test due to longstanding video issue with opening folders.
> 
> Mac OSX 10.3: mac99,via=pmu
> Your screamer: drop-outs with QuickTime and iTunes. But not the clicks heard as mentioned below. Opening the Sound preferences when playing MP3 is OK. System sounds playing through the stream produce crackling sound. systems sounds stop playing after several clicks on different ones. I hear parts of earlier clicked sound when new one clicked.
> Current screamer: intermittent clicks (0.5 seconds) when playing MP3 with QuickTime and iTunes. But QuickTime much better compared to 10.1. Currently playing mp3 gets completely distorted (doubled?) when opening Sound preferences.
> 
> Mac OSX 10.4: mac99,via=pmu
> Off-topic: From 10.4 onward, Internet radio works in iTunes. Channel update is very slow in 10.4...
> Your screamer: drop-outs with QuickTime. Sounds comparable to current screamer. Opening Sound preferences is OK, but can make stream spiral out of control with an echo. Seems to happen quicker when playing sound with strong stereo effects. But always quickly recovers, unlike current screamer. iTunes also produces drop-outs. Also with internet stream, but is almost listenable.
> Current screamer: drop-outs with QuickTime. Sounds like stream is not always in correct order. Sound crackles. iTunes almost OK. I can hear one or two clicks after stopping audio. Opening Sound preferences makes stream spiral out of control with an echo.
> 
> Mac OSX 10.5: mac99,via=pmu
> Your screamer: Drop-outs with QuickTime. A bit less-so with iTunes. Opening Sound preferences provides same experience as with 10.4. Internet stream almost listenable.
> Current screamer: QuickTime produces drop-outs. Sound control panel spirals out of control. Small audio parts still played when stopping QuickTime. iTunes almost OK with MP3 playback, only small drop-outs. Same with Internet radio. 
> 
> For good measure I also tested 10.5 with your screamer and the recent hardfloat patches which improve fpu performance from 9% to 11% of a real G4 1Ghz ;-)
> I did not experience a considerable improvement in sound quality.
> 
> Best,
> Howard
> 
> OSX host Crash report when audio extension fails:
> 
> Crashed Thread:        2
> 
> Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
> Exception Codes:       KERN_INVALID_ADDRESS at 0x0000000000000008
> Exception Note:        EXC_CORPSE_NOTIFY
> 
> Termination Signal:    Segmentation fault: 11
> Termination Reason:    Namespace SIGNAL, Code 0xb
> Terminating Process:   exc handler [0]
> 
> VM Regions Near 0x8:
> --> 
>     __TEXT                 00000001087b0000-0000000108f7f000 [ 7996K] r-x/rwx SM=COW  /Users/USER/*
> 
> Thread 2 Crashed:
> 0   qemu-system-ppc-screamer      	0x0000000108c5b9ed timer_del + 13
> 1   qemu-system-ppc-screamer      	0x000000010898bc1f audio_reset_timer + 319
> 2   qemu-system-ppc-screamer      	0x00000001089807b6 vm_state_notify + 262
> 3   qemu-system-ppc-screamer      	0x0000000108800b5f do_vm_stop + 47
> 4   qemu-system-ppc-screamer      	0x0000000108985277 qemu_main + 16775
> 5   qemu-system-ppc-screamer      	0x0000000108b46e86 call_qemu_main + 38
> 6   qemu-system-ppc-screamer      	0x0000000108c6071e qemu_thread_start + 126
> 7   libsystem_pthread.dylib       0x00007fffb627093b _pthread_body + 180
> 8   libsystem_pthread.dylib       0x00007fffb6270887 _pthread_start + 286
> 9   libsystem_pthread.dylib       0x00007fffb627008d thread_start + 13
> 

Thank you for such a detailed testing of my screamer patch. Your tests show it is an improvement over the older screamer implementation :)

I'm hoping the maintainer will review my patch soon. I haven't heard a thing from him since late December. I thinking either he does not have the time to review my patch or he does not want to see his patch be replaced with mine. 

For the binaries of QEMU you provide on emaculation.net, would you say it is time to replace the old screamer builds with this patch?
Howard Spoelstra Feb. 21, 2020, 12:09 p.m. UTC | #3
On Fri, Feb 21, 2020 at 12:30 PM Programmingkid <programmingkidx@gmail.com>
wrote:

>
> > On Feb 21, 2020, at 4:13 AM, Howard Spoelstra <hsp.cat7@gmail.com>
> wrote:
> >
> > Hi,
> >
> > It might be worth mentioning that any testing of your screamer
> implementation with MacOS/OSX guests on the mac99 machine needs a
> custom-built openbios.
> >
> > Where possible I'll compare your screamer with the current screamer
> implementation built from:
> > git clone -b screamer https://github.com/mcayland/qemu
> >
> > All tests on OSX Sierra host with system sounds and MP3 playback through
> latest QuickTime and iTunes available for the guest. Host is Intel i7-4770K
> at 3.50Ghz. 32Gb memory. Audio device is an USB headset.
> > Overall very subjective impression is that sound problems seem to arise
> quicker with strong changes in volume in the stream. Silence is produced
> perfectly...
> > I should note that I also tested earlier with a windows build and that I
> had to re-install Mac OS on three occasions to get sound going with your
> screamer. Whether that was caused by a faulty installation or your screamer
> is unclear to me.
> >
> > There we go:
> >
> > Mac OS 9.0.4: mac99,via=cuda
> > Apple audio extension often fails to load. (Not restricted to your
> screamer. This is a longstanding issue.) See at bottom for OSX crash report.
> > Your screamer: shows only CD in Sound CP Input panel. Play sound through
> output device is selected.
> > Current screamer: shows CD + External Mic. Play sound through output
> device is selected.
> >
> > Mac OS 9.1: mac99,via=cuda
> > Your screamer: No Input selection in the Sound CP.
> > Current screamer: Has External Mic (but no CD) in Sound CP. Play sound
> through output device is not selected.
> >
> > Mac OS 9.2: mac99,via=pmu
> > Your screamer: mp3 through iTunes and QuickTime OK. System sounds OK.
> > Current screamer: Has considerably more problems playing two streams
> simultaneously. (mp3 through both QuickTime and iTunes.)
> >
> > Mac OS X 10.0: mac99,via=cuda
> > Your screamer: setting the sound balance from middle position to the
> left seems to control volume.
> > Current screamer: Serious number of drop-outs when playing MP3 through
> QuickTime. Not when using iTunes. Has issues when moving the sound balance.
> >
> > Mac OS X 10.1: mac99,via=cuda
> > Off-topic: Interestingly, when booting with via=pmu, the same error
> occurs as reported above.
> > Your screamer: QuickTime: drop-outs. iTunes OK, even with playing system
> sounds through the stream. Balance has same problem as above.
> > Current screamer: Serious drop-outs through both QuickTime and iTunes
> when playing MP3. Balance sync gets completely lost after moving slider.
> More lag in response to clicking system sounds.
> >
> > Mac OSX 10.2: no test due to longstanding video issue with opening
> folders.
> >
> > Mac OSX 10.3: mac99,via=pmu
> > Your screamer: drop-outs with QuickTime and iTunes. But not the clicks
> heard as mentioned below. Opening the Sound preferences when playing MP3 is
> OK. System sounds playing through the stream produce crackling sound.
> systems sounds stop playing after several clicks on different ones. I hear
> parts of earlier clicked sound when new one clicked.
> > Current screamer: intermittent clicks (0.5 seconds) when playing MP3
> with QuickTime and iTunes. But QuickTime much better compared to 10.1.
> Currently playing mp3 gets completely distorted (doubled?) when opening
> Sound preferences.
> >
> > Mac OSX 10.4: mac99,via=pmu
> > Off-topic: From 10.4 onward, Internet radio works in iTunes. Channel
> update is very slow in 10.4...
> > Your screamer: drop-outs with QuickTime. Sounds comparable to current
> screamer. Opening Sound preferences is OK, but can make stream spiral out
> of control with an echo. Seems to happen quicker when playing sound with
> strong stereo effects. But always quickly recovers, unlike current
> screamer. iTunes also produces drop-outs. Also with internet stream, but is
> almost listenable.
> > Current screamer: drop-outs with QuickTime. Sounds like stream is not
> always in correct order. Sound crackles. iTunes almost OK. I can hear one
> or two clicks after stopping audio. Opening Sound preferences makes stream
> spiral out of control with an echo.
> >
> > Mac OSX 10.5: mac99,via=pmu
> > Your screamer: Drop-outs with QuickTime. A bit less-so with iTunes.
> Opening Sound preferences provides same experience as with 10.4. Internet
> stream almost listenable.
> > Current screamer: QuickTime produces drop-outs. Sound control panel
> spirals out of control. Small audio parts still played when stopping
> QuickTime. iTunes almost OK with MP3 playback, only small drop-outs. Same
> with Internet radio.
> >
> > For good measure I also tested 10.5 with your screamer and the recent
> hardfloat patches which improve fpu performance from 9% to 11% of a real G4
> 1Ghz ;-)
> > I did not experience a considerable improvement in sound quality.
> >
> > Best,
> > Howard
> >
> > OSX host Crash report when audio extension fails:
> >
> > Crashed Thread:        2
> >
> > Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
> > Exception Codes:       KERN_INVALID_ADDRESS at 0x0000000000000008
> > Exception Note:        EXC_CORPSE_NOTIFY
> >
> > Termination Signal:    Segmentation fault: 11
> > Termination Reason:    Namespace SIGNAL, Code 0xb
> > Terminating Process:   exc handler [0]
> >
> > VM Regions Near 0x8:
> > -->
> >     __TEXT                 00000001087b0000-0000000108f7f000 [ 7996K]
> r-x/rwx SM=COW  /Users/USER/*
> >
> > Thread 2 Crashed:
> > 0   qemu-system-ppc-screamer          0x0000000108c5b9ed timer_del + 13
> > 1   qemu-system-ppc-screamer          0x000000010898bc1f
> audio_reset_timer + 319
> > 2   qemu-system-ppc-screamer          0x00000001089807b6 vm_state_notify
> + 262
> > 3   qemu-system-ppc-screamer          0x0000000108800b5f do_vm_stop + 47
> > 4   qemu-system-ppc-screamer          0x0000000108985277 qemu_main +
> 16775
> > 5   qemu-system-ppc-screamer          0x0000000108b46e86 call_qemu_main
> + 38
> > 6   qemu-system-ppc-screamer          0x0000000108c6071e
> qemu_thread_start + 126
> > 7   libsystem_pthread.dylib       0x00007fffb627093b _pthread_body + 180
> > 8   libsystem_pthread.dylib       0x00007fffb6270887 _pthread_start + 286
> > 9   libsystem_pthread.dylib       0x00007fffb627008d thread_start + 13
> >
>
> Thank you for such a detailed testing of my screamer patch. Your tests
> show it is an improvement over the older screamer implementation :)
>
> I'm hoping the maintainer will review my patch soon. I haven't heard a
> thing from him since late December. I thinking either he does not have the
> time to review my patch or he does not want to see his patch be replaced
> with mine.
>
> For the binaries of QEMU you provide on emaculation.net, would you say it
> is time to replace the old screamer builds with this patch?
>

Hi,

The current screamer-enabled builds for OSX and Windows are on
www.emaculation.com ;-)
As you see from testing, there are reasons why the patches from Mark's
screamer branch are not in master yet, and these have not all been
addressed. There still needs to be testing on Linux and certainly on
Windows builds, and from what I mentioned above that might not be plain
sailing.
I guess I'll wait with providing new builds when the patches for both
openbios and qemu are reviewed and in some repo from which I can build
easily.

Best,
Howard
Howard Spoelstra Feb. 23, 2020, 2:17 p.m. UTC | #4
On Fri, Feb 21, 2020 at 1:09 PM Howard Spoelstra <hsp.cat7@gmail.com> wrote:

>
>
> The current screamer-enabled builds for OSX and Windows are on
> www.emaculation.com ;-)
> As you see from testing, there are reasons why the patches from Mark's
> screamer branch are not in master yet, and these have not all been
> addressed. There still needs to be testing on Linux and certainly on
> Windows builds, and from what I mentioned above that might not be plain
> sailing.
> I guess I'll wait with providing new builds when the patches for both
> openbios and qemu are reviewed and in some repo from which I can build
> easily.
>
> Best,
> Howard
>

Hi,

There is indeed an issue when building your code for Windows.
Whereas the current screamer from Mark just plays sound, a build with your
patches will not.
I need to Ctrl-Alt-G to exit grab, click on the command (cmd.exe) window in
which the Qemu textual output is showing and then grab the mouse again to
get sound. A simple grab exit/grab cycle is not enough, a click somewhere
outside the Qemu window will also not do. Only a click to activate the
command window and then grab again. Happens with both GTK and SDL GUIs.

Command line is:
qemu-system-ppc-master-screamer-john-v4.exe -L pc-bios -boot c -m 256 -M
mac99,via=pmu ^
-drive file=C:\mac-disks\9.2.img ^
-sdl -serial stdio ^
-bios openbios-qemu-screamer-john-v2.elf

Took me ages to find this regularity ;-)

Best,
Howard
Programmingkid Feb. 24, 2020, 2:33 a.m. UTC | #5
> On Feb 23, 2020, at 9:17 AM, Howard Spoelstra <hsp.cat7@gmail.com> wrote:
> 
> 
> 
> On Fri, Feb 21, 2020 at 1:09 PM Howard Spoelstra <hsp.cat7@gmail.com> wrote:
> 
> 
> The current screamer-enabled builds for OSX and Windows are on www.emaculation.com ;-)
> As you see from testing, there are reasons why the patches from Mark's screamer branch are not in master yet, and these have not all been addressed. There still needs to be testing on Linux and certainly on Windows builds, and from what I mentioned above that might not be plain sailing. 
> I guess I'll wait with providing new builds when the patches for both openbios and qemu are reviewed and in some repo from which I can build easily.
> 
> Best,
> Howard
> 
> Hi,
> 
> There is indeed an issue when building your code for Windows.
> Whereas the current screamer from Mark just plays sound, a build with your patches will not.
> I need to Ctrl-Alt-G to exit grab, click on the command (cmd.exe) window in which the Qemu textual output is showing and then grab the mouse again to get sound. A simple grab exit/grab cycle is not enough, a click somewhere outside the Qemu window will also not do. Only a click to activate the command window and then grab again. Happens with both GTK and SDL GUIs.
> 
> Command line is:
> qemu-system-ppc-master-screamer-john-v4.exe -L pc-bios -boot c -m 256 -M mac99,via=pmu ^
> -drive file=C:\mac-disks\9.2.img ^
> -sdl -serial stdio ^
> -bios openbios-qemu-screamer-john-v2.elf
> 
> Took me ages to find this regularity ;-)
> 
> Best,
> Howard

Thank you very much for catching this problem. Could you send me your Windows binary of QEMU that uses my patch. I would like to see if I can reproduce the problem on my computer.
diff mbox series

Patch

diff --git a/hw/audio/Kconfig b/hw/audio/Kconfig
index e9c6fed826..196da6c3fe 100644
--- a/hw/audio/Kconfig
+++ b/hw/audio/Kconfig
@@ -50,3 +50,6 @@  config CS4231
 
 config MARVELL_88W8618
     bool
+
+config SCREAMER
+    bool
diff --git a/hw/audio/Makefile.objs b/hw/audio/Makefile.objs
index 63db383709..55906886bc 100644
--- a/hw/audio/Makefile.objs
+++ b/hw/audio/Makefile.objs
@@ -15,4 +15,6 @@  common-obj-$(CONFIG_CS4231) += cs4231.o
 common-obj-$(CONFIG_MARVELL_88W8618) += marvell_88w8618.o
 common-obj-$(CONFIG_MILKYMIST) += milkymist-ac97.o
 
+common-obj-$(CONFIG_SCREAMER) += screamer.o
+
 common-obj-y += soundhw.o
diff --git a/hw/audio/screamer.c b/hw/audio/screamer.c
new file mode 100644
index 0000000000..7de17fe8a6
--- /dev/null
+++ b/hw/audio/screamer.c
@@ -0,0 +1,985 @@ 
+/*
+ * File: Screamer.c
+ * Description: Implement the Screamer sound chip used in Apple Macintoshes.
+ * It works by filling a buffer, then playing the buffer.
+ */
+
+#include "qemu/osdep.h"
+#include "audio/audio.h"
+#include "hw/hw.h"
+#include "hw/irq.h"
+#include <inttypes.h>
+#include "hw/ppc/mac.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "include/hw/audio/screamer.h"
+
+#define DEBUG_SCREAMER 0
+#define DPRINTF(fmt, ...) \
+do { if (DEBUG_SCREAMER) { printf(fmt , ## __VA_ARGS__); } } while (0)
+
+#define SOUND_CONTROL_REG  0
+#define CODEC_CONTROL_REG  1
+#define CODEC_STATUS_REG   2
+#define CLIP_COUNT_REG     3
+#define BYTE_SWAP_REG      4
+#define FRAME_COUNT_REG    5
+
+#define AWACS_BUSY         0x01000000
+
+/* Used with AWACS register 1 */
+#define RECALIBRATE         0x004
+#define LOOPTHRU            0x040
+#define SPEAKER_MUTE        0x080
+#define HEADPHONE_MUTE      0x200
+#define OUTPUT_ZERO         0x400
+#define OUTPUT_ONE          0x800
+#define PARALLEL_OUTPUT     0xc00
+
+/* Function prototypes */
+static uint32_t set_busy_bit(uint32_t value, int bit);
+static uint32_t set_part_ready_bit(uint32_t value, int bit_value);
+static uint32_t set_revision(uint32_t input_value);
+static uint32_t set_manufacturer(uint32_t input_value);
+static int get_sampling_rate(ScreamerState *s);
+static uint32_t get_frame_count_reg(ScreamerState *s);
+static void add_to_speaker_buffer(DBDMA_io *io);
+static void dma_request(DBDMA_io *io);
+
+
+/**************************** Getters *************************/
+
+/* Returns the codec control register's encoded AWACS address */
+static uint8_t get_codec_control_address(uint32_t value)
+{
+    uint8_t return_value;
+    return_value = (value >> 12) & 0x00000fff;
+    return return_value;
+}
+
+
+static uint32_t get_sound_control_reg(ScreamerState *s)
+{
+    DPRINTF("%s() called - returned 0x%x\n", __func__, s->sound_control);
+    return s->sound_control;
+}
+
+/* The AWACS registers are accessed thru this register */
+static uint32_t get_codec_control_reg(ScreamerState *s)
+{
+    int awacs_register = get_codec_control_address(s->codec_control);
+    uint32_t return_value = s->awacs[awacs_register];
+    return_value = set_busy_bit(return_value, 0); /* Tell CPU we are ready */
+    DPRINTF("%s() called - returned 0x%x\tAWACS register: %d\n", __func__,
+            return_value, awacs_register);
+    return return_value;
+}
+
+/*
+ * Determines if the readback bit is set.
+ * It is used by the Codec Control register.
+ */
+static bool readback_enabled(ScreamerState *s)
+{
+/* Note: bit zero is the readback enabled bit */
+    if (s->awacs[7] & 1) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+static uint32_t get_codec_status_reg(ScreamerState *s)
+{
+    uint32_t return_value;
+
+    /* if in readback mode - return AWACS register value */
+    if (readback_enabled(s)) {
+        int awacs_register = (s->awacs[7] & 0xe) >> 1;
+        s->awacs[7] = s->awacs[7] & 0xfffffffe; /* turn off readback mode */
+        return_value = s->awacs[awacs_register] << 4;
+        DPRINTF("readback enable bit is set, returning AWACS register %d\t"
+                "value:0x%x\n", awacs_register, return_value);
+
+        return return_value;
+    }
+
+    /* Tell CPU we are ready */
+    return_value = set_part_ready_bit(s->codec_status, 1);
+
+    /* Set Revision to Screamer */
+    return_value = set_revision(return_value);
+
+    /* Set the Manufacturer to Crystal */
+    return_value = set_manufacturer(return_value);
+    DPRINTF("%s() called - returned 0x%x\n", __func__, return_value);
+
+    return return_value;
+}
+
+static uint32_t get_clip_count_reg(ScreamerState *s)
+{
+    DPRINTF("%s() called - returned 0x%x\n", __func__, s->clip_count);
+    uint32_t return_value;
+    return_value = s->clip_count;
+    /* This is reset everytime it is read */
+    s->clip_count = 0;
+    return return_value;
+}
+
+static uint32_t get_byte_swap_reg(ScreamerState *s)
+{
+    DPRINTF("%s() called - returned 0x%x\n", __func__, s->byte_swap);
+    /*
+     * If all you hear is noise, it could be this register reporting the
+     * wrong value.
+     */
+    return s->byte_swap ? 0 : 1;
+}
+
+/*
+ * Returns the frame (sample) count
+ */
+static uint32_t get_frame_count_reg(ScreamerState *s)
+{
+    DPRINTF("%s() called - returned 0x%x\n", __func__, s->frame_count);
+    return s->frame_count;
+}
+
+static uint8_t get_left_vol(uint32_t value)
+{
+    return value & 0xf;
+}
+
+static uint8_t get_right_vol(uint32_t value)
+{
+    return value & 0x3c0 >> 6;
+}
+
+/*
+ * Returns the sampling rate.
+ * If the audio is playing back too fast or too slow, this function may be the
+ * cause.
+ */
+static int get_sampling_rate(ScreamerState *s)
+{
+    uint32_t screamer_rate = s->sound_control & 0x700;
+    int return_value;
+
+    /* All return values are in Hertz */
+    switch (screamer_rate) {
+    case 0x0:
+        return_value = 44100;
+        break;
+    case 0x100:
+        return_value = 29400;
+        break;
+    case 0x200:
+        return_value = 22050;
+        break;
+    case 0x300:
+        return_value = 17640;
+        break;
+    case 0x400:
+        return_value = 14700;
+        break;
+    case 0x500:
+        return_value = 11025;
+        break;
+    case 0x600:
+        return_value = 8820;
+        break;
+    case 0x700:
+        return_value = 7350;
+        break;
+    default:
+        DPRINTF("get_sampling_rate() unknown value: 0x%x\nDefaulting to"
+                " 44100 Hz.\n", screamer_rate);
+        return 44100;
+}
+    DPRINTF("%s() called - returning %dHz\n", __func__, return_value);
+    return return_value;
+}
+
+/**************************** End of getters *************************/
+
+/***************************** Speaker call back *************************/
+
+/* resets the play and buffer position markers */
+static void reset_markers(ScreamerState *s)
+{
+    s->spk_play_position = 0;
+    s->spk_buffer_position = 0;
+}
+
+
+/* Sends the samples to the host for playing */
+static void send_samples_to_host(ScreamerState *s, int max_samples)
+{
+    int write_length, requested_length;
+    requested_length = MIN(max_samples, (s->spk_buffer_position -
+                                         s->spk_play_position));
+    write_length = AUD_write(s->speaker_voice,
+                             &s->spk_buffer[s->spk_play_position],
+                             requested_length);
+    DPRINTF("requested length: %d\twrite length: %d\t",
+            requested_length, write_length);
+    s->spk_play_position += write_length;
+    DPRINTF("AUD_write %d/%d\n", s->spk_play_position, s->spk_buffer_position);
+    s->frame_count += write_length;
+}
+
+
+/*
+ * Called by QEMU's audio system to tell the output backend to send samples
+ * from the buffer to the host sound system.
+ * opaque: a pointer to the ScreamerState instance.
+ * max_samples: the number of samples that can be sent to the hardware buffer.
+ */
+static void speaker_callback(void *opaque, int max_samples)
+{
+    ScreamerState *s = (ScreamerState *) opaque;
+
+    /* if we have more samples to play */
+    if (s->spk_buffer_position > 0) {
+        if (s->spk_buffer_position > s->spk_play_position) {
+            DPRINTF("%s() called - max_samples: %d\n", __func__, max_samples);
+            send_samples_to_host(s, max_samples);
+        }
+        if (s->spk_play_position >= s->spk_buffer_position) {
+            DPRINTF("done playing buffer\n");
+            DPRINTF("pp: %d\tbp: %d\n", s->spk_play_position,
+                    s->spk_buffer_position);
+            if (s->spk_play_position > s->spk_buffer_position) {
+                DPRINTF("Error detected! - pp > bp\n\a");
+            }
+            reset_markers(s);
+            /* play postponed samples */
+            if (s->dma_io.len > 0) {
+                DPRINTF("playing postponed samples\n");
+                add_to_speaker_buffer(&s->dma_io);
+                return;
+            }
+        }
+    }
+}
+
+/************************* End of speaker call back *************************/
+
+
+/* Opens the speaker's voice */
+static void open_speaker_voice(ScreamerState *s)
+{
+    DPRINTF("%s() called\n", __func__);
+
+    /* if voice is already open return from function */
+    if (s->speaker_voice != NULL) {
+        DPRINTF("closing speaker voice\n");
+        AUD_close_out(&s->card, s->speaker_voice);
+        s->speaker_voice = NULL;
+    }
+    struct audsettings audio_settings;
+    audio_settings.freq = get_sampling_rate(s);  /* in hertz */
+    audio_settings.nchannels = 2;                /* stereo output */
+    audio_settings.fmt = AUDIO_FORMAT_S16;       /* signed 16 bit */
+    audio_settings.endianness = get_byte_swap_reg(s); /* endianness */
+    s->speaker_voice = AUD_open_out(&s->card, s->speaker_voice, SOUND_CHIP_NAME
+                                    " speaker", s, speaker_callback,
+                                    &audio_settings);
+    if (!s->speaker_voice) {
+        AUD_log(SOUND_CHIP_NAME, "Out voice could not be opened\n");
+    } else {
+        AUD_set_active_out(s->speaker_voice, true);
+    }
+}
+
+
+/******************************* Setters *************************************/
+
+
+/* Updates QEMU's audio backend settings */
+static void set_QEMU_audio_settings(ScreamerState *s)
+{
+    DPRINTF("%s() called\n", __func__);
+    open_speaker_voice(s);
+}
+
+
+/* Return value: 1 = muted  0 = not muted */
+static int is_muted(ScreamerState *s)
+{
+    int mute_state = s->awacs[1] & SPEAKER_MUTE ? 1 : 0;
+    if (s->awacs[1] & SPEAKER_MUTE) {
+        DPRINTF("speaker is muted\n");
+    } else {
+        DPRINTF("speaker is unmuted\n");
+    }
+
+    if (s->awacs[1] & HEADPHONE_MUTE) {
+        DPRINTF("headphone is muted\n");
+    } else {
+        DPRINTF("headphone is unmuted\n");
+    }
+    return mute_state;
+}
+
+
+/* Converts Screamer's volume system to QEMU's system */
+static int screamer_to_qemu_volume(int x)
+{
+    return -16 * x + 240;
+}
+
+
+/* Sets QEMU's volume. */
+static void set_volume(ScreamerState *s)
+{
+    int should_mute = is_muted(s);
+
+    /* Get Screamer volume values */
+    uint8_t left_vol = get_left_vol(s->awacs[4]);
+    uint8_t right_vol = get_right_vol(s->awacs[4]);
+    DPRINTF("set_volume() called - M:%d\tL:%d\tR:%d\n", should_mute, left_vol,
+            right_vol);
+
+    /* Convert Screamer to QEMU volume values */
+    left_vol = screamer_to_qemu_volume(left_vol);
+    right_vol = screamer_to_qemu_volume(right_vol);
+    DPRINTF("QEMU volume: L:%d\tR:%d\n", left_vol, right_vol);
+    AUD_set_volume_out(s->speaker_voice, should_mute, left_vol, right_vol);
+}
+
+
+/* Sets the sound control register */
+static void set_sound_control_reg(ScreamerState *s, uint32_t value)
+{
+    DPRINTF("set_sound_control_reg() called - value: 0x%x\n", value);
+    s->sound_control = value;
+    set_QEMU_audio_settings(s);
+}
+
+
+/* Used for input gain only - can be ignored for now. */
+static void set_awacs_0_reg(ScreamerState *s, uint32_t new_value)
+{
+    DPRINTF("Settings AWACS register 0 to 0x%x\n", s->awacs[0]);
+    s->awacs[0] = new_value;
+}
+
+
+static void set_awacs_1_reg(ScreamerState *s, uint32_t new_value)
+{
+    DPRINTF("Settings AWACS register 1 to 0x%x\n", new_value);
+
+    s->awacs[1] = new_value;
+
+    /* If recalibration requested */
+    if (new_value & RECALIBRATE) {
+        DPRINTF("Recalibration requested - unimplemented\n");
+        new_value = new_value ^ RECALIBRATE; /* Turn off recalibrate bit */
+    }
+
+    /* If loop thru set - what does this mean? */
+    if (new_value & LOOPTHRU) {
+        DPRINTF("Loopthru enabled - doing nothing\n");
+    }
+
+    /* Set headphone jack mute state */
+    if (new_value & HEADPHONE_MUTE) {
+        DPRINTF("Headphone muted\n");
+    }
+
+    else {
+        DPRINTF("Headphone unmuted\n");
+    }
+
+    if (new_value & SPEAKER_MUTE) {
+        DPRINTF("Speaker muted\n");
+    }
+
+    else {
+        DPRINTF("Speaker unmuted\n");
+    }
+
+    if (new_value & OUTPUT_ZERO) {
+        DPRINTF("output zero set - not sure what this means\n");
+    }
+
+    if (new_value & OUTPUT_ONE) {
+        DPRINTF("output one set - not sure what this means\n");
+    }
+
+    if (new_value & PARALLEL_OUTPUT) {
+        DPRINTF("parallel port enabled - but no parallel port here\n");
+    }
+
+    set_volume(s);
+}
+
+
+/* This is used for headphone volume - not needed */
+static void set_awacs_2_reg(ScreamerState *s, uint32_t new_value)
+{
+    DPRINTF("Settings AWACS register 2 to 0x%x\n"
+            "Ignoring change in headphone volume.\n", s->awacs[2]);
+    s->awacs[2] = new_value;
+}
+
+
+/* Unknown register purpose */
+static void set_awacs_3_reg(ScreamerState *s, uint32_t new_value)
+{
+    DPRINTF("Settings AWACS register 3 to 0x%x\n"
+            "This register has an unknown purpose and does not do anything\n",
+            s->awacs[3]);
+    s->awacs[3] = new_value;
+}
+
+
+/* Mostly deals with speaker volume */
+static void set_awacs_4_reg(ScreamerState *s, uint32_t new_value)
+{
+    DPRINTF("AWACS register 4 write: 0x%x\n", new_value);
+    s->awacs[4] = new_value;
+    set_volume(s);
+}
+
+
+/* This register is about loop thru stuff I don't understand */
+static void set_awacs_5_reg(ScreamerState *s, uint32_t new_value)
+{
+    DPRINTF("Settings AWACS register 5 to 0x%x\n"
+            "Loop thru update ignored.\n", s->awacs[5]);
+    s->awacs[5] = new_value;
+}
+
+
+/* Prints the states of the AWACS power register */
+static void print_power_reg_values(uint32_t value)
+{
+    if ((value & 0x3) == 0) {
+        printf("Screamer run state set\n");
+    }
+    if ((value & 0x3) == 1) {
+        printf("Screamer doze state set\n");
+    }
+    if ((value & 0x3) == 2) {
+        printf("Screamer idle state set\n");
+    }
+}
+
+
+/* Power Magement register */
+static void set_awacs_6_reg(ScreamerState *s, uint32_t new_value)
+{
+    DPRINTF("Settings AWACS register 6 to 0x%x\n"
+            "Power management update ignored.\n", s->awacs[6]);
+    if (DEBUG_SCREAMER) {
+        print_power_reg_values(new_value);
+    }
+    s->awacs[6] = new_value;
+}
+
+
+/* Read Back - repeating something that was sent to this chip? */
+static void set_awacs_7_reg(ScreamerState *s, uint32_t new_value)
+{
+    DPRINTF("Settings AWACS register 7 to 0x%x\n", new_value);
+    s->awacs[7] = new_value;
+}
+
+
+/* Sets the AWACs registers - a.k.a. shadow registers */
+static void set_awacs_register(ScreamerState *s, uint32_t value)
+{
+    int the_register = get_codec_control_address(value);
+
+    switch (the_register) {
+    case 0:
+        set_awacs_0_reg(s, value);
+        break;
+    case 1:
+        set_awacs_1_reg(s, value);
+        break;
+    case 2:
+        set_awacs_2_reg(s, value);
+        break;
+    case 3:
+        set_awacs_3_reg(s, value);
+        break;
+    case 4:
+        set_awacs_4_reg(s, value);
+        break;
+    case 5:
+        set_awacs_5_reg(s, value);
+        break;
+    case 6:
+        set_awacs_6_reg(s, value);
+        break;
+    case 7:
+        set_awacs_7_reg(s, value);
+        break;
+    default:
+        DPRINTF("Unhandled awacs registers %d\n", the_register);
+    }
+}
+
+
+/* Used to set the AWACS registers */
+static void set_codec_control_reg(ScreamerState *s, uint32_t value)
+{
+    DPRINTF("set_codec_control_reg() called - value: 0x%x\n", value);
+    s->codec_control = value;
+    set_awacs_register(s, value);
+}
+
+static void set_codec_status_reg(ScreamerState *s, uint32_t value)
+{
+    DPRINTF("set_codec_status_reg() called - value: 0x%x\n", value);
+    s->codec_status = value;
+}
+
+static void set_clip_count_reg(ScreamerState *s, uint32_t new_value)
+{
+    DPRINTF("set_clip_count_reg() called - value: 0x%x\n", new_value);
+    s->clip_count = new_value;
+}
+
+static void set_byte_swap_reg(ScreamerState *s, uint32_t value)
+{
+    DPRINTF("set_byte_swap_reg() called - value: 0x%x\n", value);
+    s->byte_swap = value;
+}
+
+static void set_frame_count_reg(ScreamerState *s, uint32_t new_value)
+{
+    DPRINTF("%s() called - value: 0x%x\n", __func__, new_value);
+    s->frame_count = new_value;
+}
+
+/*
+ * Sets the busy bit of codec control register.
+ * It is used to tell the CPU to wait.
+ * value: the codec control register's value
+ * bit_value: used to set or disable the busy bit
+ */
+static uint32_t set_busy_bit(uint32_t value, int bit_value)
+{
+    const int busy_bit = 0x01000000;
+    uint32_t return_value;
+    if (bit_value == 1)  /* Set this bit */
+        return_value = (value | busy_bit);
+    else /* bit_value == 0  Disable this bit */
+        return_value = (value & ~busy_bit);
+    return return_value;
+}
+
+
+/*
+ * Sets the part ready bit of the codec status register
+ * value: the codec status register's value
+ * bit_value: used to set or disable the part ready bit
+ */
+static uint32_t set_part_ready_bit(uint32_t value, int bit_value)
+{
+    const int part_ready_bit = 0x00400000;
+    uint32_t return_value;
+    if (bit_value == 1)  /* Set this bit */
+        return_value = (value | part_ready_bit);
+    else /* bit_value == 0  Disable this bit */
+        return_value = (value & ~part_ready_bit);
+    return return_value;
+}
+
+/* Sets bits 12 and 13 to 1 to indicate the Screamer revision */
+static uint32_t set_revision(uint32_t input_value)
+{
+    uint32_t return_value;
+    return_value = input_value | 0x3000;
+    return return_value;
+}
+
+/* Sets bit 8 to indicate Crystal as the manufacturer */
+static uint32_t set_manufacturer(uint32_t input_value)
+{
+    uint32_t return_value;
+    return_value = input_value | 0x100;
+    return return_value;
+}
+
+
+/************************** End of Setters *********************************/
+
+
+/*************************** DMA functions *********************************/
+
+/*
+ * Sends audio samples from a microphone or line-in to memory.
+ * Used for sound input.
+ * Currently only prevents a deadlock condition with Mac OS 9.
+ */
+static void screamer_to_dma(DBDMA_io *io)
+{
+    DPRINTF("%s() called\n", __func__);
+    ScreamerState *s = (ScreamerState *)io->opaque;
+    DBDMAState *dbs = s->dbdma;
+    DBDMA_channel *ch = &dbs->channels[0x12];
+    ch->regs[DBDMA_STATUS] |= DEAD;
+    ch->regs[DBDMA_STATUS] &= ~ACTIVE;
+    io->dma_end(io);
+    return;
+}
+
+
+static void print_dma_info(DBDMA_io *io)
+{
+    #define RUN        0x8000
+    #define PAUSE      0x4000
+    #define FLUSH      0x2000
+    #define WAKE       0x1000
+    #define DEAD       0x0800
+    #define ACTIVE     0x0400
+    #define BT         0x0100
+    #define DEVSTAT    0x00ff
+
+    /*
+     * RUN and PAUSE are bits under software control only.
+     * FLUSH and WAKE are set by SW and cleared by hardware.
+     * DEAD, ACTIVE and BT are only under hardware control.
+     */
+
+    DBDMA_channel *ch = io->channel;
+    printf("DMA FLAGS: ");
+
+    if (ch->regs[DBDMA_STATUS] & RUN) {
+        printf("RUN ");
+    }
+
+    if (ch->regs[DBDMA_STATUS] & ACTIVE) {
+        printf("ACTIVE ");
+    }
+
+    if (ch->regs[DBDMA_STATUS] & PAUSE) {
+        printf("PAUSE ");
+    }
+
+    if (ch->regs[DBDMA_STATUS] & DEAD) {
+        printf("DEAD ");
+    }
+
+    if (ch->regs[DBDMA_STATUS] & WAKE) {
+        printf("WAKE ");
+    }
+
+    if (ch->regs[DBDMA_STATUS] & BT) {
+        printf("BT ");
+    }
+
+    if (ch->regs[DBDMA_STATUS] & DEVSTAT) {
+        printf("DEVSTAT ");
+    }
+
+    if (ch->regs[DBDMA_STATUS] & FLUSH) {
+        printf("FLUSH ");
+    }
+
+    if (ch->io.processing == true) {
+        printf("processing  ");
+    }
+
+    printf("\n");
+}
+
+/* Tell the DMA controller we request more samples */
+static void dma_request(DBDMA_io *io)
+{
+    DPRINTF("%s() called\n", __func__);
+    if (DEBUG_SCREAMER) {
+        print_dma_info(io);
+    }
+    io->len = 0;
+    io->dma_end(io);
+}
+
+
+/* Adds sample data to the buffer */
+static void add_to_speaker_buffer(DBDMA_io *io)
+{
+    ScreamerState *s = (ScreamerState *) io->opaque;
+
+    if (s->spk_buffer_position + io->len > MAX_BUFFER_SIZE) {
+        /* postpone calling these samples until the buffer has been emptied */
+        memcpy(&s->dma_io, io, sizeof(DBDMA_io));
+        return;
+    }
+    dma_memory_read(&address_space_memory, io->addr,
+                    &s->spk_buffer[s->spk_buffer_position], io->len);
+    s->spk_buffer_position += io->len;
+    DPRINTF("%s() called - len: %d pos: %d/%d\n", __func__, io->len,
+            s->spk_buffer_position, MAX_BUFFER_SIZE);
+
+    dma_request(io);
+}
+
+/*
+ * Called by the DMA chip to transfer samples from memory to the
+ * Screamer chip.
+ * Used for sound output.
+ */
+static void dma_to_screamer(DBDMA_io *io)
+{
+    add_to_speaker_buffer(io);
+}
+
+
+/*
+ * This will flush the audio buffer of previous audio - eliminating previous
+ * audio playback.
+ */
+static void send_silence_to_speaker(ScreamerState *s)
+{
+    DPRINTF("Silencing audio buffer...\n");
+    int length = MAX_BUFFER_SIZE;
+    s->spk_buffer_position = length;
+    s->spk_play_position = 0;
+    memset(s->spk_buffer, 0, length);
+    s->dma_io.len = 0; /* stop any postponed samples from playing */
+}
+
+
+/* This is called after audio stops playing */
+static void dma_send_flush(DBDMA_io *io)
+{
+    DPRINTF("dma_send_flush() called\n");
+    if (DEBUG_SCREAMER) {
+        print_dma_info(io);
+    }
+    ScreamerState *s = (ScreamerState *)io->opaque;
+    reset_markers(s);
+    send_silence_to_speaker(s);
+    if (io->len > 0) {
+        dma_request(io);
+    }
+}
+
+
+static void dma_receive_flush(DBDMA_io *io)
+{
+    DPRINTF("dma_receive_flush() called\n");
+}
+
+
+/* Set the functions the DMA system will call */
+void screamer_register_dma_functions(ScreamerState *s, void *dbdma,
+                                   int send_channel, int receive_channel)
+{
+    DPRINTF("%s() called\n", __func__);
+    DPRINTF("send channel: %d\treceive channel: %d\n", send_channel,
+           receive_channel);
+    s->dbdma = dbdma;
+
+    /* Setup the DMA send system */
+    DBDMA_register_channel(s->dbdma, send_channel, s->dma_send_irq,
+                           dma_to_screamer, dma_send_flush, s);
+
+    /* Setup the DMA receive system */
+    DBDMA_register_channel(s->dbdma, receive_channel, s->dma_receive_irq,
+                           screamer_to_dma, dma_receive_flush, s);
+}
+
+/************************* End of DMA functions **************************/
+
+/* Resets this sound chip */
+static void screamer_reset(DeviceState *d)
+{
+    DPRINTF("screamer_reset() called\n");
+    ScreamerState *s = SCREAMER(d);
+    set_sound_control_reg(s, 0);
+    set_codec_control_reg(s, 0);
+    set_codec_status_reg(s, 0);
+    set_clip_count_reg(s, 0);
+    set_byte_swap_reg(s, 0);
+    set_frame_count_reg(s, 0);
+    int i, num_awacs_regs = 8;
+    for (i = 0; i < num_awacs_regs; i++) {
+        s->awacs[i] = 0;
+    }
+    set_QEMU_audio_settings(s);
+    reset_markers(s);
+    s->dma_io.len = 0;
+}
+
+/* Called when the CPU reads the memory addresses assigned to Screamer */
+static uint64_t screamer_mmio_read(void *opaque, hwaddr addr, unsigned size)
+{
+    ScreamerState *state = opaque;
+    uint32_t return_value;
+
+    addr = addr >> 4;
+    switch (addr) {
+    case SOUND_CONTROL_REG:
+        return_value = get_sound_control_reg(state);
+        break;
+    case CODEC_CONTROL_REG:
+        return_value = get_codec_control_reg(state);
+        break;
+    case CODEC_STATUS_REG:
+        return_value = get_codec_status_reg(state);
+        break;
+    case CLIP_COUNT_REG:
+        return_value = get_clip_count_reg(state);
+        break;
+    case BYTE_SWAP_REG:
+        return_value = get_byte_swap_reg(state);
+        break;
+    case FRAME_COUNT_REG:
+        return_value = get_frame_count_reg(state);
+        break;
+    default:
+        DPRINTF("Unknown register read - addr:%" HWADDR_PRIx "\tsize:%d\n",
+                addr, size);
+        return_value = 12021981; /* Value used for debugging purposes */
+    }
+    DPRINTF("screamer_mmio_read() called addr: %" HWADDR_PRIx "  size: %d",
+            addr >> 4, size);
+    DPRINTF("  returning 0x%x\n", return_value);
+    return return_value;
+}
+
+
+/* Called when the CPU writes to the memory addresses assigned to Screamer */
+static void screamer_mmio_write(void *opaque, hwaddr addr, uint64_t raw_value,
+                                unsigned size)
+{
+    DPRINTF("screamer_mmio_write() called - size: %d\n", size);
+    ScreamerState *state = opaque;
+    uint32_t value = raw_value & 0xffffffff;
+    addr = addr >> 4;
+
+    switch (addr) {
+    case SOUND_CONTROL_REG:
+        set_sound_control_reg(state, value);
+        break;
+    case CODEC_CONTROL_REG:
+        set_codec_control_reg(state, value);
+        break;
+    case CODEC_STATUS_REG:
+        set_codec_status_reg(state, value);
+        break;
+    case CLIP_COUNT_REG:
+        set_clip_count_reg(state, value);
+        break;
+    case BYTE_SWAP_REG:
+        set_byte_swap_reg(state, value);
+        break;
+    case FRAME_COUNT_REG:
+        set_frame_count_reg(state, value);
+        break;
+    default:
+        DPRINTF("Unknown register write - addr:%" HWADDR_PRIx "\tvalue:%d\n",
+                addr, value);
+    }
+}
+
+/* Used for memory_region_init_io() for memory mapped I/O */
+static const MemoryRegionOps screamer_ops = {
+    .read = screamer_mmio_read,
+    .write = screamer_mmio_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4
+    }
+};
+
+/* Called when the device has become active */
+static void screamer_realize(DeviceState *dev, Error **errp)
+{
+    DPRINTF("screamer_realize() called\n");
+    screamer_reset(dev);
+}
+
+
+/*
+ * Called when an instance of the Screamer device is created.
+ * Also called when this HMP command is called: device_add screamer
+ */
+static void screamer_init(Object *obj)
+{
+    DPRINTF("screamer_init() called\n");
+
+    ScreamerState *s = (ScreamerState *)obj;
+    SysBusDevice *d = SYS_BUS_DEVICE(obj);
+    const int region_size = 5 * 32;
+
+    /* Makes the read and write ops work */
+    memory_region_init_io(&s->io_memory_region, OBJECT(s),
+                          &screamer_ops, s, SOUND_CHIP_NAME, region_size);
+
+    /* Sets the SysBusDevice's memory property */
+    sysbus_init_mmio(d, &s->io_memory_region);
+
+    /* Setup all the interrupt requests */
+    sysbus_init_irq(d, &s->irq);
+    sysbus_init_irq(d, &s->dma_send_irq);
+    sysbus_init_irq(d, &s->dma_receive_irq);
+
+    /* Registers Screamer with QEMU's audio system */
+    AUD_register_card(SOUND_CHIP_NAME, &s->card);
+}
+
+
+/*
+ * When saving and restoring the state of the VM, this is used to save and
+ * restore the registers.
+ */
+static const VMStateDescription vmstate_screamer = {
+    .name = "Screamer",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT16_ARRAY(awacs, ScreamerState, 8), /* 8 AWACS registers */
+        VMSTATE_UINT32(sound_control, ScreamerState),
+        VMSTATE_UINT32(codec_control, ScreamerState),
+        VMSTATE_UINT32(codec_status, ScreamerState),
+        VMSTATE_UINT32(clip_count, ScreamerState),
+        VMSTATE_UINT32(byte_swap, ScreamerState),
+        VMSTATE_UINT32(frame_count, ScreamerState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+/*
+ * Sets the class data. It is like polymorphism and inheritance in object
+ * oriented languages.
+ */
+static void screamer_class_init(ObjectClass *class, void *data)
+{
+    DPRINTF("screamer_class_init() called\n");
+    DeviceClass *dc = DEVICE_CLASS(class);
+    dc->realize = screamer_realize;
+    dc->reset = screamer_reset;
+    dc->desc = "Apple Screamer";
+    dc->vmsd = &vmstate_screamer;
+    dc->hotpluggable = false;
+}
+
+/* Used for QOM function registration */
+static const TypeInfo screamer_info = {
+    .name          = "screamer",
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(ScreamerState),
+    .instance_init = screamer_init,
+    .class_init    = screamer_class_init,
+};
+
+/* QOM registration of above functions for calling */
+static void screamer_register_types(void)
+{
+    DPRINTF("screamer_register_types() called\n");
+    type_register_static(&screamer_info);
+}
+
+/* QEMU Object Model (QOM) stuff */
+type_init(screamer_register_types)
diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c
index 79222192e8..3307fa3818 100644
--- a/hw/misc/macio/macio.c
+++ b/hw/misc/macio/macio.c
@@ -37,6 +37,7 @@ 
 #include "hw/intc/heathrow_pic.h"
 #include "sysemu/sysemu.h"
 #include "trace.h"
+#include "include/hw/audio/screamer.h"
 
 /* Note: this code is strongly inspirated from the corresponding code
  * in PearPC */
@@ -109,7 +110,10 @@  static void macio_common_realize(PCIDevice *d, Error **errp)
     SysBusDevice *sysbus_dev;
     Error *err = NULL;
 
-    object_property_set_bool(OBJECT(&s->dbdma), true, "realized", &err);
+    const char *realized_property = "realized";
+    bool new_value = true;
+    object_property_set_bool(OBJECT(&s->dbdma), new_value, realized_property,
+                             &err);
     if (err) {
         error_propagate(errp, err);
         return;
@@ -117,6 +121,19 @@  static void macio_common_realize(PCIDevice *d, Error **errp)
     sysbus_dev = SYS_BUS_DEVICE(&s->dbdma);
     memory_region_add_subregion(&s->bar, 0x08000,
                                 sysbus_mmio_get_region(sysbus_dev, 0));
+    object_property_set_bool(OBJECT(&s->screamer), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+
+    /* Add the screamer sound chip */
+    sysbus_dev = SYS_BUS_DEVICE(&s->screamer);
+    const int offset = 0x14000; /* Offset from base address register (bar) */
+    const int region_number = 0; /* which memory region to use */
+    memory_region_add_subregion(&s->bar, offset,
+                                sysbus_mmio_get_region(sysbus_dev,
+                                                       region_number));
 
     qdev_prop_set_uint32(DEVICE(&s->escc), "disabled", 0);
     qdev_prop_set_uint32(DEVICE(&s->escc), "frequency", ESCC_CLOCK);
@@ -386,6 +403,19 @@  static void macio_newworld_realize(PCIDevice *d, Error **errp)
         memory_region_add_subregion(&s->bar, 0x16000,
                                     sysbus_mmio_get_region(sysbus_dev, 0));
     }
+
+    /* Screamer Sound Chip */
+    const int gpio_0 = 0;
+    const int gpio_1 = 1;
+    const int transmit_channel = 0x10;
+    const int receive_channel = 0x12;
+    sysbus_dev = SYS_BUS_DEVICE(&s->screamer);
+    sysbus_connect_irq(sysbus_dev, gpio_0, qdev_get_gpio_in(pic_dev,
+                                           NEWWORLD_SCREAMER_IRQ));
+    sysbus_connect_irq(sysbus_dev, gpio_1, qdev_get_gpio_in(pic_dev,
+                                           NEWWORLD_SCREAMER_DMA_IRQ));
+    screamer_register_dma_functions(SCREAMER(sysbus_dev), &s->dbdma,
+                                    transmit_channel, receive_channel);
 }
 
 static void macio_newworld_init(Object *obj)
@@ -420,6 +450,9 @@  static void macio_instance_init(Object *obj)
                          TYPE_MAC_DBDMA);
 
     macio_init_child_obj(s, "escc", &s->escc, sizeof(s->escc), TYPE_ESCC);
+
+    macio_init_child_obj(s, SOUND_CHIP_NAME, &s->screamer, sizeof(s->screamer),
+                         TYPE_SCREAMER);
 }
 
 static const VMStateDescription vmstate_macio_oldworld = {
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig
index 354828bf13..4ffc3a1c16 100644
--- a/hw/ppc/Kconfig
+++ b/hw/ppc/Kconfig
@@ -104,6 +104,7 @@  config MAC_NEWWORLD
     select MAC_PMU
     select UNIN_PCI
     select FW_CFG_PPC
+    select SCREAMER
 
 config E500
     bool
diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h
index 6af87d1fa0..c65f2fd15f 100644
--- a/hw/ppc/mac.h
+++ b/hw/ppc/mac.h
@@ -34,6 +34,8 @@ 
 #include "hw/misc/mos6522.h"
 #include "hw/pci/pci_host.h"
 #include "hw/pci-host/uninorth.h"
+#include "hw/ppc/mac_dbdma.h"
+#include "audio/audio.h"
 
 /* SMP is not enabled, for now */
 #define MAX_CPUS 1
@@ -68,6 +70,9 @@ 
 #define NEWWORLD_IDE1_DMA_IRQ  0x3
 #define NEWWORLD_EXTING_GPIO1  0x2f
 #define NEWWORLD_EXTING_GPIO9  0x37
+#define NEWWORLD_SCREAMER_IRQ  0x18
+#define NEWWORLD_SCREAMER_DMA_IRQ 0x9
+#define NEWWORLD_SCREAMER_RX_IRQ 0xa
 
 /* Core99 machine */
 #define TYPE_CORE99_MACHINE MACHINE_TYPE_NAME("mac99")
diff --git a/include/hw/audio/screamer.h b/include/hw/audio/screamer.h
new file mode 100644
index 0000000000..7155541688
--- /dev/null
+++ b/include/hw/audio/screamer.h
@@ -0,0 +1,42 @@ 
+/*
+ * File: screamer.h
+ * Description: header file to the hw/audio/screamer.c file
+ */
+
+#ifndef screamer_h
+#define screamer_h
+
+#include <inttypes.h>
+#include "audio/audio.h"
+#include "hw/ppc/mac_dbdma.h"
+
+#define TYPE_SCREAMER "screamer"
+#define SCREAMER(obj) OBJECT_CHECK(ScreamerState, (obj), TYPE_SCREAMER)
+#define SOUND_CHIP_NAME "Screamer Sound Chip"
+#define MAX_BUFFER_SIZE (128 * 64)
+
+typedef struct ScreamerState {
+    SysBusDevice parent_obj;
+    uint16_t awacs[8]; /* Shadow/awacs registers */
+    uint32_t sound_control;
+    uint32_t codec_control;
+    uint32_t codec_status;
+    uint32_t clip_count;
+    uint32_t byte_swap;
+    uint32_t frame_count;
+    SWVoiceOut *speaker_voice;
+    DBDMAState *dbdma;
+    qemu_irq dma_send_irq;
+    qemu_irq dma_receive_irq;
+    qemu_irq irq;
+    QEMUSoundCard card;
+    MemoryRegion io_memory_region;
+    uint8_t spk_buffer[MAX_BUFFER_SIZE];
+    uint16_t spk_buffer_position, spk_play_position;
+    DBDMA_io dma_io;
+} ScreamerState;
+
+void screamer_register_dma_functions(ScreamerState *s, void *dbdma,
+                                     int send_channel, int receive_channel);
+
+#endif /* screamer_h */
diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h
index 070a694eb5..81ad552d61 100644
--- a/include/hw/misc/macio/macio.h
+++ b/include/hw/misc/macio/macio.h
@@ -35,6 +35,7 @@ 
 #include "hw/ppc/mac.h"
 #include "hw/ppc/mac_dbdma.h"
 #include "hw/ppc/openpic.h"
+#include "hw/audio/screamer.h"
 
 /* MacIO virtual bus */
 #define TYPE_MACIO_BUS "macio-bus"
@@ -86,6 +87,7 @@  typedef struct MacIOState {
     PMUState pmu;
     DBDMAState dbdma;
     ESCCState escc;
+    ScreamerState screamer;
     uint64_t frequency;
 } MacIOState;