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 + +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 +#include "qemu-common.h" +#include "audio.h" +#include + +#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 @@ -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(¤t_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 #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 + 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 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 + * 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 ; - */ -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 #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 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));