diff --git a/.cvsignore b/.cvsignore
index 81d7082..232fbdd 100644
--- a/.cvsignore
+++ b/.cvsignore
@@ -1,5 +1,6 @@
 config-host.*
 dyngen
+dyngen.dSYM
 i386
 *-softmmu
 *-darwin-user
diff --git a/Makefile b/Makefile
index e76d617..88e88fe 100644
--- a/Makefile
+++ b/Makefile
@@ -7,17 +7,14 @@ include config-host.mak
 
 VPATH=$(SRC_PATH):$(SRC_PATH)/hw
 
-BASE_CFLAGS=
-BASE_LDFLAGS=
-
-BASE_CFLAGS += $(OS_CFLAGS) $(ARCH_CFLAGS)
-BASE_LDFLAGS += $(OS_LDFLAGS) $(ARCH_LDFLAGS)
+CFLAGS += $(OS_CFLAGS) $(ARCH_CFLAGS)
+LDFLAGS += $(OS_LDFLAGS) $(ARCH_LDFLAGS)
 
 CPPFLAGS += -I. -I$(SRC_PATH) -MMD -MP
 CPPFLAGS += -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 LIBS=
 ifdef CONFIG_STATIC
-BASE_LDFLAGS += -static
+LDFLAGS += -static
 endif
 ifdef BUILD_DOCS
 DOCS=qemu-doc.html qemu-tech.html qemu.1 qemu-img.1
@@ -43,7 +40,7 @@ BLOCK_OBJS+=block-dmg.o block-bochs.o block-vpc.o block-vvfat.o
 BLOCK_OBJS+=block-qcow2.o block-parallels.o
 
 ######################################################################
-# libqemu_common.a: Target indepedent part of system emulation. The
+# libqemu_common.a: Target independent part of system emulation. The
 # long term path is to suppress *all* target specific code in case of
 # system emulation, i.e. a single QEMU executable should support all
 # CPUs and machines.
@@ -57,7 +54,7 @@ OBJS+=i2c.o smbus.o smbus_eeprom.o max7310.o max111x.o wm8750.o
 OBJS+=ssd0303.o ssd0323.o ads7846.o stellaris_input.o
 OBJS+=scsi-disk.o cdrom.o
 OBJS+=scsi-generic.o
-OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o
+OBJS+=usb.o usb-hub.o usb-linux.o usb-hid.o usb-msd.o usb-wacom.o usb-serial.o
 OBJS+=sd.o ssi-sd.o
 
 ifdef CONFIG_WIN32
@@ -73,6 +70,7 @@ AUDIO_OBJS += ossaudio.o
 endif
 ifdef CONFIG_COREAUDIO
 AUDIO_OBJS += coreaudio.o
+AUDIO_PT = yes
 endif
 ifdef CONFIG_ALSA
 AUDIO_OBJS += alsaaudio.o
@@ -84,6 +82,17 @@ ifdef CONFIG_FMOD
 AUDIO_OBJS += fmodaudio.o
 audio/audio.o audio/fmodaudio.o: CPPFLAGS := -I$(CONFIG_FMOD_INC) $(CPPFLAGS)
 endif
+ifdef CONFIG_ESD
+AUDIO_PT = yes
+AUDIO_PT_INT = yes
+AUDIO_OBJS += esdaudio.o
+endif
+ifdef AUDIO_PT
+LDFLAGS += -pthread
+endif
+ifdef AUDIO_PT_INT
+AUDIO_OBJS += audio_pt_int.o
+endif
 AUDIO_OBJS+= wavcapture.o
 OBJS+=$(addprefix audio/, $(AUDIO_OBJS))
 
@@ -105,16 +114,16 @@ OBJS+=$(addprefix slirp/, $(SLIRP_OBJS))
 endif
 
 cocoa.o: cocoa.m
-	$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+	$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
 
 sdl.o: sdl.c keymaps.c sdl_keysym.h
-	$(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+	$(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) -c -o $@ $<
 
 vnc.o: vnc.c keymaps.c sdl_keysym.h vnchextile.h d3des.c d3des.h
-	$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $(CONFIG_VNC_TLS_CFLAGS) -c -o $@ $<
+	$(CC) $(CFLAGS) $(CPPFLAGS) $(CONFIG_VNC_TLS_CFLAGS) -c -o $@ $<
 
 audio/sdlaudio.o: audio/sdlaudio.c
-	$(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+	$(CC) $(CFLAGS) $(CPPFLAGS) $(SDL_CFLAGS) -c -o $@ $<
 
 libqemu_common.a: $(OBJS)
 	rm -f $@ 
@@ -130,22 +139,23 @@ endif
 ######################################################################
 
 qemu-img$(EXESUF): qemu-img.o qemu-img-block.o $(QEMU_IMG_BLOCK_OBJS)
-	$(CC) $(LDFLAGS) $(BASE_LDFLAGS) -o $@ $^ -lz $(LIBS)
+	$(CC) $(LDFLAGS) -o $@ $^ -lz $(LIBS)
 
 qemu-img-%.o: %.c
-	$(CC) $(CFLAGS) $(CPPFLAGS) -DQEMU_IMG $(BASE_CFLAGS) -c -o $@ $<
+	$(CC) $(CFLAGS) $(CPPFLAGS) -DQEMU_IMG -c -o $@ $<
 
 %.o: %.c
-	$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+	$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
 
 # dyngen host tool
 dyngen$(EXESUF): dyngen.c
-	$(HOST_CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -o $@ $^
+	$(HOST_CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^
 
 clean:
 # avoid old build problems by removing potentially incorrect old files
 	rm -f config.mak config.h op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h
 	rm -f *.o *.d *.a $(TOOLS) dyngen$(EXESUF) TAGS cscope.* *.pod *~ */*~
+	rm -rf dyngen.dSYM
 	rm -f slirp/*.o slirp/*.d audio/*.o audio/*.d
 	$(MAKE) -C tests clean
 	for d in $(TARGET_DIRS); do \
@@ -287,9 +297,5 @@ tarbin:
 	$(docdir)/qemu-tech.html \
 	$(mandir)/man1/qemu.1 $(mandir)/man1/qemu-img.1 )
 
-ifneq ($(wildcard .depend),)
-include .depend
-endif
-
 # Include automatically generated dependency files
 -include $(wildcard *.d audio/*.d slirp/*.d)
diff --git a/Makefile.target b/Makefile.target
index acd6cda..8a94ad8 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -25,22 +25,8 @@ endif
 TARGET_PATH=$(SRC_PATH)/target-$(TARGET_BASE_ARCH)
 VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw
 CPPFLAGS=-I. -I.. -I$(TARGET_PATH) -I$(SRC_PATH) -MMD -MP -DNEED_CPU_H
-ifdef CONFIG_DARWIN_USER
-VPATH+=:$(SRC_PATH)/darwin-user
-CPPFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH)
-endif
-ifdef CONFIG_LINUX_USER
-VPATH+=:$(SRC_PATH)/linux-user
-ifndef TARGET_ABI_DIR
-  TARGET_ABI_DIR=$(TARGET_ARCH)
-endif
-CPPFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)
-endif
-BASE_CFLAGS=
-BASE_LDFLAGS=
 #CFLAGS+=-Werror
 LIBS=
-HELPER_CFLAGS=$(CFLAGS)
 DYNGEN=../dyngen$(EXESUF)
 # user emulator name
 ifndef TARGET_ARCH2
@@ -71,30 +57,20 @@ ifeq ($(TARGET_ARCH),mips64)
     TARGET_ARCH2=mips64el
   endif
 endif
-QEMU_USER=qemu-$(TARGET_ARCH2)
+
+ifdef CONFIG_USER_ONLY
+# user emulator name
+QEMU_PROG=qemu-$(TARGET_ARCH2)
+else
 # system emulator name
-ifdef CONFIG_SOFTMMU
 ifeq ($(TARGET_ARCH), i386)
-QEMU_SYSTEM=qemu$(EXESUF)
+QEMU_PROG=qemu$(EXESUF)
 else
-QEMU_SYSTEM=qemu-system-$(TARGET_ARCH2)$(EXESUF)
+QEMU_PROG=qemu-system-$(TARGET_ARCH2)$(EXESUF)
 endif
-else
-QEMU_SYSTEM=qemu-fast
 endif
 
-ifdef CONFIG_USER_ONLY
-PROGS=$(QEMU_USER)
-else
-PROGS+=$(QEMU_SYSTEM)
-ifndef CONFIG_SOFTMMU
-CONFIG_STATIC=y
-endif
-endif # !CONFIG_USER_ONLY
-
-ifdef CONFIG_STATIC
-BASE_LDFLAGS+=-static
-endif
+PROGS=$(QEMU_PROG)
 
 # We require -O2 to avoid the stack setup prologue in EXIT_TB
 OP_CFLAGS := -Wall -O2 -g -fno-strict-aliasing
@@ -115,60 +91,32 @@ OP_CFLAGS+=$(call cc-option, -fno-align-jumps, "")
 OP_CFLAGS+=$(call cc-option, -fno-align-functions, $(call cc-option, -malign-functions=0, ""))
 OP_CFLAGS+=$(call cc-option, -fno-section-anchors, "")
 
+HELPER_CFLAGS=
+
 ifeq ($(ARCH),i386)
 HELPER_CFLAGS+=-fomit-frame-pointer
 OP_CFLAGS+=-mpreferred-stack-boundary=2 -fomit-frame-pointer
-ifdef TARGET_GPROF
-USE_I386_LD=y
-endif
-ifdef CONFIG_STATIC
-USE_I386_LD=y
-endif
-ifdef USE_I386_LD
-BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
-else
-ifdef CONFIG_LINUX_USER
-# WARNING: this LDFLAGS is _very_ tricky : qemu is an ELF shared object
-# that the kernel ELF loader considers as an executable. I think this
-# is the simplest way to make it self virtualizable!
-BASE_LDFLAGS+=-Wl,-shared
-endif
-endif
-endif
-
-ifeq ($(ARCH),x86_64)
-  ifneq ($(CONFIG_SOLARIS),yes)
-    BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
-  endif
 endif
 
 ifeq ($(ARCH),ppc)
 CPPFLAGS+= -D__powerpc__
-BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
-endif
-
-ifeq ($(ARCH),s390)
-BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
 endif
 
 ifeq ($(ARCH),sparc)
-  BASE_CFLAGS+=-ffixed-g2 -ffixed-g3
+  CFLAGS+=-ffixed-g2 -ffixed-g3
   OP_CFLAGS+=-fno-delayed-branch -ffixed-i0
   ifeq ($(CONFIG_SOLARIS),yes)
     OP_CFLAGS+=-fno-omit-frame-pointer
   else
-    BASE_CFLAGS+=-ffixed-g1 -ffixed-g6
-    HELPER_CFLAGS=$(CFLAGS) -ffixed-i0
-    # -static is used to avoid g1/g3 usage by the dynamic linker
-    BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld -static
+    CFLAGS+=-ffixed-g1 -ffixed-g6
+    HELPER_CFLAGS+=-ffixed-i0
   endif
 endif
 
 ifeq ($(ARCH),sparc64)
-  BASE_CFLAGS+=-ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7
+  CFLAGS+=-ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7
   OP_CFLAGS+=-mcpu=ultrasparc -m64 -fno-delayed-branch -ffixed-i0
   ifneq ($(CONFIG_SOLARIS),yes)
-    BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
     OP_CFLAGS+=-ffixed-g1 -ffixed-g4 -ffixed-g5 -ffixed-g7
   endif
 endif
@@ -177,65 +125,36 @@ ifeq ($(ARCH),alpha)
 # -msmall-data is not used for OP_CFLAGS because we want two-instruction
 # relocations for the constant constructions
 # Ensure there's only a single GP
-BASE_CFLAGS+=-msmall-data
-BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
+CFLAGS+=-msmall-data
 endif
 
 ifeq ($(ARCH),ia64)
-BASE_CFLAGS+=-mno-sdata
+CFLAGS+=-mno-sdata
 OP_CFLAGS+=-mno-sdata
-BASE_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/$(ARCH).ld
 endif
 
 ifeq ($(ARCH),arm)
 OP_CFLAGS+=-mno-sched-prolog -fno-omit-frame-pointer
-BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
 endif
 
 ifeq ($(ARCH),m68k)
 OP_CFLAGS+=-fomit-frame-pointer
-BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
 endif
 
 ifeq ($(ARCH),mips)
 OP_CFLAGS+=-mabi=32 -G0 -fno-PIC -mno-abicalls -fomit-frame-pointer -fno-delayed-branch -Wa,-O0
-ifeq ($(WORDS_BIGENDIAN),yes)
-BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
-else
-BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH)el.ld
-endif
 endif
 
 ifeq ($(ARCH),mips64)
 OP_CFLAGS+=-mabi=n32 -G0 -fno-PIC -mno-abicalls -fomit-frame-pointer -fno-delayed-branch -Wa,-O0
-ifeq ($(WORDS_BIGENDIAN),yes)
-BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
-else
-BASE_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH)el.ld
-endif
-endif
-
-ifeq ($(CONFIG_DARWIN),yes)
-LIBS+=-lmx
-endif
-
-ifdef CONFIG_DARWIN_USER
-# Leave some space for the regular program loading zone
-BASE_LDFLAGS+=-Wl,-segaddr,__STD_PROG_ZONE,0x1000 -image_base 0x0e000000
 endif
 
-BASE_CFLAGS+=$(OS_CFLAGS) $(ARCH_CFLAGS)
-BASE_LDFLAGS+=$(OS_LDFLAGS) $(ARCH_LDFLAGS)
+CFLAGS+=$(OS_CFLAGS) $(ARCH_CFLAGS)
+LDFLAGS+=$(OS_LDFLAGS) $(ARCH_LDFLAGS)
 OP_CFLAGS+=$(OS_CFLAGS) $(ARCH_CFLAGS)
-OP_LDFLAGS+=$(OS_LDFLAGS) $(ARCH_LDFLAGS)
-
-#########################################################
 
 CPPFLAGS+=-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
 LIBS+=-lm
-ifndef CONFIG_USER_ONLY
-LIBS+=-lz
-endif
 ifdef CONFIG_WIN32
 LIBS+=-lwinmm -lws2_32 -liphlpapi
 endif
@@ -245,48 +164,13 @@ ifdef NEEDS_LIBSUNMATH
 LIBS+=-lsunmath
 LDFLAGS+=-L/opt/SUNWspro/prod/lib -R/opt/SUNWspro/prod/lib
 OP_CFLAGS+=-I/opt/SUNWspro/prod/include/cc
-BASE_CFLAGS+=-I/opt/SUNWspro/prod/include/cc
-endif
-endif
-
-# profiling code
-ifdef TARGET_GPROF
-BASE_LDFLAGS+=-p
-main.o: BASE_CFLAGS+=-p
-endif
-
-ifdef CONFIG_LINUX_USER
-OBJS= main.o syscall.o strace.o mmap.o signal.o path.o osdep.o thunk.o \
-      elfload.o linuxload.o uaccess.o
-LIBS+= $(AIOLIBS)
-ifdef TARGET_HAS_BFLT
-OBJS+= flatload.o
+CFLAGS+=-I/opt/SUNWspro/prod/include/cc
 endif
-ifdef TARGET_HAS_ELFLOAD32
-OBJS+= elfload32.o
-elfload32.o: elfload.c
 endif
 
-ifeq ($(TARGET_ARCH), i386)
-OBJS+= vm86.o
-endif
-ifeq ($(TARGET_ARCH), arm)
-OBJS+=nwfpe/fpa11.o nwfpe/fpa11_cpdo.o \
-nwfpe/fpa11_cpdt.o nwfpe/fpa11_cprt.o nwfpe/fpopcode.o nwfpe/single_cpdo.o \
- nwfpe/double_cpdo.o nwfpe/extended_cpdo.o arm-semi.o
-endif
-ifeq ($(TARGET_ARCH), m68k)
-OBJS+= m68k-sim.o m68k-semi.o
-endif
-endif #CONFIG_LINUX_USER
-
-ifdef CONFIG_DARWIN_USER
-OBJS= main.o commpage.o machload.o mmap.o osdep.o signal.o syscall.o thunk.o
-endif
-
-SRCS:= $(OBJS:.o=.c)
-OBJS+= libqemu.a
+all: $(PROGS)
 
+#########################################################
 # cpu emulator library
 LIBOBJS=exec.o kqemu.o translate-op.o translate-all.o cpu-exec.o\
         translate.o op.o host-utils.o
@@ -378,32 +262,222 @@ ifeq ($(findstring s390, $(TARGET_ARCH) $(ARCH)),s390)
 LIBOBJS+=s390-dis.o
 endif
 
+# libqemu
+
+libqemu.a: $(LIBOBJS)
+	rm -f $@
+	$(AR) rcs $@ $(LIBOBJS)
+
+translate.o: translate.c gen-op.h opc.h cpu.h
+
+translate-all.o: translate-all.c opc.h cpu.h
+
+translate-op.o: translate-all.c op.h opc.h cpu.h
+
+op.h: op.o $(DYNGEN)
+	$(DYNGEN) -o $@ $<
+
+opc.h: op.o $(DYNGEN)
+	$(DYNGEN) -c -o $@ $<
+
+gen-op.h: op.o $(DYNGEN)
+	$(DYNGEN) -g -o $@ $<
+
+op.o: op.c
+	$(CC) $(OP_CFLAGS) $(CPPFLAGS) -c -o $@ $<
+
+# HELPER_CFLAGS is used for all the code compiled with static register
+# variables
+ifeq ($(TARGET_BASE_ARCH), i386)
+# XXX: rename helper.c to op_helper.c
+helper.o: helper.c
+	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+else
+op_helper.o: op_helper.c
+	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+endif
+
+cpu-exec.o: cpu-exec.c
+	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+
+#########################################################
+# Linux user emulator target
+
+ifdef CONFIG_LINUX_USER
+
+VPATH+=:$(SRC_PATH)/linux-user
+ifndef TARGET_ABI_DIR
+  TARGET_ABI_DIR=$(TARGET_ARCH)
+endif
+CPPFLAGS+=-I$(SRC_PATH)/linux-user -I$(SRC_PATH)/linux-user/$(TARGET_ABI_DIR)
+
+ifdef CONFIG_STATIC
+LDFLAGS+=-static
+endif
+
+ifeq ($(ARCH),i386)
+ifdef TARGET_GPROF
+USE_I386_LD=y
+endif
+ifdef CONFIG_STATIC
+USE_I386_LD=y
+endif
+ifdef USE_I386_LD
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
+else
+# WARNING: this LDFLAGS is _very_ tricky : qemu is an ELF shared object
+# that the kernel ELF loader considers as an executable. I think this
+# is the simplest way to make it self virtualizable!
+LDFLAGS+=-Wl,-shared
+endif
+endif
+
+ifeq ($(ARCH),x86_64)
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
+endif
+
+ifeq ($(ARCH),ppc)
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
+endif
+
+ifeq ($(ARCH),s390)
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
+endif
+
+ifeq ($(ARCH),sparc)
+# -static is used to avoid g1/g3 usage by the dynamic linker	
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld -static
+endif
+
+ifeq ($(ARCH),sparc64)
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
+endif
+
+ifeq ($(ARCH),alpha)
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
+endif
+
+ifeq ($(ARCH),ia64)
+LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/$(ARCH).ld
+endif
+
+ifeq ($(ARCH),arm)
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
+endif
+
+ifeq ($(ARCH),m68k)
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
+endif
+
+ifeq ($(ARCH),mips)
+ifeq ($(WORDS_BIGENDIAN),yes)
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
+else
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH)el.ld
+endif
+endif
+
+ifeq ($(ARCH),mips64)
+ifeq ($(WORDS_BIGENDIAN),yes)
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
+else
+LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH)el.ld
+endif
+endif
+
+OBJS= main.o syscall.o strace.o mmap.o signal.o path.o osdep.o thunk.o \
+      elfload.o linuxload.o uaccess.o
+LIBS+= $(AIOLIBS)
+ifdef TARGET_HAS_BFLT
+OBJS+= flatload.o
+endif
+ifdef TARGET_HAS_ELFLOAD32
+OBJS+= elfload32.o
+elfload32.o: elfload.c
+endif
+
+ifeq ($(TARGET_ARCH), i386)
+OBJS+= vm86.o
+endif
+ifeq ($(TARGET_ARCH), arm)
+OBJS+=nwfpe/fpa11.o nwfpe/fpa11_cpdo.o \
+nwfpe/fpa11_cpdt.o nwfpe/fpa11_cprt.o nwfpe/fpopcode.o nwfpe/single_cpdo.o \
+ nwfpe/double_cpdo.o nwfpe/extended_cpdo.o arm-semi.o
+endif
+ifeq ($(TARGET_ARCH), m68k)
+OBJS+= m68k-sim.o m68k-semi.o
+endif
+
 ifdef CONFIG_GDBSTUB
 OBJS+=gdbstub.o
 endif
 
-all: $(PROGS)
+OBJS+= libqemu.a
 
-$(QEMU_USER): $(OBJS)
-	$(CC) $(LDFLAGS) $(BASE_LDFLAGS) -o $@ $^  $(LIBS)
+# Note: this is a workaround. The real fix is to avoid compiling
+# cpu_signal_handler() in cpu-exec.c.
+signal.o: signal.c
+	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+
+$(QEMU_PROG): $(OBJS)
+	$(CC) $(LDFLAGS) -o $@ $^  $(LIBS)
 ifeq ($(ARCH),alpha)
 # Mark as 32 bit binary, i. e. it will be mapped into the low 31 bit of
 # the address space (31 bit so sign extending doesn't matter)
 	echo -ne '\001\000\000\000' | dd of=qemu bs=1 seek=48 count=4 conv=notrunc
 endif
 
-# must use static linking to avoid leaving stuff in virtual address space
-VL_OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o
-# XXX: suppress QEMU_TOOL tests
+endif #CONFIG_LINUX_USER
+
+#########################################################
+# Darwin user emulator target
+
+ifdef CONFIG_DARWIN_USER
+
+VPATH+=:$(SRC_PATH)/darwin-user
+CPPFLAGS+=-I$(SRC_PATH)/darwin-user -I$(SRC_PATH)/darwin-user/$(TARGET_ARCH)
+
+# Leave some space for the regular program loading zone
+LDFLAGS+=-Wl,-segaddr,__STD_PROG_ZONE,0x1000 -image_base 0x0e000000
+
+LIBS+=-lmx
+
+OBJS= main.o commpage.o machload.o mmap.o osdep.o signal.o syscall.o thunk.o
+
+OBJS+= libqemu.a
+
+ifdef CONFIG_GDBSTUB
+OBJS+=gdbstub.o
+endif
+
+# Note: this is a workaround. The real fix is to avoid compiling
+# cpu_signal_handler() in cpu-exec.c.
+signal.o: signal.c
+	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+
+$(QEMU_PROG): $(OBJS)
+	$(CC) $(LDFLAGS) -o $@ $^  $(LIBS)
+
+endif #CONFIG_DARWIN_USER
+
+#########################################################
+# System emulator target
+ifndef CONFIG_USER_ONLY
+
+OBJS=vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o
 ifdef CONFIG_WIN32
-VL_OBJS+=block-raw-win32.o
+OBJS+=block-raw-win32.o
 else
-VL_OBJS+=block-raw-posix.o
+OBJS+=block-raw-posix.o
 endif
 
+LIBS+=-lz
 ifdef CONFIG_ALSA
 LIBS += -lasound
 endif
+ifdef CONFIG_ESD
+LIBS += -lesd
+endif
 ifdef CONFIG_DSOUND
 LIBS += -lole32 -ldxguid
 endif
@@ -412,9 +486,15 @@ LIBS += $(CONFIG_FMOD_LIB)
 endif
 
 SOUND_HW = sb16.o es1370.o
+ifdef CONFIG_AC97
+SOUND_HW += ac97.o
+endif
 ifdef CONFIG_ADLIB
 SOUND_HW += fmopl.o adlib.o
 endif
+ifdef CONFIG_GUS
+SOUND_HW += gus.o gusemu_hal.o gusemu_mixer.o
+endif
 
 ifdef CONFIG_VNC_TLS
 CPPFLAGS += $(CONFIG_VNC_TLS_CFLAGS)
@@ -422,98 +502,98 @@ LIBS += $(CONFIG_VNC_TLS_LIBS)
 endif
 
 # SCSI layer
-VL_OBJS+= lsi53c895a.o
+OBJS+= lsi53c895a.o
 
 # USB layer
-VL_OBJS+= usb-ohci.o
+OBJS+= usb-ohci.o
 
 # EEPROM emulation
-VL_OBJS += eeprom93xx.o
+OBJS += eeprom93xx.o
 
 # PCI network cards
-VL_OBJS += eepro100.o
-VL_OBJS += ne2000.o
-VL_OBJS += pcnet.o
-VL_OBJS += rtl8139.o
+OBJS += eepro100.o
+OBJS += ne2000.o
+OBJS += pcnet.o
+OBJS += rtl8139.o
 
 ifeq ($(TARGET_BASE_ARCH), i386)
 # Hardware support
-VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
-VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
-VL_OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
-VL_OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o
+OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o
+OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o
+OBJS+= cirrus_vga.o apic.o parallel.o acpi.o piix_pci.o
+OBJS+= usb-uhci.o vmmouse.o vmport.o vmware_vga.o
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 endif
 ifeq ($(TARGET_BASE_ARCH), ppc)
 CPPFLAGS += -DHAS_AUDIO -DHAS_AUDIO_CHOICE
 # shared objects
-VL_OBJS+= ppc.o ide.o vga.o $(SOUND_HW) dma.o openpic.o
+OBJS+= ppc.o ide.o vga.o $(SOUND_HW) dma.o openpic.o
 # PREP target
-VL_OBJS+= pckbd.o ps2.o serial.o i8259.o i8254.o fdc.o m48t59.o mc146818rtc.o
-VL_OBJS+= prep_pci.o ppc_prep.o
+OBJS+= pckbd.o ps2.o serial.o i8259.o i8254.o fdc.o m48t59.o mc146818rtc.o
+OBJS+= prep_pci.o ppc_prep.o
 # Mac shared devices
-VL_OBJS+= macio.o cuda.o adb.o mac_nvram.o mac_dbdma.o
+OBJS+= macio.o cuda.o adb.o mac_nvram.o mac_dbdma.o
 # OldWorld PowerMac
-VL_OBJS+= heathrow_pic.o grackle_pci.o ppc_oldworld.o
+OBJS+= heathrow_pic.o grackle_pci.o ppc_oldworld.o
 # NewWorld PowerMac
-VL_OBJS+= unin_pci.o ppc_chrp.o
+OBJS+= unin_pci.o ppc_chrp.o
 # PowerPC 4xx boards
-VL_OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
+OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
 endif
 ifeq ($(TARGET_BASE_ARCH), mips)
-VL_OBJS+= mips_r4k.o mips_malta.o mips_pica61.o mips_mipssim.o
-VL_OBJS+= mips_timer.o mips_int.o dma.o vga.o serial.o i8254.o i8259.o
-VL_OBJS+= jazz_led.o
-VL_OBJS+= ide.o gt64xxx.o pckbd.o ps2.o fdc.o mc146818rtc.o usb-uhci.o acpi.o ds1225y.o
-VL_OBJS+= piix_pci.o parallel.o cirrus_vga.o $(SOUND_HW)
-VL_OBJS+= mipsnet.o
-VL_OBJS+= pflash_cfi01.o
+OBJS+= mips_r4k.o mips_malta.o mips_pica61.o mips_mipssim.o
+OBJS+= mips_timer.o mips_int.o dma.o vga.o serial.o i8254.o i8259.o
+OBJS+= jazz_led.o
+OBJS+= ide.o gt64xxx.o pckbd.o ps2.o fdc.o mc146818rtc.o usb-uhci.o acpi.o ds1225y.o
+OBJS+= piix_pci.o parallel.o cirrus_vga.o $(SOUND_HW)
+OBJS+= mipsnet.o
+OBJS+= pflash_cfi01.o
 CPPFLAGS += -DHAS_AUDIO
 endif
 ifeq ($(TARGET_BASE_ARCH), cris)
-VL_OBJS+= etraxfs.o
-VL_OBJS+= ptimer.o
-VL_OBJS+= etraxfs_timer.o
-VL_OBJS+= etraxfs_ser.o
+OBJS+= etraxfs.o
+OBJS+= ptimer.o
+OBJS+= etraxfs_timer.o
+OBJS+= etraxfs_ser.o
 endif
 ifeq ($(TARGET_BASE_ARCH), sparc)
 ifeq ($(TARGET_ARCH), sparc64)
-VL_OBJS+= sun4u.o ide.o pckbd.o ps2.o vga.o apb_pci.o
-VL_OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o
-VL_OBJS+= cirrus_vga.o parallel.o ptimer.o
+OBJS+= sun4u.o ide.o pckbd.o ps2.o vga.o apb_pci.o
+OBJS+= fdc.o mc146818rtc.o serial.o m48t59.o
+OBJS+= cirrus_vga.o parallel.o ptimer.o
 else
-VL_OBJS+= sun4m.o tcx.o pcnet.o iommu.o m48t59.o slavio_intctl.o
-VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o sparc32_dma.o
-VL_OBJS+= cs4231.o ptimer.o eccmemctl.o sbi.o sun4c_intctl.o
+OBJS+= sun4m.o tcx.o pcnet.o iommu.o m48t59.o slavio_intctl.o
+OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o sparc32_dma.o
+OBJS+= cs4231.o ptimer.o eccmemctl.o sbi.o sun4c_intctl.o
 endif
 endif
 ifeq ($(TARGET_BASE_ARCH), arm)
-VL_OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
-VL_OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
-VL_OBJS+= versatile_pci.o ptimer.o
-VL_OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o
-VL_OBJS+= armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
-VL_OBJS+= pl061.o
-VL_OBJS+= arm-semi.o
-VL_OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
-VL_OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
-VL_OBJS+= pflash_cfi01.o gumstix.o
-VL_OBJS+= spitz.o ide.o serial.o nand.o ecc.o
-VL_OBJS+= omap.o omap_lcdc.o omap1_clk.o omap_mmc.o omap_i2c.o
-VL_OBJS+= palm.o tsc210x.o
-VL_OBJS+= mst_fpga.o mainstone.o
+OBJS+= integratorcp.o versatilepb.o ps2.o smc91c111.o arm_pic.o arm_timer.o
+OBJS+= arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
+OBJS+= versatile_pci.o ptimer.o
+OBJS+= realview_gic.o realview.o arm_sysctl.o mpcore.o
+OBJS+= armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
+OBJS+= pl061.o
+OBJS+= arm-semi.o
+OBJS+= pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
+OBJS+= pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
+OBJS+= pflash_cfi01.o gumstix.o
+OBJS+= spitz.o ide.o serial.o nand.o ecc.o
+OBJS+= omap.o omap_lcdc.o omap1_clk.o omap_mmc.o omap_i2c.o
+OBJS+= palm.o tsc210x.o
+OBJS+= mst_fpga.o mainstone.o
 CPPFLAGS += -DHAS_AUDIO
 endif
 ifeq ($(TARGET_BASE_ARCH), sh4)
-VL_OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
-VL_OBJS+= sh_timer.o ptimer.o sh_serial.o sh_intc.o
+OBJS+= shix.o r2d.o sh7750.o sh7750_regnames.o tc58128.o
+OBJS+= sh_timer.o ptimer.o sh_serial.o sh_intc.o
 endif
 ifeq ($(TARGET_BASE_ARCH), m68k)
-VL_OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o
-VL_OBJS+= m68k-semi.o dummy_m68k.o
+OBJS+= an5206.o mcf5206.o ptimer.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o
+OBJS+= m68k-semi.o dummy_m68k.o
 endif
 ifdef CONFIG_GDBSTUB
-VL_OBJS+=gdbstub.o
+OBJS+=gdbstub.o
 endif
 ifdef CONFIG_COCOA
 COCOA_LIBS=-F/System/Library/Frameworks -framework Cocoa -framework IOKit
@@ -525,103 +605,44 @@ ifdef CONFIG_SLIRP
 CPPFLAGS+=-I$(SRC_PATH)/slirp
 endif
 
-VL_LDFLAGS=$(VL_OS_LDFLAGS)
-VL_LIBS=$(AIOLIBS)
+LIBS+=$(AIOLIBS)
 # specific flags are needed for non soft mmu emulator
 ifdef CONFIG_STATIC
-VL_LDFLAGS+=-static
-endif
-ifndef CONFIG_SOFTMMU
-VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/i386-vl.ld
+LDFLAGS+=-static
 endif
 ifndef CONFIG_DARWIN
 ifndef CONFIG_WIN32
 ifndef CONFIG_SOLARIS
-VL_LIBS+=-lutil
+LIBS+=-lutil
 endif
 endif
 endif
 ifdef TARGET_GPROF
-vl.o: BASE_CFLAGS+=-p
-VL_LDFLAGS+=-p
+vl.o: CFLAGS+=-p
+LDFLAGS+=-p
 endif
 
 ifeq ($(ARCH),ia64)
-VL_LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
-endif
-
-ifeq ($(ARCH),sparc64)
-  VL_LDFLAGS+=-m64
-  ifneq ($(CONFIG_SOLARIS),yes)
-    VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
-  endif
-endif
-
-ifeq ($(ARCH),x86_64)
-  VL_LDFLAGS+=-m64
-  ifneq ($(CONFIG_SOLARIS),yes)
-    VL_LDFLAGS+=-Wl,-T,$(SRC_PATH)/$(ARCH).ld
-  endif
+LDFLAGS+=-Wl,-G0 -Wl,-T,$(SRC_PATH)/ia64.ld
 endif
 
 ifdef CONFIG_WIN32
 SDL_LIBS := $(filter-out -mwindows, $(SDL_LIBS)) -mconsole
 endif
 
-$(QEMU_SYSTEM): $(VL_OBJS) ../libqemu_common.a libqemu.a
-	$(CC) $(VL_LDFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS) $(VL_LIBS)
-
-depend: $(SRCS)
-	$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $^ 1>.depend
-
-vldepend: $(VL_OBJS:.o=.c)
-	$(CC) -MM $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) $^ 1>.depend
-
-# libqemu
-
-libqemu.a: $(LIBOBJS)
-	rm -f $@
-	$(AR) rcs $@ $(LIBOBJS)
-
-translate.o: translate.c gen-op.h opc.h cpu.h
-
-translate-all.o: translate-all.c opc.h cpu.h
-
-translate-op.o: translate-all.c op.h opc.h cpu.h
-
-op.h: op.o $(DYNGEN)
-	$(DYNGEN) -o $@ $<
-
-opc.h: op.o $(DYNGEN)
-	$(DYNGEN) -c -o $@ $<
-
-gen-op.h: op.o $(DYNGEN)
-	$(DYNGEN) -g -o $@ $<
-
-op.o: op.c
-	$(CC) $(OP_CFLAGS) $(CPPFLAGS) -c -o $@ $<
-
-# HELPER_CFLAGS is used for all the code compiled with static register
-# variables
-ifeq ($(TARGET_BASE_ARCH), i386)
-# XXX: rename helper.c to op_helper.c
-helper.o: helper.c
-	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
-else
-op_helper.o: op_helper.c
-	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+# profiling code
+ifdef TARGET_GPROF
+LDFLAGS+=-p
+main.o: CFLAGS+=-p
 endif
 
-cpu-exec.o: cpu-exec.c
-	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+$(QEMU_PROG): $(OBJS) ../libqemu_common.a libqemu.a
+	$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(SDL_LIBS) $(COCOA_LIBS)
 
-# Note: this is a workaround. The real fix is to avoid compiling
-# cpu_signal_handler() in cpu-exec.c.
-signal.o: signal.c
-	$(CC) $(HELPER_CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+endif # !CONFIG_USER_ONLY
 
 %.o: %.c
-	$(CC) $(CFLAGS) $(CPPFLAGS) $(BASE_CFLAGS) -c -o $@ $<
+	$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
 
 %.o: %.S
 	$(CC) $(CPPFLAGS) -c -o $@ $<
@@ -635,15 +656,5 @@ ifneq ($(PROGS),)
 	$(INSTALL) -m 755 -s $(PROGS) "$(DESTDIR)$(bindir)"
 endif
 
-ifneq ($(wildcard .depend),)
-include .depend
-endif
-
-ifeq (1, 0)
-audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \
-fmodaudio.o alsaaudio.o mixeng.o sb16.o es1370.o gus.o adlib.o: \
-CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare
-endif
-
 # Include automatically generated dependency files
 -include $(wildcard *.d */*.d)
diff --git a/TODO b/TODO
index 178a071..fcf8baa 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,10 @@
 short term:
 ----------
+- x86_64: fxsave/fxrestore bugs
+- x86_64: lcall/ljmp intel/amd differences
+- x86_64: cmpxchgl
+- x86_64: cmovl bug
+- x86: monitor invalid 
 - cycle counter for all archs
 - cpu_interrupt() win32/SMP fix
 - support variable tsc freq
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 77a08a1..43cfa25 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -86,9 +86,9 @@ static struct {
 };
 
 struct alsa_params_req {
-    unsigned int freq;
-    audfmt_e fmt;
-    unsigned int nchannels;
+    int freq;
+    snd_pcm_format_t fmt;
+    int nchannels;
     unsigned int buffer_size;
     unsigned int period_size;
 };
@@ -96,6 +96,7 @@ struct alsa_params_req {
 struct alsa_params_obt {
     int freq;
     audfmt_e fmt;
+    int endianness;
     int nchannels;
     snd_pcm_uframes_t samples;
 };
@@ -143,7 +144,7 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len)
     return audio_pcm_sw_write (sw, buf, len);
 }
 
-static int aud_to_alsafmt (audfmt_e fmt)
+static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt)
 {
     switch (fmt) {
     case AUD_FMT_S8:
@@ -173,7 +174,8 @@ static int aud_to_alsafmt (audfmt_e fmt)
     }
 }
 
-static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness)
+static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt,
+                           int *endianness)
 {
     switch (alsafmt) {
     case SND_PCM_FORMAT_S8:
@@ -234,7 +236,6 @@ static int alsa_to_audfmt (int alsafmt, audfmt_e *fmt, int *endianness)
     return 0;
 }
 
-#if defined DEBUG_MISMATCHES || defined DEBUG
 static void alsa_dump_info (struct alsa_params_req *req,
                             struct alsa_params_obt *obt)
 {
@@ -248,7 +249,6 @@ static void alsa_dump_info (struct alsa_params_req *req,
            req->buffer_size, req->period_size);
     dolog ("obtained: samples %ld\n", obt->samples);
 }
-#endif
 
 static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
 {
@@ -291,6 +291,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
     unsigned int period_size, buffer_size;
     snd_pcm_uframes_t obt_buffer_size;
     const char *typ = in ? "ADC" : "DAC";
+    snd_pcm_format_t obtfmt;
 
     freq = req->freq;
     period_size = req->period_size;
@@ -327,9 +328,8 @@ static int alsa_open (int in, struct alsa_params_req *req,
     }
 
     err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
-    if (err < 0) {
+    if (err < 0 && conf.verbose) {
         alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
-        goto err;
     }
 
     err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
@@ -494,6 +494,17 @@ static int alsa_open (int in, struct alsa_params_req *req,
         goto err;
     }
 
+    err = snd_pcm_hw_params_get_format (hw_params, &obtfmt);
+    if (err < 0) {
+        alsa_logerr2 (err, typ, "Failed to get format\n");
+        goto err;
+    }
+
+    if (alsa_to_audfmt (obtfmt, &obt->fmt, &obt->endianness)) {
+        dolog ("Invalid format was returned %d\n", obtfmt);
+        goto err;
+    }
+
     err = snd_pcm_prepare (handle);
     if (err < 0) {
         alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
@@ -504,28 +515,41 @@ static int alsa_open (int in, struct alsa_params_req *req,
         snd_pcm_uframes_t threshold;
         int bytes_per_sec;
 
-        bytes_per_sec = freq
-            << (nchannels == 2)
-            << (req->fmt == AUD_FMT_S16 || req->fmt == AUD_FMT_U16);
+        bytes_per_sec = freq << (nchannels == 2);
+
+        switch (obt->fmt) {
+        case AUD_FMT_S8:
+        case AUD_FMT_U8:
+            break;
+
+        case AUD_FMT_S16:
+        case AUD_FMT_U16:
+            bytes_per_sec <<= 1;
+            break;
+
+        case AUD_FMT_S32:
+        case AUD_FMT_U32:
+            bytes_per_sec <<= 2;
+            break;
+        }
 
         threshold = (conf.threshold * bytes_per_sec) / 1000;
         alsa_set_threshold (handle, threshold);
     }
 
-    obt->fmt = req->fmt;
     obt->nchannels = nchannels;
     obt->freq = freq;
     obt->samples = obt_buffer_size;
+
     *handlep = handle;
 
-#if defined DEBUG_MISMATCHES || defined DEBUG
-    if (obt->fmt != req->fmt ||
-        obt->nchannels != req->nchannels ||
-        obt->freq != req->freq) {
-        dolog ("Audio paramters mismatch for %s\n", typ);
+    if (conf.verbose &&
+        (obt->fmt != req->fmt ||
+         obt->nchannels != req->nchannels ||
+         obt->freq != req->freq)) {
+        dolog ("Audio paramters for %s\n", typ);
         alsa_dump_info (req, obt);
     }
-#endif
 
 #ifdef DEBUG
     alsa_dump_info (req, obt);
@@ -665,9 +689,6 @@ static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
     struct alsa_params_req req;
     struct alsa_params_obt obt;
-    audfmt_e effective_fmt;
-    int endianness;
-    int err;
     snd_pcm_t *handle;
     audsettings_t obt_as;
 
@@ -681,16 +702,10 @@ static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
         return -1;
     }
 
-    err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
-    if (err) {
-        alsa_anal_close (&handle);
-        return -1;
-    }
-
     obt_as.freq = obt.freq;
     obt_as.nchannels = obt.nchannels;
-    obt_as.fmt = effective_fmt;
-    obt_as.endianness = endianness;
+    obt_as.fmt = obt.fmt;
+    obt_as.endianness = obt.endianness;
 
     audio_pcm_init_info (&hw->info, &obt_as);
     hw->samples = obt.samples;
@@ -751,9 +766,6 @@ static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
     struct alsa_params_req req;
     struct alsa_params_obt obt;
-    int endianness;
-    int err;
-    audfmt_e effective_fmt;
     snd_pcm_t *handle;
     audsettings_t obt_as;
 
@@ -767,16 +779,10 @@ static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
         return -1;
     }
 
-    err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
-    if (err) {
-        alsa_anal_close (&handle);
-        return -1;
-    }
-
     obt_as.freq = obt.freq;
     obt_as.nchannels = obt.nchannels;
-    obt_as.fmt = effective_fmt;
-    obt_as.endianness = endianness;
+    obt_as.fmt = obt.fmt;
+    obt_as.endianness = obt.endianness;
 
     audio_pcm_init_info (&hw->info, &obt_as);
     hw->samples = obt.samples;
diff --git a/audio/audio.c b/audio/audio.c
index 5e9d88b..2bea53e 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -56,6 +56,9 @@ static struct audio_driver *drvtab[] = {
 #ifdef CONFIG_SDL
     &sdl_audio_driver,
 #endif
+#ifdef CONFIG_ESD
+    &esd_audio_driver,
+#endif
     &no_audio_driver,
     &wav_audio_driver
 };
@@ -414,7 +417,7 @@ static void audio_print_options (const char *prefix,
             {
                 audfmt_e *fmtp = opt->valp;
                 printf (
-                    "format, %s = %s, (one of: U8 S8 U16 S16)\n",
+                    "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n",
                     state,
                     audio_audfmt_to_string (*fmtp)
                     );
diff --git a/audio/audio_int.h b/audio/audio_int.h
index cd22a30..f969602 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -202,6 +202,7 @@ extern struct audio_driver fmod_audio_driver;
 extern struct audio_driver alsa_audio_driver;
 extern struct audio_driver coreaudio_audio_driver;
 extern struct audio_driver dsound_audio_driver;
+extern struct audio_driver esd_audio_driver;
 extern volume_t nominal_volume;
 
 void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as);
diff --git a/audio/audio_pt_int.c b/audio/audio_pt_int.c
new file mode 100644
index 0000000..e889a98
--- /dev/null
+++ b/audio/audio_pt_int.c
@@ -0,0 +1,149 @@
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "audio-pt"
+
+#include "audio_int.h"
+#include "audio_pt_int.h"
+
+static void logerr (struct audio_pt *pt, int err, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (pt->drv, fmt, ap);
+    va_end (ap);
+
+    AUD_log (NULL, "\n");
+    AUD_log (pt->drv, "Reason: %s\n", strerror (err));
+}
+
+int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
+                   void *opaque, const char *drv, const char *cap)
+{
+    int err, err2;
+    const char *efunc;
+
+    p->drv = drv;
+
+    err = pthread_mutex_init (&p->mutex, NULL);
+    if (err) {
+        efunc = "pthread_mutex_init";
+        goto err0;
+    }
+
+    err = pthread_cond_init (&p->cond, NULL);
+    if (err) {
+        efunc = "pthread_cond_init";
+        goto err1;
+    }
+
+    err = pthread_create (&p->thread, NULL, func, opaque);
+    if (err) {
+        efunc = "pthread_create";
+        goto err2;
+    }
+
+    return 0;
+
+ err2:
+    err2 = pthread_cond_destroy (&p->cond);
+    if (err2) {
+        logerr (p, err2, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
+    }
+
+ err1:
+    err2 = pthread_mutex_destroy (&p->mutex);
+    if (err2) {
+        logerr (p, err2, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
+    }
+
+ err0:
+    logerr (p, err, "%s(%s): %s failed", cap, AUDIO_FUNC, efunc);
+    return -1;
+}
+
+int audio_pt_fini (struct audio_pt *p, const char *cap)
+{
+    int err, ret = 0;
+
+    err = pthread_cond_destroy (&p->cond);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
+        ret = -1;
+    }
+
+    err = pthread_mutex_destroy (&p->mutex);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
+        ret = -1;
+    }
+    return ret;
+}
+
+int audio_pt_lock (struct audio_pt *p, const char *cap)
+{
+    int err;
+
+    err = pthread_mutex_lock (&p->mutex);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_mutex_lock failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    return 0;
+}
+
+int audio_pt_unlock (struct audio_pt *p, const char *cap)
+{
+    int err;
+
+    err = pthread_mutex_unlock (&p->mutex);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    return 0;
+}
+
+int audio_pt_wait (struct audio_pt *p, const char *cap)
+{
+    int err;
+
+    err = pthread_cond_wait (&p->cond, &p->mutex);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_cond_wait failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    return 0;
+}
+
+int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
+{
+    int err;
+
+    err = pthread_mutex_unlock (&p->mutex);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    err = pthread_cond_signal (&p->cond);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_cond_signal failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    return 0;
+}
+
+int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
+{
+    int err;
+    void *ret;
+
+    err = pthread_join (p->thread, &ret);
+    if (err) {
+        logerr (p, err, "%s(%s): pthread_join failed", cap, AUDIO_FUNC);
+        return -1;
+    }
+    *arg = ret;
+    return 0;
+}
diff --git a/audio/audio_pt_int.h b/audio/audio_pt_int.h
new file mode 100644
index 0000000..0dfff76
--- /dev/null
+++ b/audio/audio_pt_int.h
@@ -0,0 +1,22 @@
+#ifndef QEMU_AUDIO_PT_INT_H
+#define QEMU_AUDIO_PT_INT_H
+
+#include <pthread.h>
+
+struct audio_pt {
+    const char *drv;
+    pthread_t thread;
+    pthread_cond_t cond;
+    pthread_mutex_t mutex;
+};
+
+int audio_pt_init (struct audio_pt *, void *(*) (void *), void *,
+                   const char *, const char *);
+int audio_pt_fini (struct audio_pt *, const char *);
+int audio_pt_lock (struct audio_pt *, const char *);
+int audio_pt_unlock (struct audio_pt *, const char *);
+int audio_pt_wait (struct audio_pt *, const char *);
+int audio_pt_unlock_and_signal (struct audio_pt *, const char *);
+int audio_pt_join (struct audio_pt *, void **, const char *);
+
+#endif /* audio_pt_int.h */
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
index 0896b04..9cc0b9d 100644
--- a/audio/dsound_template.h
+++ b/audio/dsound_template.h
@@ -23,16 +23,20 @@
  */
 #ifdef DSBTYPE_IN
 #define NAME "capture buffer"
+#define NAME2 "DirectSoundCapture"
 #define TYPE in
 #define IFACE IDirectSoundCaptureBuffer
 #define BUFPTR LPDIRECTSOUNDCAPTUREBUFFER
 #define FIELD dsound_capture_buffer
+#define FIELD2 dsound_capture
 #else
 #define NAME "playback buffer"
+#define NAME2 "DirectSound"
 #define TYPE out
 #define IFACE IDirectSoundBuffer
 #define BUFPTR LPDIRECTSOUNDBUFFER
 #define FIELD dsound_buffer
+#define FIELD2 dsound
 #endif
 
 static int glue (dsound_unlock_, TYPE) (
@@ -192,6 +196,11 @@ static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as)
     DSBCAPS bc;
 #endif
 
+    if (!s->FIELD2) {
+        dolog ("Attempt to initialize voice without " NAME2 " object\n");
+        return -1;
+    }
+
     err = waveformat_from_audio_settings (&wfx, as);
     if (err) {
         return -1;
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index 41ddf4a..cba8c80 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -320,23 +320,22 @@ static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
 
     switch (as->fmt) {
     case AUD_FMT_S8:
-        wfx->wBitsPerSample = 8;
-        break;
-
     case AUD_FMT_U8:
         wfx->wBitsPerSample = 8;
         break;
 
     case AUD_FMT_S16:
+    case AUD_FMT_U16:
         wfx->wBitsPerSample = 16;
         wfx->nAvgBytesPerSec <<= 1;
         wfx->nBlockAlign <<= 1;
         break;
 
-    case AUD_FMT_U16:
-        wfx->wBitsPerSample = 16;
-        wfx->nAvgBytesPerSec <<= 1;
-        wfx->nBlockAlign <<= 1;
+    case AUD_FMT_S32:
+    case AUD_FMT_U32:
+        wfx->wBitsPerSample = 32;
+        wfx->nAvgBytesPerSec <<= 2;
+        wfx->nBlockAlign <<= 2;
         break;
 
     default:
@@ -387,8 +386,13 @@ static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
         as->fmt = AUD_FMT_S16;
         break;
 
+    case 32:
+        as->fmt = AUD_FMT_S32;
+        break;
+
     default:
-        dolog ("Invalid wave format, bits per sample is not 8 or 16, but %d\n",
+        dolog ("Invalid wave format, bits per sample is not "
+               "8, 16 or 32, but %d\n",
                wfx->wBitsPerSample);
         return -1;
     }
diff --git a/audio/esdaudio.c b/audio/esdaudio.c
new file mode 100644
index 0000000..fa42348
--- /dev/null
+++ b/audio/esdaudio.c
@@ -0,0 +1,591 @@
+/*
+ * QEMU ESD audio driver
+ *
+ * Copyright (c) 2006 Frederick Reeve (brushed up by malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <esd.h>
+#include "qemu-common.h"
+#include "audio.h"
+#include <signal.h>
+
+#define AUDIO_CAP "esd"
+#include "audio_int.h"
+#include "audio_pt_int.h"
+
+typedef struct {
+    HWVoiceOut hw;
+    int done;
+    int live;
+    int decr;
+    int rpos;
+    void *pcm_buf;
+    int fd;
+    struct audio_pt pt;
+} ESDVoiceOut;
+
+typedef struct {
+    HWVoiceIn hw;
+    int done;
+    int dead;
+    int incr;
+    int wpos;
+    void *pcm_buf;
+    int fd;
+    struct audio_pt pt;
+} ESDVoiceIn;
+
+static struct {
+    int samples;
+    int divisor;
+    char *dac_host;
+    char *adc_host;
+} conf = {
+    1024,
+    2,
+    NULL,
+    NULL
+};
+
+static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start (ap, fmt);
+    AUD_vlog (AUDIO_CAP, fmt, ap);
+    va_end (ap);
+
+    AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+/* playback */
+static void *qesd_thread_out (void *arg)
+{
+    ESDVoiceOut *esd = arg;
+    HWVoiceOut *hw = &esd->hw;
+    int threshold;
+
+    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+    for (;;) {
+        int decr, to_mix, rpos;
+
+        for (;;) {
+            if (esd->done) {
+                goto exit;
+            }
+
+            if (esd->live > threshold) {
+                break;
+            }
+
+            if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
+                goto exit;
+            }
+        }
+
+        decr = to_mix = esd->live;
+        rpos = hw->rpos;
+
+        if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
+            return NULL;
+        }
+
+        while (to_mix) {
+            ssize_t written;
+            int chunk = audio_MIN (to_mix, hw->samples - rpos);
+            st_sample_t *src = hw->mix_buf + rpos;
+
+            hw->clip (esd->pcm_buf, src, chunk);
+
+        again:
+            written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
+            if (written == -1) {
+                if (errno == EINTR || errno == EAGAIN) {
+                    goto again;
+                }
+                qesd_logerr (errno, "write failed\n");
+                return NULL;
+            }
+
+            if (written != chunk << hw->info.shift) {
+                int wsamples = written >> hw->info.shift;
+                int wbytes = wsamples << hw->info.shift;
+                if (wbytes != written) {
+                    dolog ("warning: Misaligned write %d (requested %d), "
+                           "alignment %d\n",
+                           wbytes, written, hw->info.align + 1);
+                }
+                to_mix -= wsamples;
+                rpos = (rpos + wsamples) % hw->samples;
+                break;
+            }
+
+            rpos = (rpos + chunk) % hw->samples;
+            to_mix -= chunk;
+        }
+
+        if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+            return NULL;
+        }
+
+        esd->rpos = rpos;
+        esd->live -= decr;
+        esd->decr += decr;
+    }
+
+ exit:
+    audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+    return NULL;
+}
+
+static int qesd_run_out (HWVoiceOut *hw)
+{
+    int live, decr;
+    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+
+    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+        return 0;
+    }
+
+    live = audio_pcm_hw_get_live_out (hw);
+    decr = audio_MIN (live, esd->decr);
+    esd->decr -= decr;
+    esd->live = live - decr;
+    hw->rpos = esd->rpos;
+    if (esd->live > 0) {
+        audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+    }
+    else {
+        audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+    }
+    return decr;
+}
+
+static int qesd_write (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int qesd_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+    audsettings_t obt_as = *as;
+    int esdfmt = ESD_STREAM | ESD_PLAY;
+    int err;
+    sigset_t set, old_set;
+
+    sigfillset (&set);
+
+    esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+    case AUD_FMT_U8:
+        esdfmt |= ESD_BITS8;
+        obt_as.fmt = AUD_FMT_U8;
+        break;
+
+    case AUD_FMT_S32:
+    case AUD_FMT_U32:
+        dolog ("Will use 16 instead of 32 bit samples\n");
+
+    case AUD_FMT_S16:
+    case AUD_FMT_U16:
+    deffmt:
+        esdfmt |= ESD_BITS16;
+        obt_as.fmt = AUD_FMT_S16;
+        break;
+
+    default:
+        dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
+#ifdef DEBUG_FMOD
+        abort ();
+#endif
+        goto deffmt;
+
+    }
+    obt_as.endianness = 0;
+
+    audio_pcm_init_info (&hw->info, &obt_as);
+
+    hw->samples = conf.samples;
+    esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+    if (!esd->pcm_buf) {
+        dolog ("Could not allocate buffer (%d bytes)\n",
+               hw->samples << hw->info.shift);
+        return -1;
+    }
+
+    esd->fd = -1;
+    err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
+    if (err) {
+        qesd_logerr (err, "pthread_sigmask failed\n");
+        goto fail1;
+    }
+
+    esd->fd = esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
+    if (esd->fd < 0) {
+        qesd_logerr (errno, "esd_play_stream failed\n");
+        goto fail2;
+    }
+
+    if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
+        goto fail3;
+    }
+
+    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
+    if (err) {
+        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
+    }
+
+    return 0;
+
+ fail3:
+    if (close (esd->fd)) {
+        qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+                     AUDIO_FUNC, esd->fd);
+    }
+    esd->fd = -1;
+
+ fail2:
+    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
+    if (err) {
+        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
+    }
+
+ fail1:
+    qemu_free (esd->pcm_buf);
+    esd->pcm_buf = NULL;
+    return -1;
+}
+
+static void qesd_fini_out (HWVoiceOut *hw)
+{
+    void *ret;
+    ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+
+    audio_pt_lock (&esd->pt, AUDIO_FUNC);
+    esd->done = 1;
+    audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+    audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
+
+    if (esd->fd >= 0) {
+        if (close (esd->fd)) {
+            qesd_logerr (errno, "failed to close esd socket\n");
+        }
+        esd->fd = -1;
+    }
+
+    audio_pt_fini (&esd->pt, AUDIO_FUNC);
+
+    qemu_free (esd->pcm_buf);
+    esd->pcm_buf = NULL;
+}
+
+static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+/* capture */
+static void *qesd_thread_in (void *arg)
+{
+    ESDVoiceIn *esd = arg;
+    HWVoiceIn *hw = &esd->hw;
+    int threshold;
+
+    threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+    for (;;) {
+        int incr, to_grab, wpos;
+
+        for (;;) {
+            if (esd->done) {
+                goto exit;
+            }
+
+            if (esd->dead > threshold) {
+                break;
+            }
+
+            if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
+                goto exit;
+            }
+        }
+
+        incr = to_grab = esd->dead;
+        wpos = hw->wpos;
+
+        if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
+            return NULL;
+        }
+
+        while (to_grab) {
+            ssize_t nread;
+            int chunk = audio_MIN (to_grab, hw->samples - wpos);
+            void *buf = advance (esd->pcm_buf, wpos);
+
+        again:
+            nread = read (esd->fd, buf, chunk << hw->info.shift);
+            if (nread == -1) {
+                if (errno == EINTR || errno == EAGAIN) {
+                    goto again;
+                }
+                qesd_logerr (errno, "read failed\n");
+                return NULL;
+            }
+
+            if (nread != chunk << hw->info.shift) {
+                int rsamples = nread >> hw->info.shift;
+                int rbytes = rsamples << hw->info.shift;
+                if (rbytes != nread) {
+                    dolog ("warning: Misaligned write %d (requested %d), "
+                           "alignment %d\n",
+                           rbytes, nread, hw->info.align + 1);
+                }
+                to_grab -= rsamples;
+                wpos = (wpos + rsamples) % hw->samples;
+                break;
+            }
+
+            hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift,
+                      &nominal_volume);
+            wpos = (wpos + chunk) % hw->samples;
+            to_grab -= chunk;
+        }
+
+        if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+            return NULL;
+        }
+
+        esd->wpos = wpos;
+        esd->dead -= incr;
+        esd->incr += incr;
+    }
+
+ exit:
+    audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+    return NULL;
+}
+
+static int qesd_run_in (HWVoiceIn *hw)
+{
+    int live, incr, dead;
+    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+
+    if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+        return 0;
+    }
+
+    live = audio_pcm_hw_get_live_in (hw);
+    dead = hw->samples - live;
+    incr = audio_MIN (dead, esd->incr);
+    esd->incr -= incr;
+    esd->dead = dead - incr;
+    hw->wpos = esd->wpos;
+    if (esd->dead > 0) {
+        audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+    }
+    else {
+        audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+    }
+    return incr;
+}
+
+static int qesd_read (SWVoiceIn *sw, void *buf, int len)
+{
+    return audio_pcm_sw_read (sw, buf, len);
+}
+
+static int qesd_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+    audsettings_t obt_as = *as;
+    int esdfmt = ESD_STREAM | ESD_RECORD;
+    int err;
+    sigset_t set, old_set;
+
+    sigfillset (&set);
+
+    esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+    case AUD_FMT_U8:
+        esdfmt |= ESD_BITS8;
+        obt_as.fmt = AUD_FMT_U8;
+        break;
+
+    case AUD_FMT_S16:
+    case AUD_FMT_U16:
+        esdfmt |= ESD_BITS16;
+        obt_as.fmt = AUD_FMT_S16;
+        break;
+
+    case AUD_FMT_S32:
+    case AUD_FMT_U32:
+        dolog ("Will use 16 instead of 32 bit samples\n");
+        esdfmt |= ESD_BITS16;
+        obt_as.fmt = AUD_FMT_S16;
+        break;
+    }
+    obt_as.endianness = 0;
+
+    audio_pcm_init_info (&hw->info, &obt_as);
+
+    hw->samples = conf.samples;
+    esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+    if (!esd->pcm_buf) {
+        dolog ("Could not allocate buffer (%d bytes)\n",
+               hw->samples << hw->info.shift);
+        return -1;
+    }
+
+    esd->fd = -1;
+
+    err = pthread_sigmask (SIG_BLOCK, &set, &old_set);
+    if (err) {
+        qesd_logerr (err, "pthread_sigmask failed\n");
+        goto fail1;
+    }
+
+    esd->fd = esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
+    if (esd->fd < 0) {
+        qesd_logerr (errno, "esd_record_stream failed\n");
+        goto fail2;
+    }
+
+    if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
+        goto fail3;
+    }
+
+    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
+    if (err) {
+        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
+    }
+
+    return 0;
+
+ fail3:
+    if (close (esd->fd)) {
+        qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+                     AUDIO_FUNC, esd->fd);
+    }
+    esd->fd = -1;
+
+ fail2:
+    err = pthread_sigmask (SIG_SETMASK, &old_set, NULL);
+    if (err) {
+        qesd_logerr (err, "pthread_sigmask(restore) failed\n");
+    }
+
+ fail1:
+    qemu_free (esd->pcm_buf);
+    esd->pcm_buf = NULL;
+    return -1;
+}
+
+static void qesd_fini_in (HWVoiceIn *hw)
+{
+    void *ret;
+    ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+
+    audio_pt_lock (&esd->pt, AUDIO_FUNC);
+    esd->done = 1;
+    audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+    audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
+
+    if (esd->fd >= 0) {
+        if (close (esd->fd)) {
+            qesd_logerr (errno, "failed to close esd socket\n");
+        }
+        esd->fd = -1;
+    }
+
+    audio_pt_fini (&esd->pt, AUDIO_FUNC);
+
+    qemu_free (esd->pcm_buf);
+    esd->pcm_buf = NULL;
+}
+
+static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+/* common */
+static void *qesd_audio_init (void)
+{
+    return &conf;
+}
+
+static void qesd_audio_fini (void *opaque)
+{
+    (void) opaque;
+    ldebug ("esd_fini");
+}
+
+struct audio_option qesd_options[] = {
+    {"SAMPLES", AUD_OPT_INT, &conf.samples,
+     "buffer size in samples", NULL, 0},
+
+    {"DIVISOR", AUD_OPT_INT, &conf.divisor,
+     "threshold divisor", NULL, 0},
+
+    {"DAC_HOST", AUD_OPT_STR, &conf.dac_host,
+     "playback host", NULL, 0},
+
+    {"ADC_HOST", AUD_OPT_STR, &conf.adc_host,
+     "capture host", NULL, 0},
+
+    {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+struct audio_pcm_ops qesd_pcm_ops = {
+    qesd_init_out,
+    qesd_fini_out,
+    qesd_run_out,
+    qesd_write,
+    qesd_ctl_out,
+
+    qesd_init_in,
+    qesd_fini_in,
+    qesd_run_in,
+    qesd_read,
+    qesd_ctl_in,
+};
+
+struct audio_driver esd_audio_driver = {
+    INIT_FIELD (name           = ) "esd",
+    INIT_FIELD (descr          = )
+    "http://en.wikipedia.org/wiki/Esound",
+    INIT_FIELD (options        = ) qesd_options,
+    INIT_FIELD (init           = ) qesd_audio_init,
+    INIT_FIELD (fini           = ) qesd_audio_fini,
+    INIT_FIELD (pcm_ops        = ) &qesd_pcm_ops,
+    INIT_FIELD (can_be_default = ) 0,
+    INIT_FIELD (max_voices_out = ) INT_MAX,
+    INIT_FIELD (max_voices_in  = ) INT_MAX,
+    INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut),
+    INIT_FIELD (voice_size_in  = ) sizeof (ESDVoiceIn)
+};
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 5a91556..2a300c2 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -150,7 +150,7 @@ static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness)
 {
     switch (ossfmt) {
     case AFMT_S8:
-        *endianness =0;
+        *endianness = 0;
         *fmt = AUD_FMT_S8;
         break;
 
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index 11ca86d..ea7c6a8 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -44,7 +44,7 @@ static struct {
         44100,
         2,
         AUD_FMT_S16,
-        AUDIO_HOST_ENDIANNESS
+        0
     },
     "qemu.wav"
 };
diff --git a/block-vmdk.c b/block-vmdk.c
index af979a1..9b5fb73 100644
--- a/block-vmdk.c
+++ b/block-vmdk.c
@@ -341,6 +341,8 @@ static int vmdk_parent_open(BlockDriverState *bs, const char * filename)
         p_name += sizeof("parentFileNameHint") + 1;
         if ((end_name = strchr(p_name,'\"')) == 0)
             return -1;
+        if ((end_name - p_name) > sizeof (s->hd->backing_file) - 1)
+            return -1;
 
         strncpy(s->hd->backing_file, p_name, end_name - p_name);
         if (stat(s->hd->backing_file, &file_buf) != 0) {
diff --git a/cocoa.m b/cocoa.m
index d26b452..8d36336 100644
--- a/cocoa.m
+++ b/cocoa.m
@@ -1,8 +1,7 @@
 /*
- * QEMU Cocoa display driver
+ * QEMU Cocoa CG display driver
  *
- * Copyright (c) 2005 Pierre d'Herbemont
- *                    many code/inspiration from SDL 1.2 code (LGPL)
+ * Copyright (c) 2008 Mike Kronenberg
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -22,18 +21,6 @@
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  * THE SOFTWARE.
  */
-/*
-    Todo :    x  miniaturize window
-              x  center the window
-              -  save window position
-              -  handle keyboard event
-              -  handle mouse event
-              -  non 32 bpp support
-              -  full screen
-              -  mouse focus
-              x  simple graphical prompt to demo
-              -  better graphical prompt
-*/
 
 #import <Cocoa/Cocoa.h>
 
@@ -41,146 +28,41 @@
 #include "console.h"
 #include "sysemu.h"
 
-NSWindow *window = NULL;
-NSQuickDrawView *qd_view = NULL;
-
-
-int gArgc;
-char **gArgv;
-DisplayState current_ds;
-
-int grab = 0;
-int modifiers_state[256];
-
-/* main defined in qemu/vl.c */
-int qemu_main(int argc, char **argv);
-
-/* To deal with miniaturization */
-@interface QemuWindow : NSWindow
-{ }
-@end
-
-
-/*
- ------------------------------------------------------
-    Qemu Video Driver
- ------------------------------------------------------
-*/
-
-/*
- ------------------------------------------------------
-    cocoa_update
- ------------------------------------------------------
-*/
-static void cocoa_update(DisplayState *ds, int x, int y, int w, int h)
-{
-    //printf("updating x=%d y=%d w=%d h=%d\n", x, y, w, h);
-
-    /* Use QDFlushPortBuffer() to flush content to display */
-    RgnHandle dirty = NewRgn ();
-    RgnHandle temp  = NewRgn ();
-
-    SetEmptyRgn (dirty);
-
-    /* Build the region of dirty rectangles */
-    MacSetRectRgn (temp, x, y,
-                        x + w, y + h);
-    MacUnionRgn (dirty, temp, dirty);
-
-    /* Flush the dirty region */
-    QDFlushPortBuffer ( [ qd_view  qdPort ], dirty );
-    DisposeRgn (dirty);
-    DisposeRgn (temp);
-}
-
-/*
- ------------------------------------------------------
-    cocoa_resize
- ------------------------------------------------------
-*/
-static void cocoa_resize(DisplayState *ds, int w, int h)
-{
-    const int device_bpp = 32;
-    static void *screen_pixels;
-    static int  screen_pitch;
-    NSRect contentRect;
-
-    //printf("resizing to %d %d\n", w, h);
-
-    contentRect = NSMakeRect (0, 0, w, h);
-    if(window)
-    {
-        [window close];
-        [window release];
-    }
-    window = [ [ QemuWindow alloc ] initWithContentRect:contentRect
-                                  styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask
-                                  backing:NSBackingStoreBuffered defer:NO];
-    if(!window)
-    {
-        fprintf(stderr, "(cocoa) can't create window\n");
-        exit(1);
-    }
-
-    if(qd_view)
-        [qd_view release];
-
-    qd_view = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ];
-
-    if(!qd_view)
-    {
-         fprintf(stderr, "(cocoa) can't create qd_view\n");
-        exit(1);
-    }
-
-    [ window setAcceptsMouseMovedEvents:YES ];
-    [ window setTitle:@"Qemu" ];
-    [ window setReleasedWhenClosed:NO ];
-
-    /* Set screen to black */
-    [ window setBackgroundColor: [NSColor blackColor] ];
-
-    /* set window position */
-    [ window center ];
-
-    [ qd_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
-    [ [ window contentView ] addSubview:qd_view ];
-    [ qd_view release ];
-    [ window makeKeyAndOrderFront:nil ];
-
-    /* Careful here, the window seems to have to be onscreen to do that */
-    LockPortBits ( [ qd_view qdPort ] );
-    screen_pixels = GetPixBaseAddr ( GetPortPixMap ( [ qd_view qdPort ] ) );
-    screen_pitch  = GetPixRowBytes ( GetPortPixMap ( [ qd_view qdPort ] ) );
-    UnlockPortBits ( [ qd_view qdPort ] );
-    {
-            int vOffset = [ window frame ].size.height -
-                [ qd_view frame ].size.height - [ qd_view frame ].origin.y;
 
-            int hOffset = [ qd_view frame ].origin.x;
+//#define DEBUG
 
-            screen_pixels += (vOffset * screen_pitch) + hOffset * (device_bpp/8);
-    }
-    ds->data = screen_pixels;
-    ds->linesize = screen_pitch;
-    ds->depth = device_bpp;
-    ds->width = w;
-    ds->height = h;
-#ifdef __LITTLE_ENDIAN__
-    ds->bgr = 1;
+#ifdef DEBUG
+#define COCOA_DEBUG(...)  { (void) fprintf (stdout, __VA_ARGS__); }
 #else
-    ds->bgr = 0;
+#define COCOA_DEBUG(...)  ((void) 0)
 #endif
 
-    current_ds = *ds;
-}
+#define cgrect(nsrect) (*(CGRect *)&(nsrect))
+#define COCOA_MOUSE_EVENT \
+        if (isTabletEnabled) { \
+            kbd_mouse_event((int)(p.x * 0x7FFF / screen.width), (int)((screen.height - p.y) * 0x7FFF / screen.height), 0, buttons); \
+        } else if (isMouseGrabed) { \
+            kbd_mouse_event((int)[event deltaX], (int)[event deltaY], 0, buttons); \
+        } else { \
+            [NSApp sendEvent:event]; \
+        }
 
-/*
- ------------------------------------------------------
-    keymap conversion
- ------------------------------------------------------
-*/
+typedef struct {
+    int width;
+    int height;
+    int bitsPerComponent;
+    int bitsPerPixel;
+} QEMUScreen;
+
+int qemu_main(int argc, char **argv); // main defined in qemu/vl.c
+NSWindow *normalWindow;
+id cocoaView;
+static void *screenBuffer;
+
+int gArgc;
+char **gArgv;
 
+// keymap conversion
 int keymap[] =
 {
 //  SdlI    macI    macH    SdlH    104xtH  104xtC  sdl
@@ -289,7 +171,7 @@ int keymap[] =
     0,  //  102     0x66    Undefined
     87, //  103     0x67    0x57            F11     QZ_F11
     0,  //  104     0x68    Undefined
-    183,//  105     0x69    0xb7            QZ_PRINT
+    183,//  105     0x69    0xb7                    QZ_PRINT
     0,  //  106     0x6A    Undefined
     70, //  107     0x6B    0x46            SCROLL  QZ_SCROLLOCK
     0,  //  108     0x6C    Undefined
@@ -357,430 +239,566 @@ int cocoa_keycode_to_qemu(int keycode)
     return keymap[keycode];
 }
 
+
+
 /*
  ------------------------------------------------------
-    cocoa_refresh
+    QemuCocoaView
  ------------------------------------------------------
 */
-static void cocoa_refresh(DisplayState *ds)
+@interface QemuCocoaView : NSView
 {
-    //printf("cocoa_refresh \n");
-    NSDate *distantPast;
-    NSEvent *event;
-    NSAutoreleasePool *pool;
-
-    pool = [ [ NSAutoreleasePool alloc ] init ];
-    distantPast = [ NSDate distantPast ];
-
-    vga_hw_update();
-
-    do {
-        event = [ NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
-                        inMode: NSDefaultRunLoopMode dequeue:YES ];
-        if (event != nil) {
-            switch ([event type]) {
-                case NSFlagsChanged:
-                    {
-                        int keycode = cocoa_keycode_to_qemu([event keyCode]);
-
-                        if (keycode)
-                        {
-                            if (keycode == 58 || keycode == 69) {
-                                /* emulate caps lock and num lock keydown and keyup */
-                                kbd_put_keycode(keycode);
-                                kbd_put_keycode(keycode | 0x80);
-                            } else if (is_graphic_console()) {
-                                if (keycode & 0x80)
-                                    kbd_put_keycode(0xe0);
-                                if (modifiers_state[keycode] == 0) {
-                                    /* keydown */
-                                    kbd_put_keycode(keycode & 0x7f);
-                                    modifiers_state[keycode] = 1;
-                                } else {
-                                    /* keyup */
-                                    kbd_put_keycode(keycode | 0x80);
-                                    modifiers_state[keycode] = 0;
-                                }
-                            }
-                        }
-
-                        /* release Mouse grab when pressing ctrl+alt */
-                        if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask))
-                        {
-                            [window setTitle: @"QEMU"];
-                            [NSCursor unhide];
-                            CGAssociateMouseAndMouseCursorPosition ( TRUE );
-                            grab = 0;
-                        }
-                    }
-                    break;
-
-                case NSKeyDown:
-                    {
-                        int keycode = cocoa_keycode_to_qemu([event keyCode]);
-
-                        /* handle command Key Combos */
-                        if ([event modifierFlags] & NSCommandKeyMask) {
-                            switch ([event keyCode]) {
-                                /* quit */
-                                case 12: /* q key */
-                                    /* switch to windowed View */
-                                    exit(0);
-                                    return;
-                            }
-                        }
-
-                        /* handle control + alt Key Combos */
-                        if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
-                            switch (keycode) {
-                                /* toggle Monitor */
-                                case 0x02 ... 0x0a: /* '1' to '9' keys */
-                                    console_select(keycode - 0x02);
-                                    break;
-                            }
-                        } else {
-                            /* handle standard key events */
-                            if (is_graphic_console()) {
-                                if (keycode & 0x80) //check bit for e0 in front
-                                    kbd_put_keycode(0xe0);
-                                kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front
-                            /* handle monitor key events */
-                            } else {
-                                int keysym = 0;
-
-                                switch([event keyCode]) {
-                                case 115:
-                                    keysym = QEMU_KEY_HOME;
-                                    break;
-                                case 117:
-                                    keysym = QEMU_KEY_DELETE;
-                                    break;
-                                case 119:
-                                    keysym = QEMU_KEY_END;
-                                    break;
-                                case 123:
-                                    keysym = QEMU_KEY_LEFT;
-                                    break;
-                                case 124:
-                                    keysym = QEMU_KEY_RIGHT;
-                                    break;
-                                case 125:
-                                    keysym = QEMU_KEY_DOWN;
-                                    break;
-                                case 126:
-                                    keysym = QEMU_KEY_UP;
-                                    break;
-                                default:
-                                    {
-                                        NSString *ks = [event characters];
-
-                                        if ([ks length] > 0)
-                                            keysym = [ks characterAtIndex:0];
-                                    }
-                                }
-                                if (keysym)
-                                    kbd_put_keysym(keysym);
-                            }
-                        }
-                    }
-                    break;
-
-                case NSKeyUp:
-                    {
-                        int keycode = cocoa_keycode_to_qemu([event keyCode]);
-                        if (is_graphic_console()) {
-                            if (keycode & 0x80)
-                                kbd_put_keycode(0xe0);
-                            kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key
-                        }
-                    }
-                    break;
-
-                case NSMouseMoved:
-                    if (grab) {
-                        int dx = [event deltaX];
-                        int dy = [event deltaY];
-                        int dz = [event deltaZ];
-                        int buttons = 0;
-                        kbd_mouse_event(dx, dy, dz, buttons);
-                    }
-                    break;
-
-                case NSLeftMouseDown:
-                    if (grab) {
-                        int buttons = 0;
-
-                        /* leftclick+command simulates rightclick */
-                        if ([event modifierFlags] & NSCommandKeyMask) {
-                            buttons |= MOUSE_EVENT_RBUTTON;
-                        } else {
-                            buttons |= MOUSE_EVENT_LBUTTON;
-                        }
-                        kbd_mouse_event(0, 0, 0, buttons);
-                    } else {
-                        [NSApp sendEvent: event];
-                    }
-                    break;
-
-                case NSLeftMouseDragged:
-                    if (grab) {
-                        int dx = [event deltaX];
-                        int dy = [event deltaY];
-                        int dz = [event deltaZ];
-                        int buttons = 0;
-                        if ([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) { //leftclick+command simulates rightclick
-                            buttons |= MOUSE_EVENT_RBUTTON;
-                        } else {
-                            buttons |= MOUSE_EVENT_LBUTTON;
-                        }
-                        kbd_mouse_event(dx, dy, dz, buttons);
-                    }
-                    break;
-
-                case NSLeftMouseUp:
-                    if (grab) {
-                        kbd_mouse_event(0, 0, 0, 0);
-                    } else {
-                        [window setTitle: @"QEMU (Press ctrl + alt to release Mouse)"];
-                        [NSCursor hide];
-                        CGAssociateMouseAndMouseCursorPosition ( FALSE );
-                        grab = 1;
-                        //[NSApp sendEvent: event];
-                    }
-                    break;
+    QEMUScreen screen;
+    NSWindow *fullScreenWindow;
+    float cx,cy,cw,ch,cdx,cdy;
+    CGDataProviderRef dataProviderRef;
+    int modifiers_state[256];
+    BOOL isMouseGrabed;
+    BOOL isFullscreen;
+    BOOL isAbsoluteEnabled;
+    BOOL isTabletEnabled;
+}
+- (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds;
+- (void) grabMouse;
+- (void) ungrabMouse;
+- (void) toggleFullScreen:(id)sender;
+- (void) handleEvent:(NSEvent *)event;
+- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;
+- (BOOL) isMouseGrabed;
+- (BOOL) isAbsoluteEnabled;
+- (float) cdx;
+- (float) cdy;
+- (QEMUScreen) gscreen;
+@end
 
-                case NSRightMouseDown:
-                    if (grab) {
-                        int buttons = 0;
+@implementation QemuCocoaView
+- (id)initWithFrame:(NSRect)frameRect
+{
+    COCOA_DEBUG("QemuCocoaView: initWithFrame\n");
 
-                        buttons |= MOUSE_EVENT_RBUTTON;
-                        kbd_mouse_event(0, 0, 0, buttons);
-                    } else {
-                        [NSApp sendEvent: event];
-                    }
-                    break;
+    self = [super initWithFrame:frameRect];
+    if (self) {
 
-                case NSRightMouseDragged:
-                    if (grab) {
-                        int dx = [event deltaX];
-                        int dy = [event deltaY];
-                        int dz = [event deltaZ];
-                        int buttons = 0;
-                        buttons |= MOUSE_EVENT_RBUTTON;
-                        kbd_mouse_event(dx, dy, dz, buttons);
-                    }
-                    break;
+        screen.bitsPerComponent = 8;
+        screen.bitsPerPixel = 32;
+        screen.width = frameRect.size.width;
+        screen.height = frameRect.size.height;
 
-                case NSRightMouseUp:
-                    if (grab) {
-                        kbd_mouse_event(0, 0, 0, 0);
-                    } else {
-                        [NSApp sendEvent: event];
-                    }
-                    break;
+    }
+    return self;
+}
 
-                case NSOtherMouseDragged:
-                    if (grab) {
-                        int dx = [event deltaX];
-                        int dy = [event deltaY];
-                        int dz = [event deltaZ];
-                        int buttons = 0;
-                        buttons |= MOUSE_EVENT_MBUTTON;
-                        kbd_mouse_event(dx, dy, dz, buttons);
-                    }
-                    break;
+- (void) dealloc
+{
+    COCOA_DEBUG("QemuCocoaView: dealloc\n");
 
-                case NSOtherMouseDown:
-                    if (grab) {
-                        int buttons = 0;
-                        buttons |= MOUSE_EVENT_MBUTTON;
-                        kbd_mouse_event(0, 0, 0, buttons);
-                    } else {
-                        [NSApp sendEvent:event];
-                    }
-                    break;
+    if (screenBuffer)
+        free(screenBuffer);
 
-                case NSOtherMouseUp:
-                    if (grab) {
-                        kbd_mouse_event(0, 0, 0, 0);
-                    } else {
-                        [NSApp sendEvent: event];
-                    }
-                    break;
+    if (dataProviderRef)
+        CGDataProviderRelease(dataProviderRef);
 
-                case NSScrollWheel:
-                    if (grab) {
-                        int dz = [event deltaY];
-                        kbd_mouse_event(0, 0, -dz, 0);
-                    }
-                    break;
+    [super dealloc];
+}
 
-                default: [NSApp sendEvent:event];
+- (void) drawRect:(NSRect) rect
+{
+    COCOA_DEBUG("QemuCocoaView: drawRect\n");
+
+    if ((int)screenBuffer == -1)
+        return;
+
+    // get CoreGraphic context
+    CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort];
+    CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone);
+    CGContextSetShouldAntialias (viewContextRef, NO);
+
+    // draw screen bitmap directly to Core Graphics context
+    if (dataProviderRef) {
+        CGImageRef imageRef = CGImageCreate(
+            screen.width, //width
+            screen.height, //height
+            screen.bitsPerComponent, //bitsPerComponent
+            screen.bitsPerPixel, //bitsPerPixel
+            (screen.width * 4), //bytesPerRow
+#if __LITTLE_ENDIAN__
+            CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), //colorspace for OS X >= 10.4
+            kCGImageAlphaNoneSkipLast,
+#else
+            CGColorSpaceCreateDeviceRGB(), //colorspace for OS X < 10.4 (actually ppc)
+            kCGImageAlphaNoneSkipFirst, //bitmapInfo
+#endif
+            dataProviderRef, //provider
+            NULL, //decode
+            0, //interpolate
+            kCGRenderingIntentDefault //intent
+        );
+// test if host support "CGImageCreateWithImageInRect" at compiletime
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+        if (CGImageCreateWithImageInRect == NULL) { // test if "CGImageCreateWithImageInRect" is supported on host at runtime
+#endif
+            // compatibility drawing code (draws everything) (OS X < 10.4)
+            CGContextDrawImage (viewContextRef, CGRectMake(0, 0, [self bounds].size.width, [self bounds].size.height), imageRef);
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+        } else {
+            // selective drawing code (draws only dirty rectangles) (OS X >= 10.4)
+            const NSRect *rectList;
+            int rectCount;
+            int i;
+            CGImageRef clipImageRef;
+            CGRect clipRect;
+
+            [self getRectsBeingDrawn:&rectList count:&rectCount];
+            for (i = 0; i < rectCount; i++) {
+                clipRect.origin.x = rectList[i].origin.x / cdx;
+                clipRect.origin.y = (float)screen.height - (rectList[i].origin.y + rectList[i].size.height) / cdy;
+                clipRect.size.width = rectList[i].size.width / cdx;
+                clipRect.size.height = rectList[i].size.height / cdy;
+                clipImageRef = CGImageCreateWithImageInRect(
+                    imageRef,
+                    clipRect
+                );
+                CGContextDrawImage (viewContextRef, cgrect(rectList[i]), clipImageRef);
+                CGImageRelease (clipImageRef);
             }
         }
-    } while(event != nil);
+#endif
+        CGImageRelease (imageRef);
+    }
 }
 
-/*
- ------------------------------------------------------
-    cocoa_cleanup
- ------------------------------------------------------
-*/
-
-static void cocoa_cleanup(void)
+- (void) setContentDimensions
 {
-
+    COCOA_DEBUG("QemuCocoaView: setContentDimensions\n");
+
+    if (isFullscreen) {
+        cdx = [[NSScreen mainScreen] frame].size.width / (float)screen.width;
+        cdy = [[NSScreen mainScreen] frame].size.height / (float)screen.height;
+        cw = screen.width * cdx;
+        ch = screen.height * cdy;
+        cx = ([[NSScreen mainScreen] frame].size.width - cw) / 2.0;
+        cy = ([[NSScreen mainScreen] frame].size.height - ch) / 2.0;
+    } else {
+        cx = 0;
+        cy = 0;
+        cw = screen.width;
+        ch = screen.height;
+        cdx = 1.0;
+        cdy = 1.0;
+    }
 }
 
-/*
- ------------------------------------------------------
-    cocoa_display_init
- ------------------------------------------------------
-*/
-
-void cocoa_display_init(DisplayState *ds, int full_screen)
+- (void) resizeContentToWidth:(int)w height:(int)h displayState:(DisplayState *)ds
 {
-    ds->dpy_update = cocoa_update;
-    ds->dpy_resize = cocoa_resize;
-    ds->dpy_refresh = cocoa_refresh;
+    COCOA_DEBUG("QemuCocoaView: resizeContent\n");
+
+    // update screenBuffer
+    if (dataProviderRef)
+        CGDataProviderRelease(dataProviderRef);
+    if (screenBuffer)
+        free(screenBuffer);
+    screenBuffer = malloc( w * 4 * h );
+
+    ds->data = screenBuffer;
+    ds->linesize =  (w * 4);
+    ds->depth = 32;
+    ds->width = w;
+    ds->height = h;
+#ifdef __LITTLE_ENDIAN__
+    ds->bgr = 1;
+#else
+    ds->bgr = 0;
+#endif
 
-    cocoa_resize(ds, 640, 400);
+    dataProviderRef = CGDataProviderCreateWithData(NULL, screenBuffer, w * 4 * h, NULL);
 
-    atexit(cocoa_cleanup);
+    // update windows
+    if (isFullscreen) {
+        [[fullScreenWindow contentView] setFrame:[[NSScreen mainScreen] frame]];
+        [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:NO animate:NO];
+    } else {
+        if (qemu_name)
+            [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
+        [normalWindow setFrame:NSMakeRect([normalWindow frame].origin.x, [normalWindow frame].origin.y - h + screen.height, w, h + [normalWindow frame].size.height - screen.height) display:YES animate:YES];
+    }
+    screen.width = w;
+    screen.height = h;
+    [self setContentDimensions];
+    [self setFrame:NSMakeRect(cx, cy, cw, ch)];
 }
 
-/*
- ------------------------------------------------------
-    Interface with Cocoa
- ------------------------------------------------------
-*/
-
+- (void) toggleFullScreen:(id)sender
+{
+    COCOA_DEBUG("QemuCocoaView: toggleFullScreen\n");
+
+    if (isFullscreen) { // switch from fullscreen to desktop
+        isFullscreen = FALSE;
+        [self ungrabMouse];
+        [self setContentDimensions];
+// test if host support "enterFullScreenMode:withOptions" at compiletime
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+        if ([NSView respondsToSelector:@selector(exitFullScreenModeWithOptions:)]) { // test if "exitFullScreenModeWithOptions" is supported on host at runtime
+            [self exitFullScreenModeWithOptions:nil];
+        } else {
+#endif
+            [fullScreenWindow close];
+            [normalWindow setContentView: self];
+            [normalWindow makeKeyAndOrderFront: self];
+            [NSMenu setMenuBarVisible:YES];
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+        }
+#endif
+    } else { // switch from desktop to fullscreen
+        isFullscreen = TRUE;
+        [self grabMouse];
+        [self setContentDimensions];
+// test if host support "enterFullScreenMode:withOptions" at compiletime
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+        if ([NSView respondsToSelector:@selector(enterFullScreenMode:withOptions:)]) { // test if "enterFullScreenMode:withOptions" is supported on host at runtime
+            [self enterFullScreenMode:[NSScreen mainScreen] withOptions:[NSDictionary dictionaryWithObjectsAndKeys:
+                [NSNumber numberWithBool:NO], NSFullScreenModeAllScreens,
+                [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], kCGDisplayModeIsStretched, nil], NSFullScreenModeSetting,
+                 nil]];
+        } else {
+#endif
+            [NSMenu setMenuBarVisible:NO];
+            fullScreenWindow = [[NSWindow alloc] initWithContentRect:[[NSScreen mainScreen] frame]
+                styleMask:NSBorderlessWindowMask
+                backing:NSBackingStoreBuffered
+                defer:NO];
+            [fullScreenWindow setHasShadow:NO];
+            [fullScreenWindow setContentView:self];
+            [fullScreenWindow makeKeyAndOrderFront:self];
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+        }
+#endif
+    }
+}
 
-/*
- ------------------------------------------------------
-    QemuWindow
-    Some trick from SDL to use miniwindow
- ------------------------------------------------------
-*/
-static void QZ_SetPortAlphaOpaque ()
+- (void) handleEvent:(NSEvent *)event
 {
-    /* Assume 32 bit if( bpp == 32 )*/
-    if ( 1 ) {
+    COCOA_DEBUG("QemuCocoaView: handleEvent\n");
+
+    int buttons = 0;
+    int keycode;
+    NSPoint p = [event locationInWindow];
+
+    switch ([event type]) {
+        case NSFlagsChanged:
+            keycode = cocoa_keycode_to_qemu([event keyCode]);
+            if (keycode) {
+                if (keycode == 58 || keycode == 69) { // emulate caps lock and num lock keydown and keyup
+                    kbd_put_keycode(keycode);
+                    kbd_put_keycode(keycode | 0x80);
+                } else if (is_graphic_console()) {
+                    if (keycode & 0x80)
+                        kbd_put_keycode(0xe0);
+                    if (modifiers_state[keycode] == 0) { // keydown
+                        kbd_put_keycode(keycode & 0x7f);
+                        modifiers_state[keycode] = 1;
+                    } else { // keyup
+                        kbd_put_keycode(keycode | 0x80);
+                        modifiers_state[keycode] = 0;
+                    }
+                }
+            }
 
-        uint32_t    *pixels = (uint32_t*) current_ds.data;
-        uint32_t    rowPixels = current_ds.linesize / 4;
-        uint32_t    i, j;
+            // release Mouse grab when pressing ctrl+alt
+            if (!isFullscreen && ([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
+                [self ungrabMouse];
+            }
+            break;
+        case NSKeyDown:
 
-        for (i = 0; i < current_ds.height; i++)
-            for (j = 0; j < current_ds.width; j++) {
+            // forward command Key Combos
+            if ([event modifierFlags] & NSCommandKeyMask) {
+                [NSApp sendEvent:event];
+                return;
+            }
 
-                pixels[ (i * rowPixels) + j ] |= 0xFF000000;
+            // default
+            keycode = cocoa_keycode_to_qemu([event keyCode]);
+
+            // handle control + alt Key Combos (ctrl+alt is reserved for QEMU)
+            if (([event modifierFlags] & NSControlKeyMask) && ([event modifierFlags] & NSAlternateKeyMask)) {
+                switch (keycode) {
+
+                    // enable graphic console
+                    case 0x02 ... 0x0a: // '1' to '9' keys
+                        console_select(keycode - 0x02);
+                        break;
+                }
+
+            // handle keys for graphic console
+            } else if (is_graphic_console()) {
+                if (keycode & 0x80) //check bit for e0 in front
+                    kbd_put_keycode(0xe0);
+                kbd_put_keycode(keycode & 0x7f); //remove e0 bit in front
+
+            // handlekeys for Monitor
+            } else {
+                int keysym = 0;
+                switch([event keyCode]) {
+                case 115:
+                    keysym = QEMU_KEY_HOME;
+                    break;
+                case 117:
+                    keysym = QEMU_KEY_DELETE;
+                    break;
+                case 119:
+                    keysym = QEMU_KEY_END;
+                    break;
+                case 123:
+                    keysym = QEMU_KEY_LEFT;
+                    break;
+                case 124:
+                    keysym = QEMU_KEY_RIGHT;
+                    break;
+                case 125:
+                    keysym = QEMU_KEY_DOWN;
+                    break;
+                case 126:
+                    keysym = QEMU_KEY_UP;
+                    break;
+                default:
+                    {
+                        NSString *ks = [event characters];
+                        if ([ks length] > 0)
+                            keysym = [ks characterAtIndex:0];
+                    }
+                }
+                if (keysym)
+                    kbd_put_keysym(keysym);
+            }
+            break;
+        case NSKeyUp:
+            keycode = cocoa_keycode_to_qemu([event keyCode]);
+            if (is_graphic_console()) {
+                if (keycode & 0x80)
+                    kbd_put_keycode(0xe0);
+                kbd_put_keycode(keycode | 0x80); //add 128 to signal release of key
+            }
+            break;
+        case NSMouseMoved:
+            if (isAbsoluteEnabled) {
+                if (p.x < 0 || p.x > screen.width || p.y < 0 || p.y > screen.height || ![[self window] isKeyWindow]) {
+                    if (isTabletEnabled) { // if we leave the window, deactivate the tablet
+                        [NSCursor unhide];
+                        isTabletEnabled = FALSE;
+                    }
+                } else {
+                    if (!isTabletEnabled) { // if we enter the window, activate the tablet
+                        [NSCursor hide];
+                        isTabletEnabled = TRUE;
+                    }
+                }
             }
+            COCOA_MOUSE_EVENT
+            break;
+        case NSLeftMouseDown:
+            if ([event modifierFlags] & NSCommandKeyMask) {
+                buttons |= MOUSE_EVENT_RBUTTON;
+            } else {
+                buttons |= MOUSE_EVENT_LBUTTON;
+            }
+            COCOA_MOUSE_EVENT
+            break;
+        case NSRightMouseDown:
+            buttons |= MOUSE_EVENT_RBUTTON;
+            COCOA_MOUSE_EVENT
+            break;
+        case NSOtherMouseDown:
+            buttons |= MOUSE_EVENT_MBUTTON;
+            COCOA_MOUSE_EVENT
+            break;
+        case NSLeftMouseDragged:
+            if ([event modifierFlags] & NSCommandKeyMask) {
+                buttons |= MOUSE_EVENT_RBUTTON;
+            } else {
+                buttons |= MOUSE_EVENT_LBUTTON;
+            }
+            COCOA_MOUSE_EVENT
+            break;
+        case NSRightMouseDragged:
+            buttons |= MOUSE_EVENT_RBUTTON;
+            COCOA_MOUSE_EVENT
+            break;
+        case NSOtherMouseDragged:
+            buttons |= MOUSE_EVENT_MBUTTON;
+            COCOA_MOUSE_EVENT
+            break;
+        case NSLeftMouseUp:
+            if (isTabletEnabled) {
+                    COCOA_MOUSE_EVENT
+            } else if (!isMouseGrabed) {
+                if (p.x > -1 && p.x < screen.width && p.y > -1 && p.y < screen.height) {
+                    [self grabMouse];
+                } else {
+                    [NSApp sendEvent:event];
+                }
+            } else {
+                COCOA_MOUSE_EVENT
+            }
+            break;
+        case NSRightMouseUp:
+            COCOA_MOUSE_EVENT
+            break;
+        case NSOtherMouseUp:
+            COCOA_MOUSE_EVENT
+            break;
+        case NSScrollWheel:
+            if (isTabletEnabled || isMouseGrabed) {
+                kbd_mouse_event(0, 0, -[event deltaY], 0);
+            } else {
+                [NSApp sendEvent:event];
+            }
+            break;
+        default:
+            [NSApp sendEvent:event];
     }
 }
 
-@implementation QemuWindow
-- (void)miniaturize:(id)sender
+- (void) grabMouse
 {
+    COCOA_DEBUG("QemuCocoaView: grabMouse\n");
 
-    /* make the alpha channel opaque so anim won't have holes in it */
-    QZ_SetPortAlphaOpaque ();
-
-    [ super miniaturize:sender ];
-
+    if (!isFullscreen) {
+        if (qemu_name)
+            [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s - (Press ctrl + alt to release Mouse)", qemu_name]];
+        else
+            [normalWindow setTitle:@"QEMU - (Press ctrl + alt to release Mouse)"];
+    }
+    [NSCursor hide];
+    CGAssociateMouseAndMouseCursorPosition(FALSE);
+    isMouseGrabed = TRUE; // while isMouseGrabed = TRUE, QemuCocoaApp sends all events to [cocoaView handleEvent:]
 }
-- (void)display
-{
-    /*
-        This method fires just before the window deminaturizes from the Dock.
-
-        We'll save the current visible surface, let the window manager redraw any
-        UI elements, and restore the SDL surface. This way, no expose event
-        is required, and the deminiaturize works perfectly.
-    */
-
-    /* make sure pixels are fully opaque */
-    QZ_SetPortAlphaOpaque ();
 
-    /* save current visible SDL surface */
-    [ self cacheImageInRect:[ qd_view frame ] ];
-
-    /* let the window manager redraw controls, border, etc */
-    [ super display ];
+- (void) ungrabMouse
+{
+    COCOA_DEBUG("QemuCocoaView: ungrabMouse\n");
 
-    /* restore visible SDL surface */
-    [ self restoreCachedImage ];
+    if (!isFullscreen) {
+        if (qemu_name)
+            [normalWindow setTitle:[NSString stringWithFormat:@"QEMU %s", qemu_name]];
+        else
+            [normalWindow setTitle:@"QEMU"];
+    }
+    [NSCursor unhide];
+    CGAssociateMouseAndMouseCursorPosition(TRUE);
+    isMouseGrabed = FALSE;
 }
 
+- (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled {isAbsoluteEnabled = tIsAbsoluteEnabled;}
+- (BOOL) isMouseGrabed {return isMouseGrabed;}
+- (BOOL) isAbsoluteEnabled {return isAbsoluteEnabled;}
+- (float) cdx {return cdx;}
+- (float) cdy {return cdy;}
+- (QEMUScreen) gscreen {return screen;}
 @end
 
 
+
 /*
  ------------------------------------------------------
-    QemuCocoaGUIController
-    NSApp's delegate - indeed main object
+    QemuCocoaAppController
  ------------------------------------------------------
 */
-
-@interface QemuCocoaGUIController : NSObject
+@interface QemuCocoaAppController : NSObject
 {
 }
-- (void)applicationDidFinishLaunching: (NSNotification *) note;
-- (void)applicationWillTerminate:(NSNotification *)aNotification;
-
-- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
-
 - (void)startEmulationWithArgc:(int)argc argv:(char**)argv;
+- (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo;
+- (void)toggleFullScreen:(id)sender;
+- (void)showQEMUDoc:(id)sender;
+- (void)showQEMUTec:(id)sender;
 @end
 
-@implementation QemuCocoaGUIController
-/* Called when the internal event loop has just started running */
-- (void)applicationDidFinishLaunching: (NSNotification *) note
+@implementation QemuCocoaAppController
+- (id) init
 {
+    COCOA_DEBUG("QemuCocoaAppController: init\n");
 
-    /* Display an open dialog box if no argument were passed or
-       if qemu was launched from the finder ( the Finder passes "-psn" ) */
+    self = [super init];
+    if (self) {
 
-    if( gArgc <= 1 || strncmp (gArgv[1], "-psn", 4) == 0)
-    {
-        NSOpenPanel *op = [[NSOpenPanel alloc] init];
+        // create a view and add it to the window
+        cocoaView = [[QemuCocoaView alloc] initWithFrame:NSMakeRect(0.0, 0.0, 640.0, 480.0)];
+        if(!cocoaView) {
+            fprintf(stderr, "(cocoa) can't create a view\n");
+            exit(1);
+        }
 
-        cocoa_resize(&current_ds, 640, 400);
+        // create a window
+        normalWindow = [[NSWindow alloc] initWithContentRect:[cocoaView frame]
+            styleMask:NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask
+            backing:NSBackingStoreBuffered defer:NO];
+        if(!normalWindow) {
+            fprintf(stderr, "(cocoa) can't create window\n");
+            exit(1);
+        }
+        [normalWindow setAcceptsMouseMovedEvents:YES];
+        [normalWindow setTitle:[NSString stringWithFormat:@"QEMU"]];
+        [normalWindow setContentView:cocoaView];
+        [normalWindow makeKeyAndOrderFront:self];
 
-        [op setPrompt:@"Boot image"];
+    }
+    return self;
+}
 
-        [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
+- (void) dealloc
+{
+    COCOA_DEBUG("QemuCocoaAppController: dealloc\n");
+
+    if (cocoaView)
+        [cocoaView release];
+    [super dealloc];
+}
 
+- (void)applicationDidFinishLaunching: (NSNotification *) note
+{
+    COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n");
+
+    // Display an open dialog box if no argument were passed or
+    // if qemu was launched from the finder ( the Finder passes "-psn" )
+    if( gArgc <= 1 || strncmp ((char *)gArgv[1], "-psn", 4) == 0) {
+        NSOpenPanel *op = [[NSOpenPanel alloc] init];
+        [op setPrompt:@"Boot image"];
+        [op setMessage:@"Select the disk image you want to boot.\n\nHit the \"Cancel\" button to quit"];
         [op beginSheetForDirectory:nil file:nil types:[NSArray arrayWithObjects:@"img",@"iso",@"dmg",@"qcow",@"cow",@"cloop",@"vmdk",nil]
-              modalForWindow:window modalDelegate:self
+              modalForWindow:normalWindow modalDelegate:self
               didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) contextInfo:NULL];
-    }
-    else
-    {
-        /* or Launch Qemu, with the global args */
-        [self startEmulationWithArgc:gArgc argv:gArgv];
+    } else {
+        // or Launch Qemu, with the global args
+        [self startEmulationWithArgc:gArgc argv:(char **)gArgv];
     }
 }
 
 - (void)applicationWillTerminate:(NSNotification *)aNotification
 {
-    printf("Application will terminate\n");
+    COCOA_DEBUG("QemuCocoaAppController: applicationWillTerminate\n");
+
     qemu_system_shutdown_request();
-    /* In order to avoid a crash */
     exit(0);
 }
 
+- (void)startEmulationWithArgc:(int)argc argv:(char**)argv
+{
+    COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n");
+
+    int status;
+    status = qemu_main(argc, argv);
+    exit(status);
+}
+
 - (void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
 {
-    if(returnCode == NSCancelButton)
-    {
-        exit(0);
-    }
+    COCOA_DEBUG("QemuCocoaAppController: openPanelDidEnd\n");
 
-    if(returnCode == NSOKButton)
-    {
+    if(returnCode == NSCancelButton) {
+        exit(0);
+    } else if(returnCode == NSOKButton) {
         char *bin = "qemu";
-        char *img = (char*)[ [ sheet filename ] cString];
+        char *img = (char*)[ [ sheet filename ] cStringUsingEncoding:NSASCIIStringEncoding];
 
         char **argv = (char**)malloc( sizeof(char*)*3 );
 
@@ -793,24 +811,33 @@ static void QZ_SetPortAlphaOpaque ()
         [self startEmulationWithArgc:3 argv:(char**)argv];
     }
 }
+- (void)toggleFullScreen:(id)sender
+{
+    COCOA_DEBUG("QemuCocoaAppController: toggleFullScreen\n");
+
+    [cocoaView toggleFullScreen:sender];
+}
 
-- (void)startEmulationWithArgc:(int)argc argv:(char**)argv
+- (void)showQEMUDoc:(id)sender
 {
-    int status;
-    /* Launch Qemu */
-    printf("starting qemu...\n");
-    status = qemu_main (argc, argv);
-    exit(status);
+    COCOA_DEBUG("QemuCocoaAppController: showQEMUDoc\n");
+
+    [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-doc.html",
+        [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"];
+}
+
+- (void)showQEMUTec:(id)sender
+{
+    COCOA_DEBUG("QemuCocoaAppController: showQEMUTec\n");
+
+    [[NSWorkspace sharedWorkspace] openFile:[NSString stringWithFormat:@"%@/../doc/qemu/qemu-tech.html",
+        [[NSBundle mainBundle] resourcePath]] withApplication:@"Help Viewer"];
 }
 @end
 
-/*
- ------------------------------------------------------
-    Application Creation
- ------------------------------------------------------
-*/
 
-/* Dock Connection */
+
+// Dock Connection
 typedef struct CPSProcessSerNum
 {
         UInt32                lo;
@@ -821,114 +848,148 @@ extern OSErr    CPSGetCurrentProcess( CPSProcessSerNum *psn);
 extern OSErr    CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
 extern OSErr    CPSSetFrontProcess( CPSProcessSerNum *psn);
 
-/* Menu Creation */
-static void setApplicationMenu(void)
-{
-    /* warning: this code is very odd */
-    NSMenu *appleMenu;
-    NSMenuItem *menuItem;
-    NSString *title;
-    NSString *appName;
-
-    appName = @"Qemu";
-    appleMenu = [[NSMenu alloc] initWithTitle:@""];
-
-    /* Add menu items */
-    title = [@"About " stringByAppendingString:appName];
-    [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
+int main (int argc, const char * argv[]) {
 
-    [appleMenu addItem:[NSMenuItem separatorItem]];
-
-    title = [@"Hide " stringByAppendingString:appName];
-    [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
+    gArgc = argc;
+    gArgv = (char **)argv;
+    CPSProcessSerNum PSN;
 
-    menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
-    [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+    [NSApplication sharedApplication];
 
-    [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
+    if (!CPSGetCurrentProcess(&PSN))
+        if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
+            if (!CPSSetFrontProcess(&PSN))
+                [NSApplication sharedApplication];
 
-    [appleMenu addItem:[NSMenuItem separatorItem]];
+    // Add menus
+    NSMenu      *menu;
+    NSMenuItem  *menuItem;
 
-    title = [@"Quit " stringByAppendingString:appName];
-    [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
+    [NSApp setMainMenu:[[NSMenu alloc] init]];
 
+    // Application menu
+    menu = [[NSMenu alloc] initWithTitle:@""];
+    [menu addItemWithTitle:@"About QEMU" action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; // About QEMU
+    [menu addItem:[NSMenuItem separatorItem]]; //Separator
+    [menu addItemWithTitle:@"Hide QEMU" action:@selector(hide:) keyEquivalent:@"h"]; //Hide QEMU
+    menuItem = (NSMenuItem *)[menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; // Hide Others
+    [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
+    [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; // Show All
+    [menu addItem:[NSMenuItem separatorItem]]; //Separator
+    [menu addItemWithTitle:@"Quit QEMU" action:@selector(terminate:) keyEquivalent:@"q"];
+    menuItem = [[NSMenuItem alloc] initWithTitle:@"Apple" action:nil keyEquivalent:@""];
+    [menuItem setSubmenu:menu];
+    [[NSApp mainMenu] addItem:menuItem];
+    [NSApp performSelector:@selector(setAppleMenu:) withObject:menu]; // Workaround (this method is private since 10.4+)
 
-    /* Put menu into the menubar */
-    menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
-    [menuItem setSubmenu:appleMenu];
+    // View menu
+    menu = [[NSMenu alloc] initWithTitle:@"View"];
+    [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Enter Fullscreen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"] autorelease]]; // Fullscreen
+    menuItem = [[[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""] autorelease];
+    [menuItem setSubmenu:menu];
     [[NSApp mainMenu] addItem:menuItem];
 
-    /* Tell the application object that this is now the application menu */
-    [NSApp setAppleMenu:appleMenu];
+    // Window menu
+    menu = [[NSMenu alloc] initWithTitle:@"Window"];
+    [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"] autorelease]]; // Miniaturize
+    menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease];
+    [menuItem setSubmenu:menu];
+    [[NSApp mainMenu] addItem:menuItem];
+    [NSApp setWindowsMenu:menu];
+
+    // Help menu
+    menu = [[NSMenu alloc] initWithTitle:@"Help"];
+    [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Documentation" action:@selector(showQEMUDoc:) keyEquivalent:@"?"] autorelease]]; // QEMU Help
+    [menu addItem: [[[NSMenuItem alloc] initWithTitle:@"QEMU Technology" action:@selector(showQEMUTec:) keyEquivalent:@""] autorelease]]; // QEMU Help
+    menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease];
+    [menuItem setSubmenu:menu];
+    [[NSApp mainMenu] addItem:menuItem];
 
-    /* Finally give up our references to the objects */
-    [appleMenu release];
-    [menuItem release];
-}
+    // Create an Application controller
+    QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init];
+    [NSApp setDelegate:appController];
 
-/* Create a window menu */
-static void setupWindowMenu(void)
-{
-    NSMenu      *windowMenu;
-    NSMenuItem  *windowMenuItem;
-    NSMenuItem  *menuItem;
+    // Start the main event loop
+    [NSApp run];
 
-    windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
+    [appController release];
+    [pool release];
 
-    /* "Minimize" item */
-    menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
-    [windowMenu addItem:menuItem];
-    [menuItem release];
+    return 0;
+}
 
-    /* Put menu into the menubar */
-    windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
-    [windowMenuItem setSubmenu:windowMenu];
-    [[NSApp mainMenu] addItem:windowMenuItem];
 
-    /* Tell the application object that this is now the window menu */
-    [NSApp setWindowsMenu:windowMenu];
 
-    /* Finally give up our references to the objects */
-    [windowMenu release];
-    [windowMenuItem release];
+#pragma mark qemu
+static void cocoa_update(DisplayState *ds, int x, int y, int w, int h)
+{
+    COCOA_DEBUG("qemu_cocoa: cocoa_update\n");
+
+    NSRect rect;
+    if ([cocoaView cdx] == 1.0) {
+        rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h);
+    } else {
+        rect = NSMakeRect(
+            x * [cocoaView cdx],
+            ([cocoaView gscreen].height - y - h) * [cocoaView cdy],
+            w * [cocoaView cdx],
+            h * [cocoaView cdy]);
+    }
+    [cocoaView displayRect:rect];
 }
 
-static void CustomApplicationMain(void)
+static void cocoa_resize(DisplayState *ds, int w, int h)
 {
-    NSAutoreleasePool   *pool = [[NSAutoreleasePool alloc] init];
-    QemuCocoaGUIController *gui_controller;
-    CPSProcessSerNum PSN;
+    COCOA_DEBUG("qemu_cocoa: cocoa_resize\n");
 
-    [NSApplication sharedApplication];
+    [cocoaView resizeContentToWidth:w height:h displayState:ds];
+}
 
-    if (!CPSGetCurrentProcess(&PSN))
-        if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
-            if (!CPSSetFrontProcess(&PSN))
-                [NSApplication sharedApplication];
+static void cocoa_refresh(DisplayState *ds)
+{
+    COCOA_DEBUG("qemu_cocoa: cocoa_refresh\n");
 
-    /* Set up the menubar */
-    [NSApp setMainMenu:[[NSMenu alloc] init]];
-    setApplicationMenu();
-    setupWindowMenu();
+    if (kbd_mouse_is_absolute()) {
+        if (![cocoaView isAbsoluteEnabled]) {
+            if ([cocoaView isMouseGrabed]) {
+                [cocoaView ungrabMouse];
+            }
+        }
+        [cocoaView setAbsoluteEnabled:YES];
+    }
 
-    /* Create SDLMain and make it the app delegate */
-    gui_controller = [[QemuCocoaGUIController alloc] init];
-    [NSApp setDelegate:gui_controller];
+    NSDate *distantPast;
+    NSEvent *event;
+    distantPast = [NSDate distantPast];
+    do {
+        event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:distantPast
+                        inMode: NSDefaultRunLoopMode dequeue:YES];
+        if (event != nil) {
+            [cocoaView handleEvent:event];
+        }
+    } while(event != nil);
+    vga_hw_update();
+}
 
-    /* Start the main event loop */
-    [NSApp run];
+static void cocoa_cleanup(void)
+{
+    COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n");
 
-    [gui_controller release];
-    [pool release];
 }
 
-/* Real main of qemu-cocoa */
-int main(int argc, char **argv)
+void cocoa_display_init(DisplayState *ds, int full_screen)
 {
-    gArgc = argc;
-    gArgv = argv;
+    COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n");
+
+    // register vga outpu callbacks
+    ds->dpy_update = cocoa_update;
+    ds->dpy_resize = cocoa_resize;
+    ds->dpy_refresh = cocoa_refresh;
 
-    CustomApplicationMain();
+    // give window a initial Size
+    cocoa_resize(ds, 640, 400);
 
-    return 0;
+    // register cleanup function
+    atexit(cocoa_cleanup);
 }
diff --git a/configure b/configure
index d86020e..b8b3681 100755
--- a/configure
+++ b/configure
@@ -85,10 +85,13 @@ EXESUF=""
 gdbstub="yes"
 slirp="yes"
 adlib="no"
+ac97="no"
+gus="no"
 oss="no"
 dsound="no"
 coreaudio="no"
 alsa="no"
+esd="no"
 fmod="no"
 fmod_lib=""
 fmod_inc=""
@@ -112,7 +115,6 @@ case $targetos in
 CYGWIN*)
 mingw32="yes"
 OS_CFLAGS="-mno-cygwin"
-VL_OS_LDFLAGS="-mno-cygwin"
 if [ "$cpu" = "i386" ] ; then
     kqemu="yes"
 fi
@@ -151,6 +153,7 @@ darwin_user="yes"
 cocoa="yes"
 coreaudio="yes"
 OS_CFLAGS="-mdynamic-no-pic"
+OS_LDFLAGS="-framework CoreFoundation -framework IOKit"
 ;;
 SunOS)
     solaris="yes"
@@ -260,6 +263,8 @@ for opt do
   ;;
   --enable-alsa) alsa="yes"
   ;;
+  --enable-esd) esd="yes"
+  ;;
   --enable-dsound) dsound="yes"
   ;;
   --enable-fmod) fmod="yes"
@@ -276,6 +281,10 @@ for opt do
   ;;
   --enable-adlib) adlib="yes"
   ;;
+  --enable-ac97) ac97="yes"
+  ;;
+  --enable-gus) gus="yes"
+  ;;
   --disable-kqemu) kqemu="no"
   ;;
   --enable-profiler) profiler="yes"
@@ -339,7 +348,7 @@ fi
 # If cpu ~= sparc and  sparc_cpu hasn't been defined, plug in the right
 # ARCH_CFLAGS/ARCH_LDFLAGS (assume sparc_v8plus for 32-bit and sparc_v9 for 64-bit)
 #
-case $cpu in
+case "$cpu" in
     sparc) if test -z "$sparc_cpu" ; then
                ARCH_CFLAGS="-m32 -mcpu=ultrasparc -D__sparc_v8plus__"
                ARCH_LDFLAGS="-m32"
@@ -359,19 +368,16 @@ case $cpu in
     s390)
            ARCH_CFLAGS="-march=z900"
            ;;
+    i386)
+           ARCH_CFLAGS="-m32"
+           ARCH_LDFLAGS="-m32"
+           ;;
+    x86_64)
+           ARCH_CFLAGS="-m64"
+           ARCH_LDFLAGS="-m64"
+           ;;
 esac
 
-if [ "$solaris" = "yes" -a  "$cpu" = "x86_64" ] ; then
-    CFLAGS="${CFLAGS} -m64"
-    OS_CFLAGS="${OS_CFLAGS} -m64"
-    OS_LDFLAGS="${OS_LDFLAGS} -m64"
-fi
-
-if [ "$solaris" = "yes" -a  "$cpu" = "i386" ] ; then
-    CFLAGS="${CFLAGS} -m32"
-    OS_CFLAGS="${OS_CFLAGS} -m32"
-fi
-
 if test x"$show_help" = x"yes" ; then
 cat << EOF
 
@@ -402,8 +408,11 @@ echo "  --disable-sdl            disable SDL"
 echo "  --enable-cocoa           enable COCOA (Mac OS X only)"
 echo "  --enable-mingw32         enable Win32 cross compilation with mingw32"
 echo "  --enable-adlib           enable Adlib emulation"
+echo "  --enable-ac97            enable AC97 emulation"
+echo "  --enable-gus             enable Gravis Ultrasound emulation"
 echo "  --enable-coreaudio       enable Coreaudio audio driver"
 echo "  --enable-alsa            enable ALSA audio driver"
+echo "  --enable-esd             enable EsoundD audio driver"
 echo "  --enable-fmod            enable FMOD audio driver"
 echo "  --enable-dsound          enable DirectSound audio driver"
 echo "  --disable-vnc-tls        disable TLS encryption for VNC server"
@@ -714,8 +723,11 @@ if test "$sdl" != "no" ; then
 fi
 echo "mingw32 support   $mingw32"
 echo "Adlib support     $adlib"
+echo "AC97 support      $ac97"
+echo "GUS support       $gus"
 echo "CoreAudio support $coreaudio"
 echo "ALSA support      $alsa"
+echo "EsounD support    $esd"
 echo "DSound support    $dsound"
 if test "$fmod" = "yes"; then
     if test -z $fmod_lib || test -z $fmod_inc; then
@@ -778,9 +790,11 @@ echo "CC=$cc" >> $config_mak
 echo "HOST_CC=$host_cc" >> $config_mak
 echo "AR=$ar" >> $config_mak
 echo "STRIP=$strip -s -R .comment -R .note" >> $config_mak
+# XXX: only use CFLAGS and LDFLAGS ?  
+# XXX: should export HOST_CFLAGS and HOST_LDFLAGS for cross
+# compilation of dyngen tool (useful for win32 build on Linux host)
 echo "OS_CFLAGS=$OS_CFLAGS" >> $config_mak
 echo "OS_LDFLAGS=$OS_LDFLAGS" >> $config_mak
-echo "VL_OS_LDFLAGS=$VL_OS_LDFLAGS" >> $config_mak
 echo "ARCH_CFLAGS=$ARCH_CFLAGS" >> $config_mak
 echo "ARCH_LDFLAGS=$ARCH_LDFLAGS" >> $config_mak
 echo "CFLAGS=$CFLAGS" >> $config_mak
@@ -889,6 +903,14 @@ if test "$adlib" = "yes" ; then
   echo "CONFIG_ADLIB=yes" >> $config_mak
   echo "#define CONFIG_ADLIB 1" >> $config_h
 fi
+if test "$ac97" = "yes" ; then
+  echo "CONFIG_AC97=yes" >> $config_mak
+  echo "#define CONFIG_AC97 1" >> $config_h
+fi
+if test "$gus" = "yes" ; then
+  echo "CONFIG_GUS=yes" >> $config_mak
+  echo "#define CONFIG_GUS 1" >> $config_h
+fi
 if test "$oss" = "yes" ; then
   echo "CONFIG_OSS=yes" >> $config_mak
   echo "#define CONFIG_OSS 1" >> $config_h
@@ -901,6 +923,10 @@ if test "$alsa" = "yes" ; then
   echo "CONFIG_ALSA=yes" >> $config_mak
   echo "#define CONFIG_ALSA 1" >> $config_h
 fi
+if test "$esd" = "yes" ; then
+  echo "CONFIG_ESD=yes" >> $config_mak
+  echo "#define CONFIG_ESD 1" >> $config_h
+fi
 if test "$dsound" = "yes" ; then
   echo "CONFIG_DSOUND=yes" >> $config_mak
   echo "#define CONFIG_DSOUND 1" >> $config_h
diff --git a/cpu-all.h b/cpu-all.h
index 3df0fb8..968323d 100644
--- a/cpu-all.h
+++ b/cpu-all.h
@@ -787,6 +787,20 @@ void cpu_set_log(int log_flags);
 void cpu_set_log_filename(const char *filename);
 int cpu_str_to_log_mask(const char *str);
 
+#define CPU_SETTING_NO_CACHE (1 << 0)
+
+/* define translation settings */
+typedef struct CPUTranslationSetting {
+    int mask;
+    const char *name;
+    const char *help;
+} CPUTranslationSetting;
+
+extern CPUTranslationSetting cpu_translation_settings[];
+
+void cpu_set_translation_settings(int translation_flags);
+int cpu_str_to_translation_mask(const char *str);
+
 /* IO ports API */
 
 /* NOTE: as these functions may be even used when there is an isa
diff --git a/cpu-defs.h b/cpu-defs.h
index 5e0f046..b581d94 100644
--- a/cpu-defs.h
+++ b/cpu-defs.h
@@ -102,6 +102,12 @@ typedef unsigned long ram_addr_t;
 #define CPU_TLB_BITS 8
 #define CPU_TLB_SIZE (1 << CPU_TLB_BITS)
 
+#if TARGET_PHYS_ADDR_BITS == 32 && TARGET_LONG_BITS == 32
+#define CPU_TLB_ENTRY_BITS 4
+#else
+#define CPU_TLB_ENTRY_BITS 5
+#endif
+
 typedef struct CPUTLBEntry {
     /* bit 31 to TARGET_PAGE_BITS : virtual address
        bit TARGET_PAGE_BITS-1..IO_MEM_SHIFT : if non zero, memory io
@@ -113,7 +119,17 @@ typedef struct CPUTLBEntry {
     target_ulong addr_write;
     target_ulong addr_code;
     /* addend to virtual address to get physical address */
+#if TARGET_PHYS_ADDR_BITS == 64
+    /* on i386 Linux make sure it is aligned */
+    target_phys_addr_t addend __attribute__((aligned(8)));
+#else
     target_phys_addr_t addend;
+#endif
+    /* padding to get a power of two size */
+    uint8_t dummy[(1 << CPU_TLB_ENTRY_BITS) - 
+                  (sizeof(target_ulong) * 3 + 
+                   ((-sizeof(target_ulong) * 3) & (sizeof(target_phys_addr_t) - 1)) + 
+                   sizeof(target_phys_addr_t))];
 } CPUTLBEntry;
 
 #define CPU_COMMON                                                      \
diff --git a/cpu-exec.c b/cpu-exec.c
index 8134c22..af5c58f 100644
--- a/cpu-exec.c
+++ b/cpu-exec.c
@@ -20,6 +20,7 @@
 #include "config.h"
 #include "exec.h"
 #include "disas.h"
+#include <string.h>
 
 #if !defined(CONFIG_SOFTMMU)
 #undef EAX
@@ -40,6 +41,9 @@ int tb_invalidated_flag;
 //#define DEBUG_EXEC
 //#define DEBUG_SIGNAL
 
+/* translation settings */
+int translation_settings = 0;
+
 #define SAVE_GLOBALS()
 #define RESTORE_GLOBALS()
 
@@ -120,6 +124,56 @@ void cpu_resume_from_signal(CPUState *env1, void *puc)
     longjmp(env->jmp_env, 1);
 }
 
+CPUTranslationSetting cpu_translation_settings[] = {
+    { CPU_SETTING_NO_CACHE, "no-cache",
+      "Do not use translation blocks cache (very slow!)" },
+    { 0, NULL, NULL },
+};
+
+void cpu_set_translation_settings(int translation_flags)
+{
+    translation_settings = translation_flags;
+}
+
+static int cmp1(const char *s1, int n, const char *s2)
+{
+    if (strlen(s2) != n)
+        return 0;
+    return memcmp(s1, s2, n) == 0;
+}
+
+/* takes a comma separated list of translation settings. Return 0 if error. */
+int cpu_str_to_translation_mask(const char *str)
+{
+    CPUTranslationSetting *setting;
+    int mask;
+    const char *p, *p1;
+
+    p = str;
+    mask = 0;
+    for(;;) {
+        p1 = strchr(p, ',');
+        if (!p1)
+            p1 = p + strlen(p);
+        if(cmp1(p,p1-p,"all")) {
+            for(setting = cpu_translation_settings; setting->mask != 0; setting++) {
+                mask |= setting->mask;
+            }
+        } else {
+            for(setting = cpu_translation_settings; setting->mask != 0; setting++) {
+                if (cmp1(p, p1 - p, setting->name))
+                    goto found;
+            }
+            return 0;
+        }
+    found:
+        mask |= setting->mask;
+        if (*p1 != ',')
+            break;
+        p = p1 + 1;
+    }
+    return mask;
+}
 
 static TranslationBlock *tb_find_slow(target_ulong pc,
                                       target_ulong cs_base,
@@ -141,6 +195,9 @@ static TranslationBlock *tb_find_slow(target_ulong pc,
     phys_pc = get_phys_addr_code(env, pc);
     phys_page1 = phys_pc & TARGET_PAGE_MASK;
     phys_page2 = -1;
+    if (translation_settings & CPU_SETTING_NO_CACHE)
+        goto not_found;
+
     h = tb_phys_hash_func(phys_pc);
     ptb1 = &tb_phys_hash[h];
     for(;;) {
@@ -264,7 +321,10 @@ static inline TranslationBlock *tb_find_fast(void)
 #else
 #error unsupported CPU
 #endif
-    tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
+    if (translation_settings & CPU_SETTING_NO_CACHE)
+        tb = NULL;
+    else
+        tb = env->tb_jmp_cache[tb_jmp_cache_hash_func(pc)];
     if (__builtin_expect(!tb || tb->pc != pc || tb->cs_base != cs_base ||
                          tb->flags != flags, 0)) {
         tb = tb_find_slow(pc, cs_base, flags);
diff --git a/hw/ac97.c b/hw/ac97.c
new file mode 100644
index 0000000..ba4ea1e
--- /dev/null
+++ b/hw/ac97.c
@@ -0,0 +1,1349 @@
+/*
+ * Copyright (C) 2006 InnoTek Systemberatung GmbH
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License as published by the Free Software Foundation,
+ * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
+ * distribution. VirtualBox OSE is distributed in the hope that it will
+ * be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * If you received this file as part of a commercial VirtualBox
+ * distribution, then only the terms of your commercial VirtualBox
+ * license agreement apply instead of the previous paragraph.
+ */
+
+#include "hw.h"
+#include "audiodev.h"
+#include "audio/audio.h"
+#include "pci.h"
+
+enum {
+    AC97_Reset                     = 0x00,
+    AC97_Master_Volume_Mute        = 0x02,
+    AC97_Headphone_Volume_Mute     = 0x04,
+    AC97_Master_Volume_Mono_Mute   = 0x06,
+    AC97_Master_Tone_RL            = 0x08,
+    AC97_PC_BEEP_Volume_Mute       = 0x0A,
+    AC97_Phone_Volume_Mute         = 0x0C,
+    AC97_Mic_Volume_Mute           = 0x0E,
+    AC97_Line_In_Volume_Mute       = 0x10,
+    AC97_CD_Volume_Mute            = 0x12,
+    AC97_Video_Volume_Mute         = 0x14,
+    AC97_Aux_Volume_Mute           = 0x16,
+    AC97_PCM_Out_Volume_Mute       = 0x18,
+    AC97_Record_Select             = 0x1A,
+    AC97_Record_Gain_Mute          = 0x1C,
+    AC97_Record_Gain_Mic_Mute      = 0x1E,
+    AC97_General_Purpose           = 0x20,
+    AC97_3D_Control                = 0x22,
+    AC97_AC_97_RESERVED            = 0x24,
+    AC97_Powerdown_Ctrl_Stat       = 0x26,
+    AC97_Extended_Audio_ID         = 0x28,
+    AC97_Extended_Audio_Ctrl_Stat  = 0x2A,
+    AC97_PCM_Front_DAC_Rate        = 0x2C,
+    AC97_PCM_Surround_DAC_Rate     = 0x2E,
+    AC97_PCM_LFE_DAC_Rate          = 0x30,
+    AC97_PCM_LR_ADC_Rate           = 0x32,
+    AC97_MIC_ADC_Rate              = 0x34,
+    AC97_6Ch_Vol_C_LFE_Mute        = 0x36,
+    AC97_6Ch_Vol_L_R_Surround_Mute = 0x38,
+    AC97_Vendor_Reserved           = 0x58,
+    AC97_Vendor_ID1                = 0x7c,
+    AC97_Vendor_ID2                = 0x7e
+};
+
+#define SOFT_VOLUME
+#define SR_FIFOE 16             /* rwc */
+#define SR_BCIS  8              /* rwc */
+#define SR_LVBCI 4              /* rwc */
+#define SR_CELV  2              /* ro */
+#define SR_DCH   1              /* ro */
+#define SR_VALID_MASK ((1 << 5) - 1)
+#define SR_WCLEAR_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
+#define SR_RO_MASK (SR_DCH | SR_CELV)
+#define SR_INT_MASK (SR_FIFOE | SR_BCIS | SR_LVBCI)
+
+#define CR_IOCE  16             /* rw */
+#define CR_FEIE  8              /* rw */
+#define CR_LVBIE 4              /* rw */
+#define CR_RR    2              /* rw */
+#define CR_RPBM  1              /* rw */
+#define CR_VALID_MASK ((1 << 5) - 1)
+#define CR_DONT_CLEAR_MASK (CR_IOCE | CR_FEIE | CR_LVBIE)
+
+#define GC_WR    4              /* rw */
+#define GC_CR    2              /* rw */
+#define GC_VALID_MASK ((1 << 6) - 1)
+
+#define GS_MD3   (1<<17)        /* rw */
+#define GS_AD3   (1<<16)        /* rw */
+#define GS_RCS   (1<<15)        /* rwc */
+#define GS_B3S12 (1<<14)        /* ro */
+#define GS_B2S12 (1<<13)        /* ro */
+#define GS_B1S12 (1<<12)        /* ro */
+#define GS_S1R1  (1<<11)        /* rwc */
+#define GS_S0R1  (1<<10)        /* rwc */
+#define GS_S1CR  (1<<9)         /* ro */
+#define GS_S0CR  (1<<8)         /* ro */
+#define GS_MINT  (1<<7)         /* ro */
+#define GS_POINT (1<<6)         /* ro */
+#define GS_PIINT (1<<5)         /* ro */
+#define GS_RSRVD ((1<<4)|(1<<3))
+#define GS_MOINT (1<<2)         /* ro */
+#define GS_MIINT (1<<1)         /* ro */
+#define GS_GSCI  1              /* rwc */
+#define GS_RO_MASK (GS_B3S12|                   \
+                    GS_B2S12|                   \
+                    GS_B1S12|                   \
+                    GS_S1CR|                    \
+                    GS_S0CR|                    \
+                    GS_MINT|                    \
+                    GS_POINT|                   \
+                    GS_PIINT|                   \
+                    GS_RSRVD|                   \
+                    GS_MOINT|                   \
+                    GS_MIINT)
+#define GS_VALID_MASK ((1 << 18) - 1)
+#define GS_WCLEAR_MASK (GS_RCS|GS_S1R1|GS_S0R1|GS_GSCI)
+
+#define BD_IOC (1<<31)
+#define BD_BUP (1<<30)
+
+#define EACS_VRA 1
+#define EACS_VRM 8
+
+#define VOL_MASK 0x1f
+#define MUTE_SHIFT 15
+
+#define REC_MASK 7
+enum {
+    REC_MIC = 0,
+    REC_CD,
+    REC_VIDEO,
+    REC_AUX,
+    REC_LINE_IN,
+    REC_STEREO_MIX,
+    REC_MONO_MIX,
+    REC_PHONE
+};
+
+typedef struct BD {
+    uint32_t addr;
+    uint32_t ctl_len;
+} BD;
+
+typedef struct AC97BusMasterRegs {
+    uint32_t bdbar;             /* rw 0 */
+    uint8_t civ;                /* ro 0 */
+    uint8_t lvi;                /* rw 0 */
+    uint16_t sr;                /* rw 1 */
+    uint16_t picb;              /* ro 0 */
+    uint8_t piv;                /* ro 0 */
+    uint8_t cr;                 /* rw 0 */
+    unsigned int bd_valid;
+    BD bd;
+} AC97BusMasterRegs;
+
+typedef struct AC97LinkState {
+    PCIDevice *pci_dev;
+    QEMUSoundCard card;
+    uint32_t glob_cnt;
+    uint32_t glob_sta;
+    uint32_t cas;
+    uint32_t last_samp;
+    AC97BusMasterRegs bm_regs[3];
+    uint8_t mixer_data[256];
+    SWVoiceIn *voice_pi;
+    SWVoiceOut *voice_po;
+    SWVoiceIn *voice_mc;
+    uint8_t silence[128];
+    uint32_t base[2];
+    int bup_flag;
+} AC97LinkState;
+
+enum {
+    BUP_SET = 1,
+    BUP_LAST = 2
+};
+
+#ifdef DEBUG_AC97
+#define dolog(...) AUD_log ("ac97", __VA_ARGS__)
+#else
+#define dolog(...)
+#endif
+
+typedef struct PCIAC97LinkState {
+    PCIDevice dev;
+    AC97LinkState ac97;
+} PCIAC97LinkState;
+
+#define MKREGS(prefix, start)                   \
+enum {                                          \
+    prefix ## _BDBAR = start,                   \
+    prefix ## _CIV = start + 4,                 \
+    prefix ## _LVI = start + 5,                 \
+    prefix ## _SR = start + 6,                  \
+    prefix ## _PICB = start + 8,                \
+    prefix ## _PIV = start + 10,                \
+    prefix ## _CR = start + 11                  \
+}
+
+enum {
+    PI_INDEX = 0,
+    PO_INDEX,
+    MC_INDEX,
+    LAST_INDEX
+};
+
+MKREGS (PI, PI_INDEX * 16);
+MKREGS (PO, PO_INDEX * 16);
+MKREGS (MC, MC_INDEX * 16);
+
+enum {
+    GLOB_CNT = 0x2c,
+    GLOB_STA = 0x30,
+    CAS      = 0x34
+};
+
+#define GET_BM(index) (((index) >> 4) & 3)
+
+static void po_callback (void *opaque, int free);
+static void pi_callback (void *opaque, int avail);
+static void mc_callback (void *opaque, int avail);
+
+static void warm_reset (AC97LinkState *s)
+{
+    (void) s;
+}
+
+static void cold_reset (AC97LinkState * s)
+{
+    (void) s;
+}
+
+static void fetch_bd (AC97LinkState *s, AC97BusMasterRegs *r)
+{
+    uint8_t b[8];
+
+    cpu_physical_memory_read (r->bdbar + r->civ * 8, b, 8);
+    r->bd_valid = 1;
+    r->bd.addr = le32_to_cpu (*(uint32_t *) &b[0]) & ~3;
+    r->bd.ctl_len = le32_to_cpu (*(uint32_t *) &b[4]);
+    r->picb = r->bd.ctl_len & 0xffff;
+    dolog ("bd %2d addr=%#x ctl=%#06x len=%#x(%d bytes)\n",
+           r->civ, r->bd.addr, r->bd.ctl_len >> 16,
+           r->bd.ctl_len & 0xffff,
+           (r->bd.ctl_len & 0xffff) << 1);
+}
+
+static void update_sr (AC97LinkState *s, AC97BusMasterRegs *r, uint32_t new_sr)
+{
+    int event = 0;
+    int level = 0;
+    uint32_t new_mask = new_sr & SR_INT_MASK;
+    uint32_t old_mask = r->sr & SR_INT_MASK;
+    uint32_t masks[] = {GS_PIINT, GS_POINT, GS_MINT};
+
+    if (new_mask ^ old_mask) {
+        /** @todo is IRQ deasserted when only one of status bits is cleared? */
+        if (!new_mask) {
+            event = 1;
+            level = 0;
+        }
+        else {
+            if ((new_mask & SR_LVBCI) && (r->cr & CR_LVBIE)) {
+                event = 1;
+                level = 1;
+            }
+            if ((new_mask & SR_BCIS) && (r->cr & CR_IOCE)) {
+                event = 1;
+                level = 1;
+            }
+        }
+    }
+
+    r->sr = new_sr;
+
+    dolog ("IOC%d LVB%d sr=%#x event=%d level=%d\n",
+           r->sr & SR_BCIS, r->sr & SR_LVBCI,
+           r->sr,
+           event, level);
+
+    if (!event)
+        return;
+
+    if (level) {
+        s->glob_sta |= masks[r - s->bm_regs];
+        dolog ("set irq level=1\n");
+        qemu_set_irq(s->pci_dev->irq[0], 1);
+    }
+    else {
+        s->glob_sta &= ~masks[r - s->bm_regs];
+        dolog ("set irq level=0\n");
+        qemu_set_irq(s->pci_dev->irq[0], 0);
+    }
+}
+
+static void voice_set_active (AC97LinkState *s, int bm_index, int on)
+{
+    switch (bm_index) {
+    case PI_INDEX:
+        AUD_set_active_in (s->voice_pi, on);
+        break;
+
+    case PO_INDEX:
+        AUD_set_active_out (s->voice_po, on);
+        break;
+
+    case MC_INDEX:
+        AUD_set_active_in (s->voice_mc, on);
+        break;
+
+    default:
+        AUD_log ("ac97", "invalid bm_index(%d) in voice_set_active", bm_index);
+        break;
+    }
+}
+
+static void reset_bm_regs (AC97LinkState *s, AC97BusMasterRegs *r)
+{
+    dolog ("reset_bm_regs\n");
+    r->bdbar = 0;
+    r->civ = 0;
+    r->lvi = 0;
+    /** todo do we need to do that? */
+    update_sr (s, r, SR_DCH);
+    r->picb = 0;
+    r->piv = 0;
+    r->cr = r->cr & CR_DONT_CLEAR_MASK;
+    r->bd_valid = 0;
+
+    voice_set_active (s, r - s->bm_regs, 0);
+    memset (s->silence, 0, sizeof (s->silence));
+}
+
+static void mixer_store (AC97LinkState *s, uint32_t i, uint16_t v)
+{
+    if (i + 2 > sizeof (s->mixer_data)) {
+        dolog ("mixer_store: index %d out of bounds %d\n",
+               i, sizeof (s->mixer_data));
+        return;
+    }
+
+    s->mixer_data[i + 0] = v & 0xff;
+    s->mixer_data[i + 1] = v >> 8;
+}
+
+static uint16_t mixer_load (AC97LinkState *s, uint32_t i)
+{
+    uint16_t val = 0xffff;
+
+    if (i + 2 > sizeof (s->mixer_data)) {
+        dolog ("mixer_store: index %d out of bounds %d\n",
+               i, sizeof (s->mixer_data));
+    }
+    else {
+        val = s->mixer_data[i + 0] | (s->mixer_data[i + 1] << 8);
+    }
+
+    return val;
+}
+
+static void open_voice (AC97LinkState *s, int index, int freq)
+{
+    audsettings_t as;
+
+    as.freq = freq;
+    as.nchannels = 2;
+    as.fmt = AUD_FMT_S16;
+    as.endianness = 0;
+
+    switch (index) {
+    case PI_INDEX:
+        s->voice_pi = AUD_open_in (
+            &s->card,
+            s->voice_pi,
+            "ac97.pi",
+            s,
+            pi_callback,
+            &as
+            );
+        break;
+
+    case PO_INDEX:
+        s->voice_po = AUD_open_out (
+            &s->card,
+            s->voice_po,
+            "ac97.po",
+            s,
+            po_callback,
+            &as
+            );
+        break;
+
+    case MC_INDEX:
+        s->voice_mc = AUD_open_in (
+            &s->card,
+            s->voice_mc,
+            "ac97.mc",
+            s,
+            mc_callback,
+            &as
+            );
+        break;
+    }
+}
+
+static void reset_voices (AC97LinkState *s, uint8_t active[LAST_INDEX])
+{
+    uint16_t freq;
+
+    freq = mixer_load (s, AC97_PCM_LR_ADC_Rate);
+    open_voice (s, PI_INDEX, freq);
+    AUD_set_active_in (s->voice_pi, active[PI_INDEX]);
+
+    freq = mixer_load (s, AC97_PCM_Front_DAC_Rate);
+    open_voice (s, PO_INDEX, freq);
+    AUD_set_active_out (s->voice_po, active[PO_INDEX]);
+
+    freq = mixer_load (s, AC97_MIC_ADC_Rate);
+    open_voice (s, MC_INDEX, freq);
+    AUD_set_active_in (s->voice_mc, active[MC_INDEX]);
+}
+
+#ifdef USE_MIXER
+static void set_volume (AC97LinkState *s, int index,
+                        audmixerctl_t mt, uint32_t val)
+{
+    int mute = (val >> MUTE_SHIFT) & 1;
+    uint8_t rvol = VOL_MASK - (val & VOL_MASK);
+    uint8_t lvol = VOL_MASK - ((val >> 8) & VOL_MASK);
+    rvol = 255 * rvol / VOL_MASK;
+    lvol = 255 * lvol / VOL_MASK;
+
+#ifdef SOFT_VOLUME
+    if (index == AC97_Master_Volume_Mute) {
+        AUD_set_volume_out (s->voice_po, mute, lvol, rvol);
+    }
+    else {
+        AUD_set_volume (mt, &mute, &lvol, &rvol);
+    }
+#else
+    AUD_set_volume (mt, &mute, &lvol, &rvol);
+#endif
+
+    rvol = VOL_MASK - ((VOL_MASK * rvol) / 255);
+    lvol = VOL_MASK - ((VOL_MASK * lvol) / 255);
+    mixer_store (s, index, val);
+}
+
+static audrecsource_t ac97_to_aud_record_source (uint8_t i)
+{
+    switch (i) {
+    case REC_MIC:
+        return AUD_REC_MIC;
+
+    case REC_CD:
+        return AUD_REC_CD;
+
+    case REC_VIDEO:
+        return AUD_REC_VIDEO;
+
+    case REC_AUX:
+        return AUD_REC_AUX;
+
+    case REC_LINE_IN:
+        return AUD_REC_LINE_IN;
+
+    case REC_PHONE:
+        return AUD_REC_PHONE;
+
+    default:
+        dolog ("Unknown record source %d, using MIC\n", i);
+        return AUD_REC_MIC;
+    }
+}
+
+static uint8_t aud_to_ac97_record_source (audrecsource_t rs)
+{
+    switch (rs) {
+    case AUD_REC_MIC:
+        return REC_MIC;
+
+    case AUD_REC_CD:
+        return REC_CD;
+
+    case AUD_REC_VIDEO:
+        return REC_VIDEO;
+
+    case AUD_REC_AUX:
+        return REC_AUX;
+
+    case AUD_REC_LINE_IN:
+        return REC_LINE_IN;
+
+    case AUD_REC_PHONE:
+        return REC_PHONE;
+
+    default:
+        dolog ("Unknown audio recording source %d using MIC\n", rs);
+        return REC_MIC;
+    }
+}
+
+static void record_select (AC97LinkState *s, uint32_t val)
+{
+    uint8_t rs = val & REC_MASK;
+    uint8_t ls = (val >> 8) & REC_MASK;
+    audrecsource_t ars = ac97_to_aud_record_source (rs);
+    audrecsource_t als = ac97_to_aud_record_source (ls);
+    AUD_set_record_source (&als, &ars);
+    rs = aud_to_ac97_record_source (ars);
+    ls = aud_to_ac97_record_source (als);
+    mixer_store (s, AC97_Record_Select, rs | (ls << 8));
+}
+#endif
+
+static void mixer_reset (AC97LinkState *s)
+{
+    uint8_t active[LAST_INDEX];
+
+    dolog ("mixer_reset\n");
+    memset (s->mixer_data, 0, sizeof (s->mixer_data));
+    memset (active, 0, sizeof (active));
+    mixer_store (s, AC97_Reset                   , 0x0000); /* 6940 */
+    mixer_store (s, AC97_Master_Volume_Mono_Mute , 0x8000);
+    mixer_store (s, AC97_PC_BEEP_Volume_Mute     , 0x0000);
+
+    mixer_store (s, AC97_Phone_Volume_Mute       , 0x8008);
+    mixer_store (s, AC97_Mic_Volume_Mute         , 0x8008);
+    mixer_store (s, AC97_CD_Volume_Mute          , 0x8808);
+    mixer_store (s, AC97_Aux_Volume_Mute         , 0x8808);
+    mixer_store (s, AC97_Record_Gain_Mic_Mute    , 0x8000);
+    mixer_store (s, AC97_General_Purpose         , 0x0000);
+    mixer_store (s, AC97_3D_Control              , 0x0000);
+    mixer_store (s, AC97_Powerdown_Ctrl_Stat     , 0x000f);
+
+    /*
+     * Sigmatel 9700 (STAC9700)
+     */
+    mixer_store (s, AC97_Vendor_ID1              , 0x8384);
+    mixer_store (s, AC97_Vendor_ID2              , 0x7600); /* 7608 */
+
+    mixer_store (s, AC97_Extended_Audio_ID       , 0x0809);
+    mixer_store (s, AC97_Extended_Audio_Ctrl_Stat, 0x0009);
+    mixer_store (s, AC97_PCM_Front_DAC_Rate      , 0xbb80);
+    mixer_store (s, AC97_PCM_Surround_DAC_Rate   , 0xbb80);
+    mixer_store (s, AC97_PCM_LFE_DAC_Rate        , 0xbb80);
+    mixer_store (s, AC97_PCM_LR_ADC_Rate         , 0xbb80);
+    mixer_store (s, AC97_MIC_ADC_Rate            , 0xbb80);
+
+#ifdef USE_MIXER
+    record_select (s, 0);
+    set_volume (s, AC97_Master_Volume_Mute, AUD_MIXER_VOLUME  , 0x8000);
+    set_volume (s, AC97_PCM_Out_Volume_Mute, AUD_MIXER_PCM    , 0x8808);
+    set_volume (s, AC97_Line_In_Volume_Mute, AUD_MIXER_LINE_IN, 0x8808);
+#endif
+    reset_voices (s, active);
+}
+
+/**
+ * Native audio mixer
+ * I/O Reads
+ */
+static uint32_t nam_readb (void *opaque, uint32_t addr)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    dolog ("U nam readb %#x\n", addr);
+    s->cas = 0;
+    return ~0U;
+}
+
+static uint32_t nam_readw (void *opaque, uint32_t addr)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    uint32_t val = ~0U;
+    uint32_t index = addr - s->base[0];
+    s->cas = 0;
+    val = mixer_load (s, index);
+    return val;
+}
+
+static uint32_t nam_readl (void *opaque, uint32_t addr)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    dolog ("U nam readl %#x\n", addr);
+    s->cas = 0;
+    return ~0U;
+}
+
+/**
+ * Native audio mixer
+ * I/O Writes
+ */
+static void nam_writeb (void *opaque, uint32_t addr, uint32_t val)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    dolog ("U nam writeb %#x <- %#x\n", addr, val);
+    s->cas = 0;
+}
+
+static void nam_writew (void *opaque, uint32_t addr, uint32_t val)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    uint32_t index = addr - s->base[0];
+    s->cas = 0;
+    switch (index) {
+    case AC97_Reset:
+        mixer_reset (s);
+        break;
+    case AC97_Powerdown_Ctrl_Stat:
+        val &= ~0xf;
+        val |= mixer_load (s, index) & 0xf;
+        mixer_store (s, index, val);
+        break;
+#ifdef USE_MIXER
+    case AC97_Master_Volume_Mute:
+        set_volume (s, index, AUD_MIXER_VOLUME, val);
+        break;
+    case AC97_PCM_Out_Volume_Mute:
+        set_volume (s, index, AUD_MIXER_PCM, val);
+        break;
+    case AC97_Line_In_Volume_Mute:
+        set_volume (s, index, AUD_MIXER_LINE_IN, val);
+        break;
+    case AC97_Record_Select:
+        record_select (s, val);
+        break;
+#endif
+    case AC97_Vendor_ID1:
+    case AC97_Vendor_ID2:
+        dolog ("Attempt to write vendor ID to %#x\n", val);
+        break;
+    case AC97_Extended_Audio_ID:
+        dolog ("Attempt to write extended audio ID to %#x\n", val);
+        break;
+    case AC97_Extended_Audio_Ctrl_Stat:
+        if (!(val & EACS_VRA)) {
+            mixer_store (s, AC97_PCM_Front_DAC_Rate, 0xbb80);
+            mixer_store (s, AC97_PCM_LR_ADC_Rate,    0xbb80);
+            open_voice (s, PI_INDEX, 48000);
+            open_voice (s, PO_INDEX, 48000);
+        }
+        if (!(val & EACS_VRM)) {
+            mixer_store (s, AC97_MIC_ADC_Rate, 0xbb80);
+            open_voice (s, MC_INDEX, 48000);
+        }
+        dolog ("Setting extended audio control to %#x\n", val);
+        mixer_store (s, AC97_Extended_Audio_Ctrl_Stat, val);
+        break;
+    case AC97_PCM_Front_DAC_Rate:
+        if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
+            mixer_store (s, index, val);
+            dolog ("Set front DAC rate to %d\n", val);
+            open_voice (s, PO_INDEX, val);
+        }
+        else {
+            dolog ("Attempt to set front DAC rate to %d, "
+                   "but VRA is not set\n",
+                   val);
+        }
+        break;
+    case AC97_MIC_ADC_Rate:
+        if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRM) {
+            mixer_store (s, index, val);
+            dolog ("Set MIC ADC rate to %d\n", val);
+            open_voice (s, MC_INDEX, val);
+        }
+        else {
+            dolog ("Attempt to set MIC ADC rate to %d, "
+                   "but VRM is not set\n",
+                   val);
+        }
+        break;
+    case AC97_PCM_LR_ADC_Rate:
+        if (mixer_load (s, AC97_Extended_Audio_Ctrl_Stat) & EACS_VRA) {
+            mixer_store (s, index, val);
+            dolog ("Set front LR ADC rate to %d\n", val);
+            open_voice (s, PI_INDEX, val);
+        }
+        else {
+            dolog ("Attempt to set LR ADC rate to %d, but VRA is not set\n",
+                    val);
+        }
+        break;
+    default:
+        dolog ("U nam writew %#x <- %#x\n", addr, val);
+        mixer_store (s, index, val);
+        break;
+    }
+}
+
+static void nam_writel (void *opaque, uint32_t addr, uint32_t val)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    dolog ("U nam writel %#x <- %#x\n", addr, val);
+    s->cas = 0;
+}
+
+/**
+ * Native audio bus master
+ * I/O Reads
+ */
+static uint32_t nabm_readb (void *opaque, uint32_t addr)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr - s->base[1];
+    uint32_t val = ~0U;
+
+    switch (index) {
+    case CAS:
+        dolog ("CAS %d\n", s->cas);
+        val = s->cas;
+        s->cas = 1;
+        break;
+    case PI_CIV:
+    case PO_CIV:
+    case MC_CIV:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->civ;
+        dolog ("CIV[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_LVI:
+    case PO_LVI:
+    case MC_LVI:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->lvi;
+        dolog ("LVI[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_PIV:
+    case PO_PIV:
+    case MC_PIV:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->piv;
+        dolog ("PIV[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_CR:
+    case PO_CR:
+    case MC_CR:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->cr;
+        dolog ("CR[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_SR:
+    case PO_SR:
+    case MC_SR:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->sr & 0xff;
+        dolog ("SRb[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    default:
+        dolog ("U nabm readb %#x -> %#x\n", addr, val);
+        break;
+    }
+    return val;
+}
+
+static uint32_t nabm_readw (void *opaque, uint32_t addr)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr - s->base[1];
+    uint32_t val = ~0U;
+
+    switch (index) {
+    case PI_SR:
+    case PO_SR:
+    case MC_SR:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->sr;
+        dolog ("SR[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_PICB:
+    case PO_PICB:
+    case MC_PICB:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->picb;
+        dolog ("PICB[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    default:
+        dolog ("U nabm readw %#x -> %#x\n", addr, val);
+        break;
+    }
+    return val;
+}
+
+static uint32_t nabm_readl (void *opaque, uint32_t addr)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr - s->base[1];
+    uint32_t val = ~0U;
+
+    switch (index) {
+    case PI_BDBAR:
+    case PO_BDBAR:
+    case MC_BDBAR:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->bdbar;
+        dolog ("BMADDR[%d] -> %#x\n", GET_BM (index), val);
+        break;
+    case PI_CIV:
+    case PO_CIV:
+    case MC_CIV:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->civ | (r->lvi << 8) | (r->sr << 16);
+        dolog ("CIV LVI SR[%d] -> %#x, %#x, %#x\n", GET_BM (index),
+               r->civ, r->lvi, r->sr);
+        break;
+    case PI_PICB:
+    case PO_PICB:
+    case MC_PICB:
+        r = &s->bm_regs[GET_BM (index)];
+        val = r->picb | (r->piv << 16) | (r->cr << 24);
+        dolog ("PICB PIV CR[%d] -> %#x %#x %#x %#x\n", GET_BM (index),
+               val, r->picb, r->piv, r->cr);
+        break;
+    case GLOB_CNT:
+        val = s->glob_cnt;
+        dolog ("glob_cnt -> %#x\n", val);
+        break;
+    case GLOB_STA:
+        val = s->glob_sta | GS_S0CR;
+        dolog ("glob_sta -> %#x\n", val);
+        break;
+    default:
+        dolog ("U nabm readl %#x -> %#x\n", addr, val);
+        break;
+    }
+    return val;
+}
+
+/**
+ * Native audio bus master
+ * I/O Writes
+ */
+static void nabm_writeb (void *opaque, uint32_t addr, uint32_t val)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr - s->base[1];
+    switch (index) {
+    case PI_LVI:
+    case PO_LVI:
+    case MC_LVI:
+        r = &s->bm_regs[GET_BM (index)];
+        if ((r->cr & CR_RPBM) && (r->sr & SR_DCH)) {
+            r->sr &= ~(SR_DCH | SR_CELV);
+            r->civ = r->piv;
+            r->piv = (r->piv + 1) % 32;
+            fetch_bd (s, r);
+        }
+        r->lvi = val % 32;
+        dolog ("LVI[%d] <- %#x\n", GET_BM (index), val);
+        break;
+    case PI_CR:
+    case PO_CR:
+    case MC_CR:
+        r = &s->bm_regs[GET_BM (index)];
+        if (val & CR_RR) {
+            reset_bm_regs (s, r);
+        }
+        else {
+            r->cr = val & CR_VALID_MASK;
+            if (!(r->cr & CR_RPBM)) {
+                voice_set_active (s, r - s->bm_regs, 0);
+                r->sr |= SR_DCH;
+            }
+            else {
+                r->civ = r->piv;
+                r->piv = (r->piv + 1) % 32;
+                fetch_bd (s, r);
+                r->sr &= ~SR_DCH;
+                voice_set_active (s, r - s->bm_regs, 1);
+            }
+        }
+        dolog ("CR[%d] <- %#x (cr %#x)\n", GET_BM (index), val, r->cr);
+        break;
+    case PI_SR:
+    case PO_SR:
+    case MC_SR:
+        r = &s->bm_regs[GET_BM (index)];
+        r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
+        update_sr (s, r, r->sr & ~(val & SR_WCLEAR_MASK));
+        dolog ("SR[%d] <- %#x (sr %#x)\n", GET_BM (index), val, r->sr);
+        break;
+    default:
+        dolog ("U nabm writeb %#x <- %#x\n", addr, val);
+        break;
+    }
+}
+
+static void nabm_writew (void *opaque, uint32_t addr, uint32_t val)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr - s->base[1];
+    switch (index) {
+    case PI_SR:
+    case PO_SR:
+    case MC_SR:
+        r = &s->bm_regs[GET_BM (index)];
+        r->sr |= val & ~(SR_RO_MASK | SR_WCLEAR_MASK);
+        update_sr (s, r, r->sr & ~(val & SR_WCLEAR_MASK));
+        dolog ("SR[%d] <- %#x (sr %#x)\n", GET_BM (index), val, r->sr);
+        break;
+    default:
+        dolog ("U nabm writew %#x <- %#x\n", addr, val);
+        break;
+    }
+}
+
+static void nabm_writel (void *opaque, uint32_t addr, uint32_t val)
+{
+    PCIAC97LinkState *d = opaque;
+    AC97LinkState *s = &d->ac97;
+    AC97BusMasterRegs *r = NULL;
+    uint32_t index = addr - s->base[1];
+    switch (index) {
+    case PI_BDBAR:
+    case PO_BDBAR:
+    case MC_BDBAR:
+        r = &s->bm_regs[GET_BM (index)];
+        r->bdbar = val & ~3;
+        dolog ("BDBAR[%d] <- %#x (bdbar %#x)\n",
+               GET_BM (index), val, r->bdbar);
+        break;
+    case GLOB_CNT:
+        if (val & GC_WR)
+            warm_reset (s);
+        if (val & GC_CR)
+            cold_reset (s);
+        if (!(val & (GC_WR | GC_CR)))
+            s->glob_cnt = val & GC_VALID_MASK;
+        dolog ("glob_cnt <- %#x (glob_cnt %#x)\n", val, s->glob_cnt);
+        break;
+    case GLOB_STA:
+        s->glob_sta &= ~(val & GS_WCLEAR_MASK);
+        s->glob_sta |= (val & ~(GS_WCLEAR_MASK | GS_RO_MASK)) & GS_VALID_MASK;
+        dolog ("glob_sta <- %#x (glob_sta %#x)\n", val, s->glob_sta);
+        break;
+    default:
+        dolog ("U nabm writel %#x <- %#x\n", addr, val);
+        break;
+    }
+}
+
+static int write_audio (AC97LinkState *s, AC97BusMasterRegs *r,
+                        int max, int *stop)
+{
+    uint8_t tmpbuf[4096];
+    uint32_t addr = r->bd.addr;
+    uint32_t temp = r->picb << 1;
+    uint32_t written = 0;
+    int to_copy = 0;
+    temp = audio_MIN (temp, max);
+
+    if (!temp) {
+        *stop = 1;
+        return 0;
+    }
+
+    while (temp) {
+        int copied;
+        to_copy = audio_MIN (temp, sizeof (tmpbuf));
+        cpu_physical_memory_read (addr, tmpbuf, to_copy);
+        copied = AUD_write (s->voice_po, tmpbuf, to_copy);
+        dolog ("write_audio max=%x to_copy=%x copied=%x\n",
+               max, to_copy, copied);
+        if (!copied) {
+            *stop = 1;
+            break;
+        }
+        temp -= copied;
+        addr += copied;
+        written += copied;
+    }
+
+    if (!temp) {
+        if (to_copy < 4) {
+            dolog ("whoops\n");
+            s->last_samp = 0;
+        }
+        else {
+            s->last_samp = *(uint32_t *) &tmpbuf[to_copy - 4];
+        }
+    }
+
+    r->bd.addr = addr;
+    return written;
+}
+
+static void write_bup (AC97LinkState *s, int elapsed)
+{
+    int written = 0;
+
+    dolog ("write_bup\n");
+    if (!(s->bup_flag & BUP_SET)) {
+        if (s->bup_flag & BUP_LAST) {
+            int i;
+            uint8_t *p = s->silence;
+            for (i = 0; i < sizeof (s->silence) / 4; i++, p += 4) {
+                *(uint32_t *) p = s->last_samp;
+            }
+        }
+        else {
+            memset (s->silence, 0, sizeof (s->silence));
+        }
+        s->bup_flag |= BUP_SET;
+    }
+
+    while (elapsed) {
+        int temp = audio_MIN (elapsed, sizeof (s->silence));
+        while (temp) {
+            int copied = AUD_write (s->voice_po, s->silence, temp);
+            if (!copied)
+                return;
+            temp -= copied;
+            elapsed -= copied;
+            written += copied;
+        }
+    }
+}
+
+static int read_audio (AC97LinkState *s, AC97BusMasterRegs *r,
+                       int max, int *stop)
+{
+    uint8_t tmpbuf[4096];
+    uint32_t addr = r->bd.addr;
+    uint32_t temp = r->picb << 1;
+    uint32_t nread = 0;
+    int to_copy = 0;
+    SWVoiceIn *voice = (r - s->bm_regs) == MC_INDEX ? s->voice_mc : s->voice_pi;
+
+    temp = audio_MIN (temp, max);
+
+    if (!temp) {
+        *stop = 1;
+        return 0;
+    }
+
+    while (temp) {
+        int acquired;
+        to_copy = audio_MIN (temp, sizeof (tmpbuf));
+        acquired = AUD_read (voice, tmpbuf, to_copy);
+        if (!acquired) {
+            *stop = 1;
+            break;
+        }
+        cpu_physical_memory_write (addr, tmpbuf, acquired);
+        temp -= acquired;
+        addr += acquired;
+        nread += acquired;
+    }
+
+    r->bd.addr = addr;
+    return nread;
+}
+
+static void transfer_audio (AC97LinkState *s, int index, int elapsed)
+{
+    AC97BusMasterRegs *r = &s->bm_regs[index];
+    int written = 0, stop = 0;
+
+    if (r->sr & SR_DCH) {
+        if (r->cr & CR_RPBM) {
+            switch (index) {
+            case PO_INDEX:
+                write_bup (s, elapsed);
+                break;
+            }
+        }
+        return;
+    }
+
+    while ((elapsed >> 1) && !stop) {
+        int temp;
+
+        if (!r->bd_valid) {
+            dolog ("invalid bd\n");
+            fetch_bd (s, r);
+        }
+
+        if (!r->picb) {
+            dolog ("fresh bd %d is empty %#x %#x\n",
+                   r->civ, r->bd.addr, r->bd.ctl_len);
+            if (r->civ == r->lvi) {
+                r->sr |= SR_DCH; /* CELV? */
+                s->bup_flag = 0;
+                break;
+            }
+            r->sr &= ~SR_CELV;
+            r->civ = r->piv;
+            r->piv = (r->piv + 1) % 32;
+            fetch_bd (s, r);
+            return;
+        }
+
+        switch (index) {
+        case PO_INDEX:
+            temp = write_audio (s, r, elapsed, &stop);
+            written += temp;
+            elapsed -= temp;
+            r->picb -= (temp >> 1);
+            break;
+
+        case PI_INDEX:
+        case MC_INDEX:
+            temp = read_audio (s, r, elapsed, &stop);
+            elapsed -= temp;
+            r->picb -= (temp >> 1);
+            break;
+        }
+
+        if (!r->picb) {
+            uint32_t new_sr = r->sr & ~SR_CELV;
+
+            if (r->bd.ctl_len & BD_IOC) {
+                new_sr |= SR_BCIS;
+            }
+
+            if (r->civ == r->lvi) {
+                dolog ("Underrun civ (%d) == lvi (%d)\n", r->civ, r->lvi);
+
+                new_sr |= SR_LVBCI | SR_DCH | SR_CELV;
+                stop = 1;
+                s->bup_flag = (r->bd.ctl_len & BD_BUP) ? BUP_LAST : 0;
+            }
+            else {
+                r->civ = r->piv;
+                r->piv = (r->piv + 1) % 32;
+                fetch_bd (s, r);
+            }
+
+            update_sr (s, r, new_sr);
+        }
+    }
+}
+
+static void pi_callback (void *opaque, int avail)
+{
+    transfer_audio (opaque, PI_INDEX, avail);
+}
+
+static void mc_callback (void *opaque, int avail)
+{
+    transfer_audio (opaque, MC_INDEX, avail);
+}
+
+static void po_callback (void *opaque, int free)
+{
+    transfer_audio (opaque, PO_INDEX, free);
+}
+
+static void ac97_save (QEMUFile *f, void *opaque)
+{
+    size_t i;
+    uint8_t active[LAST_INDEX];
+    AC97LinkState *s = opaque;
+
+    qemu_put_be32s (f, &s->glob_cnt);
+    qemu_put_be32s (f, &s->glob_sta);
+    qemu_put_be32s (f, &s->cas);
+
+    for (i = 0; i < sizeof (s->bm_regs) / sizeof (s->bm_regs[0]); ++i) {
+        AC97BusMasterRegs *r = &s->bm_regs[i];
+        qemu_put_be32s (f, &r->bdbar);
+        qemu_put_8s (f, &r->civ);
+        qemu_put_8s (f, &r->lvi);
+        qemu_put_be16s (f, &r->sr);
+        qemu_put_be16s (f, &r->picb);
+        qemu_put_8s (f, &r->piv);
+        qemu_put_8s (f, &r->cr);
+        qemu_put_be32s (f, &r->bd_valid);
+        qemu_put_be32s (f, &r->bd.addr);
+        qemu_put_be32s (f, &r->bd.ctl_len);
+    }
+    qemu_put_buffer (f, s->mixer_data, sizeof (s->mixer_data));
+
+    active[PI_INDEX] = AUD_is_active_in (s->voice_pi) ? 1 : 0;
+    active[PO_INDEX] = AUD_is_active_out (s->voice_po) ? 1 : 0;
+    active[MC_INDEX] = AUD_is_active_in (s->voice_mc) ? 1 : 0;
+    qemu_put_buffer (f, active, sizeof (active));
+}
+
+static int ac97_load (QEMUFile *f, void *opaque, int version_id)
+{
+    size_t i;
+    uint8_t active[LAST_INDEX];
+    AC97LinkState *s = opaque;
+
+    if (version_id != 1)
+        return -EINVAL;
+
+    qemu_get_be32s (f, &s->glob_cnt);
+    qemu_get_be32s (f, &s->glob_sta);
+    qemu_get_be32s (f, &s->cas);
+
+    for (i = 0; i < sizeof (s->bm_regs) / sizeof (s->bm_regs[0]); ++i) {
+        AC97BusMasterRegs *r = &s->bm_regs[i];
+        qemu_get_be32s (f, &r->bdbar);
+        qemu_get_8s (f, &r->civ);
+        qemu_get_8s (f, &r->lvi);
+        qemu_get_be16s (f, &r->sr);
+        qemu_get_be16s (f, &r->picb);
+        qemu_get_8s (f, &r->piv);
+        qemu_get_8s (f, &r->cr);
+        qemu_get_be32s (f, &r->bd_valid);
+        qemu_get_be32s (f, &r->bd.addr);
+        qemu_get_be32s (f, &r->bd.ctl_len);
+    }
+    qemu_get_buffer (f, s->mixer_data, sizeof (s->mixer_data));
+    qemu_get_buffer (f, active, sizeof (active));
+
+#ifdef USE_MIXER
+    record_select (s, mixer_load (s, AC97_Record_Select));
+#define V_(a, b) set_volume (s, a, b, mixer_load (s, a))
+    V_ (AC97_Master_Volume_Mute, AUD_MIXER_VOLUME);
+    V_ (AC97_PCM_Out_Volume_Mute, AUD_MIXER_PCM);
+    V_ (AC97_Line_In_Volume_Mute, AUD_MIXER_LINE_IN);
+#undef V_
+#endif
+    reset_voices (s, active);
+
+    s->bup_flag = 0;
+    s->last_samp = 0;
+    return 0;
+}
+
+static void ac97_map (PCIDevice *pci_dev, int region_num,
+                      uint32_t addr, uint32_t size, int type)
+{
+    PCIAC97LinkState *d = (PCIAC97LinkState *) pci_dev;
+    AC97LinkState *s = &d->ac97;
+
+    if (!region_num) {
+        s->base[0] = addr;
+        register_ioport_read (addr, 256 * 1, 1, nam_readb, d);
+        register_ioport_read (addr, 256 * 2, 2, nam_readw, d);
+        register_ioport_read (addr, 256 * 4, 4, nam_readl, d);
+        register_ioport_write (addr, 256 * 1, 1, nam_writeb, d);
+        register_ioport_write (addr, 256 * 2, 2, nam_writew, d);
+        register_ioport_write (addr, 256 * 4, 4, nam_writel, d);
+    }
+    else {
+        s->base[1] = addr;
+        register_ioport_read (addr, 64 * 1, 1, nabm_readb, d);
+        register_ioport_read (addr, 64 * 2, 2, nabm_readw, d);
+        register_ioport_read (addr, 64 * 4, 4, nabm_readl, d);
+        register_ioport_write (addr, 64 * 1, 1, nabm_writeb, d);
+        register_ioport_write (addr, 64 * 2, 2, nabm_writew, d);
+        register_ioport_write (addr, 64 * 4, 4, nabm_writel, d);
+    }
+}
+
+static void ac97_on_reset (void *opaque)
+{
+    AC97LinkState *s = opaque;
+
+    reset_bm_regs (s, &s->bm_regs[0]);
+    reset_bm_regs (s, &s->bm_regs[1]);
+    reset_bm_regs (s, &s->bm_regs[2]);
+
+    /*
+     * Reset the mixer too. The Windows XP driver seems to rely on
+     * this. At least it wants to read the vendor id before it resets
+     * the codec manually.
+     */
+    mixer_reset (s);
+}
+
+int ac97_init (PCIBus *bus, AudioState *audio)
+{
+    PCIAC97LinkState *d;
+    AC97LinkState *s;
+    uint8_t *c;
+
+    if (!bus) {
+        AUD_log ("ac97", "No PCI bus\n");
+        return -1;
+    }
+
+    if (!audio) {
+        AUD_log ("ac97", "No audio state\n");
+        return -1;
+    }
+
+    d = (PCIAC97LinkState *) pci_register_device (bus, "AC97",
+                                                  sizeof (PCIAC97LinkState),
+                                                  -1, NULL, NULL);
+
+    if (!d) {
+        AUD_log ("ac97", "Failed to register PCI device\n");
+        return -1;
+    }
+
+    s = &d->ac97;
+    s->pci_dev = &d->dev;
+    c = d->dev.config;
+    c[0x00] = 0x86;      /* vid vendor id intel ro */
+    c[0x01] = 0x80;      /* intel */
+
+    c[0x02] = 0x15;      /* did device id 82801 ro */
+    c[0x03] = 0x24;      /* 82801aa */
+
+    c[0x04] = 0x00;      /* pcicmd pci command rw, ro */
+    c[0x05] = 0x00;
+
+    c[0x06] = 0x80;      /* pcists pci status rwc, ro */
+    c[0x07] = 0x02;
+
+    c[0x08] = 0x01;      /* rid revision ro */
+    c[0x09] = 0x00;      /* pi programming interface ro */
+    c[0x0a] = 0x01;      /* scc sub class code ro */
+    c[0x0b] = 0x04;      /* bcc base class code ro */
+    c[0x0e] = 0x00;      /* headtyp header type ro */
+
+    c[0x10] = 0x01;      /* nabmar native audio mixer base
+                            address rw */
+    c[0x11] = 0x00;
+    c[0x12] = 0x00;
+    c[0x13] = 0x00;
+
+    c[0x14] = 0x01;      /* nabmbar native audio bus mastering
+                            base address rw */
+    c[0x15] = 0x00;
+    c[0x16] = 0x00;
+    c[0x17] = 0x00;
+
+    c[0x2c] = 0x86;      /* svid subsystem vendor id rwo */
+    c[0x2d] = 0x80;
+
+    c[0x2e] = 0x00;      /* sid subsystem id rwo */
+    c[0x2f] = 0x00;
+
+    c[0x3c] = 0x00;      /* intr_ln interrupt line rw */
+    c[0x3d] = 0x01;      /* intr_pn interrupt pin ro */
+
+    pci_register_io_region (&d->dev, 0, 256 * 4, PCI_ADDRESS_SPACE_IO, ac97_map);
+    pci_register_io_region (&d->dev, 1, 64 * 4, PCI_ADDRESS_SPACE_IO, ac97_map);
+    register_savevm ("ac97", 0, 1, ac97_save, ac97_load, s);
+    qemu_register_reset (ac97_on_reset, s);
+    AUD_register_card (audio, "ac97", &s->card);
+    ac97_on_reset (s);
+    return 0;
+}
diff --git a/hw/acpi.c b/hw/acpi.c
index a2efd9c..2669e4e 100644
--- a/hw/acpi.c
+++ b/hw/acpi.c
@@ -482,7 +482,7 @@ i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base)
     pci_conf[0x03] = 0x71;
     pci_conf[0x06] = 0x80;
     pci_conf[0x07] = 0x02;
-    pci_conf[0x08] = 0x00; // revision number
+    pci_conf[0x08] = 0x03; // revision number
     pci_conf[0x09] = 0x00;
     pci_conf[0x0a] = 0x80; // other bridge device
     pci_conf[0x0b] = 0x06; // bridge device
diff --git a/hw/audiodev.h b/hw/audiodev.h
index 18cdf67..3c02f19 100644
--- a/hw/audiodev.h
+++ b/hw/audiodev.h
@@ -10,3 +10,5 @@ int Adlib_init (AudioState *s, qemu_irq *pic);
 /* gus.c */
 int GUS_init (AudioState *s, qemu_irq *pic);
 
+/* ac97.c */
+int ac97_init (PCIBus *buf, AudioState *s);
diff --git a/hw/dma.c b/hw/dma.c
index 1891b22..f192c63 100644
--- a/hw/dma.c
+++ b/hw/dma.c
@@ -439,6 +439,13 @@ static void dma_reset(void *opaque)
     write_cont (d, (0x0d << d->dshift), 0);
 }
 
+static int dma_phony_handler (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+    dolog ("unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d\n",
+           nchan, dma_pos, dma_len);
+    return dma_pos;
+}
+
 /* dshift = 0: 8 bit DMA, 1 = 16 bit DMA */
 static void dma_init2(struct dma_cont *d, int base, int dshift,
                       int page_base, int pageh_base)
@@ -471,6 +478,9 @@ static void dma_init2(struct dma_cont *d, int base, int dshift,
     }
     qemu_register_reset(dma_reset, d);
     dma_reset(d);
+    for (i = 0; i < LENOFA (d->regs); ++i) {
+        d->regs[i].transfer_handler = dma_phony_handler;
+    }
 }
 
 static void dma_save (QEMUFile *f, void *opaque)
diff --git a/hw/eccmemctl.c b/hw/eccmemctl.c
index a63a528..02b2f8d 100755
--- a/hw/eccmemctl.c
+++ b/hw/eccmemctl.c
@@ -68,7 +68,7 @@
 #define ECC_FAR0_TYPE  0x000000f0      /* Transaction type */
 #define ECC_FAR0_SIZE  0x00000700      /* Transaction size */
 #define ECC_FAR0_CACHE 0x00000800      /* Mapped cacheable */
-#define ECC_FAR0_LOCK  0x00001000      /* Error occurred in attomic cycle */
+#define ECC_FAR0_LOCK  0x00001000      /* Error occurred in atomic cycle */
 #define ECC_FAR0_BMODE 0x00002000      /* Boot mode */
 #define ECC_FAR0_VADDR 0x003fc000      /* VA[12-19] (superset bits) */
 #define ECC_FAR0_S     0x08000000      /* Supervisor mode */
@@ -90,6 +90,7 @@
 #define ECC_ADDR_MASK  (ECC_SIZE - 1)
 
 typedef struct ECCState {
+    qemu_irq irq;
     uint32_t regs[ECC_NREGS];
 } ECCState;
 
@@ -222,7 +223,7 @@ static void ecc_reset(void *opaque)
         s->regs[i] = 0;
 }
 
-void * ecc_init(target_phys_addr_t base, uint32_t version)
+void * ecc_init(target_phys_addr_t base, qemu_irq irq, uint32_t version)
 {
     int ecc_io_memory;
     ECCState *s;
@@ -232,6 +233,7 @@ void * ecc_init(target_phys_addr_t base, uint32_t version)
         return NULL;
 
     s->regs[0] = version;
+    s->irq = irq;
 
     ecc_io_memory = cpu_register_io_memory(0, ecc_mem_read, ecc_mem_write, s);
     cpu_register_physical_memory(base, ECC_SIZE, ecc_io_memory);
diff --git a/hw/gus.c b/hw/gus.c
new file mode 100644
index 0000000..57753a7
--- /dev/null
+++ b/hw/gus.c
@@ -0,0 +1,300 @@
+/*
+ * QEMU Proxy for Gravis Ultrasound GF1 emulation by Tibor "TS" Schütz
+ *
+ * Copyright (c) 2002-2005 Vassili Karpov (malc)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "hw.h"
+#include "audiodev.h"
+#include "audio/audio.h"
+#include "isa.h"
+#include "gusemu.h"
+#include "gustate.h"
+
+#define dolog(...) AUD_log ("audio", __VA_ARGS__)
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#ifdef WORDS_BIGENDIAN
+#define GUS_ENDIANNESS 1
+#else
+#define GUS_ENDIANNESS 0
+#endif
+
+#define IO_READ_PROTO(name) \
+    uint32_t name (void *opaque, uint32_t nport)
+#define IO_WRITE_PROTO(name) \
+    void name (void *opaque, uint32_t nport, uint32_t val)
+
+static struct {
+    int port;
+    int irq;
+    int dma;
+    int freq;
+} conf = {0x240, 7, 3, 44100};
+
+typedef struct GUSState {
+    GUSEmuState emu;
+    QEMUSoundCard card;
+    int freq;
+    int pos, left, shift, irqs;
+    uint16_t *mixbuf;
+    uint8_t himem[1024 * 1024 + 32 + 4096];
+    int samples;
+    SWVoiceOut *voice;
+    int64_t last_ticks;
+    qemu_irq *pic;
+} GUSState;
+
+IO_READ_PROTO (gus_readb)
+{
+    GUSState *s = opaque;
+
+    return gus_read (&s->emu, nport, 1);
+}
+
+IO_READ_PROTO (gus_readw)
+{
+    GUSState *s = opaque;
+
+    return gus_read (&s->emu, nport, 2);
+}
+
+IO_WRITE_PROTO (gus_writeb)
+{
+    GUSState *s = opaque;
+
+    gus_write (&s->emu, nport, 1, val);
+}
+
+IO_WRITE_PROTO (gus_writew)
+{
+    GUSState *s = opaque;
+
+    gus_write (&s->emu, nport, 2, val);
+}
+
+static int write_audio (GUSState *s, int samples)
+{
+    int net = 0;
+    int pos = s->pos;
+
+    while (samples) {
+        int nbytes, wbytes, wsampl;
+
+        nbytes = samples << s->shift;
+        wbytes = AUD_write (
+            s->voice,
+            s->mixbuf + (pos << (s->shift - 1)),
+            nbytes
+            );
+
+        if (wbytes) {
+            wsampl = wbytes >> s->shift;
+
+            samples -= wsampl;
+            pos = (pos + wsampl) % s->samples;
+
+            net += wsampl;
+        }
+        else {
+            break;
+        }
+    }
+
+    return net;
+}
+
+static void GUS_callback (void *opaque, int free)
+{
+    int samples, to_play, net = 0;
+    GUSState *s = opaque;
+
+    samples = free >> s->shift;
+    to_play = audio_MIN (samples, s->left);
+
+    while (to_play) {
+        int written = write_audio (s, to_play);
+
+        if (!written) {
+            goto reset;
+        }
+
+        s->left -= written;
+        to_play -= written;
+        samples -= written;
+        net += written;
+    }
+
+    samples = audio_MIN (samples, s->samples);
+    if (samples) {
+        gus_mixvoices (&s->emu, s->freq, samples, s->mixbuf);
+
+        while (samples) {
+            int written = write_audio (s, samples);
+            if (!written) {
+                break;
+            }
+            samples -= written;
+            net += written;
+        }
+    }
+    s->left = samples;
+
+reset:
+    gus_irqgen (&s->emu, (double) (net * 1000000) / s->freq);
+}
+
+int GUS_irqrequest (GUSEmuState *emu, int hwirq, int n)
+{
+    GUSState *s = emu->opaque;
+    /* qemu_irq_lower (s->pic[hwirq]); */
+    qemu_irq_raise (s->pic[hwirq]);
+    s->irqs += n;
+    ldebug ("irqrequest %d %d %d\n", hwirq, n, s->irqs);
+    return n;
+}
+
+void GUS_irqclear (GUSEmuState *emu, int hwirq)
+{
+    GUSState *s = emu->opaque;
+    ldebug ("irqclear %d %d\n", hwirq, s->irqs);
+    qemu_irq_lower (s->pic[hwirq]);
+    s->irqs -= 1;
+#ifdef IRQ_STORM
+    if (s->irqs > 0) {
+        qemu_irq_raise (s->pic[hwirq]);
+    }
+#endif
+}
+
+void GUS_dmarequest (GUSEmuState *der)
+{
+    /* GUSState *s = (GUSState *) der; */
+    ldebug ("dma request %d\n", der->gusdma);
+    DMA_hold_DREQ (der->gusdma);
+}
+
+int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
+{
+    GUSState *s = opaque;
+    int8_t tmpbuf[4096];
+    int pos = dma_pos, mode, left = dma_len - dma_pos;
+
+    ldebug ("read DMA %#x %d\n", dma_pos, dma_len);
+    mode = DMA_get_channel_mode (s->emu.gusdma);
+    while (left) {
+        int to_copy = audio_MIN ((size_t) left, sizeof (tmpbuf));
+        int copied;
+
+        ldebug ("left=%d to_copy=%d pos=%d\n", left, to_copy, pos);
+        copied = DMA_read_memory (nchan, tmpbuf, pos, to_copy);
+        gus_dma_transferdata (&s->emu, tmpbuf, copied, left == copied);
+        left -= copied;
+        pos += copied;
+    }
+
+    if (0 == ((mode >> 4) & 1)) {
+        DMA_release_DREQ (s->emu.gusdma);
+    }
+    return dma_len;
+}
+
+int GUS_init (AudioState *audio, qemu_irq *pic)
+{
+    GUSState *s;
+    audsettings_t as;
+
+    if (!audio) {
+        dolog ("No audio state\n");
+        return -1;
+    }
+
+    s = qemu_mallocz (sizeof (*s));
+    if (!s) {
+        dolog ("Could not allocate memory for GUS (%zu bytes)\n",
+               sizeof (*s));
+        return -1;
+    }
+
+    AUD_register_card (audio, "gus", &s->card);
+
+    as.freq = conf.freq;
+    as.nchannels = 2;
+    as.fmt = AUD_FMT_S16;
+    as.endianness = GUS_ENDIANNESS;
+
+    s->voice = AUD_open_out (
+        &s->card,
+        NULL,
+        "gus",
+        s,
+        GUS_callback,
+        &as
+        );
+
+    if (!s->voice) {
+        AUD_remove_card (&s->card);
+        qemu_free (s);
+        return -1;
+    }
+
+    s->shift = 2;
+    s->samples = AUD_get_buffer_size_out (s->voice) >> s->shift;
+    s->mixbuf = qemu_mallocz (s->samples << s->shift);
+    if (!s->mixbuf) {
+        AUD_close_out (&s->card, s->voice);
+        AUD_remove_card (&s->card);
+        qemu_free (s);
+        return -1;
+    }
+
+    register_ioport_write (conf.port, 1, 1, gus_writeb, s);
+    register_ioport_write (conf.port, 1, 2, gus_writew, s);
+
+    register_ioport_read ((conf.port + 0x100) & 0xf00, 1, 1, gus_readb, s);
+    register_ioport_read ((conf.port + 0x100) & 0xf00, 1, 2, gus_readw, s);
+
+    register_ioport_write (conf.port + 6, 10, 1, gus_writeb, s);
+    register_ioport_write (conf.port + 6, 10, 2, gus_writew, s);
+    register_ioport_read (conf.port + 6, 10, 1, gus_readb, s);
+    register_ioport_read (conf.port + 6, 10, 2, gus_readw, s);
+
+
+    register_ioport_write (conf.port + 0x100, 8, 1, gus_writeb, s);
+    register_ioport_write (conf.port + 0x100, 8, 2, gus_writew, s);
+    register_ioport_read (conf.port + 0x100, 8, 1, gus_readb, s);
+    register_ioport_read (conf.port + 0x100, 8, 2, gus_readw, s);
+
+    DMA_register_channel (conf.dma, GUS_read_DMA, s);
+    s->emu.gusirq = conf.irq;
+    s->emu.gusdma = conf.dma;
+    s->emu.himemaddr = s->himem;
+    s->emu.gusdatapos = s->emu.himemaddr + 1024 * 1024 + 32;
+    s->emu.opaque = s;
+    s->freq = conf.freq;
+    s->pic = pic;
+
+    AUD_set_active_out (s->voice, 1);
+    return 0;
+}
diff --git a/hw/gusemu.h b/hw/gusemu.h
new file mode 100644
index 0000000..2e9c1c0
--- /dev/null
+++ b/hw/gusemu.h
@@ -0,0 +1,103 @@
+/*
+ * GUSEMU32 - API
+ *
+ * Copyright (C) 2000-2007 Tibor "TS" Schütz
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUSEMU_H
+#define GUSEMU_H
+
+/* data types (need to be adjusted if neither a VC6 nor a C99 compatible compiler is used) */
+
+#if defined _WIN32 && defined _MSC_VER /* doesnt support other win32 compilers yet, do it yourself... */
+ typedef unsigned char GUSbyte;
+ typedef unsigned short GUSword;
+ typedef unsigned int GUSdword;
+ typedef signed char GUSchar;
+#else
+ #include <stdint.h>
+ typedef int8_t GUSchar;
+ typedef uint8_t GUSbyte;
+ typedef uint16_t GUSword;
+ typedef uint32_t GUSdword;
+#endif
+
+typedef struct _GUSEmuState
+{
+ GUSbyte *himemaddr; /* 1024*1024 bytes used for storing uploaded samples (+32 additional bytes for read padding) */
+ GUSbyte *gusdatapos; /* (gusdataend-gusdata) bytes used for storing emulated GF1/mixer register states (32*32+4 bytes in initial GUSemu32 version) */
+ int gusirq;
+ int gusdma;
+ unsigned int timer1fraction;
+ unsigned int timer2fraction;
+ void *opaque;
+} GUSEmuState;
+
+/* ** Callback functions needed: */
+/* NMI is defined as hwirq=-1 (not supported (yet?)) */
+/* GUS_irqrequest returns the number of IRQs actually scheduled into the virtual machine */
+/* Level triggered IRQ simulations normally return 1 */
+/* Event triggered IRQ simulation can safely ignore GUS_irqclear calls */
+int  GUS_irqrequest(GUSEmuState *state, int hwirq, int num);/* needed in both mixer and bus emulation functions. */
+void GUS_irqclear(  GUSEmuState *state, int hwirq); /* used by gus_write() only - can be left empty for mixer functions */
+void GUS_dmarequest(GUSEmuState *state);            /* used by gus_write() only - can be left empty for mixer functions */
+
+/* ** ISA bus interface functions: */
+
+/* Port I/O handlers */
+/* support the following ports: */
+/* 2x0,2x6,2x8...2xF,3x0...3x7;  */
+/* optional: 388,389 (at least writes should be forwarded or some GUS detection algorithms will fail) */
+/* data is passed in host byte order */
+unsigned int gus_read( GUSEmuState *state, int port, int size);
+void         gus_write(GUSEmuState *state, int port, int size, unsigned int data);
+/* size is given in bytes (1 for byte, 2 for word) */
+
+/* DMA data transfer function */
+/* data pointed to is passed in native x86 order */
+void gus_dma_transferdata(GUSEmuState *state, char *dma_addr, unsigned int count, int TC);
+/* Called back by GUS_start_DMA as soon as the emulated DMA controller is ready for a transfer to or from GUS */
+/* (might be immediately if the DMA controller was programmed first) */
+/* dma_addr is an already translated address directly pointing to the beginning of the memory block */
+/* do not forget to update DMA states after the call, including the DREQ and TC flags */
+/* it is possible to break down a single transfer into multiple ones, but take care that: */
+/* -dma_count is actually count-1 */
+/* -before and during a transfer, DREQ is set and TC cleared */
+/* -when calling gus_dma_transferdata(), TC is only set true for call transfering the last byte */
+/* -after the last transfer, DREQ is cleared and TC is set */
+
+/* ** GF1 mixer emulation functions: */
+/* Usually, gus_irqgen should be called directly after gus_mixvoices if you can meet the recommended ranges. */
+/* If the interrupts are executed immediately (i.e., are synchronous), it may be useful to break this */
+/* down into a sequence of gus_mixvoice();gus_irqgen(); calls while mixing an audio block. */
+/* If the interrupts are asynchronous, it may be needed to use a separate thread mixing into a temporary */
+/* audio buffer in order to avoid quality loss caused by large numsamples and elapsed_time values. */
+
+void gus_mixvoices(GUSEmuState *state, unsigned int playback_freq, unsigned int numsamples, short *bufferpos);
+/* recommended range: 10 < numsamples < 100 */
+/* lower values may result in increased rounding error, higher values often cause audible timing delays */
+
+void gus_irqgen(GUSEmuState *state, unsigned int elapsed_time);
+/* recommended range: 80us < elapsed_time < max(1000us, numsamples/playback_freq) */
+/* lower values won´t provide any benefit at all, higher values can cause audible timing delays */
+/* note: masked timers are also calculated by this function, thus it might be needed even without any IRQs in use! */
+
+#endif  /* gusemu.h */
diff --git a/hw/gusemu_hal.c b/hw/gusemu_hal.c
new file mode 100644
index 0000000..c6f9537
--- /dev/null
+++ b/hw/gusemu_hal.c
@@ -0,0 +1,554 @@
+/*
+ * GUSEMU32 - bus interface part
+ *
+ * Copyright (C) 2000-2007 Tibor "TS" Schütz
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+ * TODO: check mixer: see 7.20 of sdk for panning pos (applies to all gus models?)?
+ */
+
+#include "gustate.h"
+#include "gusemu.h"
+
+#define GUSregb(position) (*            (gusptr+(position)))
+#define GUSregw(position) (*(GUSword *) (gusptr+(position)))
+#define GUSregd(position) (*(GUSdword *)(gusptr+(position)))
+
+/* size given in bytes */
+unsigned int gus_read(GUSEmuState * state, int port, int size)
+{
+    int             value_read = 0;
+
+    GUSbyte        *gusptr;
+    gusptr = state->gusdatapos;
+    GUSregd(portaccesses)++;
+
+    switch (port & 0xff0f)
+    {
+        /* MixerCtrlReg (read not supported on GUS classic) */
+        /* case 0x200: return GUSregb(MixerCtrlReg2x0); */
+    case 0x206:                          /* IRQstatReg / SB2x6IRQ */
+        /* adlib/sb bits set in port handlers */
+        /* timer/voice bits set in gus_irqgen() */
+        /* dma bit set in gus_dma_transferdata */
+        /* midi not implemented yet */
+        return GUSregb(IRQStatReg2x6);
+    /* case 0x308:                       */ /* AdLib388 */
+    case 0x208:
+        if (GUSregb(GUS45TimerCtrl) & 1)
+            return GUSregb(TimerStatus2x8);
+        return GUSregb(AdLibStatus2x8);  /* AdLibStatus */
+    case 0x309:                          /* AdLib389 */
+    case 0x209:
+        return GUSregb(AdLibData2x9);    /* AdLibData */
+    case 0x20A:
+        return GUSregb(AdLibCommand2xA); /* AdLib2x8_2xA */
+
+#if 0
+    case 0x20B:                          /* GUS hidden registers (read not supported on GUS classic) */
+        switch (GUSregb(RegCtrl_2xF) & 0x07)
+        {
+        case 0:                                 /* IRQ/DMA select */
+            if (GUSregb(MixerCtrlReg2x0) & 0x40)
+                return GUSregb(IRQ_2xB);        /* control register select bit */
+            else
+                return GUSregb(DMA_2xB);
+            /* case 1-5:                        */ /* general purpose emulation regs  */
+            /*  return ...                      */ /* + status reset reg (write only) */
+        case 6:
+            return GUSregb(Jumper_2xB);         /* Joystick/MIDI enable (JumperReg) */
+        default:;
+        }
+        break;
+#endif
+
+    case 0x20C:                          /* SB2xCd */
+        value_read = GUSregb(SB2xCd);
+        if (GUSregb(StatRead_2xF) & 0x20)
+            GUSregb(SB2xCd) ^= 0x80; /* toggle MSB on read */
+        return value_read;
+        /* case 0x20D:                   */ /* SB2xD is write only -> 2xE writes to it*/
+    case 0x20E:
+        if (GUSregb(RegCtrl_2xF) & 0x80) /* 2xE read IRQ enabled? */
+        {
+            GUSregb(StatRead_2xF) |= 0x80;
+            GUS_irqrequest(state, state->gusirq, 1);
+        }
+        return GUSregb(SB2xE);           /* SB2xE */
+    case 0x20F:                          /* StatRead_2xF */
+        /*set/clear fixed bits */
+        /*value_read = (GUSregb(StatRead_2xF) & 0xf9)|1; */ /*(LSB not set on GUS classic!)*/
+        value_read = (GUSregb(StatRead_2xF) & 0xf9);
+        if (GUSregb(MixerCtrlReg2x0) & 0x08)
+            value_read |= 2;    /* DMA/IRQ enabled flag */
+        return value_read;
+    /* case 0x300:                      */ /* MIDI (not implemented) */
+    /* case 0x301:                      */ /* MIDI (not implemented) */
+    case 0x302:
+        return GUSregb(VoiceSelReg3x2); /* VoiceSelReg */
+    case 0x303:
+        return GUSregb(FunkSelReg3x3);  /* FunkSelReg */
+    case 0x304:                         /* DataRegLoByte3x4 + DataRegWord3x4 */
+    case 0x305:                         /* DataRegHiByte3x5 */
+        switch (GUSregb(FunkSelReg3x3))
+        {
+    /* common functions */
+        case 0x41:                      /* DramDMAContrReg */
+            value_read = GUSregb(GUS41DMACtrl); /* &0xfb */
+            GUSregb(GUS41DMACtrl) &= 0xbb;
+            if (state->gusdma >= 4)
+                value_read |= 0x04;
+            if (GUSregb(IRQStatReg2x6) & 0x80)
+            {
+                value_read |= 0x40;
+                GUSregb(IRQStatReg2x6) &= 0x7f;
+                if (!GUSregb(IRQStatReg2x6))
+                    GUS_irqclear(state, state->gusirq);
+            }
+            return (GUSbyte) value_read;
+            /* DramDMAmemPosReg */
+            /* case 0x42: value_read=GUSregw(GUS42DMAStart); break;*/
+            /* 43h+44h write only */
+        case 0x45:
+            return GUSregb(GUS45TimerCtrl);         /* TimerCtrlReg */
+            /* 46h+47h write only */
+            /* 48h: samp freq - write only */
+        case 0x49:
+            return GUSregb(GUS49SampCtrl) & 0xbf;   /* SampCtrlReg */
+        /* case 4bh:                                */ /* joystick trim not supported */
+        /* case 0x4c: return GUSregb(GUS4cReset);   */ /* GUSreset: write only*/
+    /* voice specific functions */
+        case 0x80:
+        case 0x81:
+        case 0x82:
+        case 0x83:
+        case 0x84:
+        case 0x85:
+        case 0x86:
+        case 0x87:
+        case 0x88:
+        case 0x89:
+        case 0x8a:
+        case 0x8b:
+        case 0x8c:
+        case 0x8d:
+            {
+                int             offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f);
+                offset += ((int) GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /* = Voice*32 + Funktion*2 */
+                value_read = GUSregw(offset);
+            }
+            break;
+    /* voice unspecific functions */
+        case 0x8e:                                  /* NumVoice */
+            return GUSregb(NumVoices);
+        case 0x8f:                                  /* irqstatreg */
+            /* (pseudo IRQ-FIFO is processed during a gus_write(0x3X3,0x8f)) */
+            return GUSregb(SynVoiceIRQ8f);
+        default:
+            return 0xffff;
+        }
+        if (size == 1)
+        {
+            if ((port & 0xff0f) == 0x305)
+                value_read = value_read >> 8;
+            value_read &= 0xff;
+        }
+        return (GUSword) value_read;
+    /* case 0x306:                                  */ /* Mixer/Version info */
+        /*  return 0xff; */ /* Pre 3.6 boards, ICS mixer NOT present */
+    case 0x307:                                     /* DRAMaccess */
+        {
+            GUSbyte        *adr;
+            adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
+            return *adr;
+        }
+    default:;
+    }
+    return 0xffff;
+}
+
+void gus_write(GUSEmuState * state, int port, int size, unsigned int data)
+{
+    GUSbyte        *gusptr;
+    gusptr = state->gusdatapos;
+    GUSregd(portaccesses)++;
+
+    switch (port & 0xff0f)
+    {
+    case 0x200:                 /* MixerCtrlReg */
+        GUSregb(MixerCtrlReg2x0) = (GUSbyte) data;
+        break;
+    case 0x206:                 /* IRQstatReg / SB2x6IRQ */
+        if (GUSregb(GUS45TimerCtrl) & 0x20) /* SB IRQ enabled? -> set 2x6IRQ bit */
+        {
+            GUSregb(TimerStatus2x8) |= 0x08;
+            GUSregb(IRQStatReg2x6) = 0x10;
+            GUS_irqrequest(state, state->gusirq, 1);
+        }
+        break;
+    case 0x308:                /* AdLib 388h */
+    case 0x208:                /* AdLibCommandReg */
+        GUSregb(AdLibCommand2xA) = (GUSbyte) data;
+        break;
+    case 0x309:                /* AdLib 389h */
+    case 0x209:                /* AdLibDataReg */
+        if ((GUSregb(AdLibCommand2xA) == 0x04) && (!(GUSregb(GUS45TimerCtrl) & 1))) /* GUS auto timer mode enabled? */
+        {
+            if (data & 0x80)
+                GUSregb(TimerStatus2x8) &= 0x1f; /* AdLib IRQ reset? -> clear maskable adl. timer int regs */
+            else
+                GUSregb(TimerDataReg2x9) = (GUSbyte) data;
+        }
+        else
+        {
+            GUSregb(AdLibData2x9) = (GUSbyte) data;
+            if (GUSregb(GUS45TimerCtrl) & 0x02)
+            {
+                GUSregb(TimerStatus2x8) |= 0x01;
+                GUSregb(IRQStatReg2x6) = 0x10;
+                GUS_irqrequest(state, state->gusirq, 1);
+            }
+        }
+        break;
+    case 0x20A:
+        GUSregb(AdLibStatus2x8) = (GUSbyte) data;
+        break;                 /* AdLibStatus2x8 */
+    case 0x20B:                /* GUS hidden registers */
+        switch (GUSregb(RegCtrl_2xF) & 0x7)
+        {
+        case 0:
+            if (GUSregb(MixerCtrlReg2x0) & 0x40)
+                GUSregb(IRQ_2xB) = (GUSbyte) data; /* control register select bit */
+            else
+                GUSregb(DMA_2xB) = (GUSbyte) data;
+            break;
+            /* case 1-4: general purpose emulation regs */
+        case 5:                                    /* clear stat reg 2xF */
+            GUSregb(StatRead_2xF) = 0; /* ToDo: is this identical with GUS classic? */
+            if (!GUSregb(IRQStatReg2x6))
+                GUS_irqclear(state, state->gusirq);
+            break;
+        case 6:                                    /* Jumper reg (Joystick/MIDI enable) */
+            GUSregb(Jumper_2xB) = (GUSbyte) data;
+            break;
+        default:;
+        }
+        break;
+    case 0x20C:                /* SB2xCd */
+        if (GUSregb(GUS45TimerCtrl) & 0x20)
+        {
+            GUSregb(TimerStatus2x8) |= 0x10; /* SB IRQ enabled? -> set 2xCIRQ bit */
+            GUSregb(IRQStatReg2x6) = 0x10;
+            GUS_irqrequest(state, state->gusirq, 1);
+        }
+    case 0x20D:                /* SB2xCd no IRQ */
+        GUSregb(SB2xCd) = (GUSbyte) data;
+        break;
+    case 0x20E:                /* SB2xE */
+        GUSregb(SB2xE) = (GUSbyte) data;
+        break;
+    case 0x20F:
+        GUSregb(RegCtrl_2xF) = (GUSbyte) data;
+        break;                 /* CtrlReg2xF */
+    case 0x302:                /* VoiceSelReg */
+        GUSregb(VoiceSelReg3x2) = (GUSbyte) data;
+        break;
+    case 0x303:                /* FunkSelReg */
+        GUSregb(FunkSelReg3x3) = (GUSbyte) data;
+        if ((GUSbyte) data == 0x8f) /* set irqstatreg, get voicereg and clear IRQ */
+        {
+            int             voice;
+            if (GUSregd(voicewavetableirq)) /* WavetableIRQ */
+            {
+                for (voice = 0; voice < 31; voice++)
+                {
+                    if (GUSregd(voicewavetableirq) & (1 << voice))
+                    {
+                        GUSregd(voicewavetableirq) ^= (1 << voice); /* clear IRQ bit */
+                        GUSregb(voice << 5) &= 0x7f; /* clear voice reg irq bit */
+                        if (!GUSregd(voicewavetableirq))
+                            GUSregb(IRQStatReg2x6) &= 0xdf;
+                        if (!GUSregb(IRQStatReg2x6))
+                            GUS_irqclear(state, state->gusirq);
+                        GUSregb(SynVoiceIRQ8f) = voice | 0x60; /* (bit==0 => IRQ wartend) */
+                        return;
+                    }
+                }
+            }
+            else if (GUSregd(voicevolrampirq)) /* VolRamp IRQ */
+            {
+                for (voice = 0; voice < 31; voice++)
+                {
+                    if (GUSregd(voicevolrampirq) & (1 << voice))
+                    {
+                        GUSregd(voicevolrampirq) ^= (1 << voice); /* clear IRQ bit */
+                        GUSregb((voice << 5) + VSRVolRampControl) &= 0x7f; /* clear voice volume reg irq bit */
+                        if (!GUSregd(voicevolrampirq))
+                            GUSregb(IRQStatReg2x6) &= 0xbf;
+                        if (!GUSregb(IRQStatReg2x6))
+                            GUS_irqclear(state, state->gusirq);
+                        GUSregb(SynVoiceIRQ8f) = voice | 0x80; /* (bit==0 => IRQ wartend) */
+                        return;
+                    }
+                }
+            }
+            GUSregb(SynVoiceIRQ8f) = 0xe8; /* kein IRQ wartet */
+        }
+        break;
+    case 0x304:
+    case 0x305:
+        {
+            GUSword         writedata = (GUSword) data;
+            GUSword         readmask = 0x0000;
+            if (size == 1)
+            {
+                readmask = 0xff00;
+                writedata &= 0xff;
+                if ((port & 0xff0f) == 0x305)
+                {
+                    writedata = (GUSword) (writedata << 8);
+                    readmask = 0x00ff;
+                }
+            }
+            switch (GUSregb(FunkSelReg3x3))
+            {
+                /* voice specific functions */
+            case 0x00:
+            case 0x01:
+            case 0x02:
+            case 0x03:
+            case 0x04:
+            case 0x05:
+            case 0x06:
+            case 0x07:
+            case 0x08:
+            case 0x09:
+            case 0x0a:
+            case 0x0b:
+            case 0x0c:
+            case 0x0d:
+                {
+                    int             offset;
+                    if (!(GUSregb(GUS4cReset) & 0x01))
+                        break;  /* reset flag active? */
+                    offset = 2 * (GUSregb(FunkSelReg3x3) & 0x0f);
+                    offset += (GUSregb(VoiceSelReg3x2) & 0x1f) << 5; /*  = Voice*32 + Funktion*2 */
+                    GUSregw(offset) = (GUSword) ((GUSregw(offset) & readmask) | writedata);
+                }
+                break;
+                /* voice unspecific functions */
+            case 0x0e:         /* NumVoices */
+                GUSregb(NumVoices) = (GUSbyte) data;
+                break;
+            /* case 0x0f:      */ /* read only */
+                /* common functions */
+            case 0x41:         /* DramDMAContrReg */
+                GUSregb(GUS41DMACtrl) = (GUSbyte) data;
+                if (data & 0x01)
+                    GUS_dmarequest(state);
+                break;
+            case 0x42:         /* DramDMAmemPosReg */
+                GUSregw(GUS42DMAStart) = (GUSregw(GUS42DMAStart) & readmask) | writedata;
+                GUSregb(GUS50DMAHigh) &= 0xf; /* compatibility stuff... */
+                break;
+            case 0x43:         /* DRAMaddrLo */
+                GUSregd(GUSDRAMPOS24bit) =
+                    (GUSregd(GUSDRAMPOS24bit) & (readmask | 0xff0000)) | writedata;
+                break;
+            case 0x44:         /* DRAMaddrHi */
+                GUSregd(GUSDRAMPOS24bit) =
+                    (GUSregd(GUSDRAMPOS24bit) & 0xffff) | ((data & 0x0f) << 16);
+                break;
+            case 0x45:         /* TCtrlReg */
+                GUSregb(GUS45TimerCtrl) = (GUSbyte) data;
+                if (!(data & 0x20))
+                    GUSregb(TimerStatus2x8) &= 0xe7;    /* sb IRQ dis? -> clear 2x8/2xC sb IRQ flags */
+                if (!(data & 0x02))
+                    GUSregb(TimerStatus2x8) &= 0xfe;    /* adlib data IRQ dis? -> clear 2x8 adlib IRQ flag */
+                if (!(GUSregb(TimerStatus2x8) & 0x19))
+                    GUSregb(IRQStatReg2x6) &= 0xef;     /* 0xe6; $$clear IRQ if both IRQ bits are inactive or cleared */
+                /* catch up delayed timer IRQs: */
+                if ((GUSregw(TimerIRQs) > 1) && (GUSregb(TimerDataReg2x9) & 3))
+                {
+                    if (GUSregb(TimerDataReg2x9) & 1)   /* start timer 1 (80us decrement rate) */
+                    {
+                        if (!(GUSregb(TimerDataReg2x9) & 0x40))
+                            GUSregb(TimerStatus2x8) |= 0xc0;    /* maskable bits */
+                        if (data & 4) /* timer1 irq enable */
+                        {
+                            GUSregb(TimerStatus2x8) |= 4;       /* nonmaskable bit */
+                            GUSregb(IRQStatReg2x6) |= 4;        /* timer 1 irq pending */
+                        }
+                    }
+                    if (GUSregb(TimerDataReg2x9) & 2)   /* start timer 2 (320us decrement rate) */
+                    {
+                        if (!(GUSregb(TimerDataReg2x9) & 0x20))
+                            GUSregb(TimerStatus2x8) |= 0xa0;    /* maskable bits */
+                        if (data & 8) /* timer2 irq enable */
+                        {
+                            GUSregb(TimerStatus2x8) |= 2;       /* nonmaskable bit */
+                            GUSregb(IRQStatReg2x6) |= 8;        /* timer 2 irq pending */
+                        }
+                    }
+                    GUSregw(TimerIRQs)--;
+                    if (GUSregw(BusyTimerIRQs) > 1)
+                        GUSregw(BusyTimerIRQs)--;
+                    else
+                        GUSregw(BusyTimerIRQs) =
+                            GUS_irqrequest(state, state->gusirq, GUSregw(TimerIRQs));
+                }
+                else
+                    GUSregw(TimerIRQs) = 0;
+
+                if (!(data & 0x04))
+                {
+                    GUSregb(TimerStatus2x8) &= 0xfb; /* clear non-maskable timer1 bit */
+                    GUSregb(IRQStatReg2x6)  &= 0xfb;
+                }
+                if (!(data & 0x08))
+                {
+                    GUSregb(TimerStatus2x8) &= 0xfd; /* clear non-maskable timer2 bit */
+                    GUSregb(IRQStatReg2x6)  &= 0xf7;
+                }
+                if (!GUSregb(IRQStatReg2x6))
+                    GUS_irqclear(state, state->gusirq);
+                break;
+            case 0x46:          /* Counter1 */
+                GUSregb(GUS46Counter1) = (GUSbyte) data;
+                break;
+            case 0x47:          /* Counter2 */
+                GUSregb(GUS47Counter2) = (GUSbyte) data;
+                break;
+            /* case 0x48:       */ /* sampling freq reg not emulated (same as interwave) */
+            case 0x49:          /* SampCtrlReg */
+                GUSregb(GUS49SampCtrl) = (GUSbyte) data;
+                break;
+            /* case 0x4b:       */ /* joystick trim not emulated */
+            case 0x4c:          /* GUSreset */
+                GUSregb(GUS4cReset) = (GUSbyte) data;
+                if (!(GUSregb(GUS4cReset) & 1)) /* reset... */
+                {
+                    GUSregd(voicewavetableirq) = 0;
+                    GUSregd(voicevolrampirq) = 0;
+                    GUSregw(TimerIRQs) = 0;
+                    GUSregw(BusyTimerIRQs) = 0;
+                    GUSregb(NumVoices) = 0xcd;
+                    GUSregb(IRQStatReg2x6) = 0;
+                    GUSregb(TimerStatus2x8) = 0;
+                    GUSregb(AdLibData2x9) = 0;
+                    GUSregb(TimerDataReg2x9) = 0;
+                    GUSregb(GUS41DMACtrl) = 0;
+                    GUSregb(GUS45TimerCtrl) = 0;
+                    GUSregb(GUS49SampCtrl) = 0;
+                    GUSregb(GUS4cReset) &= 0xf9; /* clear IRQ and DAC enable bits */
+                    GUS_irqclear(state, state->gusirq);
+                }
+                /* IRQ enable bit checked elsewhere */
+                /* EnableDAC bit may be used by external callers */
+                break;
+            }
+        }
+        break;
+    case 0x307:                /* DRAMaccess */
+        {
+            GUSbyte        *adr;
+            adr = state->himemaddr + (GUSregd(GUSDRAMPOS24bit) & 0xfffff);
+            *adr = (GUSbyte) data;
+        }
+        break;
+    }
+}
+
+/* Attention when breaking up a single DMA transfer to multiple ones:
+ * it may lead to multiple terminal count interrupts and broken transfers:
+ *
+ * 1. Whenever you transfer a piece of data, the gusemu callback is invoked
+ * 2. The callback may generate a TC irq (if the register was set up to do so)
+ * 3. The irq may result in the program using the GUS to reprogram the GUS
+ *
+ * Some programs also decide to upload by just checking if TC occurs
+ * (via interrupt or a cleared GUS dma flag)
+ * and then start the next transfer, without checking DMA state
+ *
+ * Thus: Always make sure to set the TC flag correctly!
+ *
+ * Note that the genuine GUS had a granularity of 16 bytes/words for low/high DMA
+ * while later cards had atomic granularity provided by an additional GUS50DMAHigh register
+ * GUSemu also uses this register to support byte-granular transfers for better compatibility
+ * with emulators other than GUSemu32
+ */
+
+void gus_dma_transferdata(GUSEmuState * state, char *dma_addr, unsigned int count, int TC)
+{
+    /* this function gets called by the callback function as soon as a DMA transfer is about to start
+     * dma_addr is a translated address within accessible memory, not the physical one,
+     * count is (real dma count register)+1
+     * note that the amount of bytes transfered is fully determined by values in the DMA registers
+     * do not forget to update DMA states after transferring the entire block:
+     * DREQ cleared & TC asserted after the _whole_ transfer */
+
+    char           *srcaddr;
+    char           *destaddr;
+    char            msbmask = 0;
+    GUSbyte        *gusptr;
+    gusptr = state->gusdatapos;
+
+    srcaddr = dma_addr; /* system memory address */
+    {
+        int             offset = (GUSregw(GUS42DMAStart) << 4) + (GUSregb(GUS50DMAHigh) & 0xf);
+        if (state->gusdma >= 4)
+            offset = (offset & 0xc0000) + (2 * (offset & 0x1fff0)); /* 16 bit address translation */
+        destaddr = (char *) state->himemaddr + offset; /* wavetable RAM adress */
+    }
+
+    GUSregw(GUS42DMAStart) += (GUSword)  (count >> 4);                           /* ToDo: add 16bit GUS page limit? */
+    GUSregb(GUS50DMAHigh)   = (GUSbyte) ((count + GUSregb(GUS50DMAHigh)) & 0xf); /* ToDo: add 16bit GUS page limit? */
+
+    if (GUSregb(GUS41DMACtrl) & 0x02)   /* direction, 0 := sysram->gusram */
+    {
+        char           *tmpaddr = destaddr;
+        destaddr = srcaddr;
+        srcaddr = tmpaddr;
+    }
+
+    if ((GUSregb(GUS41DMACtrl) & 0x80) && (!(GUSregb(GUS41DMACtrl) & 0x02)))
+        msbmask = (const char) 0x80;    /* invert MSB */
+    for (; count > 0; count--)
+    {
+        if (GUSregb(GUS41DMACtrl) & 0x40)
+            *(destaddr++) = *(srcaddr++);               /* 16 bit lobyte */
+        else
+            *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 8 bit */
+        if (state->gusdma >= 4)
+            *(destaddr++) = (msbmask ^ (*(srcaddr++))); /* 16 bit hibyte */
+    }
+
+    if (TC)
+    {
+        (GUSregb(GUS41DMACtrl)) &= 0xfe;        /* clear DMA request bit */
+        if (GUSregb(GUS41DMACtrl) & 0x20)       /* DMA terminal count IRQ */
+        {
+            GUSregb(IRQStatReg2x6) |= 0x80;
+            GUS_irqrequest(state, state->gusirq, 1);
+        }
+    }
+}
diff --git a/hw/gusemu_mixer.c b/hw/gusemu_mixer.c
new file mode 100644
index 0000000..b3b5aa5
--- /dev/null
+++ b/hw/gusemu_mixer.c
@@ -0,0 +1,240 @@
+/*
+ * GUSEMU32 - mixing engine (similar to Interwave GF1 compatibility)
+ *
+ * Copyright (C) 2000-2007 Tibor "TS" Schütz
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "gusemu.h"
+#include "gustate.h"
+
+#define GUSregb(position)  (*            (gusptr+(position)))
+#define GUSregw(position)  (*(GUSword *) (gusptr+(position)))
+#define GUSregd(position)  (*(GUSdword *)(gusptr+(position)))
+
+#define GUSvoice(position) (*(GUSword *)(voiceptr+(position)))
+
+/* samples are always 16bit stereo (4 bytes each, first right then left interleaved) */
+void gus_mixvoices(GUSEmuState * state, unsigned int playback_freq, unsigned int numsamples,
+                   short *bufferpos)
+{
+    /* note that byte registers are stored in the upper half of each voice register! */
+    GUSbyte        *gusptr;
+    int             Voice;
+    GUSword        *voiceptr;
+
+    unsigned int    count;
+    for (count = 0; count < numsamples * 2; count++)
+        *(bufferpos + count) = 0;       /* clear */
+
+    gusptr = state->gusdatapos;
+    voiceptr = (GUSword *) gusptr;
+    if (!(GUSregb(GUS4cReset) & 0x01))  /* reset flag active? */
+        return;
+
+    for (Voice = 0; Voice <= (GUSregb(NumVoices) & 31); Voice++)
+    {
+        if (GUSvoice(wVSRControl)        &  0x200)
+            GUSvoice(wVSRControl)        |= 0x100; /* voice stop request */
+        if (GUSvoice(wVSRVolRampControl) &  0x200)
+            GUSvoice(wVSRVolRampControl) |= 0x100; /* Volume ramp stop request */
+        if (!(GUSvoice(wVSRControl) & GUSvoice(wVSRVolRampControl) & 0x100)) /* neither voice nor volume calculation active - save some time here ;) */
+        {
+            unsigned int    sample;
+
+            unsigned int    LoopStart = (GUSvoice(wVSRLoopStartHi) << 16) | GUSvoice(wVSRLoopStartLo); /* 23.9 format */
+            unsigned int    LoopEnd   = (GUSvoice(wVSRLoopEndHi)   << 16) | GUSvoice(wVSRLoopEndLo);   /* 23.9 format */
+            unsigned int    CurrPos   = (GUSvoice(wVSRCurrPosHi)   << 16) | GUSvoice(wVSRCurrPosLo);   /* 23.9 format */
+            int             VoiceIncrement = ((((unsigned long) GUSvoice(wVSRFreq) * 44100) / playback_freq) * (14 >> 1)) /
+                                             ((GUSregb(NumVoices) & 31) + 1); /* 6.10 increment/frame to 23.9 increment/sample */
+
+            int             PanningPos = (GUSvoice(wVSRPanning) >> 8) & 0xf;
+
+            unsigned int    Volume32   = 32 * GUSvoice(wVSRCurrVol); /* 32 times larger than original gus for maintaining precision while ramping */
+            unsigned int    StartVol32 = (GUSvoice(wVSRVolRampStartVol) & 0xff00) * 32;
+            unsigned int    EndVol32   = (GUSvoice(wVSRVolRampEndVol)   & 0xff00) * 32;
+            int             VolumeIncrement32 = (32 * 16 * (GUSvoice(wVSRVolRampRate) & 0x3f00) >> 8) >> ((((GUSvoice(wVSRVolRampRate) & 0xc000) >> 8) >> 6) * 3); /* including 1/8/64/512 volume speed divisor */
+            VolumeIncrement32 = (((VolumeIncrement32 * 44100 / 2) / playback_freq) * 14) / ((GUSregb(NumVoices) & 31) + 1); /* adjust ramping speed to playback speed */
+
+            if (GUSvoice(wVSRControl) & 0x4000)
+                VoiceIncrement    = -VoiceIncrement;    /* reverse playback */
+            if (GUSvoice(wVSRVolRampControl) & 0x4000)
+                VolumeIncrement32 = -VolumeIncrement32; /* reverse ramping */
+
+            for (sample = 0; sample < numsamples; sample++)
+            {
+                int             sample1, sample2, Volume;
+                if (GUSvoice(wVSRControl) & 0x400)      /* 16bit */
+                {
+                    int offset = ((CurrPos >> 9) & 0xc0000) + (((CurrPos >> 9) & 0x1ffff) << 1);
+                    GUSchar *adr;
+                    adr = (GUSchar *) state->himemaddr + offset;
+                    sample1 = (*adr & 0xff) + (*(adr + 1) * 256);
+                    sample2 = (*(adr + 2) & 0xff) + (*(adr + 2 + 1) * 256);
+                }
+                else            /* 8bit */
+                {
+                    int offset = (CurrPos >> 9) & 0xfffff;
+                    GUSchar *adr;
+                    adr = (GUSchar *) state->himemaddr + offset;
+                    sample1 = (*adr) * 256;
+                    sample2 = (*(adr + 1)) * 256;
+                }
+
+                Volume = ((((Volume32 >> (4 + 5)) & 0xff) + 256) << (Volume32 >> ((4 + 8) + 5))) / 512; /* semi-logarithmic volume, +5 due to additional precision */
+                sample1 = (((sample1 * Volume) >> 16) * (512 - (CurrPos % 512))) / 512;
+                sample2 = (((sample2 * Volume) >> 16) * (CurrPos % 512)) / 512;
+                sample1 += sample2;
+
+                if (!(GUSvoice(wVSRVolRampControl) & 0x100))
+                {
+                    Volume32 += VolumeIncrement32;
+                    if ((GUSvoice(wVSRVolRampControl) & 0x4000) ? (Volume32 <= StartVol32) : (Volume32 >= EndVol32)) /* ramp up boundary cross */
+                    {
+                        if (GUSvoice(wVSRVolRampControl) & 0x2000)
+                            GUSvoice(wVSRVolRampControl) |= 0x8000;     /* volramp IRQ enabled? -> IRQ wait flag */
+                        if (GUSvoice(wVSRVolRampControl) & 0x800)       /* loop enabled */
+                        {
+                            if (GUSvoice(wVSRVolRampControl) & 0x1000)  /* bidir. loop */
+                            {
+                                GUSvoice(wVSRVolRampControl) ^= 0x4000; /* toggle dir */
+                                VolumeIncrement32 = -VolumeIncrement32;
+                            }
+                            else
+                                Volume32 = (GUSvoice(wVSRVolRampControl) & 0x4000) ? EndVol32 : StartVol32; /* unidir. loop ramp */
+                        }
+                        else
+                        {
+                            GUSvoice(wVSRVolRampControl) |= 0x100;
+                            Volume32 =
+                                (GUSvoice(wVSRVolRampControl) & 0x4000) ? StartVol32 : EndVol32;
+                        }
+                    }
+                }
+                if ((GUSvoice(wVSRVolRampControl) & 0xa000) == 0xa000)  /* volramp IRQ set and enabled? */
+                {
+                    GUSregd(voicevolrampirq) |= 1 << Voice;             /* set irq slot */
+                }
+                else
+                {
+                    GUSregd(voicevolrampirq) &= (~(1 << Voice));        /* clear irq slot */
+                    GUSvoice(wVSRVolRampControl) &= 0x7f00;
+                }
+
+                if (!(GUSvoice(wVSRControl) & 0x100))
+                {
+                    CurrPos += VoiceIncrement;
+                    if ((GUSvoice(wVSRControl) & 0x4000) ? (CurrPos <= LoopStart) : (CurrPos >= LoopEnd)) /* playback boundary cross */
+                    {
+                        if (GUSvoice(wVSRControl) & 0x2000)
+                            GUSvoice(wVSRControl) |= 0x8000;       /* voice IRQ enabled -> IRQ wait flag */
+                        if (GUSvoice(wVSRControl) & 0x800)         /* loop enabled */
+                        {
+                            if (GUSvoice(wVSRControl) & 0x1000)    /* pingpong loop */
+                            {
+                                GUSvoice(wVSRControl) ^= 0x4000;   /* toggle dir */
+                                VoiceIncrement = -VoiceIncrement;
+                            }
+                            else
+                                CurrPos = (GUSvoice(wVSRControl) & 0x4000) ? LoopEnd : LoopStart; /* unidir. loop */
+                        }
+                        else if (!(GUSvoice(wVSRVolRampControl) & 0x400))
+                            GUSvoice(wVSRControl) |= 0x100;        /* loop disabled, rollover check */
+                    }
+                }
+                if ((GUSvoice(wVSRControl) & 0xa000) == 0xa000)    /* wavetable IRQ set and enabled? */
+                {
+                    GUSregd(voicewavetableirq) |= 1 << Voice;      /* set irq slot */
+                }
+                else
+                {
+                    GUSregd(voicewavetableirq) &= (~(1 << Voice)); /* clear irq slot */
+                    GUSvoice(wVSRControl) &= 0x7f00;
+                }
+
+                /* mix samples into buffer */
+                *(bufferpos + 2 * sample)     += (short) ((sample1 * PanningPos) >> 4);        /* right */
+                *(bufferpos + 2 * sample + 1) += (short) ((sample1 * (15 - PanningPos)) >> 4); /* left */
+            }
+            /* write back voice and volume */
+            GUSvoice(wVSRCurrVol)   = Volume32 / 32;
+            GUSvoice(wVSRCurrPosHi) = CurrPos >> 16;
+            GUSvoice(wVSRCurrPosLo) = CurrPos & 0xffff;
+        }
+        voiceptr += 16; /* next voice */
+    }
+}
+
+void gus_irqgen(GUSEmuState * state, unsigned int elapsed_time)
+/* time given in microseconds */
+{
+    int             requestedIRQs = 0;
+    GUSbyte        *gusptr;
+    gusptr = state->gusdatapos;
+    if (GUSregb(TimerDataReg2x9) & 1) /* start timer 1 (80us decrement rate) */
+    {
+        unsigned int    timer1fraction = state->timer1fraction;
+        int             newtimerirqs;
+        newtimerirqs          = (elapsed_time + timer1fraction) / (80 * (256 - GUSregb(GUS46Counter1)));
+        state->timer1fraction = (elapsed_time + timer1fraction) % (80 * (256 - GUSregb(GUS46Counter1)));
+        if (newtimerirqs)
+        {
+            if (!(GUSregb(TimerDataReg2x9) & 0x40))
+                GUSregb(TimerStatus2x8) |= 0xc0; /* maskable bits */
+            if (GUSregb(GUS45TimerCtrl) & 4)     /* timer1 irq enable */
+            {
+                GUSregb(TimerStatus2x8) |= 4;    /* nonmaskable bit */
+                GUSregb(IRQStatReg2x6)  |= 4;    /* timer 1 irq pending */
+                GUSregw(TimerIRQs) += newtimerirqs;
+                requestedIRQs += newtimerirqs;
+            }
+        }
+    }
+    if (GUSregb(TimerDataReg2x9) & 2) /* start timer 2 (320us decrement rate) */
+    {
+        unsigned int timer2fraction = state->timer2fraction;
+        int             newtimerirqs;
+        newtimerirqs          = (elapsed_time + timer2fraction) / (320 * (256 - GUSregb(GUS47Counter2)));
+        state->timer2fraction = (elapsed_time + timer2fraction) % (320 * (256 - GUSregb(GUS47Counter2)));
+        if (newtimerirqs)
+        {
+            if (!(GUSregb(TimerDataReg2x9) & 0x20))
+                GUSregb(TimerStatus2x8) |= 0xa0; /* maskable bits */
+            if (GUSregb(GUS45TimerCtrl) & 8)     /* timer2 irq enable */
+            {
+                GUSregb(TimerStatus2x8) |= 2;    /* nonmaskable bit */
+                GUSregb(IRQStatReg2x6)  |= 8;    /* timer 2 irq pending */
+                GUSregw(TimerIRQs) += newtimerirqs;
+                requestedIRQs += newtimerirqs;
+            }
+        }
+    }
+    if (GUSregb(GUS4cReset) & 0x4) /* synth IRQ enable */
+    {
+        if (GUSregd(voicewavetableirq))
+            GUSregb(IRQStatReg2x6) |= 0x20;
+        if (GUSregd(voicevolrampirq))
+            GUSregb(IRQStatReg2x6) |= 0x40;
+    }
+    if ((!requestedIRQs) && GUSregb(IRQStatReg2x6))
+        requestedIRQs++;
+    if (GUSregb(IRQStatReg2x6))
+        GUSregw(BusyTimerIRQs) = GUS_irqrequest(state, state->gusirq, requestedIRQs);
+}
diff --git a/hw/gustate.h b/hw/gustate.h
new file mode 100644
index 0000000..ece903a
--- /dev/null
+++ b/hw/gustate.h
@@ -0,0 +1,132 @@
+/*
+ * GUSEMU32 - persistent GUS register state
+ *
+ * Copyright (C) 2000-2007 Tibor "TS" Schütz
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef GUSTATE_H
+#define GUSTATE_H
+
+/*state block offset*/
+#define gusdata (0)
+
+/* data stored using this structure is in host byte order! */
+
+/*access type*/
+#define PortRead  (0)
+#define PortWrite (1)
+
+#define Port8Bitacc  (0)
+#define Port16Bitacc (1)
+
+/*voice register offsets (in bytes)*/
+#define VSRegs (0)
+#define VSRControl          (0)
+#define VSRegsEnd (VSRControl+VSRegs + 32*(16*2))
+#define VSRFreq             (2)
+#define VSRLoopStartHi      (4)
+#define VSRLoopStartLo      (6)
+#define VSRLoopEndHi        (8)
+#define VSRLoopEndLo       (10)
+#define VSRVolRampRate     (12)
+#define VSRVolRampStartVol (14)
+#define VSRVolRampEndVol   (16)
+#define VSRCurrVol         (18)
+#define VSRCurrPosHi       (20)
+#define VSRCurrPosLo       (22)
+#define VSRPanning         (24)
+#define VSRVolRampControl  (26)
+
+/*voice register offsets (in words)*/
+#define wVSRegs (0)
+#define wVSRControl         (0)
+#define wVSRegsEnd (wVSRControl+wVSRegs + 32*(16))
+#define wVSRFreq            (1)
+#define wVSRLoopStartHi     (2)
+#define wVSRLoopStartLo     (3)
+#define wVSRLoopEndHi       (4)
+#define wVSRLoopEndLo       (5)
+#define wVSRVolRampRate     (6)
+#define wVSRVolRampStartVol (7)
+#define wVSRVolRampEndVol   (8)
+#define wVSRCurrVol         (9)
+#define wVSRCurrPosHi      (10)
+#define wVSRCurrPosLo      (11)
+#define wVSRPanning        (12)
+#define wVSRVolRampControl (13)
+
+/*GUS register state block: 32 voices, padding filled with remaining registers*/
+#define DataRegLoByte3x4  (VSRVolRampControl+2)
+#define  DataRegWord3x4 (DataRegLoByte3x4)
+#define DataRegHiByte3x5  (VSRVolRampControl+2       +1)
+#define DMA_2xB (VSRVolRampControl+2+2)
+#define IRQ_2xB (VSRVolRampControl+2+3)
+
+#define RegCtrl_2xF       (VSRVolRampControl+2+(16*2))
+#define Jumper_2xB        (VSRVolRampControl+2+(16*2)+1)
+#define GUS42DMAStart     (VSRVolRampControl+2+(16*2)+2)
+
+#define GUS43DRAMIOlo     (VSRVolRampControl+2+(16*2)*2)
+#define  GUSDRAMPOS24bit (GUS43DRAMIOlo)
+#define GUS44DRAMIOhi     (VSRVolRampControl+2+(16*2)*2+2)
+
+#define voicewavetableirq (VSRVolRampControl+2+(16*2)*3) /* voice IRQ pseudoqueue: 1 bit per voice */
+
+#define voicevolrampirq   (VSRVolRampControl+2+(16*2)*4) /* voice IRQ pseudoqueue: 1 bit per voice */
+
+#define startvoices       (VSRVolRampControl+2+(16*2)*5) /* statistics / optimizations */
+
+#define IRQStatReg2x6     (VSRVolRampControl+2+(16*2)*6)
+#define TimerStatus2x8    (VSRVolRampControl+2+(16*2)*6+1)
+#define TimerDataReg2x9   (VSRVolRampControl+2+(16*2)*6+2)
+#define MixerCtrlReg2x0   (VSRVolRampControl+2+(16*2)*6+3)
+
+#define VoiceSelReg3x2    (VSRVolRampControl+2+(16*2)*7)
+#define FunkSelReg3x3     (VSRVolRampControl+2+(16*2)*7+1)
+#define AdLibStatus2x8    (VSRVolRampControl+2+(16*2)*7+2)
+#define StatRead_2xF      (VSRVolRampControl+2+(16*2)*7+3)
+
+#define GUS48SampSpeed    (VSRVolRampControl+2+(16*2)*8)
+#define GUS41DMACtrl      (VSRVolRampControl+2+(16*2)*8+1)
+#define GUS45TimerCtrl    (VSRVolRampControl+2+(16*2)*8+2)
+#define GUS46Counter1     (VSRVolRampControl+2+(16*2)*8+3)
+
+#define GUS47Counter2     (VSRVolRampControl+2+(16*2)*9)
+#define GUS49SampCtrl     (VSRVolRampControl+2+(16*2)*9+1)
+#define GUS4cReset        (VSRVolRampControl+2+(16*2)*9+2)
+#define NumVoices         (VSRVolRampControl+2+(16*2)*9+3)
+
+#define TimerIRQs         (VSRVolRampControl+2+(16*2)*10)   /* delayed IRQ, statistics */
+#define BusyTimerIRQs     (VSRVolRampControl+2+(16*2)*10+2) /* delayed IRQ, statistics */
+
+#define AdLibCommand2xA   (VSRVolRampControl+2+(16*2)*11)
+#define AdLibData2x9      (VSRVolRampControl+2+(16*2)*11+1)
+#define SB2xCd            (VSRVolRampControl+2+(16*2)*11+2)
+#define SB2xE             (VSRVolRampControl+2+(16*2)*11+3)
+
+#define SynVoiceIRQ8f     (VSRVolRampControl+2+(16*2)*12)
+#define GUS50DMAHigh      (VSRVolRampControl+2+(16*2)*12+1)
+
+#define portaccesses (VSRegsEnd) /* statistics / suspend mode */
+
+#define gusdataend (VSRegsEnd+4)
+
+#endif  /* gustate.h */
diff --git a/hw/ide.c b/hw/ide.c
index 6b6c66f..0e883b3 100644
--- a/hw/ide.c
+++ b/hw/ide.c
@@ -1,5 +1,5 @@
 /*
- * QEMU IDE disk and CD-ROM Emulator
+ * QEMU IDE disk and CD/DVD-ROM Emulator
  *
  * Copyright (c) 2003 Fabrice Bellard
  * Copyright (c) 2006 Openedhand Ltd.
@@ -284,6 +284,58 @@
  * of MODE_SENSE_POWER_PAGE */
 #define GPMODE_CDROM_PAGE		0x0d
 
+/*
+ * Based on values from <linux/cdrom.h> but extending CD_MINS
+ * to the maximum common size allowed by the Orange's Book ATIP
+ *
+ * 90 and 99 min CDs are also available but using them as the
+ * upper limit reduces the effectiveness of the heuristic to
+ * detect DVDs burned to less than 25% of their maximum capacity
+ */
+
+/* Some generally useful CD-ROM information */
+#define CD_MINS                       80 /* max. minutes per CD */
+#define CD_SECS                       60 /* seconds per minute */
+#define CD_FRAMES                     75 /* frames per second */
+#define CD_FRAMESIZE                2048 /* bytes per frame, "cooked" mode */
+#define CD_MAX_BYTES       (CD_MINS * CD_SECS * CD_FRAMES * CD_FRAMESIZE)
+#define CD_MAX_SECTORS     (CD_MAX_BYTES / 512)
+
+/*
+ * The MMC values are not IDE specific and might need to be moved
+ * to a common header if they are also needed for the SCSI emulation
+ */
+
+/* Profile list from MMC-6 revision 1 table 91 */
+#define MMC_PROFILE_NONE                0x0000
+#define MMC_PROFILE_CD_ROM              0x0008
+#define MMC_PROFILE_CD_R                0x0009
+#define MMC_PROFILE_CD_RW               0x000A
+#define MMC_PROFILE_DVD_ROM             0x0010
+#define MMC_PROFILE_DVD_R_SR            0x0011
+#define MMC_PROFILE_DVD_RAM             0x0012
+#define MMC_PROFILE_DVD_RW_RO           0x0013
+#define MMC_PROFILE_DVD_RW_SR           0x0014
+#define MMC_PROFILE_DVD_R_DL_SR         0x0015
+#define MMC_PROFILE_DVD_R_DL_JR         0x0016
+#define MMC_PROFILE_DVD_RW_DL           0x0017
+#define MMC_PROFILE_DVD_DDR             0x0018
+#define MMC_PROFILE_DVD_PLUS_RW         0x001A
+#define MMC_PROFILE_DVD_PLUS_R          0x001B
+#define MMC_PROFILE_DVD_PLUS_RW_DL      0x002A
+#define MMC_PROFILE_DVD_PLUS_R_DL       0x002B
+#define MMC_PROFILE_BD_ROM              0x0040
+#define MMC_PROFILE_BD_R_SRM            0x0041
+#define MMC_PROFILE_BD_R_RRM            0x0042
+#define MMC_PROFILE_BD_RE               0x0043
+#define MMC_PROFILE_HDDVD_ROM           0x0050
+#define MMC_PROFILE_HDDVD_R             0x0051
+#define MMC_PROFILE_HDDVD_RAM           0x0052
+#define MMC_PROFILE_HDDVD_RW            0x0053
+#define MMC_PROFILE_HDDVD_R_DL          0x0058
+#define MMC_PROFILE_HDDVD_RW_DL         0x005A
+#define MMC_PROFILE_INVALID             0xFFFF
+
 #define ATAPI_INT_REASON_CD             0x01 /* 0 = data transfer */
 #define ATAPI_INT_REASON_IO             0x02 /* 1 = transfer to the host */
 #define ATAPI_INT_REASON_REL            0x04
@@ -540,7 +592,7 @@ static void ide_atapi_identify(IDEState *s)
     put_le16(p + 21, 512); /* cache size in sectors */
     put_le16(p + 22, 4); /* ecc bytes */
     padstr((char *)(p + 23), QEMU_VERSION, 8); /* firmware version */
-    padstr((char *)(p + 27), "QEMU CD-ROM", 40); /* model */
+    padstr((char *)(p + 27), "QEMU DVD-ROM", 40); /* model */
     put_le16(p + 48, 1); /* dword I/O (XXX: should not be set on CDROM) */
 #ifdef USE_DMA_CDROM
     put_le16(p + 49, 1 << 9 | 1 << 8); /* DMA and LBA supported */
@@ -1290,6 +1342,22 @@ static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors,
     }
 }
 
+static inline uint8_t ide_atapi_set_profile(uint8_t *buf, uint8_t *index,
+                                            uint16_t profile)
+{
+    uint8_t *buf_profile = buf + 12; /* start of profiles */
+
+    buf_profile += ((*index) * 4); /* start of indexed profile */
+    cpu_to_ube16 (buf_profile, profile);
+    buf_profile[2] = ((buf_profile[0] == buf[6]) && (buf_profile[1] == buf[7]));
+
+    /* each profile adds 4 bytes to the response */
+    (*index)++;
+    buf[11] += 4; /* Additional Length */
+
+    return 4;
+}
+
 static void ide_atapi_cmd(IDEState *s)
 {
     const uint8_t *packet;
@@ -1634,13 +1702,13 @@ static void ide_atapi_cmd(IDEState *s)
         buf[6] = 0; /* reserved */
         buf[7] = 0; /* reserved */
         padstr8(buf + 8, 8, "QEMU");
-        padstr8(buf + 16, 16, "QEMU CD-ROM");
+        padstr8(buf + 16, 16, "QEMU DVD-ROM");
         padstr8(buf + 32, 4, QEMU_VERSION);
         ide_atapi_cmd_reply(s, 36, max_len);
         break;
     case GPCMD_GET_CONFIGURATION:
         {
-            uint64_t total_sectors;
+            uint32_t len;
 
             /* only feature 0 is supported */
             if (packet[2] != 0 || packet[3] != 0) {
@@ -1648,17 +1716,46 @@ static void ide_atapi_cmd(IDEState *s)
                                     ASC_INV_FIELD_IN_CMD_PACKET);
                 break;
             }
-            memset(buf, 0, 32);
-            bdrv_get_geometry(s->bs, &total_sectors);
-            buf[3] = 16;
-            buf[7] = total_sectors <= 1433600 ? 0x08 : 0x10; /* current profile */
-            buf[10] = 0x10 | 0x1;
-            buf[11] = 0x08; /* size of profile list */
-            buf[13] = 0x10; /* DVD-ROM profile */
-            buf[14] = buf[7] == 0x10; /* (in)active */
-            buf[17] = 0x08; /* CD-ROM profile */
-            buf[18] = buf[7] == 0x08; /* (in)active */
-            ide_atapi_cmd_reply(s, 32, 32);
+
+            /* XXX: could result in alignment problems in some architectures */
+            max_len = ube16_to_cpu(packet + 7);
+            /*
+             * XXX: avoid overflow for io_buffer if max_len is bigger than the
+             *      size of that buffer (dimensioned to max number of sectors
+             *      to transfer at once)
+             *
+             *      Only a problem if the feature/profiles grow exponentially.
+             */
+            if (max_len > 512) /* XXX: assume 1 sector */
+                max_len = 512;
+
+            memset(buf, 0, max_len);
+            /* 
+             * the number of sectors from the media tells us which profile
+             * to use as current.  0 means there is no media
+             *
+             * XXX: fails to detect correctly DVDs with less data burned
+             *      than what a CD can hold
+             */
+            if ((s -> nb_sectors)) {
+                if ((s -> nb_sectors > CD_MAX_SECTORS))
+                    cpu_to_ube16(buf + 6, MMC_PROFILE_DVD_ROM);
+                else
+                    cpu_to_ube16(buf + 6, MMC_PROFILE_CD_ROM);
+            }
+
+            len = 8; /* header completed */
+            if (max_len > len) {
+                uint8_t index = 0;
+
+                buf[10] = 0x02 | 0x01; /* persistent and current */
+                len += 4; /* header */
+                len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_DVD_ROM);
+                len += ide_atapi_set_profile(buf, &index, MMC_PROFILE_CD_ROM);
+            }
+            cpu_to_ube32(buf, len - 4); /* data length */
+
+            ide_atapi_cmd_reply(s, len, max_len);
             break;
         }
     default:
@@ -2044,7 +2141,7 @@ static void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val)
             break;
         case WIN_DIAGNOSE:
             ide_set_signature(s);
-            s->status = 0x00; /* NOTE: READY is _not_ set */
+            s->status = READY_STAT;
             s->error = 0x01;
             ide_set_irq(s);
             break;
diff --git a/hw/iommu.c b/hw/iommu.c
index f83a9e3..440847f 100644
--- a/hw/iommu.c
+++ b/hw/iommu.c
@@ -34,7 +34,7 @@ do { printf("IOMMU: " fmt , ##args); } while (0)
 #define DPRINTF(fmt, args...)
 #endif
 
-#define IOMMU_NREGS (3*4096/4)
+#define IOMMU_NREGS         (4*4096/4)
 #define IOMMU_CTRL          (0x0000 >> 2)
 #define IOMMU_CTRL_IMPL     0xf0000000 /* Implementation */
 #define IOMMU_CTRL_VERS     0x0f000000 /* Version */
@@ -95,6 +95,12 @@ do { printf("IOMMU: " fmt , ##args); } while (0)
 #define IOMMU_ARBEN_MASK    0x001f0000
 #define IOMMU_MID           0x00000008
 
+#define IOMMU_MASK_ID       (0x3018 >> 2) /* Mask ID */
+#define IOMMU_MASK_ID_MASK  0x00ffffff
+
+#define IOMMU_MSII_MASK     0x26000000 /* microSPARC II mask number */
+#define IOMMU_TS_MASK       0x23000000 /* turboSPARC mask number */
+
 /* The format of an iopte in the page tables */
 #define IOPTE_PAGE          0xffffff00 /* Physical page number (PA[35:12]) */
 #define IOPTE_CACHE         0x00000080 /* Cached (in vme IOCACHE or
@@ -206,6 +212,9 @@ static void iommu_mem_writel(void *opaque, target_phys_addr_t addr,
         // addresses, fault cause and address stored to MMU/IOMMU
         s->regs[saddr] = (val & IOMMU_ARBEN_MASK) | IOMMU_MID;
         break;
+    case IOMMU_MASK_ID:
+        s->regs[saddr] |= val & IOMMU_MASK_ID_MASK;
+        break;
     default:
         s->regs[saddr] = val;
         break;
@@ -337,6 +346,7 @@ static void iommu_reset(void *opaque)
     s->regs[IOMMU_CTRL] = s->version;
     s->regs[IOMMU_ARBEN] = IOMMU_MID;
     s->regs[IOMMU_AFSR] = IOMMU_AFSR_RESV;
+    s->regs[IOMMU_MASK_ID] = IOMMU_TS_MASK;
     qemu_irq_lower(s->irq);
 }
 
diff --git a/hw/sb16.c b/hw/sb16.c
index effbfbc..c22de7a 100644
--- a/hw/sb16.c
+++ b/hw/sb16.c
@@ -1193,6 +1193,12 @@ static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len)
     SB16State *s = opaque;
     int till, copy, written, free;
 
+    if (s->block_size <= 0) {
+        dolog ("invalid block size=%d nchan=%d dma_pos=%d dma_len=%d\n",
+               s->block_size, nchan, dma_pos, dma_len);
+        return dma_pos;
+    }
+
     if (s->left_till_irq < 0) {
         s->left_till_irq = s->block_size;
     }
diff --git a/hw/slavio_misc.c b/hw/slavio_misc.c
index c54e00a..71128c2 100644
--- a/hw/slavio_misc.c
+++ b/hw/slavio_misc.c
@@ -50,7 +50,7 @@ typedef struct MiscState {
     uint8_t diag, mctrl;
     uint32_t sysctrl;
     uint16_t leds;
-    target_phys_addr_t power_base;
+    CPUState *env;
 } MiscState;
 
 #define MISC_SIZE 1
@@ -62,8 +62,6 @@ typedef struct MiscState {
 #define MISC_MASK 0x0fff0000
 #define MISC_LEDS 0x01600000
 #define MISC_CFG  0x01800000
-#define MISC_AUX1 0x01900000
-#define MISC_AUX2 0x01910000
 #define MISC_DIAG 0x01a00000
 #define MISC_MDM  0x01b00000
 #define MISC_SYS  0x01f00000
@@ -122,21 +120,6 @@ static void slavio_misc_mem_writeb(void *opaque, target_phys_addr_t addr,
         s->config = val & 0xff;
         slavio_misc_update_irq(s);
         break;
-    case MISC_AUX1:
-        MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff);
-        s->aux1 = val & 0xff;
-        break;
-    case MISC_AUX2:
-        val &= AUX2_PWRINTCLR | AUX2_PWROFF;
-        MISC_DPRINTF("Write aux2 %2.2x\n", val);
-        val |= s->aux2 & AUX2_PWRFAIL;
-        if (val & AUX2_PWRINTCLR) // Clear Power Fail int
-            val &= AUX2_PWROFF;
-        s->aux2 = val;
-        if (val & AUX2_PWROFF)
-            qemu_system_shutdown_request();
-        slavio_misc_update_irq(s);
-        break;
     case MISC_DIAG:
         MISC_DPRINTF("Write diag %2.2x\n", val & 0xff);
         s->diag = val & 0xff;
@@ -146,10 +129,6 @@ static void slavio_misc_mem_writeb(void *opaque, target_phys_addr_t addr,
         s->mctrl = val & 0xff;
         break;
     default:
-        if (addr == s->power_base) {
-            MISC_DPRINTF("Write power management %2.2x\n", val & 0xff);
-            cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT);
-        }
         break;
     }
 }
@@ -164,14 +143,6 @@ static uint32_t slavio_misc_mem_readb(void *opaque, target_phys_addr_t addr)
         ret = s->config;
         MISC_DPRINTF("Read config %2.2x\n", ret);
         break;
-    case MISC_AUX1:
-        ret = s->aux1;
-        MISC_DPRINTF("Read aux1 %2.2x\n", ret);
-        break;
-    case MISC_AUX2:
-        ret = s->aux2;
-        MISC_DPRINTF("Read aux2 %2.2x\n", ret);
-        break;
     case MISC_DIAG:
         ret = s->diag;
         MISC_DPRINTF("Read diag %2.2x\n", ret);
@@ -181,9 +152,6 @@ static uint32_t slavio_misc_mem_readb(void *opaque, target_phys_addr_t addr)
         MISC_DPRINTF("Read modem control %2.2x\n", ret);
         break;
     default:
-        if (addr == s->power_base) {
-            MISC_DPRINTF("Read power management %2.2x\n", ret);
-        }
         break;
     }
     return ret;
@@ -201,6 +169,105 @@ static CPUWriteMemoryFunc *slavio_misc_mem_write[3] = {
     NULL,
 };
 
+static void slavio_aux1_mem_writeb(void *opaque, target_phys_addr_t addr,
+                                   uint32_t val)
+{
+    MiscState *s = opaque;
+
+    MISC_DPRINTF("Write aux1 %2.2x\n", val & 0xff);
+    s->aux1 = val & 0xff;
+}
+
+static uint32_t slavio_aux1_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+    MiscState *s = opaque;
+    uint32_t ret = 0;
+
+    ret = s->aux1;
+    MISC_DPRINTF("Read aux1 %2.2x\n", ret);
+
+    return ret;
+}
+
+static CPUReadMemoryFunc *slavio_aux1_mem_read[3] = {
+    slavio_aux1_mem_readb,
+    NULL,
+    NULL,
+};
+
+static CPUWriteMemoryFunc *slavio_aux1_mem_write[3] = {
+    slavio_aux1_mem_writeb,
+    NULL,
+    NULL,
+};
+
+static void slavio_aux2_mem_writeb(void *opaque, target_phys_addr_t addr,
+                                   uint32_t val)
+{
+    MiscState *s = opaque;
+
+    val &= AUX2_PWRINTCLR | AUX2_PWROFF;
+    MISC_DPRINTF("Write aux2 %2.2x\n", val);
+    val |= s->aux2 & AUX2_PWRFAIL;
+    if (val & AUX2_PWRINTCLR) // Clear Power Fail int
+        val &= AUX2_PWROFF;
+    s->aux2 = val;
+    if (val & AUX2_PWROFF)
+        qemu_system_shutdown_request();
+    slavio_misc_update_irq(s);
+}
+
+static uint32_t slavio_aux2_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+    MiscState *s = opaque;
+    uint32_t ret = 0;
+
+    ret = s->aux2;
+    MISC_DPRINTF("Read aux2 %2.2x\n", ret);
+
+    return ret;
+}
+
+static CPUReadMemoryFunc *slavio_aux2_mem_read[3] = {
+    slavio_aux2_mem_readb,
+    NULL,
+    NULL,
+};
+
+static CPUWriteMemoryFunc *slavio_aux2_mem_write[3] = {
+    slavio_aux2_mem_writeb,
+    NULL,
+    NULL,
+};
+
+static void apc_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
+{
+    MiscState *s = opaque;
+
+    MISC_DPRINTF("Write power management %2.2x\n", val & 0xff);
+    cpu_interrupt(s->env, CPU_INTERRUPT_HALT);
+}
+
+static uint32_t apc_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+    uint32_t ret = 0;
+
+    MISC_DPRINTF("Read power management %2.2x\n", ret);
+    return ret;
+}
+
+static CPUReadMemoryFunc *apc_mem_read[3] = {
+    apc_mem_readb,
+    NULL,
+    NULL,
+};
+
+static CPUWriteMemoryFunc *apc_mem_write[3] = {
+    apc_mem_writeb,
+    NULL,
+    NULL,
+};
+
 static uint32_t slavio_sysctrl_mem_readl(void *opaque, target_phys_addr_t addr)
 {
     MiscState *s = opaque;
@@ -338,57 +405,68 @@ static int slavio_misc_load(QEMUFile *f, void *opaque, int version_id)
 }
 
 void *slavio_misc_init(target_phys_addr_t base, target_phys_addr_t power_base,
-                       qemu_irq irq)
+                       target_phys_addr_t aux1_base,
+                       target_phys_addr_t aux2_base, qemu_irq irq,
+                       CPUState *env)
 {
-    int slavio_misc_io_memory;
+    int io;
     MiscState *s;
 
     s = qemu_mallocz(sizeof(MiscState));
     if (!s)
         return NULL;
 
-    /* 8 bit registers */
-    slavio_misc_io_memory = cpu_register_io_memory(0, slavio_misc_mem_read,
-                                                   slavio_misc_mem_write, s);
-    // Slavio control
-    cpu_register_physical_memory(base + MISC_CFG, MISC_SIZE,
-                                 slavio_misc_io_memory);
-    // AUX 1
-    cpu_register_physical_memory(base + MISC_AUX1, MISC_SIZE,
-                                 slavio_misc_io_memory);
-    // AUX 2
-    cpu_register_physical_memory(base + MISC_AUX2, MISC_SIZE,
-                                 slavio_misc_io_memory);
-    // Diagnostics
-    cpu_register_physical_memory(base + MISC_DIAG, MISC_SIZE,
-                                 slavio_misc_io_memory);
-    // Modem control
-    cpu_register_physical_memory(base + MISC_MDM, MISC_SIZE,
-                                 slavio_misc_io_memory);
-    // Power management
-    cpu_register_physical_memory(power_base, MISC_SIZE, slavio_misc_io_memory);
-    s->power_base = power_base;
-
-    /* 16 bit registers */
-    slavio_misc_io_memory = cpu_register_io_memory(0, slavio_led_mem_read,
-                                                   slavio_led_mem_write, s);
-    /* ss600mp diag LEDs */
-    cpu_register_physical_memory(base + MISC_LEDS, MISC_SIZE,
-                                 slavio_misc_io_memory);
-
-    /* 32 bit registers */
-    slavio_misc_io_memory = cpu_register_io_memory(0, slavio_sysctrl_mem_read,
-                                                   slavio_sysctrl_mem_write,
-                                                   s);
-    // System control
-    cpu_register_physical_memory(base + MISC_SYS, SYSCTRL_SIZE,
-                                 slavio_misc_io_memory);
+    if (base) {
+        /* 8 bit registers */
+        io = cpu_register_io_memory(0, slavio_misc_mem_read,
+                                    slavio_misc_mem_write, s);
+        // Slavio control
+        cpu_register_physical_memory(base + MISC_CFG, MISC_SIZE, io);
+        // Diagnostics
+        cpu_register_physical_memory(base + MISC_DIAG, MISC_SIZE, io);
+        // Modem control
+        cpu_register_physical_memory(base + MISC_MDM, MISC_SIZE, io);
+
+        /* 16 bit registers */
+        io = cpu_register_io_memory(0, slavio_led_mem_read,
+                                    slavio_led_mem_write, s);
+        /* ss600mp diag LEDs */
+        cpu_register_physical_memory(base + MISC_LEDS, MISC_SIZE, io);
+
+        /* 32 bit registers */
+        io = cpu_register_io_memory(0, slavio_sysctrl_mem_read,
+                                    slavio_sysctrl_mem_write, s);
+        // System control
+        cpu_register_physical_memory(base + MISC_SYS, SYSCTRL_SIZE, io);
+    }
+
+    // AUX 1 (Misc System Functions)
+    if (aux1_base) {
+        io = cpu_register_io_memory(0, slavio_aux1_mem_read,
+                                    slavio_aux1_mem_write, s);
+        cpu_register_physical_memory(aux1_base, MISC_SIZE, io);
+    }
+
+    // AUX 2 (Software Powerdown Control)
+    if (aux2_base) {
+        io = cpu_register_io_memory(0, slavio_aux2_mem_read,
+                                    slavio_aux2_mem_write, s);
+        cpu_register_physical_memory(aux2_base, MISC_SIZE, io);
+    }
+
+    // Power management (APC) XXX: not a Slavio device
+    if (power_base) {
+        io = cpu_register_io_memory(0, apc_mem_read, apc_mem_write, s);
+        cpu_register_physical_memory(power_base, MISC_SIZE, io);
+    }
 
     s->irq = irq;
+    s->env = env;
 
     register_savevm("slavio_misc", base, 1, slavio_misc_save, slavio_misc_load,
                     s);
     qemu_register_reset(slavio_misc_reset, s);
     slavio_misc_reset(s);
+
     return s;
 }
diff --git a/hw/slavio_serial.c b/hw/slavio_serial.c
index 0791911..bd572b0 100644
--- a/hw/slavio_serial.c
+++ b/hw/slavio_serial.c
@@ -215,7 +215,6 @@ struct SerialState {
 static void handle_kbd_command(ChannelState *s, int val);
 static int serial_can_receive(void *opaque);
 static void serial_receive_byte(ChannelState *s, int ch);
-static inline void set_txint(ChannelState *s);
 
 static void clear_queue(void *opaque)
 {
@@ -321,28 +320,6 @@ static void slavio_serial_reset(void *opaque)
     slavio_serial_reset_chn(&s->chn[1]);
 }
 
-static inline void clr_rxint(ChannelState *s)
-{
-    s->rxint = 0;
-    s->rxint_under_svc = 0;
-    if (s->chn == chn_a) {
-        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-            s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
-        else
-            s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
-        s->rregs[R_INTR] &= ~INTR_RXINTA;
-    } else {
-        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-            s->rregs[R_IVEC] = IVEC_HINOINT;
-        else
-            s->rregs[R_IVEC] = IVEC_LONOINT;
-        s->otherchn->rregs[R_INTR] &= ~INTR_RXINTB;
-    }
-    if (s->txint)
-        set_txint(s);
-    slavio_serial_update_irq(s);
-}
-
 static inline void set_rxint(ChannelState *s)
 {
     s->rxint = 1;
@@ -367,6 +344,49 @@ static inline void set_rxint(ChannelState *s)
     slavio_serial_update_irq(s);
 }
 
+static inline void set_txint(ChannelState *s)
+{
+    s->txint = 1;
+    if (!s->rxint_under_svc) {
+        s->txint_under_svc = 1;
+        if (s->chn == chn_a) {
+            if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+                s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA;
+            else
+                s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA;
+        } else {
+            s->rregs[R_IVEC] = IVEC_TXINTB;
+        }
+    }
+    if (s->chn == chn_a)
+        s->rregs[R_INTR] |= INTR_TXINTA;
+    else
+        s->otherchn->rregs[R_INTR] |= INTR_TXINTB;
+    slavio_serial_update_irq(s);
+}
+
+static inline void clr_rxint(ChannelState *s)
+{
+    s->rxint = 0;
+    s->rxint_under_svc = 0;
+    if (s->chn == chn_a) {
+        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+            s->otherchn->rregs[R_IVEC] = IVEC_HINOINT;
+        else
+            s->otherchn->rregs[R_IVEC] = IVEC_LONOINT;
+        s->rregs[R_INTR] &= ~INTR_RXINTA;
+    } else {
+        if (s->wregs[W_MINTR] & MINTR_STATUSHI)
+            s->rregs[R_IVEC] = IVEC_HINOINT;
+        else
+            s->rregs[R_IVEC] = IVEC_LONOINT;
+        s->otherchn->rregs[R_INTR] &= ~INTR_RXINTB;
+    }
+    if (s->txint)
+        set_txint(s);
+    slavio_serial_update_irq(s);
+}
+
 static inline void clr_txint(ChannelState *s)
 {
     s->txint = 0;
@@ -389,27 +409,6 @@ static inline void clr_txint(ChannelState *s)
     slavio_serial_update_irq(s);
 }
 
-static inline void set_txint(ChannelState *s)
-{
-    s->txint = 1;
-    if (!s->rxint_under_svc) {
-        s->txint_under_svc = 1;
-        if (s->chn == chn_a) {
-            if (s->wregs[W_MINTR] & MINTR_STATUSHI)
-                s->otherchn->rregs[R_IVEC] = IVEC_HITXINTA;
-            else
-                s->otherchn->rregs[R_IVEC] = IVEC_LOTXINTA;
-        } else {
-            s->rregs[R_IVEC] = IVEC_TXINTB;
-        }
-    }
-    if (s->chn == chn_a)
-        s->rregs[R_INTR] |= INTR_TXINTA;
-    else
-        s->otherchn->rregs[R_INTR] |= INTR_TXINTB;
-    slavio_serial_update_irq(s);
-}
-
 static void slavio_serial_update_parameters(ChannelState *s)
 {
     int speed, parity, data_bits, stop_bits;
diff --git a/hw/slavio_timer.c b/hw/slavio_timer.c
index c0d849a..f129a5f 100644
--- a/hw/slavio_timer.c
+++ b/hw/slavio_timer.c
@@ -122,10 +122,9 @@ static void slavio_timer_irq(void *opaque)
 
     slavio_timer_get_out(s);
     DPRINTF("callback: count %x%08x\n", s->counthigh, s->count);
-    if (!slavio_timer_is_user(s)) {
-        s->reached = TIMER_REACHED;
+    s->reached = TIMER_REACHED;
+    if (!slavio_timer_is_user(s))
         qemu_irq_raise(s->irq);
-    }
 }
 
 static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr)
@@ -141,7 +140,7 @@ static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr)
         if (slavio_timer_is_user(s)) {
             // read user timer MSW
             slavio_timer_get_out(s);
-            ret = s->counthigh;
+            ret = s->counthigh | s->reached;
         } else {
             // read limit
             // clear irq
@@ -155,7 +154,7 @@ static uint32_t slavio_timer_mem_readl(void *opaque, target_phys_addr_t addr)
         // of counter (user mode)
         slavio_timer_get_out(s);
         if (slavio_timer_is_user(s)) // read user timer LSW
-            ret = s->count & TIMER_COUNT_MASK32;
+            ret = s->count & TIMER_MAX_COUNT64;
         else // read limit
             ret = (s->count & TIMER_MAX_COUNT32) | s->reached;
         break;
@@ -190,12 +189,18 @@ static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr,
     switch (saddr) {
     case TIMER_LIMIT:
         if (slavio_timer_is_user(s)) {
+            uint64_t count;
+
             // set user counter MSW, reset counter
             qemu_irq_lower(s->irq);
             s->limit = TIMER_MAX_COUNT64;
-            DPRINTF("processor %d user timer reset\n", s->slave_index);
+            s->counthigh = val & (TIMER_MAX_COUNT64 >> 32);
+            s->reached = 0;
+            count = ((uint64_t)s->counthigh << 32) | s->count;
+            DPRINTF("processor %d user timer set to %016llx\n", s->slave_index,
+                    count);
             if (s->timer)
-                ptimer_set_limit(s->timer, LIMIT_TO_PERIODS(s->limit), 1);
+                ptimer_set_count(s->timer, LIMIT_TO_PERIODS(s->limit - count));
         } else {
             // set limit, reset counter
             qemu_irq_lower(s->irq);
@@ -210,12 +215,18 @@ static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr,
         break;
     case TIMER_COUNTER:
         if (slavio_timer_is_user(s)) {
+            uint64_t count;
+
             // set user counter LSW, reset counter
             qemu_irq_lower(s->irq);
             s->limit = TIMER_MAX_COUNT64;
-            DPRINTF("processor %d user timer reset\n", s->slave_index);
+            s->count = val & TIMER_MAX_COUNT64;
+            s->reached = 0;
+            count = ((uint64_t)s->counthigh) << 32 | s->count;
+            DPRINTF("processor %d user timer set to %016llx\n", s->slave_index,
+                    count);
             if (s->timer)
-                ptimer_set_limit(s->timer, LIMIT_TO_PERIODS(s->limit), 1);
+                ptimer_set_count(s->timer, LIMIT_TO_PERIODS(s->limit - count));
         } else
             DPRINTF("not user timer\n");
         break;
@@ -250,22 +261,39 @@ static void slavio_timer_mem_writel(void *opaque, target_phys_addr_t addr,
             unsigned int i;
 
             for (i = 0; i < s->num_slaves; i++) {
-                if (val & (1 << i)) {
-                    qemu_irq_lower(s->slave[i]->irq);
-                    s->slave[i]->limit = -1ULL;
-                } else {
-                    ptimer_stop(s->slave[i]->timer);
-                }
-                if ((val & (1 << i)) != (s->slave_mode & (1 << i))) {
-                    ptimer_stop(s->slave[i]->timer);
-                    ptimer_set_limit(s->slave[i]->timer,
-                                     LIMIT_TO_PERIODS(s->slave[i]->limit), 1);
-                    DPRINTF("processor %d timer changed\n",
-                            s->slave[i]->slave_index);
-                    ptimer_run(s->slave[i]->timer, 0);
+                unsigned int processor = 1 << i;
+
+                // check for a change in timer mode for this processor
+                if ((val & processor) != (s->slave_mode & processor)) {
+                    if (val & processor) { // counter -> user timer
+                        qemu_irq_lower(s->slave[i]->irq);
+                        // counters are always running
+                        ptimer_stop(s->slave[i]->timer);
+                        s->slave[i]->running = 0;
+                        // user timer limit is always the same
+                        s->slave[i]->limit = TIMER_MAX_COUNT64;
+                        ptimer_set_limit(s->slave[i]->timer,
+                                         LIMIT_TO_PERIODS(s->slave[i]->limit), 1);
+                        // set this processors user timer bit in config
+                        // register
+                        s->slave_mode |= processor;
+                        DPRINTF("processor %d changed from counter to user "
+                                "timer\n", s->slave[i]->slave_index);
+                    } else { // user timer -> counter
+                        // stop the user timer if it is running
+                        if (s->slave[i]->running)
+                            ptimer_stop(s->slave[i]->timer);
+                        // start the counter
+                        ptimer_run(s->slave[i]->timer, 0);
+                        s->slave[i]->running = 1;
+                        // clear this processors user timer bit in config
+                        // register
+                        s->slave_mode &= ~processor;
+                        DPRINTF("processor %d changed from user timer to "
+                                "counter\n", s->slave[i]->slave_index);
+                    }
                 }
             }
-            s->slave_mode = val & ((1 << s->num_slaves) - 1);
         } else
             DPRINTF("not system timer\n");
         break;
diff --git a/hw/sun4m.c b/hw/sun4m.c
index d46056f..a0a17f2 100644
--- a/hw/sun4m.c
+++ b/hw/sun4m.c
@@ -83,7 +83,7 @@ struct hwdef {
     target_phys_addr_t intctl_base, counter_base, nvram_base, ms_kb_base;
     target_phys_addr_t serial_base, fd_base;
     target_phys_addr_t idreg_base, dma_base, esp_base, le_base;
-    target_phys_addr_t tcx_base, cs_base, power_base;
+    target_phys_addr_t tcx_base, cs_base, apc_base, aux1_base, aux2_base;
     target_phys_addr_t ecc_base;
     uint32_t ecc_version;
     target_phys_addr_t sun4c_intctl_base, sun4c_counter_base;
@@ -91,7 +91,7 @@ struct hwdef {
     // IRQ numbers are not PIL ones, but master interrupt controller
     // register bit numbers
     int intctl_g_intr, esp_irq, le_irq, clock_irq, clock1_irq;
-    int ser_irq, ms_kb_irq, fd_irq, me_irq, cs_irq;
+    int ser_irq, ms_kb_irq, fd_irq, me_irq, cs_irq, ecc_irq;
     int machine_id; // For NVRAM
     uint32_t iommu_version;
     uint32_t intbit_to_level[32];
@@ -515,8 +515,9 @@ static void sun4m_hw_init(const struct hwdef *hwdef, int RAM_size,
         esp_scsi_attach(main_esp, drives_table[index].bdrv, i);
     }
 
-    slavio_misc = slavio_misc_init(hwdef->slavio_base, hwdef->power_base,
-                                   slavio_irq[hwdef->me_irq]);
+    slavio_misc = slavio_misc_init(hwdef->slavio_base, hwdef->apc_base,
+                                   hwdef->aux1_base, hwdef->aux2_base,
+                                   slavio_irq[hwdef->me_irq], envs[0]);
     if (hwdef->cs_base != (target_phys_addr_t)-1)
         cs_init(hwdef->cs_base, hwdef->cs_irq, slavio_intctl);
 
@@ -528,7 +529,8 @@ static void sun4m_hw_init(const struct hwdef *hwdef, int RAM_size,
                graphic_height, graphic_depth, hwdef->machine_id, "Sun4m");
 
     if (hwdef->ecc_base != (target_phys_addr_t)-1)
-        ecc_init(hwdef->ecc_base, hwdef->ecc_version);
+        ecc_init(hwdef->ecc_base, slavio_irq[hwdef->ecc_irq],
+                 hwdef->ecc_version);
 }
 
 static void sun4c_hw_init(const struct hwdef *hwdef, int RAM_size,
@@ -661,6 +663,10 @@ static void sun4c_hw_init(const struct hwdef *hwdef, int RAM_size,
         esp_scsi_attach(main_esp, drives_table[index].bdrv, i);
     }
 
+    slavio_misc = slavio_misc_init(-1, hwdef->apc_base,
+                                   hwdef->aux1_base, hwdef->aux2_base,
+                                   slavio_irq[hwdef->me_irq], env);
+
     kernel_size = sun4m_load_kernel(kernel_filename, kernel_cmdline,
                                     initrd_filename);
 
@@ -686,7 +692,9 @@ static const struct hwdef hwdefs[] = {
         .dma_base     = 0x78400000,
         .esp_base     = 0x78800000,
         .le_base      = 0x78c00000,
-        .power_base   = 0x7a000000,
+        .apc_base     = 0x7a000000, // XXX 0x6a000000,
+        .aux1_base    = 0x71900000,
+        .aux2_base    = 0x71910000,
         .ecc_base     = -1,
         .sun4c_intctl_base  = -1,
         .sun4c_counter_base = -1,
@@ -726,7 +734,9 @@ static const struct hwdef hwdefs[] = {
         .dma_base     = 0xef0400000ULL,
         .esp_base     = 0xef0800000ULL,
         .le_base      = 0xef0c00000ULL,
-        .power_base   = 0xefa000000ULL,
+        .apc_base     = 0xefa000000ULL, // XXX should not exist
+        .aux1_base    = 0xff1900000ULL, // XXX 0xff1800000ULL,
+        .aux2_base    = 0xff1910000ULL, // XXX 0xff1a01000ULL,
         .ecc_base     = 0xf00000000ULL,
         .ecc_version  = 0x10000000, // version 0, implementation 1
         .sun4c_intctl_base  = -1,
@@ -742,6 +752,7 @@ static const struct hwdef hwdefs[] = {
         .fd_irq = 22,
         .me_irq = 30,
         .cs_irq = -1,
+        .ecc_irq = 28,
         .machine_id = 0x72,
         .iommu_version = 0x03000000,
         .intbit_to_level = {
@@ -767,7 +778,9 @@ static const struct hwdef hwdefs[] = {
         .dma_base     = 0xef0081000ULL,
         .esp_base     = 0xef0080000ULL,
         .le_base      = 0xef0060000ULL,
-        .power_base   = 0xefa000000ULL,
+        .apc_base     = 0xefa000000ULL, // XXX should not exist
+        .aux1_base    = 0xff1900000ULL, // XXX 0xff1800000ULL,
+        .aux2_base    = 0xff1910000ULL, // XXX should not exist
         .ecc_base     = 0xf00000000ULL,
         .ecc_version  = 0x00000000, // version 0, implementation 0
         .sun4c_intctl_base  = -1,
@@ -783,6 +796,7 @@ static const struct hwdef hwdefs[] = {
         .fd_irq = 22,
         .me_irq = 30,
         .cs_irq = -1,
+        .ecc_irq = 28,
         .machine_id = 0x71,
         .iommu_version = 0x01000000,
         .intbit_to_level = {
@@ -808,7 +822,9 @@ static const struct hwdef hwdefs[] = {
         .dma_base     = 0xef0400000ULL,
         .esp_base     = 0xef0800000ULL,
         .le_base      = 0xef0c00000ULL,
-        .power_base   = 0xefa000000ULL,
+        .apc_base     = 0xefa000000ULL, // XXX should not exist
+        .aux1_base    = 0xff1900000ULL, // XXX 0xff1800000ULL,
+        .aux2_base    = 0xff1910000ULL, // XXX 0xff1a01000ULL,
         .ecc_base     = 0xf00000000ULL,
         .ecc_version  = 0x20000000, // version 0, implementation 2
         .sun4c_intctl_base  = -1,
@@ -824,6 +840,7 @@ static const struct hwdef hwdefs[] = {
         .fd_irq = 22,
         .me_irq = 30,
         .cs_irq = -1,
+        .ecc_irq = 28,
         .machine_id = 0x72,
         .iommu_version = 0x13000000,
         .intbit_to_level = {
@@ -848,7 +865,9 @@ static const struct hwdef hwdefs[] = {
         .dma_base     = 0xf8400000,
         .esp_base     = 0xf8800000,
         .le_base      = 0xf8c00000,
-        .power_base   = -1,
+        .apc_base     = -1,
+        .aux1_base    = 0xf7400003,
+        .aux2_base    = -1,
         .sun4c_intctl_base  = 0xf5000000,
         .sun4c_counter_base = 0xf3000000,
         .vram_size    = 0x00100000,
diff --git a/hw/sun4m.h b/hw/sun4m.h
index 4b27d86..484e3e8 100644
--- a/hw/sun4m.h
+++ b/hw/sun4m.h
@@ -54,7 +54,9 @@ void slavio_serial_ms_kbd_init(target_phys_addr_t base, qemu_irq irq,
 
 /* slavio_misc.c */
 void *slavio_misc_init(target_phys_addr_t base, target_phys_addr_t power_base,
-                       qemu_irq irq);
+                       target_phys_addr_t aux1_base,
+                       target_phys_addr_t aux2_base, qemu_irq irq,
+                       CPUState *env);
 void slavio_set_power_fail(void *opaque, int power_failing);
 
 /* esp.c */
@@ -81,6 +83,6 @@ void lance_init(NICInfo *nd, target_phys_addr_t leaddr, void *dma_opaque,
                 qemu_irq irq, qemu_irq *reset);
 
 /* eccmemctl.c */
-void *ecc_init(target_phys_addr_t base, uint32_t version);
+void *ecc_init(target_phys_addr_t base, qemu_irq irq, uint32_t version);
 
 #endif
diff --git a/hw/usb-hid.c b/hw/usb-hid.c
index 6ea6c4d..1453b42 100644
--- a/hw/usb-hid.c
+++ b/hw/usb-hid.c
@@ -262,7 +262,9 @@ static const uint8_t qemu_mouse_hid_report_descriptor[] = {
     0x81, 0x02, 0x95, 0x01, 0x75, 0x05, 0x81, 0x01,
     0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81,
     0x25, 0x7F, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06,
-    0xC0, 0xC0,
+    0x05, 0x01, 0x09, 0x38, 0x15, 0x81, 0x25, 0x7F,
+    0x35, 0x00, 0x45, 0x00, 0x75, 0x08, 0x95, 0x01,
+    0x81, 0x02, 0xC0, 0xC0,
 };
 
 static const uint8_t qemu_tablet_hid_report_descriptor[] = {
diff --git a/hw/usb-serial.c b/hw/usb-serial.c
new file mode 100644
index 0000000..b666c99
--- /dev/null
+++ b/hw/usb-serial.c
@@ -0,0 +1,549 @@
+/*
+ * FTDI FT232BM Device emulation
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ * Written by Paul Brook, reused for FTDI by Samuel Thibault
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-common.h"
+#include "usb.h"
+#include "qemu-char.h"
+
+//#define DEBUG_Serial
+
+#ifdef DEBUG_Serial
+#define DPRINTF(fmt, args...) \
+do { printf("usb-serial: " fmt , ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#endif
+
+#define RECV_BUF 384
+#define SEND_BUF 128        // Not used for now
+
+/* Commands */
+#define FTDI_RESET		0
+#define FTDI_SET_MDM_CTRL	1
+#define FTDI_SET_FLOW_CTRL	2
+#define FTDI_SET_BAUD		3
+#define FTDI_SET_DATA		4
+#define FTDI_GET_MDM_ST		5
+#define FTDI_SET_EVENT_CHR	6
+#define FTDI_SET_ERROR_CHR	7
+#define FTDI_SET_LATENCY	9
+#define FTDI_GET_LATENCY	10
+
+#define DeviceOutVendor	((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+#define DeviceInVendor	((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+
+/* RESET */
+
+#define FTDI_RESET_SIO	0
+#define FTDI_RESET_RX	1
+#define FTDI_RESET_TX	2
+
+/* SET_MDM_CTRL */
+
+#define FTDI_MDM_CTRL	3
+#define FTDI_DTR	1
+#define FTDI_RTS	2
+
+/* SET_FLOW_CTRL */
+
+#define FTDI_RTS_CTS_HS		1
+#define FTDI_DTR_DSR_HS		2
+#define FTDI_XON_XOFF_HS	4
+
+/* SET_DATA */
+
+#define FTDI_PARITY	(0x7 << 8)
+#define FTDI_ODD	(0x1 << 8)
+#define FTDI_EVEN	(0x2 << 8)
+#define FTDI_MARK	(0x3 << 8)
+#define FTDI_SPACE	(0x4 << 8)
+
+#define FTDI_STOP	(0x3 << 11)
+#define FTDI_STOP1	(0x0 << 11)
+#define FTDI_STOP15	(0x1 << 11)
+#define FTDI_STOP2	(0x2 << 11)
+
+/* GET_MDM_ST */
+/* TODO: should be sent every 40ms */
+#define FTDI_CTS  (1<<4)        // CTS line status
+#define FTDI_DSR  (1<<5)        // DSR line status
+#define FTDI_RI   (1<<6)        // RI line status
+#define FTDI_RLSD (1<<7)        // Receive Line Signal Detect
+
+/* Status */
+
+#define FTDI_DR   (1<<0)        // Data Ready
+#define FTDI_OE   (1<<1)        // Overrun Err
+#define FTDI_PE   (1<<2)        // Parity Err
+#define FTDI_FE   (1<<3)        // Framing Err
+#define FTDI_BI   (1<<4)        // Break Interrupt
+#define FTDI_THRE (1<<5)        // Transmitter Holding Register
+#define FTDI_TEMT (1<<6)        // Transmitter Empty
+#define FTDI_FIFO (1<<7)        // Error in FIFO
+
+typedef struct {
+    USBDevice dev;
+    uint16_t vendorid;
+    uint16_t productid;
+    uint8_t recv_buf[RECV_BUF];
+    uint8_t recv_ptr;
+    uint8_t recv_used;
+    uint8_t send_buf[SEND_BUF];
+    uint8_t event_chr;
+    uint8_t error_chr;
+    uint8_t event_trigger;
+    uint8_t lines;
+    QEMUSerialSetParams params;
+    int latency;        /* ms */
+    CharDriverState *cs;
+} USBSerialState;
+
+static const uint8_t qemu_serial_dev_descriptor[] = {
+        0x12,       /*  u8 bLength; */
+        0x01,       /*  u8 bDescriptorType; Device */
+        0x00, 0x02, /*  u16 bcdUSB; v2.0 */
+
+        0x00,       /*  u8  bDeviceClass; */
+        0x00,       /*  u8  bDeviceSubClass; */
+        0x00,       /*  u8  bDeviceProtocol; [ low/full speeds only ] */
+        0x08,       /*  u8  bMaxPacketSize0; 8 Bytes */
+
+        /* Vendor and product id are arbitrary.  */
+        0x03, 0x04, /*  u16 idVendor; */
+        0x00, 0xFF, /*  u16 idProduct; */
+        0x00, 0x04, /*  u16 bcdDevice */
+
+        0x01,       /*  u8  iManufacturer; */
+        0x02,       /*  u8  iProduct; */
+        0x03,       /*  u8  iSerialNumber; */
+        0x01        /*  u8  bNumConfigurations; */
+};
+
+static const uint8_t qemu_serial_config_descriptor[] = {
+
+        /* one configuration */
+        0x09,       /*  u8  bLength; */
+        0x02,       /*  u8  bDescriptorType; Configuration */
+        0x20, 0x00, /*  u16 wTotalLength; */
+        0x01,       /*  u8  bNumInterfaces; (1) */
+        0x01,       /*  u8  bConfigurationValue; */
+        0x00,       /*  u8  iConfiguration; */
+        0x80,       /*  u8  bmAttributes;
+                                 Bit 7: must be set,
+                                     6: Self-powered,
+                                     5: Remote wakeup,
+                                     4..0: resvd */
+        100/2,       /*  u8  MaxPower; */
+
+        /* one interface */
+        0x09,       /*  u8  if_bLength; */
+        0x04,       /*  u8  if_bDescriptorType; Interface */
+        0x00,       /*  u8  if_bInterfaceNumber; */
+        0x00,       /*  u8  if_bAlternateSetting; */
+        0x02,       /*  u8  if_bNumEndpoints; */
+        0xff,       /*  u8  if_bInterfaceClass; Vendor Specific */
+        0xff,       /*  u8  if_bInterfaceSubClass; Vendor Specific */
+        0xff,       /*  u8  if_bInterfaceProtocol; Vendor Specific */
+        0x02,       /*  u8  if_iInterface; */
+
+        /* Bulk-In endpoint */
+        0x07,       /*  u8  ep_bLength; */
+        0x05,       /*  u8  ep_bDescriptorType; Endpoint */
+        0x81,       /*  u8  ep_bEndpointAddress; IN Endpoint 1 */
+        0x02,       /*  u8  ep_bmAttributes; Bulk */
+        0x40, 0x00, /*  u16 ep_wMaxPacketSize; */
+        0x00,       /*  u8  ep_bInterval; */
+
+        /* Bulk-Out endpoint */
+        0x07,       /*  u8  ep_bLength; */
+        0x05,       /*  u8  ep_bDescriptorType; Endpoint */
+        0x02,       /*  u8  ep_bEndpointAddress; OUT Endpoint 2 */
+        0x02,       /*  u8  ep_bmAttributes; Bulk */
+        0x40, 0x00, /*  u16 ep_wMaxPacketSize; */
+        0x00        /*  u8  ep_bInterval; */
+};
+
+static void usb_serial_reset(USBSerialState *s)
+{
+    /* TODO: Set flow control to none */
+    s->event_chr = 0x0d;
+    s->event_trigger = 0;
+    s->recv_ptr = 0;
+    s->recv_used = 0;
+    /* TODO: purge in char driver */
+    s->lines &= ~(FTDI_DTR|FTDI_RTS);
+}
+
+static void usb_serial_handle_reset(USBDevice *dev)
+{
+    USBSerialState *s = (USBSerialState *)dev;
+
+    DPRINTF("Reset\n");
+
+    usb_serial_reset(s);
+    /* TODO: Reset char device, send BREAK? */
+}
+
+static int usb_serial_handle_control(USBDevice *dev, int request, int value,
+                                  int index, int length, uint8_t *data)
+{
+    USBSerialState *s = (USBSerialState *)dev;
+    int ret = 0;
+
+    //DPRINTF("got control %x, value %x\n",request, value);
+    switch (request) {
+    case DeviceRequest | USB_REQ_GET_STATUS:
+        data[0] = (0 << USB_DEVICE_SELF_POWERED) |
+            (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+        data[1] = 0x00;
+        ret = 2;
+        break;
+    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 0;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 1;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        dev->addr = value;
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+        switch(value >> 8) {
+        case USB_DT_DEVICE:
+            memcpy(data, qemu_serial_dev_descriptor,
+                   sizeof(qemu_serial_dev_descriptor));
+            data[8] = s->vendorid & 0xff;
+            data[9] = ((s->vendorid) >> 8) & 0xff;
+            data[10] = s->productid & 0xff;
+            data[11] = ((s->productid) >> 8) & 0xff;
+            ret = sizeof(qemu_serial_dev_descriptor);
+            break;
+        case USB_DT_CONFIG:
+            memcpy(data, qemu_serial_config_descriptor,
+                   sizeof(qemu_serial_config_descriptor));
+            ret = sizeof(qemu_serial_config_descriptor);
+            break;
+        case USB_DT_STRING:
+            switch(value & 0xff) {
+            case 0:
+                /* language ids */
+                data[0] = 4;
+                data[1] = 3;
+                data[2] = 0x09;
+                data[3] = 0x04;
+                ret = 4;
+                break;
+            case 1:
+                /* vendor description */
+                ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+                break;
+            case 2:
+                /* product description */
+                ret = set_usb_string(data, "QEMU USB SERIAL");
+                break;
+            case 3:
+                /* serial number */
+                ret = set_usb_string(data, "1");
+                break;
+            default:
+                goto fail;
+            }
+            break;
+        default:
+            goto fail;
+        }
+        break;
+    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+        data[0] = 1;
+        ret = 1;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_INTERFACE:
+        data[0] = 0;
+        ret = 1;
+        break;
+    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+        ret = 0;
+        break;
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        ret = 0;
+        break;
+
+        /* Class specific requests.  */
+    case DeviceOutVendor | FTDI_RESET:
+        switch (value) {
+        case FTDI_RESET_SIO:
+            usb_serial_reset(s);
+            break;
+        case FTDI_RESET_RX:
+            s->recv_ptr = 0;
+            s->recv_used = 0;
+            /* TODO: purge from char device */
+            break;
+        case FTDI_RESET_TX:
+            /* TODO: purge from char device */
+            break;
+        }
+        break;
+    case DeviceOutVendor | FTDI_SET_MDM_CTRL:
+        s->lines = value & FTDI_MDM_CTRL;
+        break;
+    case DeviceOutVendor | FTDI_SET_FLOW_CTRL:
+        /* TODO: ioctl */
+        break;
+    case DeviceOutVendor | FTDI_SET_BAUD: {
+        static const int subdivisors8[8] = { 0, 4, 2, 1, 3, 5, 6, 7 };
+        int subdivisor8 = subdivisors8[((value & 0xc000) >> 14)
+                                     | ((index & 1) << 2)];
+        int divisor = value & 0x3fff;
+
+        /* chip special cases */
+        if (divisor == 1 && subdivisor8 == 0)
+            subdivisor8 = 4;
+        if (divisor == 0 && subdivisor8 == 0)
+            divisor = 1;
+
+        s->params.speed = (48000000 / 2) / (8 * divisor + subdivisor8);
+        qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+        break;
+    }
+    case DeviceOutVendor | FTDI_SET_DATA:
+        switch (value & FTDI_PARITY) {
+            case 0:
+                s->params.parity = 'N';
+                break;
+            case FTDI_ODD:
+                s->params.parity = 'O';
+                break;
+            case FTDI_EVEN:
+                s->params.parity = 'E';
+                break;
+            default:
+                DPRINTF("unsupported parity %d\n", value & FTDI_PARITY);
+                goto fail;
+        }
+        switch (value & FTDI_STOP) {
+            case FTDI_STOP1:
+                s->params.stop_bits = 1;
+                break;
+            case FTDI_STOP2:
+                s->params.stop_bits = 2;
+                break;
+            default:
+                DPRINTF("unsupported stop bits %d\n", value & FTDI_STOP);
+                goto fail;
+        }
+        qemu_chr_ioctl(s->cs, CHR_IOCTL_SERIAL_SET_PARAMS, &s->params);
+        /* TODO: TX ON/OFF */
+        break;
+    case DeviceInVendor | FTDI_GET_MDM_ST:
+        /* TODO: return modem status */
+        data[0] = 0;
+        ret = 1;
+        break;
+    case DeviceOutVendor | FTDI_SET_EVENT_CHR:
+        /* TODO: handle it */
+        s->event_chr = value;
+        break;
+    case DeviceOutVendor | FTDI_SET_ERROR_CHR:
+        /* TODO: handle it */
+        s->error_chr = value;
+        break;
+    case DeviceOutVendor | FTDI_SET_LATENCY:
+        s->latency = value;
+        break;
+    case DeviceInVendor | FTDI_GET_LATENCY:
+        data[0] = s->latency;
+        ret = 1;
+        break;
+    default:
+    fail:
+        DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static int usb_serial_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBSerialState *s = (USBSerialState *)dev;
+    int ret = 0;
+    uint8_t devep = p->devep;
+    uint8_t *data = p->data;
+    int len = p->len;
+    int first_len;
+
+    switch (p->pid) {
+    case USB_TOKEN_OUT:
+        if (devep != 2)
+            goto fail;
+        qemu_chr_write(s->cs, data, len);
+        break;
+
+    case USB_TOKEN_IN:
+        if (devep != 1)
+            goto fail;
+        first_len = RECV_BUF - s->recv_ptr;
+        if (len <= 2) {
+            ret = USB_RET_NAK;
+            break;
+        }
+        /* TODO: Report serial line status */
+        *data++ = 0;
+        *data++ = 0;
+        len -= 2;
+        if (len > s->recv_used)
+            len = s->recv_used;
+        if (!len) {
+            ret = USB_RET_NAK;
+            break;
+        }
+        if (first_len > len)
+            first_len = len;
+        memcpy(data, s->recv_buf + s->recv_ptr, first_len);
+        if (len > first_len)
+            memcpy(data + first_len, s->recv_buf, len - first_len);
+        s->recv_used -= len;
+        s->recv_ptr = (s->recv_ptr + len) % RECV_BUF;
+        ret = len + 2;
+        break;
+
+    default:
+        DPRINTF("Bad token\n");
+    fail:
+        ret = USB_RET_STALL;
+        break;
+    }
+
+    return ret;
+}
+
+static void usb_serial_handle_destroy(USBDevice *dev)
+{
+    USBSerialState *s = (USBSerialState *)dev;
+
+    qemu_chr_close(s->cs);
+    qemu_free(s);
+}
+
+int usb_serial_can_read(void *opaque)
+{
+    USBSerialState *s = opaque;
+    return RECV_BUF - s->recv_used;
+}
+
+void usb_serial_read(void *opaque, const uint8_t *buf, int size)
+{
+    USBSerialState *s = opaque;
+    int first_size = RECV_BUF - s->recv_ptr;
+    if (first_size > size)
+        first_size = size;
+    memcpy(s->recv_buf + s->recv_ptr + s->recv_used, buf, first_size);
+    if (size > first_size)
+        memcpy(s->recv_buf, buf + first_size, size - first_size);
+    s->recv_used += size;
+}
+
+void usb_serial_event(void *opaque, int event)
+{
+    USBSerialState *s = opaque;
+
+    switch (event) {
+        case CHR_EVENT_BREAK:
+            /* TODO: Send Break to USB */
+            break;
+        case CHR_EVENT_FOCUS:
+            break;
+        case CHR_EVENT_RESET:
+            usb_serial_reset(s);
+            /* TODO: Reset USB port */
+            break;
+    }
+}
+
+USBDevice *usb_serial_init(const char *filename)
+{
+    USBSerialState *s;
+    CharDriverState *cdrv;
+    unsigned short vendorid = 0x0403, productid = 0x6001;
+
+    while (*filename && *filename != ':') {
+        const char *p;
+        char *e;
+        if (strstart(filename, "vendorid=", &p)) {
+            vendorid = strtol(p, &e, 16);
+            if (e == p || (*e && *e != ',' && *e != ':')) {
+                printf("bogus vendor ID %s\n", p);
+                return NULL;
+            }
+            filename = e;
+        } else if (strstart(filename, "productid=", &p)) {
+            productid = strtol(p, &e, 16);
+            if (e == p || (*e && *e != ',' && *e != ':')) {
+                printf("bogus product ID %s\n", p);
+                return NULL;
+            }
+            filename = e;
+        } else {
+            printf("unrecognized serial USB option %s\n", filename);
+            return NULL;
+        }
+        while(*filename == ',')
+            filename++;
+    }
+    if (!*filename) {
+        printf("character device specification needed\n");
+        return NULL;
+    }
+    filename++;
+    s = qemu_mallocz(sizeof(USBSerialState));
+    if (!s)
+        return NULL;
+
+    cdrv = qemu_chr_open(filename);
+    if (!cdrv)
+        goto fail;
+    s->cs = cdrv;
+    qemu_chr_add_handlers(cdrv, usb_serial_can_read, usb_serial_read, usb_serial_event, s);
+
+    s->dev.speed = USB_SPEED_FULL;
+    s->dev.handle_packet = usb_generic_handle_packet;
+
+    s->dev.handle_reset = usb_serial_handle_reset;
+    s->dev.handle_control = usb_serial_handle_control;
+    s->dev.handle_data = usb_serial_handle_data;
+    s->dev.handle_destroy = usb_serial_handle_destroy;
+
+    s->vendorid = vendorid;
+    s->productid = productid;
+
+    snprintf(s->dev.devname, sizeof(s->dev.devname), "QEMU USB Serial(%.16s)",
+             filename);
+
+    usb_serial_handle_reset((USBDevice *)s);
+    return (USBDevice *)s;
+ fail:
+    qemu_free(s);
+    return NULL;
+}
diff --git a/hw/usb.h b/hw/usb.h
index e6fd3c0..db6146e 100644
--- a/hw/usb.h
+++ b/hw/usb.h
@@ -217,6 +217,9 @@ USBDevice *usb_msd_init(const char *filename);
 /* usb-wacom.c */
 USBDevice *usb_wacom_init(void);
 
+/* usb-serial.c */
+USBDevice *usb_serial_init(const char *filename);
+
 /* usb ports of the VM */
 
 void qemu_register_usb_port(USBPort *port, void *opaque, int index,
diff --git a/hw/vmware_vga.c b/hw/vmware_vga.c
index 6cae25c..bd96e6b 100644
--- a/hw/vmware_vga.c
+++ b/hw/vmware_vga.c
@@ -1100,19 +1100,6 @@ static void vmsvga_init(struct vmsvga_state_s *s, DisplayState *ds,
     cpu_register_physical_memory(SVGA_MEM_BASE, vga_ram_size,
                     iomemtype);
 
-    register_ioport_read(SVGA_IO_BASE + SVGA_IO_MUL * SVGA_INDEX_PORT,
-                    1, 4, vmsvga_index_read, s);
-    register_ioport_write(SVGA_IO_BASE + SVGA_IO_MUL * SVGA_INDEX_PORT,
-                    1, 4, vmsvga_index_write, s);
-    register_ioport_read(SVGA_IO_BASE + SVGA_IO_MUL * SVGA_VALUE_PORT,
-                    1, 4, vmsvga_value_read, s);
-    register_ioport_write(SVGA_IO_BASE + SVGA_IO_MUL * SVGA_VALUE_PORT,
-                    1, 4, vmsvga_value_write, s);
-    register_ioport_read(SVGA_IO_BASE + SVGA_IO_MUL * SVGA_BIOS_PORT,
-                    1, 4, vmsvga_bios_read, s);
-    register_ioport_write(SVGA_IO_BASE + SVGA_IO_MUL * SVGA_BIOS_PORT,
-                    1, 4, vmsvga_bios_write, s);
-
     graphic_console_init(ds, vmsvga_update_display,
                     vmsvga_invalidate_display, vmsvga_screen_dump, s);
 
@@ -1146,6 +1133,26 @@ static int pci_vmsvga_load(QEMUFile *f, void *opaque, int version_id)
     return 0;
 }
 
+static void pci_vmsvga_map_ioport(PCIDevice *pci_dev, int region_num,
+                uint32_t addr, uint32_t size, int type)
+{
+    struct pci_vmsvga_state_s *d = (struct pci_vmsvga_state_s *) pci_dev;
+    struct vmsvga_state_s *s = &d->chip;
+
+    register_ioport_read(addr + SVGA_IO_MUL * SVGA_INDEX_PORT,
+                    1, 4, vmsvga_index_read, s);
+    register_ioport_write(addr + SVGA_IO_MUL * SVGA_INDEX_PORT,
+                    1, 4, vmsvga_index_write, s);
+    register_ioport_read(addr + SVGA_IO_MUL * SVGA_VALUE_PORT,
+                    1, 4, vmsvga_value_read, s);
+    register_ioport_write(addr + SVGA_IO_MUL * SVGA_VALUE_PORT,
+                    1, 4, vmsvga_value_write, s);
+    register_ioport_read(addr + SVGA_IO_MUL * SVGA_BIOS_PORT,
+                    1, 4, vmsvga_bios_read, s);
+    register_ioport_write(addr + SVGA_IO_MUL * SVGA_BIOS_PORT,
+                    1, 4, vmsvga_bios_write, s);
+}
+
 #define PCI_VENDOR_ID_VMWARE		0x15ad
 #define PCI_DEVICE_ID_VMWARE_SVGA2	0x0405
 #define PCI_DEVICE_ID_VMWARE_SVGA	0x0710
@@ -1189,6 +1196,9 @@ void pci_vmsvga_init(PCIBus *bus, DisplayState *ds, uint8_t *vga_ram_base,
     s->card.config[0x2f]		= SVGA_PCI_DEVICE_ID >> 8;
     s->card.config[0x3c]		= 0xff;		/* End */
 
+    pci_register_io_region(&s->card, 0, 0x10,
+                    PCI_ADDRESS_SPACE_IO, pci_vmsvga_map_ioport);
+
     vmsvga_init(&s->chip, ds, vga_ram_base, vga_ram_offset, vga_ram_size);
 
     register_savevm("vmware_vga", 0, 0, pci_vmsvga_save, pci_vmsvga_load, s);
diff --git a/i386-vl.ld b/i386-vl.ld
deleted file mode 100644
index 428fe83..0000000
--- a/i386-vl.ld
+++ /dev/null
@@ -1,140 +0,0 @@
-/* ld script to make i386 Linux kernel
- * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>;
- */
-OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
-OUTPUT_ARCH(i386)
-SEARCH_DIR(/lib); SEARCH_DIR(/usr/lib); SEARCH_DIR(/usr/local/lib); SEARCH_DIR(/usr/alpha-unknown-linux-gnu/lib);
-ENTRY(_start)
-SECTIONS
-{
-  /* Read-only sections, merged into text segment: */
-  . = 0xa8000000 + SIZEOF_HEADERS;
-  .interp     : { *(.interp) 	}
-  .hash          : { *(.hash)		}
-  .dynsym        : { *(.dynsym)		}
-  .dynstr        : { *(.dynstr)		}
-  .gnu.version   : { *(.gnu.version)	}
-  .gnu.version_d   : { *(.gnu.version_d)	}
-  .gnu.version_r   : { *(.gnu.version_r)	}
-  .rel.text      :
-    { *(.rel.text) *(.rel.gnu.linkonce.t*) }
-  .rela.text     :
-    { *(.rela.text) *(.rela.gnu.linkonce.t*) }
-  .rel.data      :
-    { *(.rel.data) *(.rel.gnu.linkonce.d*) }
-  .rela.data     :
-    { *(.rela.data) *(.rela.gnu.linkonce.d*) }
-  .rel.rodata    :
-    { *(.rel.rodata) *(.rel.gnu.linkonce.r*) }
-  .rela.rodata   :
-    { *(.rela.rodata) *(.rela.gnu.linkonce.r*) }
-  .rel.got       : { *(.rel.got)		}
-  .rela.got      : { *(.rela.got)		}
-  .rel.ctors     : { *(.rel.ctors)	}
-  .rela.ctors    : { *(.rela.ctors)	}
-  .rel.dtors     : { *(.rel.dtors)	}
-  .rela.dtors    : { *(.rela.dtors)	}
-  .rel.init      : { *(.rel.init)	}
-  .rela.init     : { *(.rela.init)	}
-  .rel.fini      : { *(.rel.fini)	}
-  .rela.fini     : { *(.rela.fini)	}
-  .rel.bss       : { *(.rel.bss)		}
-  .rela.bss      : { *(.rela.bss)		}
-  .rel.plt       : { *(.rel.plt)		}
-  .rela.plt      : { *(.rela.plt)		}
-  .init          : { *(.init)	} =0x47ff041f
-  .text      :
-  {
-    *(.text)
-    /* .gnu.warning sections are handled specially by elf32.em.  */
-    *(.gnu.warning)
-    *(.gnu.linkonce.t*)
-  } =0x47ff041f
-  _etext = .;
-  PROVIDE (etext = .);
-  .fini      : { *(.fini)    } =0x47ff041f
-  .rodata    : { *(.rodata) *(.gnu.linkonce.r*) }
-  .rodata1   : { *(.rodata1) }
-  .reginfo : { *(.reginfo) }
-  __preinit_array_start = .;
-  .preinit_array : { *(.preinit_array) }
-  __preinit_array_end = .;
-  __init_array_start = .;
-  .init_array : { *(.init_array) }
-  __init_array_end = .;
-  __fini_array_start = .;
-  .fini_array : { *(.fini_array) }
-  __fini_array_end = .;
-
-  /* Adjust the address for the data segment.  We want to adjust up to
-     the same address within the page on the next page up.  */
-  . = ALIGN(0x100000) + (. & (0x100000 - 1));
-  .data    :
-  {
-    *(.data)
-    *(.gnu.linkonce.d*)
-    CONSTRUCTORS
-  }
-  .data1   : { *(.data1) }
-  .ctors         :
-  {
-    *(.ctors)
-  }
-  .dtors         :
-  {
-    *(.dtors)
-  }
-  .plt      : { *(.plt)	}
-  .got           : { *(.got.plt) *(.got) }
-  .dynamic       : { *(.dynamic) }
-  /* We want the small data sections together, so single-instruction offsets
-     can access them all, and initialized data all before uninitialized, so
-     we can shorten the on-disk segment size.  */
-  .sdata     : { *(.sdata) }
-  _edata  =  .;
-  PROVIDE (edata = .);
-  __bss_start = .;
-  .sbss      : { *(.sbss) *(.scommon) }
-  .bss       :
-  {
-   *(.dynbss)
-   *(.bss)
-   *(COMMON)
-  }
-  _end = . ;
-  PROVIDE (end = .);
-  /* Stabs debugging sections.  */
-  .stab 0 : { *(.stab) }
-  .stabstr 0 : { *(.stabstr) }
-  .stab.excl 0 : { *(.stab.excl) }
-  .stab.exclstr 0 : { *(.stab.exclstr) }
-  .stab.index 0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment 0 : { *(.comment) }
-  /* DWARF debug sections.
-     Symbols in the DWARF debugging sections are relative to the beginning
-     of the section so we begin them at 0.  */
-  /* DWARF 1 */
-  .debug          0 : { *(.debug) }
-  .line           0 : { *(.line) }
-  /* GNU DWARF 1 extensions */
-  .debug_srcinfo  0 : { *(.debug_srcinfo) }
-  .debug_sfnames  0 : { *(.debug_sfnames) }
-  /* DWARF 1.1 and DWARF 2 */
-  .debug_aranges  0 : { *(.debug_aranges) }
-  .debug_pubnames 0 : { *(.debug_pubnames) }
-  /* DWARF 2 */
-  .debug_info     0 : { *(.debug_info) }
-  .debug_abbrev   0 : { *(.debug_abbrev) }
-  .debug_line     0 : { *(.debug_line) }
-  .debug_frame    0 : { *(.debug_frame) }
-  .debug_str      0 : { *(.debug_str) }
-  .debug_loc      0 : { *(.debug_loc) }
-  .debug_macinfo  0 : { *(.debug_macinfo) }
-  /* SGI/MIPS DWARF 2 extensions */
-  .debug_weaknames 0 : { *(.debug_weaknames) }
-  .debug_funcnames 0 : { *(.debug_funcnames) }
-  .debug_typenames 0 : { *(.debug_typenames) }
-  .debug_varnames  0 : { *(.debug_varnames) }
-  /* These must appear regardless of  .  */
-}
diff --git a/linux-user/main.c b/linux-user/main.c
index e3a2374..164fdb9 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -53,7 +53,8 @@ asm(".globl __preinit_array_start\n"
     "__init_array_end:\n"
     "__fini_array_start:\n"
     "__fini_array_end:\n"
-    ".long 0\n");
+    ".long 0\n"
+    ".previous\n");
 #endif
 
 /* XXX: on x86 MAP_GROWSDOWN only works if ESP <= address + 32, so
diff --git a/osdep.h b/osdep.h
index bc513ad..0666e78 100644
--- a/osdep.h
+++ b/osdep.h
@@ -36,9 +36,9 @@
 #define inline always_inline
 
 #ifdef __i386__
-#define REGPARM(n) __attribute((regparm(n)))
+#define REGPARM __attribute((regparm(3)))
 #else
-#define REGPARM(n)
+#define REGPARM
 #endif
 
 #define qemu_printf printf
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 9b56f99..a18d3d0 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -166,21 +166,28 @@ Creative SoundBlaster 16 sound card
 @item
 ENSONIQ AudioPCI ES1370 sound card
 @item
+Intel 82801AA AC97 Audio compatible sound card
+@item
 Adlib(OPL2) - Yamaha YM3812 compatible chip
 @item
+Gravis Ultrasound GF1 sound card
+@item
 PCI UHCI USB controller and a virtual USB hub.
 @end itemize
 
 SMP is supported with up to 255 CPUs.
 
-Note that adlib is only available when QEMU was configured with
--enable-adlib
+Note that adlib, ac97 and gus are only available when QEMU was configured
+with --enable-adlib, --enable-ac97 or --enable-gus respectively.
 
 QEMU uses the PC BIOS from the Bochs project and the Plex86/Bochs LGPL
 VGA BIOS.
 
 QEMU uses YM3812 emulation by Tatsuyuki Satoh.
 
+QEMU uses GUS emulation(GUSEMU32 @url{http://www.deinmeister.de/gusemu/})
+by Tibor "TS" Schütz.
+
 @c man end
 
 @node pcsys_quickstart
@@ -234,7 +241,8 @@ Define a new drive. Valid options are:
 @table @code
 @item file=@var{file}
 This option defines which disk image (@pxref{disk_images}) to use with
-this drive.
+this drive. If the filename contains comma, you must double it
+(for instance, "file=my,,file" to use file "my,file").
 @item if=@var{interface}
 This option defines on which type on interface the drive is connected.
 Available types are: ide, scsi, sd, mtd, floppy, pflash.
@@ -333,10 +341,18 @@ available sound hardware.
 @example
 qemu -soundhw sb16,adlib hda
 qemu -soundhw es1370 hda
+qemu -soundhw ac97 hda
 qemu -soundhw all hda
 qemu -soundhw ?
 @end example
 
+Note that Linux's i810_audio OSS kernel (for AC97) module might
+require manually specifying clocking.
+
+@example
+modprobe i810_audio clocking=48000
+@end example
+
 @item -localtime
 Set the real time clock to local time (the default is to UTC
 time). This option is needed to have correct date in MS-DOS or
@@ -347,6 +363,17 @@ Set the initial date of the real time clock. Valid format for
 @var{date} are: @code{now} or @code{2006-06-17T16:01:21} or
 @code{2006-06-17}. The default value is @code{now}.
 
+@item -translation @var{setting1[,...]}
+Select dynamic translation options @var{setting}, @code{-translation ?}
+shows a list of settings. Valid settings are:
+
+@table @code
+@item @var{no-cache}
+This option disables caching of translated code. Is useful for low-level
+debugging of the emulated environment. This option incurs a massive
+slow-down in emulation speed.
+@end table
+
 @item -pidfile @var{file}
 Store the QEMU process PID in @var{file}. It is useful if you launch QEMU
 from a script.
@@ -492,6 +519,32 @@ Enable the USB driver (will be the default soon)
 
 @item -usbdevice @var{devname}
 Add the USB device @var{devname}. @xref{usb_devices}.
+
+@table @code
+
+@item mouse
+Virtual Mouse. This will override the PS/2 mouse emulation when activated.
+
+@item tablet
+Pointer device that uses absolute coordinates (like a touchscreen). This
+means qemu is able to report the mouse position without having to grab the
+mouse. Also overrides the PS/2 mouse emulation when activated.
+
+@item disk:file
+Mass storage device based on file
+
+@item host:bus.addr
+Pass through the host device identified by bus.addr (Linux only).
+
+@item host:vendor_id:product_id
+Pass through the host device identified by vendor_id:product_id (Linux only).
+
+@item serial:[vendorid=@var{vendor_id}][,productid=@var{product_id}]:@var{dev}
+Serial converter to host character device @var{dev}, see @code{-serial} for the
+available devices.
+
+@end table
+
 @end table
 
 Network options:
@@ -1527,27 +1580,37 @@ as necessary to connect multiple USB devices.
 USB devices can be connected with the @option{-usbdevice} commandline option
 or the @code{usb_add} monitor command.  Available devices are:
 
-@table @var
-@item @code{mouse}
+@table @code
+@item mouse
 Virtual Mouse.  This will override the PS/2 mouse emulation when activated.
-@item @code{tablet}
+@item tablet
 Pointer device that uses absolute coordinates (like a touchscreen).
 This means qemu is able to report the mouse position without having
 to grab the mouse.  Also overrides the PS/2 mouse emulation when activated.
-@item @code{disk:@var{file}}
+@item disk:@var{file}
 Mass storage device based on @var{file} (@pxref{disk_images})
-@item @code{host:@var{bus.addr}}
+@item host:@var{bus.addr}
 Pass through the host device identified by @var{bus.addr}
 (Linux only)
-@item @code{host:@var{vendor_id:product_id}}
+@item host:@var{vendor_id:product_id}
 Pass through the host device identified by @var{vendor_id:product_id}
 (Linux only)
-@item @code{wacom-tablet}
+@item wacom-tablet
 Virtual Wacom PenPartner tablet.  This device is similar to the @code{tablet}
 above but it can be used with the tslib library because in addition to touch
 coordinates it reports touch pressure.
-@item @code{keyboard}
+@item keyboard
 Standard USB keyboard.  Will override the PS/2 keyboard (if present).
+@item serial:[vendorid=@var{vendor_id}][,product_id=@var{product_id}]:@var{dev}
+Serial converter. This emulates an FTDI FT232BM chip connected to host character
+device @var{dev}. The available character devices are the same as for the
+@code{-serial} option. The @code{vendorid} and @code{productid} options can be
+used to override the default 0403:6001. For instance, 
+@example
+usb_add serial:productid=FA00:tcp:192.168.0.2:4444
+@end example
+will connect to tcp port 4444 of ip 192.168.0.2, and plug that to the virtual
+serial converter, faking a Matrix Orbital LCD Display (USB ID 0403:FA00).
 @end table
 
 @node host_usb_devices
diff --git a/s390-dis.c b/s390-dis.c
index 6e2558b..a447a19 100644
--- a/s390-dis.c
+++ b/s390-dis.c
@@ -1,23 +1,23 @@
 /* s390-dis.c -- Disassemble S390 instructions
-   Copyright 2000, 2001, 2002, 2003, 2005, 2007 Free Software Foundation, Inc.
+   Copyright 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
    Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
 
-   This file is part of the GNU opcodes library.
+   This file is part of GDB, GAS and the GNU binutils.
 
-   This library is free software; you can redistribute it and/or modify
+   This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 3, or (at your option)
-   any later version.
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
 
-   It is distributed in the hope that it will be useful, but WITHOUT
-   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
-   License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this file; see the file COPYING.  If not, write to the
-   Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
-   MA 02110-1301, USA.  */
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
 
 #include <stdio.h>
 #include "dis-asm.h"
@@ -397,25 +397,25 @@ print_insn_s390 (bfd_vma memaddr, struct disassemble_info *info)
     }
 }
 /* s390-opc.c -- S390 opcode list
-   Copyright 2000, 2001, 2003, 2007 Free Software Foundation, Inc.
+   Copyright 2000, 2001, 2003 Free Software Foundation, Inc.
    Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
 
-   This file is part of the GNU opcodes library.
+   This file is part of GDB, GAS, and the GNU binutils.
 
-   This library is free software; you can redistribute it and/or modify
+   This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 3, or (at your option)
-   any later version.
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
 
-   It is distributed in the hope that it will be useful, but WITHOUT
-   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
-   License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
-   along with this file; see the file COPYING.  If not, write to the
-   Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
-   MA 02110-1301, USA.  */
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+   02110-1301, USA.  */
 
 #include <stdio.h>
 
diff --git a/softmmu_header.h b/softmmu_header.h
index 80eefa8..51bd22d 100644
--- a/softmmu_header.h
+++ b/softmmu_header.h
@@ -70,15 +70,13 @@
 #define ADDR_READ addr_read
 #endif
 
-DATA_TYPE REGPARM(1) glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
                                                          int mmu_idx);
-void REGPARM(2) glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, DATA_TYPE v, int mmu_idx);
+void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr, DATA_TYPE v, int mmu_idx);
 
 #if (DATA_SIZE <= 4) && (TARGET_LONG_BITS == 32) && defined(__i386__) && \
     (ACCESS_TYPE < NB_MMU_MODES) && defined(ASM_SOFTMMU)
 
-#define CPU_TLB_ENTRY_BITS 4
-
 static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr)
 {
     int res;
@@ -92,9 +90,8 @@ static inline RES_TYPE glue(glue(ld, USUFFIX), MEMSUFFIX)(target_ulong ptr)
                   "cmpl (%%edx), %%eax\n"
                   "movl %1, %%eax\n"
                   "je 1f\n"
-                  "pushl %6\n"
+                  "movl %6, %%edx\n"
                   "call %7\n"
-                  "popl %%edx\n"
                   "movl %%eax, %0\n"
                   "jmp 2f\n"
                   "1:\n"
@@ -135,9 +132,8 @@ static inline int glue(glue(lds, SUFFIX), MEMSUFFIX)(target_ulong ptr)
                   "cmpl (%%edx), %%eax\n"
                   "movl %1, %%eax\n"
                   "je 1f\n"
-                  "pushl %6\n"
+                  "movl %6, %%edx\n"
                   "call %7\n"
-                  "popl %%edx\n"
 #if DATA_SIZE == 1
                   "movsbl %%al, %0\n"
 #elif DATA_SIZE == 2
@@ -189,9 +185,8 @@ static inline void glue(glue(st, SUFFIX), MEMSUFFIX)(target_ulong ptr, RES_TYPE
 #else
 #error unsupported size
 #endif
-                  "pushl %6\n"
+                  "movl %6, %%ecx\n"
                   "call %7\n"
-                  "popl %%eax\n"
                   "jmp 2f\n"
                   "1:\n"
                   "addl 8(%%edx), %%eax\n"
@@ -207,9 +202,11 @@ static inline void glue(glue(st, SUFFIX), MEMSUFFIX)(target_ulong ptr, RES_TYPE
                   "2:\n"
                   :
                   : "r" (ptr),
-/* NOTE: 'q' would be needed as constraint, but we could not use it
-   with T1 ! */
+#if DATA_SIZE == 1
+                  "q" (v),
+#else
                   "r" (v),
+#endif
                   "i" ((CPU_TLB_SIZE - 1) << CPU_TLB_ENTRY_BITS),
                   "i" (TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS),
                   "i" (TARGET_PAGE_MASK | (DATA_SIZE - 1)),
diff --git a/softmmu_template.h b/softmmu_template.h
index 45fcd4e..0a4bc7e 100644
--- a/softmmu_template.h
+++ b/softmmu_template.h
@@ -75,8 +75,8 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(target_phys_addr_t physaddr,
 }
 
 /* handle all cases except unaligned access which span two pages */
-DATA_TYPE REGPARM(1) glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
-                                                         int mmu_idx)
+DATA_TYPE REGPARM glue(glue(__ld, SUFFIX), MMUSUFFIX)(target_ulong addr,
+                                                      int mmu_idx)
 {
     DATA_TYPE res;
     int index;
@@ -209,9 +209,9 @@ static inline void glue(io_write, SUFFIX)(target_phys_addr_t physaddr,
 #endif
 }
 
-void REGPARM(2) glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr,
-                                                    DATA_TYPE val,
-                                                    int mmu_idx)
+void REGPARM glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr,
+                                                 DATA_TYPE val,
+                                                 int mmu_idx)
 {
     target_phys_addr_t physaddr;
     target_ulong tlb_addr;
diff --git a/target-cris/op.c b/target-cris/op.c
index 6e17719..3c50574 100644
--- a/target-cris/op.c
+++ b/target-cris/op.c
@@ -205,6 +205,7 @@ void OPPROTO op_ccs_lshift (void)
 	ccs = env->pregs[SR_CCS];
 	ccs = (ccs & 0xc0000000) | ((ccs << 12) >> 2);
 	env->pregs[SR_CCS] = ccs;
+	RETURN();
 }
 void OPPROTO op_ccs_rshift (void)
 {
@@ -214,6 +215,7 @@ void OPPROTO op_ccs_rshift (void)
 	ccs = env->pregs[SR_CCS];
 	ccs = (ccs & 0xc0000000) | (ccs >> 10);
 	env->pregs[SR_CCS] = ccs;
+	RETURN();
 }
 
 void OPPROTO op_setf (void)
diff --git a/target-cris/translate.c b/target-cris/translate.c
index 44ba804..a05d139 100644
--- a/target-cris/translate.c
+++ b/target-cris/translate.c
@@ -110,15 +110,6 @@ typedef struct DisasContext {
 	unsigned int mode;
 	unsigned int postinc;
 
-
-	struct
-	{
-		int op;
-		int size;
-		unsigned int mask;
-	} cc_state[3];
-	int cc_i;
-
 	int update_cc;
 	int cc_op;
 	int cc_size;
@@ -183,6 +174,10 @@ static void gen_vr_read(void) {
 	gen_op_movl_T0_im(32);
 }
 
+static void gen_movl_T0_p0(void) {
+	gen_op_movl_T0_im(0);
+}
+
 static void gen_ccs_read(void) {
 	gen_op_movl_T0_p13();
 }
@@ -209,7 +204,7 @@ static GenOpFunc *gen_movl_preg_T0[16] =
 };
 static GenOpFunc *gen_movl_T0_preg[16] =
 {
-	gen_op_movl_T0_p0,
+	gen_movl_T0_p0,
 	gen_vr_read,
 	gen_op_movl_T0_p2, gen_op_movl_T0_p3,
 	gen_op_movl_T0_p4, gen_op_movl_T0_p5,
@@ -345,6 +340,8 @@ static void cris_cc_mask(DisasContext *dc, unsigned int mask)
 {
 	uint32_t ovl;
 
+	/* Check if we need to evaluate the condition codes due to 
+	   CC overlaying.  */
 	ovl = (dc->cc_mask ^ mask) & ~mask;
 	if (ovl) {
 		/* TODO: optimize this case. It trigs all the time.  */
@@ -987,7 +984,6 @@ static unsigned int dec_btstq(DisasContext *dc)
 {
 	dc->op1 = EXTRACT_FIELD(dc->ir, 0, 4);
 	DIS(fprintf (logfile, "btstq %u, $r%d\n", dc->op1, dc->op2));
-	cris_evaluate_flags(dc);
 	cris_cc_mask(dc, CC_MASK_NZ);
 	gen_movl_T0_reg[dc->op2]();
 	gen_op_movl_T1_im(dc->op1);
@@ -1333,7 +1329,6 @@ static unsigned int dec_btst_r(DisasContext *dc)
 {
 	DIS(fprintf (logfile, "btst $r%u, $r%u\n",
 		    dc->op1, dc->op2));
-	cris_evaluate_flags(dc);
 	cris_cc_mask(dc, CC_MASK_NZ);
 	dec_prep_alu_r(dc, dc->op1, dc->op2, 4, 0);
 	crisv32_alu_op(dc, CC_OP_BTST, dc->op2, 4);
@@ -1518,8 +1513,14 @@ static unsigned int dec_move_pr(DisasContext *dc)
 {
 	DIS(fprintf (logfile, "move $p%u, $r%u\n", dc->op1, dc->op2));
 	cris_cc_mask(dc, 0);
-	gen_movl_T0_preg[dc->op2]();
-	gen_op_movl_T1_T0();
+	/* Support register 0 is hardwired to zero. 
+	   Treat it specially. */
+	if (dc->op2 == 0)
+		gen_op_movl_T1_im(0);
+	else {
+		gen_movl_T0_preg[dc->op2]();
+		gen_op_movl_T1_T0();
+	}
 	crisv32_alu_op(dc, CC_OP_MOVE, dc->op1, preg_sizes[dc->op2]);
 	return 2;
 }
@@ -1846,13 +1847,21 @@ static unsigned int dec_move_pm(DisasContext *dc)
 
 	memsize = preg_sizes[dc->op2];
 
-	DIS(fprintf (logfile, "move.%d $p%u, [$r%u%s\n",
-		     memsize, dc->op2, dc->op1, dc->postinc ? "+]" : "]"));
+	DIS(fprintf (logfile, "move.%c $p%u, [$r%u%s\n",
+		     memsize_char(memsize), 
+		     dc->op2, dc->op1, dc->postinc ? "+]" : "]"));
 
 	cris_cc_mask(dc, 0);
-	/* prepare store.  */
-	gen_movl_T0_preg[dc->op2]();
-	gen_op_movl_T1_T0();
+	/* prepare store. Address in T0, value in T1.  */
+	/* Support register 0 is hardwired to zero. 
+	   Treat it specially. */
+	if (dc->op2 == 0)
+		gen_op_movl_T1_im(0);
+	else
+	{
+		gen_movl_T0_preg[dc->op2]();
+		gen_op_movl_T1_T0();
+	}
 	gen_movl_T0_reg[dc->op1]();
 	gen_store_T0_T1(dc, memsize);
 	if (dc->postinc)
diff --git a/target-mips/exec.h b/target-mips/exec.h
index 35e71e4..b612cec 100644
--- a/target-mips/exec.h
+++ b/target-mips/exec.h
@@ -258,7 +258,7 @@ static always_inline void compute_hflags(CPUState *env)
     if (env->CP0_Status & (1 << CP0St_FR))
         env->hflags |= MIPS_HFLAG_F64;
     if (env->insn_flags & ISA_MIPS32R2) {
-        if (env->fpu->fcr0 & FCR0_F64)
+        if (env->fpu->fcr0 & (1 << FCR0_F64))
             env->hflags |= MIPS_HFLAG_COP1X;
     } else if (env->insn_flags & ISA_MIPS32) {
         if (env->hflags & MIPS_HFLAG_64)
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 93ba79c..0735356 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -1250,8 +1250,8 @@ void do_cmp_d_ ## op (long cc)                 \
 void do_cmpabs_d_ ## op (long cc)              \
 {                                              \
     int c;                                     \
-    FDT0 = float64_chs(FDT0);                  \
-    FDT1 = float64_chs(FDT1);                  \
+    FDT0 = float64_abs(FDT0);                  \
+    FDT1 = float64_abs(FDT1);                  \
     c = cond;                                  \
     update_fcr31();                            \
     if (c)                                     \
diff --git a/tests/Makefile b/tests/Makefile
index f8bad32..1775be8 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -31,12 +31,12 @@ test_path: test_path.c
 # i386/x86_64 emulation test (test various opcodes) */
 test-i386: test-i386.c test-i386-code16.S test-i386-vm86.S \
            test-i386.h test-i386-shift.h test-i386-muldiv.h
-	$(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ \
+	$(CC) -m32 $(CFLAGS) $(LDFLAGS) -static -o $@ \
               test-i386.c test-i386-code16.S test-i386-vm86.S -lm
 
 test-x86_64: test-i386.c \
            test-i386.h test-i386-shift.h test-i386-muldiv.h
-	$(CC) $(CFLAGS) $(LDFLAGS) -static -o $@ test-i386.c -lm
+	$(CC) -m64 $(CFLAGS) $(LDFLAGS) -static -o $@ test-i386.c -lm
 
 ifeq ($(ARCH),i386)
 test: test-i386
diff --git a/tests/test-i386-code16.S b/tests/test-i386-code16.S
index 8f51052..816c24b 100644
--- a/tests/test-i386-code16.S
+++ b/tests/test-i386-code16.S
@@ -44,9 +44,9 @@ code16_func3 = . - code16_start
         ljmp $CS_SEG, $(myjmp1 - code16_start)
 myjmp1_next:
 
-        cs lcall myfunc2_addr - code16_start
+        cs lcall *myfunc2_addr - code16_start
 
-        cs ljmp myjmp2_addr - code16_start
+        cs ljmp *myjmp2_addr - code16_start
 myjmp2_next:
 
         data32 lret
diff --git a/tests/test-i386.c b/tests/test-i386.c
index 1cc34e3..91769bf 100644
--- a/tests/test-i386.c
+++ b/tests/test-i386.c
@@ -1065,7 +1065,7 @@ void test_bcd(void)
     op1 = i2l(0xfbca7654);\
     asm(#op " %" size "0, %" size "1" \
         : "=q" (op0), opconst (op1) \
-        : "0" (op0), "1" (op1));\
+        : "0" (op0));\
     printf("%-10s A=" FMTLX " B=" FMTLX "\n",\
            #op, op0, op1);\
 }
@@ -1078,7 +1078,7 @@ void test_bcd(void)
     op2 = i2l(eax);\
     asm(#op " %" size "0, %" size "1" \
         : "=q" (op0), opconst (op1) \
-        : "0" (op0), "1" (op1), "a" (op2));\
+        : "0" (op0), "a" (op2));\
     printf("%-10s EAX=" FMTLX " A=" FMTLX " C=" FMTLX "\n",\
            #op, op2, op0, op1);\
 }
@@ -1086,25 +1086,25 @@ void test_bcd(void)
 void test_xchg(void)
 {
 #if defined(__x86_64__)
-    TEST_XCHG(xchgq, "", "=q");
+    TEST_XCHG(xchgq, "", "+q");
 #endif
-    TEST_XCHG(xchgl, "k", "=q");
-    TEST_XCHG(xchgw, "w", "=q");
-    TEST_XCHG(xchgb, "b", "=q");
+    TEST_XCHG(xchgl, "k", "+q");
+    TEST_XCHG(xchgw, "w", "+q");
+    TEST_XCHG(xchgb, "b", "+q");
 
 #if defined(__x86_64__)
     TEST_XCHG(xchgq, "", "=m");
 #endif
-    TEST_XCHG(xchgl, "k", "=m");
-    TEST_XCHG(xchgw, "w", "=m");
-    TEST_XCHG(xchgb, "b", "=m");
+    TEST_XCHG(xchgl, "k", "+m");
+    TEST_XCHG(xchgw, "w", "+m");
+    TEST_XCHG(xchgb, "b", "+m");
 
 #if defined(__x86_64__)
-    TEST_XCHG(xaddq, "", "=q");
+    TEST_XCHG(xaddq, "", "+q");
 #endif
-    TEST_XCHG(xaddl, "k", "=q");
-    TEST_XCHG(xaddw, "w", "=q");
-    TEST_XCHG(xaddb, "b", "=q");
+    TEST_XCHG(xaddl, "k", "+q");
+    TEST_XCHG(xaddw, "w", "+q");
+    TEST_XCHG(xaddb, "b", "+q");
 
     {
         int res;
@@ -1114,39 +1114,39 @@ void test_xchg(void)
     }
 
 #if defined(__x86_64__)
-    TEST_XCHG(xaddq, "", "=m");
+    TEST_XCHG(xaddq, "", "+m");
 #endif
-    TEST_XCHG(xaddl, "k", "=m");
-    TEST_XCHG(xaddw, "w", "=m");
-    TEST_XCHG(xaddb, "b", "=m");
+    TEST_XCHG(xaddl, "k", "+m");
+    TEST_XCHG(xaddw, "w", "+m");
+    TEST_XCHG(xaddb, "b", "+m");
 
 #if defined(__x86_64__)
-    TEST_CMPXCHG(cmpxchgq, "", "=q", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgq, "", "+q", 0xfbca7654);
 #endif
-    TEST_CMPXCHG(cmpxchgl, "k", "=q", 0xfbca7654);
-    TEST_CMPXCHG(cmpxchgw, "w", "=q", 0xfbca7654);
-    TEST_CMPXCHG(cmpxchgb, "b", "=q", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgl, "k", "+q", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgw, "w", "+q", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgb, "b", "+q", 0xfbca7654);
 
 #if defined(__x86_64__)
-    TEST_CMPXCHG(cmpxchgq, "", "=q", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgq, "", "+q", 0xfffefdfc);
 #endif
-    TEST_CMPXCHG(cmpxchgl, "k", "=q", 0xfffefdfc);
-    TEST_CMPXCHG(cmpxchgw, "w", "=q", 0xfffefdfc);
-    TEST_CMPXCHG(cmpxchgb, "b", "=q", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgl, "k", "+q", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgw, "w", "+q", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgb, "b", "+q", 0xfffefdfc);
 
 #if defined(__x86_64__)
-    TEST_CMPXCHG(cmpxchgq, "", "=m", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgq, "", "+m", 0xfbca7654);
 #endif
-    TEST_CMPXCHG(cmpxchgl, "k", "=m", 0xfbca7654);
-    TEST_CMPXCHG(cmpxchgw, "w", "=m", 0xfbca7654);
-    TEST_CMPXCHG(cmpxchgb, "b", "=m", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgl, "k", "+m", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgw, "w", "+m", 0xfbca7654);
+    TEST_CMPXCHG(cmpxchgb, "b", "+m", 0xfbca7654);
 
 #if defined(__x86_64__)
-    TEST_CMPXCHG(cmpxchgq, "", "=m", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgq, "", "+m", 0xfffefdfc);
 #endif
-    TEST_CMPXCHG(cmpxchgl, "k", "=m", 0xfffefdfc);
-    TEST_CMPXCHG(cmpxchgw, "w", "=m", 0xfffefdfc);
-    TEST_CMPXCHG(cmpxchgb, "b", "=m", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgl, "k", "+m", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgw, "w", "+m", 0xfffefdfc);
+    TEST_CMPXCHG(cmpxchgb, "b", "+m", 0xfffefdfc);
 
     {
         uint64_t op0, op1, op2;
@@ -1369,9 +1369,13 @@ void test_misc(void)
     printf("xlat: EAX=" FMTLX "\n", res);
 
 #if defined(__x86_64__)
+#if 0
     {
+        /* XXX: see if Intel Core2 and AMD64 behavior really
+           differ. Here we implemented the Intel way which is not
+           compatible yet with QEMU. */
         static struct __attribute__((packed)) {
-            uint32_t offset;
+            uint64_t offset;
             uint16_t seg;
         } desc;
         long cs_sel;
@@ -1384,27 +1388,27 @@ void test_misc(void)
                       : "r" (cs_sel) : "memory", "cc");
         printf("func_lret=" FMTLX "\n", res);
 
-        /* NOTE: we assume that &func_lret < 4GB */
         desc.offset = (long)&func_lret;
         desc.seg = cs_sel;
 
         asm volatile ("xor %%rax, %%rax\n"
-                      "rex64 lcall %1\n"
+                      "rex64 lcall *(%%rcx)\n"
                       : "=a" (res)
-                      : "m" (desc)
+                      : "c" (&desc)
                       : "memory", "cc");
         printf("func_lret2=" FMTLX "\n", res);
 
         asm volatile ("push %2\n"
                       "mov $ 1f, %%rax\n"
                       "push %%rax\n"
-                      "ljmp %1\n"
+                      "rex64 ljmp *(%%rcx)\n"
                       "1:\n"
                       : "=a" (res)
-                      : "m" (desc), "b" (cs_sel)
+                      : "c" (&desc), "b" (cs_sel)
                       : "memory", "cc");
         printf("func_lret3=" FMTLX "\n", res);
     }
+#endif
 #else
     asm volatile ("push %%cs ; call %1"
                   : "=a" (res)
@@ -2029,7 +2033,7 @@ static void test_enter(void)
 #ifdef TEST_SSE
 
 typedef int __m64 __attribute__ ((__mode__ (__V2SI__)));
-typedef int __m128 __attribute__ ((__mode__(__V4SF__)));
+typedef float __m128 __attribute__ ((__mode__(__V4SF__)));
 
 typedef union {
     double d[2];
@@ -2619,6 +2623,21 @@ void test_conv(void)
 #if defined(__x86_64__)
     TEST_CONV_RAX_RDX(cqo);
 #endif
+
+    {
+        unsigned long a, r;
+        a = i2l(0x12345678);
+        asm volatile("bswapl %k0" : "=r" (r) : "0" (a));
+        printf("%-10s: A=" FMTLX " R=" FMTLX "\n", "bswapl", a, r);
+    }
+#if defined(__x86_64__)
+    {
+        unsigned long a, r;
+        a = i2l(0x12345678);
+        asm volatile("bswapq %0" : "=r" (r) : "0" (a));
+        printf("%-10s: A=" FMTLX " R=" FMTLX "\n", "bswapq", a, r);
+    }
+#endif
 }
 
 extern void *__start_initcall;
@@ -2653,8 +2672,8 @@ int main(int argc, char **argv)
 #ifdef TEST_VM86
     test_vm86();
 #endif
-    test_exceptions();
 #if !defined(__x86_64__)
+    test_exceptions();
     test_self_modifying_code();
     test_single_step();
 #endif
diff --git a/vl.c b/vl.c
index a04edbc..8c63ad9 100644
--- a/vl.c
+++ b/vl.c
@@ -231,12 +231,17 @@ unsigned int nb_prom_envs = 0;
 const char *prom_envs[MAX_PROM_ENVS];
 #endif
 int nb_drives_opt;
-char drives_opt[MAX_DRIVES][1024];
+struct drive_opt {
+    const char *file;
+    char opt[1024];
+} drives_opt[MAX_DRIVES];
 
 static CPUState *cur_cpu;
 static CPUState *next_cpu;
 static int event_pending = 1;
 
+extern char *logfilename;
+
 #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR)
 
 /***********************************************************/
@@ -2047,6 +2052,20 @@ static void fd_chr_update_read_handler(CharDriverState *chr)
     }
 }
 
+static void fd_chr_close(struct CharDriverState *chr)
+{
+    FDCharDriver *s = chr->opaque;
+
+    if (s->fd_in >= 0) {
+        if (nographic && s->fd_in == 0) {
+        } else {
+            qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL);
+        }
+    }
+
+    qemu_free(s);
+}
+
 /* open a character device to a unix fd */
 static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
 {
@@ -2066,6 +2085,7 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
     chr->opaque = s;
     chr->chr_write = fd_chr_write;
     chr->chr_update_read_handler = fd_chr_update_read_handler;
+    chr->chr_close = fd_chr_close;
 
     qemu_chr_reset(chr);
 
@@ -2152,6 +2172,7 @@ static void stdio_read(void *opaque)
 /* init terminal so that we can grab keys */
 static struct termios oldtty;
 static int old_fd0_flags;
+static int term_atexit_done;
 
 static void term_exit(void)
 {
@@ -2181,11 +2202,20 @@ static void term_init(void)
 
     tcsetattr (0, TCSANOW, &tty);
 
-    atexit(term_exit);
+    if (!term_atexit_done++)
+        atexit(term_exit);
 
     fcntl(0, F_SETFL, O_NONBLOCK);
 }
 
+static void qemu_chr_close_stdio(struct CharDriverState *chr)
+{
+    term_exit();
+    stdio_nb_clients--;
+    qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL);
+    fd_chr_close(chr);
+}
+
 static CharDriverState *qemu_chr_open_stdio(void)
 {
     CharDriverState *chr;
@@ -2193,6 +2223,7 @@ static CharDriverState *qemu_chr_open_stdio(void)
     if (stdio_nb_clients >= STDIO_MAX_CLIENTS)
         return NULL;
     chr = qemu_chr_open_fd(0, 1);
+    chr->chr_close = qemu_chr_close_stdio;
     qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr);
     stdio_nb_clients++;
     term_init();
@@ -2237,45 +2268,33 @@ static void tty_serial_init(int fd, int speed,
 #endif
     tcgetattr (fd, &tty);
 
-    switch(speed) {
-    case 50:
+#define MARGIN 1.1
+    if (speed <= 50 * MARGIN)
         spd = B50;
-        break;
-    case 75:
+    else if (speed <= 75 * MARGIN)
         spd = B75;
-        break;
-    case 300:
+    else if (speed <= 300 * MARGIN)
         spd = B300;
-        break;
-    case 600:
+    else if (speed <= 600 * MARGIN)
         spd = B600;
-        break;
-    case 1200:
+    else if (speed <= 1200 * MARGIN)
         spd = B1200;
-        break;
-    case 2400:
+    else if (speed <= 2400 * MARGIN)
         spd = B2400;
-        break;
-    case 4800:
+    else if (speed <= 4800 * MARGIN)
         spd = B4800;
-        break;
-    case 9600:
+    else if (speed <= 9600 * MARGIN)
         spd = B9600;
-        break;
-    case 19200:
+    else if (speed <= 19200 * MARGIN)
         spd = B19200;
-        break;
-    case 38400:
+    else if (speed <= 38400 * MARGIN)
         spd = B38400;
-        break;
-    case 57600:
+    else if (speed <= 57600 * MARGIN)
         spd = B57600;
-        break;
-    default:
-    case 115200:
+    else if (speed <= 115200 * MARGIN)
+        spd = B115200;
+    else
         spd = B115200;
-        break;
-    }
 
     cfsetispeed(&tty, spd);
     cfsetospeed(&tty, spd);
@@ -3427,6 +3446,7 @@ void qemu_chr_close(CharDriverState *chr)
 {
     if (chr->chr_close)
         chr->chr_close(chr);
+    qemu_free(chr);
 }
 
 /***********************************************************/
@@ -3772,27 +3792,35 @@ static void net_slirp_redir(const char *redir_str)
 
 char smb_dir[1024];
 
-static void smb_exit(void)
+static void erase_dir(char *dir_name)
 {
     DIR *d;
     struct dirent *de;
     char filename[1024];
 
     /* erase all the files in the directory */
-    d = opendir(smb_dir);
-    for(;;) {
-        de = readdir(d);
-        if (!de)
-            break;
-        if (strcmp(de->d_name, ".") != 0 &&
-            strcmp(de->d_name, "..") != 0) {
-            snprintf(filename, sizeof(filename), "%s/%s",
-                     smb_dir, de->d_name);
-            unlink(filename);
+    if ((d = opendir(dir_name)) != 0) {
+        for(;;) {
+            de = readdir(d);
+            if (!de)
+                break;
+            if (strcmp(de->d_name, ".") != 0 &&
+                strcmp(de->d_name, "..") != 0) {
+                snprintf(filename, sizeof(filename), "%s/%s",
+                         smb_dir, de->d_name);
+                if (unlink(filename) != 0)  /* is it a directory? */
+                    erase_dir(filename);
+            }
         }
+        closedir(d);
+        rmdir(dir_name);
     }
-    closedir(d);
-    rmdir(smb_dir);
+}
+
+/* automatic user mode samba server configuration */
+static void smb_exit(void)
+{
+    erase_dir(smb_dir);
 }
 
 /* automatic user mode samba server configuration */
@@ -4581,24 +4609,33 @@ static int net_socket_mcast_init(VLANState *vlan, const char *host_str)
 
 }
 
-static const char *get_word(char *buf, int buf_size, const char *p)
+static const char *get_opt_name(char *buf, int buf_size, const char *p)
+{
+    char *q;
+
+    q = buf;
+    while (*p != '\0' && *p != '=') {
+        if (q && (q - buf) < buf_size - 1)
+            *q++ = *p;
+        p++;
+    }
+    if (q)
+        *q = '\0';
+
+    return p;
+}
+
+static const char *get_opt_value(char *buf, int buf_size, const char *p)
 {
     char *q;
-    int substring;
 
-    substring = 0;
     q = buf;
     while (*p != '\0') {
-        if (*p == '\\') {
-            p++;
-            if (*p == '\0')
+        if (*p == ',') {
+            if (*(p + 1) != ',')
                 break;
-        } else if (*p == '\"') {
-            substring = !substring;
             p++;
-            continue;
-        } else if (!substring && (*p == ',' || *p == '='))
-            break;
+        }
         if (q && (q - buf) < buf_size - 1)
             *q++ = *p;
         p++;
@@ -4617,15 +4654,15 @@ static int get_param_value(char *buf, int buf_size,
 
     p = str;
     for(;;) {
-        p = get_word(option, sizeof(option), p);
+        p = get_opt_name(option, sizeof(option), p);
         if (*p != '=')
             break;
         p++;
         if (!strcmp(tag, option)) {
-            (void)get_word(buf, buf_size, p);
+            (void)get_opt_value(buf, buf_size, p);
             return strlen(buf);
         } else {
-            p = get_word(NULL, 0, p);
+            p = get_opt_value(NULL, 0, p);
         }
         if (*p != ',')
             break;
@@ -4642,7 +4679,7 @@ static int check_params(char *buf, int buf_size,
 
     p = str;
     for(;;) {
-        p = get_word(buf, buf_size, p);
+        p = get_opt_name(buf, buf_size, p);
         if (*p != '=')
             return -1;
         p++;
@@ -4651,7 +4688,7 @@ static int check_params(char *buf, int buf_size,
                 break;
         if (params[i] == NULL)
             return -1;
-        p = get_word(NULL, 0, p);
+        p = get_opt_value(NULL, 0, p);
         if (*p != ',')
             break;
         p++;
@@ -4810,18 +4847,18 @@ void do_info_network(void)
     }
 }
 
-#define HD_ALIAS "file=\"%s\",index=%d,media=disk"
+#define HD_ALIAS "index=%d,media=disk"
 #ifdef TARGET_PPC
 #define CDROM_ALIAS "index=1,media=cdrom"
 #else
 #define CDROM_ALIAS "index=2,media=cdrom"
 #endif
 #define FD_ALIAS "index=%d,if=floppy"
-#define PFLASH_ALIAS "file=\"%s\",if=pflash"
-#define MTD_ALIAS "file=\"%s\",if=mtd"
+#define PFLASH_ALIAS "if=pflash"
+#define MTD_ALIAS "if=mtd"
 #define SD_ALIAS "index=0,if=sd"
 
-static int drive_add(const char *fmt, ...)
+static int drive_add(const char *file, const char *fmt, ...)
 {
     va_list ap;
 
@@ -4830,8 +4867,10 @@ static int drive_add(const char *fmt, ...)
         exit(1);
     }
 
+    drives_opt[nb_drives_opt].file = file;
     va_start(ap, fmt);
-    vsnprintf(drives_opt[nb_drives_opt], sizeof(drives_opt[0]), fmt, ap);
+    vsnprintf(drives_opt[nb_drives_opt].opt,
+              sizeof(drives_opt[0].opt), fmt, ap);
     va_end(ap);
 
     return nb_drives_opt++;
@@ -4866,7 +4905,8 @@ int drive_get_max_bus(BlockInterfaceType type)
     return max_bus;
 }
 
-static int drive_init(const char *str, int snapshot, QEMUMachine *machine)
+static int drive_init(struct drive_opt *arg, int snapshot,
+                      QEMUMachine *machine)
 {
     char buf[128];
     char file[1024];
@@ -4881,6 +4921,7 @@ static int drive_init(const char *str, int snapshot, QEMUMachine *machine)
     int index;
     int cache;
     int bdrv_flags;
+    char *str = arg->opt;
     char *params[] = { "bus", "unit", "if", "index", "cyls", "heads",
                        "secs", "trans", "media", "snapshot", "file",
                        "cache", NULL };
@@ -5051,7 +5092,10 @@ static int drive_init(const char *str, int snapshot, QEMUMachine *machine)
         }
     }
 
-    get_param_value(file, sizeof(file), "file", str);
+    if (arg->file == NULL)
+        get_param_value(file, sizeof(file), "file", str);
+    else
+        pstrcpy(file, sizeof(file), arg->file);
 
     /* compute bus and unit according index */
 
@@ -5196,6 +5240,8 @@ static int usb_device_add(const char *devname)
         dev = usb_msd_init(p);
     } else if (!strcmp(devname, "wacom-tablet")) {
         dev = usb_wacom_init();
+    } else if (strstart(devname, "serial:", &p)) {
+        dev = usb_serial_init(p);
     } else {
         return -1;
     }
@@ -7617,6 +7663,9 @@ static void help(int exitcode)
 #endif
            "-clock          force the use of the given methods for timer alarm.\n"
            "                To see what timers are available use -clock help\n"
+           "-startdate      select initial date of the Qemu clock\n"
+           "-translation setting1,... configures code translation\n"
+           "                (use -translation ? for a list of settings)\n"
            "\n"
            "During emulation, the following keys are useful:\n"
            "ctrl-alt-f      toggle full screen\n"
@@ -7632,7 +7681,7 @@ static void help(int exitcode)
            DEFAULT_NETWORK_DOWN_SCRIPT,
 #endif
            DEFAULT_GDBSTUB_PORT,
-           "/tmp/qemu.log");
+           logfilename);
     exit(exitcode);
 }
 
@@ -7719,6 +7768,7 @@ enum {
     QEMU_OPTION_old_param,
     QEMU_OPTION_clock,
     QEMU_OPTION_startdate,
+    QEMU_OPTION_translation,
 };
 
 typedef struct QEMUOption {
@@ -7827,6 +7877,7 @@ const QEMUOption qemu_options[] = {
 #endif
     { "clock", HAS_ARG, QEMU_OPTION_clock },
     { "startdate", HAS_ARG, QEMU_OPTION_startdate },
+    { "translation", HAS_ARG, QEMU_OPTION_translation },
     { NULL },
 };
 
@@ -7973,6 +8024,16 @@ struct soundhw soundhw[] = {
     },
 #endif
 
+#ifdef CONFIG_AC97
+    {
+        "ac97",
+        "Intel 82801AA AC97 Audio",
+        0,
+        0,
+        { .init_pci = ac97_init }
+    },
+#endif
+
     {
         "es1370",
         "ENSONIQ AudioPCI ES1370",
@@ -8163,7 +8224,7 @@ int main(int argc, char **argv)
             break;
         r = argv[optind];
         if (r[0] != '-') {
-	    hda_index = drive_add(HD_ALIAS, argv[optind++], 0);
+	    hda_index = drive_add(argv[optind++], HD_ALIAS, 0);
         } else {
             const QEMUOption *popt;
 
@@ -8224,11 +8285,11 @@ int main(int argc, char **argv)
                 break;
             case QEMU_OPTION_hda:
                 if (cyls == 0)
-                    hda_index = drive_add(HD_ALIAS, optarg, 0);
+                    hda_index = drive_add(optarg, HD_ALIAS, 0);
                 else
-                    hda_index = drive_add(HD_ALIAS
+                    hda_index = drive_add(optarg, HD_ALIAS
 			     ",cyls=%d,heads=%d,secs=%d%s",
-                             optarg, 0, cyls, heads, secs,
+                             0, cyls, heads, secs,
                              translation == BIOS_ATA_TRANSLATION_LBA ?
                                  ",trans=lba" :
                              translation == BIOS_ATA_TRANSLATION_NONE ?
@@ -8237,19 +8298,19 @@ int main(int argc, char **argv)
             case QEMU_OPTION_hdb:
             case QEMU_OPTION_hdc:
             case QEMU_OPTION_hdd:
-		drive_add(HD_ALIAS, optarg, popt->index - QEMU_OPTION_hda);
+                drive_add(optarg, HD_ALIAS, popt->index - QEMU_OPTION_hda);
                 break;
             case QEMU_OPTION_drive:
-                drive_add("%s", optarg);
+                drive_add(NULL, "%s", optarg);
 	        break;
             case QEMU_OPTION_mtdblock:
-	        drive_add(MTD_ALIAS, optarg);
+                drive_add(optarg, MTD_ALIAS);
                 break;
             case QEMU_OPTION_sd:
-                drive_add("file=\"%s\"," SD_ALIAS, optarg);
+                drive_add(optarg, SD_ALIAS);
                 break;
             case QEMU_OPTION_pflash:
-	        drive_add(PFLASH_ALIAS, optarg);
+                drive_add(optarg, PFLASH_ALIAS);
                 break;
             case QEMU_OPTION_snapshot:
                 snapshot = 1;
@@ -8289,12 +8350,10 @@ int main(int argc, char **argv)
                         exit(1);
                     }
 		    if (hda_index != -1)
-		        snprintf(drives_opt[hda_index] +
-			         strlen(drives_opt[hda_index]),
-			         sizeof(drives_opt[0]) -
-				 strlen(drives_opt[hda_index]),
-		                 ",cyls=%d,heads=%d,secs=%d%s",
-			         cyls, heads, secs,
+                        snprintf(drives_opt[hda_index].opt,
+                                 sizeof(drives_opt[hda_index].opt),
+                                 HD_ALIAS ",cyls=%d,heads=%d,secs=%d%s",
+                                 0, cyls, heads, secs,
 			         translation == BIOS_ATA_TRANSLATION_LBA ?
 			     	    ",trans=lba" :
 			         translation == BIOS_ATA_TRANSLATION_NONE ?
@@ -8317,7 +8376,7 @@ int main(int argc, char **argv)
                 kernel_cmdline = optarg;
                 break;
             case QEMU_OPTION_cdrom:
-		drive_add("file=\"%s\"," CDROM_ALIAS, optarg);
+                drive_add(optarg, CDROM_ALIAS);
                 break;
             case QEMU_OPTION_boot:
                 boot_devices = optarg;
@@ -8352,8 +8411,7 @@ int main(int argc, char **argv)
                 break;
             case QEMU_OPTION_fda:
             case QEMU_OPTION_fdb:
-		drive_add("file=\"%s\"," FD_ALIAS, optarg,
-		          popt->index - QEMU_OPTION_fda);
+                drive_add(optarg, FD_ALIAS, popt->index - QEMU_OPTION_fda);
                 break;
 #ifdef TARGET_I386
             case QEMU_OPTION_no_fd_bootchk:
@@ -8621,6 +8679,7 @@ int main(int argc, char **argv)
 #ifdef TARGET_ARM
             case QEMU_OPTION_old_param:
                 old_param = 1;
+                break;
 #endif
             case QEMU_OPTION_clock:
                 configure_alarms(optarg);
@@ -8661,6 +8720,22 @@ int main(int argc, char **argv)
                     }
                 }
                 break;
+            case QEMU_OPTION_translation:
+                {
+                    int mask;
+                    CPUTranslationSetting *setting;
+
+                    mask = cpu_str_to_translation_mask(optarg);
+                    if (!mask) {
+                        printf("Translation settings (comma separated):\n");
+                        for(setting = cpu_translation_settings; setting->mask != 0; setting++) {
+                            printf("%-10s %s\n", setting->name, setting->help);
+                    }
+                    exit(1);
+                    }
+                    cpu_set_translation_settings(mask);
+                }
+                break;
             }
         }
     }
@@ -8821,22 +8896,22 @@ int main(int argc, char **argv)
     /* we always create the cdrom drive, even if no disk is there */
 
     if (nb_drives_opt < MAX_DRIVES)
-        drive_add(CDROM_ALIAS);
+        drive_add(NULL, CDROM_ALIAS);
 
     /* we always create at least one floppy */
 
     if (nb_drives_opt < MAX_DRIVES)
-        drive_add(FD_ALIAS, 0);
+        drive_add(NULL, FD_ALIAS, 0);
 
     /* we always create one sd slot, even if no card is in it */
 
     if (nb_drives_opt < MAX_DRIVES)
-        drive_add(SD_ALIAS);
+        drive_add(NULL, SD_ALIAS);
 
     /* open the virtual block devices */
 
     for(i = 0; i < nb_drives_opt; i++)
-        if (drive_init(drives_opt[i], snapshot, machine) == -1)
+        if (drive_init(&drives_opt[i], snapshot, machine) == -1)
 	    exit(1);
 
     register_savevm("timer", 0, 2, timer_save, timer_load, NULL);
diff --git a/vnc.c b/vnc.c
index 9fb8c85..64889de 100644
--- a/vnc.c
+++ b/vnc.c
@@ -506,6 +506,8 @@ static void vnc_update_client(void *opaque)
 	int saved_offset;
 	int has_dirty = 0;
 
+        vga_hw_update();
+
         vnc_set_bits(width_mask, (vs->width / 16), VNC_DIRTY_WORDS);
 
 	/* Walk through the dirty map and eliminate tiles that
@@ -580,22 +582,11 @@ static void vnc_update_client(void *opaque)
 	vnc_flush(vs);
 
     }
-    qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
-}
 
-static void vnc_timer_init(VncState *vs)
-{
-    if (vs->timer == NULL) {
-	vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
-	qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock));
+    if (vs->csock != -1) {
+        qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
     }
-}
 
-static void vnc_dpy_refresh(DisplayState *ds)
-{
-    VncState *vs = ds->opaque;
-    vnc_timer_init(vs);
-    vga_hw_update();
 }
 
 static int vnc_listen_poll(void *opaque)
@@ -1913,6 +1904,9 @@ static void vnc_listen_read(void *opaque)
     struct sockaddr_in addr;
     socklen_t addrlen = sizeof(addr);
 
+    /* Catch-up */
+    vga_hw_update();
+
     vs->csock = accept(vs->lsock, (struct sockaddr *)&addr, &addrlen);
     if (vs->csock != -1) {
 	VNC_DEBUG("New client on socket %d\n", vs->csock);
@@ -1926,6 +1920,7 @@ static void vnc_listen_read(void *opaque)
 	vs->has_resize = 0;
 	vs->has_hextile = 0;
 	vs->ds->dpy_copy = NULL;
+        vnc_update_client(vs);
     }
 }
 
@@ -1959,10 +1954,12 @@ void vnc_display_init(DisplayState *ds)
     if (!vs->kbd_layout)
 	exit(1);
 
+    vs->timer = qemu_new_timer(rt_clock, vnc_update_client, vs);
+
     vs->ds->data = NULL;
     vs->ds->dpy_update = vnc_dpy_update;
     vs->ds->dpy_resize = vnc_dpy_resize;
-    vs->ds->dpy_refresh = vnc_dpy_refresh;
+    vs->ds->dpy_refresh = NULL;
 
     memset(vs->dirty_row, 0xFF, sizeof(vs->dirty_row));
 

